替换文档中的文本,同时保留插入符号

Jea*_*ner 5 c# document mef visual-studio-extensions

我正在开发一个扩展,它使用外部程序来格式化 Visual Studio 内的代码。

如果我使用ITextEdit.Replace(...)替换文件的内容,插入符号将放在文档的末尾,这是错误的。

我想做的是在替换文件的内容之前保存文本缓冲区中当前插入符号位置的快照,然后在替换内容之前将插入符号位置设置为文本缓冲区中之前的位置。

但是,ITextEdit.Apply()正在生成新快照,导致_textView.Caret.MoveTo(point)引发异常:

System.ArgumentException: The supplied SnapshotPoint is on an incorrect snapshot.
Parameter name: bufferPosition
at Microsoft.VisualStudio.Text.Editor.Implementation.CaretElement.InternalMoveTo(VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, Boolean captureHorizontalPosition, Boolean captureVerticalPosition, Boolean raiseEvent)
at Microsoft.VisualStudio.Text.Editor.Implementation.CaretElement.MoveTo(SnapshotPoint bufferPosition)
Run Code Online (Sandbox Code Playgroud)

我还尝试创建一个新的快照点而不是使用 _textView.Caret.Position.BufferPosition,如下所示:

var point = new SnapshotPoint(_textView.TextSnapshot, 0);
Run Code Online (Sandbox Code Playgroud)

抛出相同的“提供的 SnapshotPoint 位于不正确的快照上。” 例外。

public class MyCommand
{
    private readonly IWpfTextView _textView;
    private readonly MyFormatter _formatter;
    private readonly ITextDocument _document;

    public MyCommand(IWpfTextView textView, MyFormatter formatter, ITextDocument document)
    {
        _textView = textView;
        _formatter = formatter;
        _document = document;
    }

    public void Format()
    {
        var input = _document.TextBuffer.CurrentSnapshot.GetText();
        var output = _formatter.format(input);

        // get caret snapshot point
        var point = _textView.Caret.Position.BufferPosition;

        using (var edit = _document.TextBuffer.CreateEdit())
        {
            edit.Replace(0, _document.TextBuffer.CurrentSnapshot.Length, output);
            edit.Apply();
        }

        // set caret position
        _textView.Caret.MoveTo(point);
    }
}
Run Code Online (Sandbox Code Playgroud)

我不想实现一些自定义插入符“历史”,我想按照它本来的方式来做。另外,我希望移动插入符被视为编辑的一部分,保持“ctrl+z”功能完整。

一如既往,我们非常感谢任何帮助!

Jea*_*ner 1

尽管 Cole Wu - MSFT 对我的问题的回答帮助我解决了我的问题,但它并没有完全达到我想要的效果:

使用提供的代码可以移动插入符号,但是由于我替换了文档的全部内容,因此即使插入符号位于文档的更下方,它也会将滚动位置重置到顶部。

另外:这仍然生成“多个撤消”而不是一次:即替换文档内容和移动插入符是两个单独的“撤消”,这需要用户按CTRL + Z 两次而不是一次来撤消代替。

为了解决这个问题,我将代码更改为以下内容:

public class MyCommand
{
    private readonly IWpfTextView _textView;
    private readonly MyFormatter _formatter;
    private readonly ITextDocument _document;
    private readonly ITextBufferUndoManager _undoManager;

    public MyCommand(IWpfTextView textView, MyFormatter formatter, ITextDocument document, ITextBufferUndoManager undoManager)
    {
        _textView = textView;
        _formatter = formatter;
        _document = document;
        _undoManager = undoManager;
    }

    public void Format()
    {
        var input = _textView.TextSnapshot.GetText();
        var output = _formatter.format(input);

        using (var undo = _undoManager.TextBufferUndoHistory.CreateTransaction("Format"))
        using (var edit = _undoManager.TextBuffer.CreateEdit(EditOptions.DefaultMinimalChange, 0, null))
        {
            edit.Replace(0, _textView.TextSnapshot.Length, output);
            edit.Apply();

            undo.Complete();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这并不完全按照我想要的方式工作,因为插入符号有时(当位于尾随空白时)跳到下一行的开头而不是当前行的末尾。

然而,它已经足够接近了!