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

时间:2017-03-10 06:56:23

标签: c# mef document visual-studio-extensions

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

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

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

然而, ITextEdit.Apply()正在生成一个新的快照,导致 _textView.Caret.MoveTo(指向)引发异常:

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)

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

var point = new SnapshotPoint(_textView.TextSnapshot, 0);

抛出相同的“提供的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);
    }
}

我不想实现一些自定义插入符号“历史记录”,我希望按照它的意图来实现。 此外,我希望将移动的插入符号视为编辑的一部分,保持“ctrl + z”功能不变。

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

2 个答案:

答案 0 :(得分:1)

您可以检索点的位置,然后创建一个新的SnapshotPoint并移动到它。

像这样:

var point = _textView.Caret.Position.BufferPosition;
int position = point.Position;

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

        // set caret position
        _textView.Caret.MoveTo(new SnapshotPoint(_textView.TextSnapshot, position));

此外,您可以创建和使用这样的扩展程序: https://github.com/jaredpar/EditorUtils/blob/master/Src/EditorUtils/Extensions.cs

答案 1 :(得分:0)

尽管Cole Wu - MSFT对我的问题的回答帮助我解决了我的问题,但它并没有完全符合我的要求:

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

另外:这仍然生成“multiple undos”而不只是一个:即替换文档的内容并移动插入符是两个单独的“undos”,其中要求用户点击 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();
        }
    }
}

这不能正常按照我想要的方式,因为插入符号有时(当位于尾随空格时)跳转到下一行的开头而不是当前行的结尾。

然而,它足够接近!