次の方法で共有


エディターでテキストを変更する

編集、つまり、Visual Studio エディターで開いているテキスト ドキュメントに対する変更は、Visual Studio でのユーザー操作や言語サービスやその他の拡張機能からのプログラムによる変更によって発生する可能性があります。 リアルタイムで発生するドキュメント テキストの変更に対処するには、拡張機能を準備する必要があります。

メインの Visual Studio IDE プロセスの外部で実行されている拡張機能は、Visual Studio IDE プロセスと通信するために非同期設計パターンを使用します。 これは、C# の async キーワードで示され、メソッド名の Async サフィックスによって強化された非同期メソッド呼び出しの使用を意味します。 非同期性は、ユーザーアクションに対する応答性が期待されるエディターのコンテキストにおいて大きな利点です。 従来の同期 API 呼び出しでは、予想以上に時間がかかる場合、ユーザー入力への応答が停止し、API 呼び出しが完了するまで続く UI フリーズが作成されます。 最新の対話型アプリケーションに対するユーザーの期待は、テキスト エディターは常に応答性を維持し、動作を妨げないということです。 そのため、拡張機能を非同期にすることは、ユーザーの期待に応えるために不可欠です。

非同期プログラミングについてさらに知りたい方は、「async および await を使用した非同期プログラミング 」をご覧ください。

新しい Visual Studio 拡張機能モデルでは、拡張機能はユーザーに対する 2 番目のクラスです。エディターやテキスト ドキュメントを直接変更することはできません。 すべての状態変更は非同期で協調的であり、Visual Studio IDE は拡張機能に代わって要求された変更を実行します。 拡張機能は、ドキュメントまたはテキスト ビューの特定のバージョンに対して 1 つ以上の変更を要求できますが、ドキュメントのその領域が変更された場合など、拡張機能からの変更が拒否される可能性があります。

編集は、EditorExtensibilityEditAsync() メソッドを使用して要求されます。

従来の 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. その要求には、1 つ以上のテキスト編集、キャレット位置の変更などが含まれる場合があります。 IEditable を実装する任意の型は、ITextViewSnapshotITextDocumentSnapshotなど、1 つの EditAsync() 要求で変更できます。 編集はエディターによって行われます。これは、AsEditable()を介して特定のクラスで要求できます。
  3. 編集要求は Visual Studio IDE に送信され、その要求がなされたバージョン以降、変更対象のオブジェクトに変更が加えられていない場合にのみ成功します。 ドキュメントが変更された場合、変更が拒否される可能性があり、拡張機能は新しいバージョンで再試行する必要があります。 変異操作の結果は、resultに格納されます。
  4. 編集はアトミックに適用されます。つまり、他の実行中のスレッドからの中断はありません。 ベスト プラクティスは、狭い時間枠内で発生する必要があるすべての変更を 1 つの 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 の既定の順序付けとコンカレンシーの を参照してください。