Поделиться через


Изменение текста в редакторе

Изменения — это изменения текстового документа, открытого в редакторе Visual Studio. Изменения могут возникать из взаимодействия пользователей в Visual Studio или программных изменениях языковых служб и других расширений. Расширение должно быть готово к изменениям в тексте документа, происходящим в режиме реального времени.

Расширения, выполняемые за пределами основного процесса интегрированной среды разработки Visual Studio , используют асинхронные шаблоны проектирования для взаимодействия с процессом интегрированной среды разработки Visual Studio. Это поведение означает использование асинхронных вызовов методов, как указано ключевым словом async в C# и подтверждённым суффиксом Async в именах методов. Асинхронность является значительным преимуществом для редактора, который должен быстро реагировать на действия пользователей.

Если традиционный синхронный вызов API занимает больше времени, чем ожидалось, он перестает отвечать на входные данные пользователя. Эта ситуация создает замораживание пользовательского интерфейса, которое длится до завершения вызова API. Пользователи современных интерактивных приложений ожидают, что текстовые редакторы всегда остаются адаптивными и никогда не блокируют работу. Для удовлетворения этих ожиданий пользователей важно иметь асинхронные расширения.

Узнайте больше об асинхронном программировании в разделе Асинхронное программирование с использованием async и await.

В новой модели расширяемости Visual Studio расширение является вторым классом относительно пользователя. Он не может напрямую изменять редактор или текстовый документ. Все изменения состояния происходят асинхронно и кооперативно. Интегрированная среда разработки Visual Studio выполняет запрошенные изменения для расширения. Расширение может запросить одно или несколько изменений в определенной версии документа или текстового представления. Изменения из расширения могут быть отклонены, например, если эта область документа изменилась.

Изменения запрашиваются методом EditAsync() на EditorExtensibility.

Если вы знакомы с устаревшими расширениями Visual Studio, ITextDocumentEditor почти то же самое, что методы изменения состояния из ITextBuffer и ITextDocument и поддерживает большинство тех же возможностей.

MutationResult result = await this.Extensibility.Editor().EditAsync(
batch =>
{
    var editor = document.AsEditable(batch);
    editor.Replace(textView.Selection.Extent, newGuidString);
},
cancellationToken);

Чтобы избежать неуместных изменений, изменения из расширений редактора применяются следующим образом:

  1. Расширение требует внесения изменений в соответствии с последней версией документа.
  2. Этот запрос может содержать один или несколько текстовых правок, изменения положения курсора и т. д. Вы можете изменить любой тип, реализующий IEditable в одном EditAsync() запросе, включая ITextViewSnapshot и ITextDocumentSnapshot. Редактор выполняет изменения. Вы можете запросить изменения для определенного класса с помощью AsEditable().
  3. Запросы на изменение отправляются в интегрированную среду разработки Visual Studio. Они успешно выполняются только в том случае, если мутируемый объект не изменился с тех пор, как была создана версия, на которой был выполнен запрос. Если документ изменился, это изменение может быть отклонено. Затем расширение должно повторить попытку в более новой версии. Результат операции мутации хранится в result.
  4. Изменения применяются атомарно, что означает отсутствие прерываний от других выполняемых потоков. Рекомендуется вносить все изменения, которые необходимо произвести в пределах узкого интервала времени, в одном EditAsync() вызове. Эта практика снижает вероятность непредвиденного поведения, возникающего из-за изменений пользователей или действий службы языка, происходящих между изменениями. (Примером могут служить изменения расширений, которые накладываются друг на друга из-за того, что Roslyn C# переместил курсор.)

Измените положение курсора или выделите текст в расширении

Редактирование текстового документа с помощью расширения неявно влияет на положение курсора. Например, вставка текста в курсор перемещает курсор в конец вставленного текста. Расширения также могут использовать ITextViewSnapshot.AsEditable().SetSelections() для явного задания курсора на другую позицию или выделения текста. Чтобы проиллюстрировать, следующий код вставляет некоторый текст, но сохраняет курсор на исходной позиции.

await this.Extensibility.Editor().EditAsync(batch =>
{
    var caret = textView.Selection.Extent.Start;
    textView.Document.AsEditable(batch).Replace(textView.Selection.Extent, newGuidString);
    textView.AsEditable(batch).SetSelections([new Selection(activePosition: caret, anchorPosition: caret, insertionPosition: caret)]);
},
cancellationToken);

Параллельное выполнение

️ ⚠ Расширения редактора иногда могут выполняться одновременно.

Первоначальный выпуск имеет известную проблему, которая может привести к параллельному выполнению кода расширения редактора. Каждый асинхронный метод вызывается в правильном порядке, но продолжение после первого await может быть переплетается. Если расширение зависит от порядка выполнения, рекомендуется сохранить очередь входящих запросов, чтобы сохранить порядок до устранения этой проблемы.

Дополнительные сведения см. в статье Упорядочивание и параллелизм по умолчанию в StreamJsonRpc.