Zmienianie tekstu w edytorze
Zmiany w dokumencie tekstowym otwieranym w edytorze programu Visual Studio mogą wynikać z interakcji użytkownika w programie Visual Studio lub zmiany programowe z usług językowych i innych rozszerzeń. Rozszerzenie musi być przygotowane do obsługi zmian w tekście dokumentu występujących w czasie rzeczywistym.
Rozszerzenia działające poza głównym procesem IDE programu Visual Studio używają asynchronicznych wzorców projektowych do komunikowania się z procesem IDE programu Visual Studio. Oznacza to użycie wywołań asynchronicznych metod, określonych za pomocą słowa kluczowego async
w języku C# i dodatkowo oznaczonych przez sufiks Async
w nazwach metod. Asynchroniność jest znaczącą zaletą w kontekście edytora, który ma odpowiadać na akcje użytkownika. Tradycyjne synchroniczne wywołanie interfejsu API, jeśli trwa dłużej niż oczekiwano, przestanie odpowiadać na dane wejściowe użytkownika, tworząc zawieszenie interfejsu użytkownika aż do zakończenia wywołania API. Oczekiwania użytkowników dotyczące nowoczesnych aplikacji interaktywnych są takie, że edytory tekstów zawsze pozostają responsywne i nigdy nie utrudniają pracy. Asynchroniczność rozszerzeń jest zatem niezbędna, by sprostać oczekiwaniom użytkowników.
Dowiedz się więcej na temat programowania asynchronicznego w programowanie asynchroniczne za pomocą async i await.
W nowym modelu rozszerzalności programu Visual Studio rozszerzenie jest drugą klasą względem użytkownika: nie może bezpośrednio modyfikować edytora ani dokumentu tekstowego. Wszystkie zmiany stanu są asynchroniczne i kooperatywne, a środowisko IDE programu Visual Studio wykonuje żądaną zmianę w imieniu rozszerzenia. Rozszerzenie może zażądać co najmniej jednej zmiany w określonej wersji dokumentu lub widoku tekstowego, ale zmiany z rozszerzenia mogą zostać odrzucone, na przykład jeśli ten obszar dokumentu uległ zmianie.
Prosi się o edycje przy użyciu metody EditAsync()
w EditorExtensibility
.
Jeśli znasz starsze rozszerzenia programu Visual Studio, ITextDocumentEditor
jest prawie taka sama jak metody zmiany stanu z ITextBuffer i ITextDocument i obsługuje większość tych samych funkcji.
MutationResult result = await this.Extensibility.Editor().EditAsync(
batch =>
{
var editor = document.AsEditable(batch);
editor.Replace(textView.Selection.Extent, newGuidString);
},
cancellationToken);
Aby uniknąć zagubionych edycji, zmiany z rozszerzeń edytora są stosowane w następujący sposób:
- Rozszerzenie prosi o edycję na podstawie najnowszej wersji dokumentu.
- To żądanie może zawierać jedną lub więcej edycję tekstu, zmiany położenia kursora itd. Każdy typ implementujący
IEditable
można zmienić w jednym żądaniuEditAsync()
, w tymITextViewSnapshot
iITextDocumentSnapshot
. Edycje są wykonywane przez edytor, który można zażądać dla określonej klasy za pośrednictwemAsEditable()
. - Żądania edycji są wysyłane do środowiska IDE programu Visual Studio, gdzie kończy się powodzeniem tylko wtedy, gdy obiekt będący w trakcie modyfikacji nie zmienił się od wersji, w której złożono żądanie. Jeśli dokument uległ zmianie, zmiana może zostać odrzucona, co wymaga ponownego ponawiania próby rozszerzenia w nowszej wersji. Wynik operacji mutacji jest przechowywany w
result
. - Zmiany są stosowane niepodziealnie, co oznacza bez przerw w wykonywaniu innych wątków. Najlepszym rozwiązaniem jest wprowadzanie wszystkich zmian, które powinny nastąpić w krótkim przedziale czasowym, w jednym wywołaniu
EditAsync()
, aby zmniejszyć prawdopodobieństwo wystąpienia nieoczekiwanego zachowania wynikającego z edycji użytkownika lub działań usług językowych, które mają miejsce między edycjami (na przykład gdy zmiany rozszerzeń są przeplatane z przenoszeniem kursora w Roslyn C#).
Zmienianie położenia kursora lub wybieranie tekstu w rozszerzeniu
Edytowanie dokumentu tekstowego z rozszerzenia niejawnie wpływa na pozycję kursora. Na przykład wstawienie tekstu przy kursorze spowoduje przeniesienie kursora na koniec wstawionego tekstu. Rozszerzenia mogą również użyć ITextViewSnapshot.AsEditable().SetSelections()
, aby jawnie ustawić kursor na inną pozycję lub dokonać zaznaczenia tekstu. Dla zobrazowania, poniższy kod wstawia jakiś tekst, ale zachowuje kursor na oryginalnej pozycji:
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);
Współbieżne wykonywanie
️ ⚠ | Rozszerzenia edytora mogą być czasami uruchamiane współbieżnie |
---|
Wersja początkowa ma znany problem, który może spowodować współbieżne wykonanie kodu rozszerzenia edytora. Każda metoda asynchronizowana ma być wywoływana w odpowiedniej kolejności, ale kontynuacje po pierwszym await
mogą być przeplatane. Jeśli rozszerzenie opiera się na kolejności wykonywania, rozważ utrzymanie kolejki żądań przychodzących w celu zachowania kolejności, dopóki ten problem nie zostanie rozwiązany.
Aby uzyskać więcej informacji, zobacz „StreamJsonRpc Default Ordering and Concurrency”.