Изменение текста в редакторе
Изменения в текстовом документе, открытом в редакторе 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);
Чтобы избежать неуместных изменений, изменения из расширений редактора применяются следующим образом:
- Расширение запрашивает изменение, основанное на последней версии документа.
- Этот запрос может содержать один или несколько текстовых правок, изменения положения курсора и т. д. Любой тип, реализующий
IEditable
, можно изменить в одном запросеEditAsync()
, включаяITextViewSnapshot
иITextDocumentSnapshot
. Изменения выполняются редактором, который можно запросить для определенного класса с помощьюAsEditable()
. - Запросы на редактирование отправляются в интегрированную среду разработки Visual Studio, где они завершаются успешно только в том случае, если изменяемый объект не изменился с того момента, как была выполнена версия запроса. Если документ изменился, изменение может быть отклонено, требуя повтора расширения в более новой версии. Результат операции мутации хранится в
result
. - Изменения применяются атомарно, что означает без прерывания выполнения других потоков. Рекомендуется вносить все изменения, которые должны произойти в пределах узкого временного интервала, в одном вызове
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 Default Ordering and Concurrency.