VisualStudio.Extensibility 拡張機能のコンポーネント

VisualStudio.Extensibility を使用する拡張機能には通常、相互にも Visual Studio とも連携する複数のコンポーネントがあります。

拡張機能のインスタンス

拡張機能には、Extension から派生したクラスが必要です。 実装例については、「MarkdownLinter」を参照してください。

Extension クラスのインスタンスは、拡張機能が実行における起点となります。 このインスタンスには、Visual Studio が拡張機能の提供するサービスを照会するために必要なメソッドが含まれています。 また、拡張機能がローカライズされたリソースを提供したり、拡張機能の各コンポーネント間で共有される拡張機能独自のローカル サービスを提供したりするための仮想メソッドも備わっています。

Extension クラスの構成には、拡張機能のメタデータも含まれています。これらのメタデータは、Visual Studio の [拡張機能の管理] ウィンドウに表示され、公開されている拡張機能の場合は Visual Studio Marketplace にも表示されます。

[VisualStudioContribution]
public class MarkdownLinterExtension : Extension
{
    /// <inheritdoc/>
    public override ExtensionConfiguration ExtensionConfiguration => new()
    {
        Metadata = new(
                id: "MarkdownLinter.0cf26ba2-edd5-4419-8646-a55d0a83f7d8",
                version: this.ExtensionAssemblyVersion,
                publisherName: "Microsoft",
                displayName: "Markdown Linter Sample Extension",
                description: "Sample markdown linter extension"),
    };
    ...

既存の VSSDK API に精通している拡張機能開発者向けに説明すれば、ExtensionConfiguration に含まれる Metadata.vsixmanifest ファイルの生成に使用されます。 また、Extension クラスは、VSSDK 拡張モデルで使用される AsyncPackage クラスに似ています。

VisualStudioExtensibility オブジェクト

VisualStudioExtensibility オブジェクトは、Visual Studio によって公開されている拡張機能にとっての窓口となります。 このクラスには、拡張機能 SDK で使用可能な機能をすばやく列挙するためのさまざまな拡張メソッドやプロパティがあります。 使用可能なメソッドについては、API のドキュメントを参照してください。

拡張機能のコンポーネント

拡張機能がコマンド、エディター リスナーなどのコンポーネントを Visual Studio に提供する場合は、属性付きクラスを利用します。 ビルド プロセスは、これらのコンポーネントが Visual Studio によって検出できるように、適切なメタデータを生成します。

拡張機能がコマンド、エディター リスナー、ツール ウィンドウなどのコンポーネントを Visual Studio に提供する場合は、VisualStudioContribution 属性でマークされたクラスを利用します。 ビルド プロセスは、これらのコンポーネントが Visual Studio によって検出できるように、適切なメタデータを生成します。

現在、SDK で提供可能なコンポーネントは限定されています。

これらのクラスのインスタンスは、SDK が提供する拡張フレーム ワークの一部として、依存関係注入ライブラリを使用して作成されます。コンストラクターを使用して、SDK または拡張機能自体が提供するサービスのインスタンスを取得し、コンポーネント間で状態を共有できます。

拡張機能のコンポーネントの有効期間

各コンポーネントの有効期間は、Visual Studio IDE プロセス内でそれらのコンポーネントを読み込む、それぞれのコンポーネントによって管理されます。

  • コマンド ハンドラーは、対応するコマンド セットがアクティブ化されたとき (通常はそのコマンドが最初に実行されるとき) に初期化されます。 一度アクティブ化されると、コマンド ハンドラーは IDE がシャットダウンされるまで解放されません。

  • 同様に、テキスト ビュー イベント リスナーは、指定されたコンテンツ タイプに一致する最初のテキスト ビューが IDE に読み込まれたときに初期化されます。 現在、このようなリスナーは IDE がシャットダウンされるまでアクティブですが、この動作は将来変更される可能性があります。

一般的に、複雑な拡張機能の場合、拡張機能がローカル サービスを提供し、拡張機能のコンポーネントのコンストラクターでそれらのサービスをインポートして、コンポーネント間や同じコンポーネントのインスタンス間で状態を共有することをお勧めします。 この方法を採用することで、拡張機能の状態がコンポーネントの有効期間の変更によって影響を受けないようにすることができます。

注入用に SDK によって提供されるサービス

次のサービスは SDK によって提供され、任意の拡張機能のコンポーネントのコンストラクターで使用できます。

  • VisualStudioExtensibility: すべての拡張機能のコンポーネントには、Visual Studio IDE と連携するために、VisualStudioExtensibility のインスタンスを注入できます。

  • Extension: 拡張機能のコンポーネントには、Microsoft.VisualStudio.Extensibility.Extension 型やそれを継承した独自の型を注入できます。

  • TraceSource: 各拡張機能には、診断情報を記録するために、必要に応じて TraceSource インスタンスが作成されます。 これらのインスタンスは Visual Studio の診断プロバイダーに登録されます。これにより、複数のサービスからのログをマージしたり、将来提供されるツールを使用してリアルタイム ログにアクセスしたりできます。 詳細については、「ログ記録」を参照してください。

  • ローカル サービス: 拡張機能自体が提供するローカル サービスも依存関係の注入に使用できます。

  • MefInjection<TService> および AsyncServiceProviderInjection<TService, TInterface>: インプロセス拡張機能では、従来は MEF または AsyncServiceProvider を介して利用されていた Visual Studio SDK のサービスを注入できます。

ローカル拡張機能サービス

特定のシナリオでは、MarkdownLinter の例で見られるように、拡張機能がコマンド ハンドラーとテキスト ビュー変更リスナーなど、異なるコンポーネント間で状態を共有することが必要になる場合があります。 これらのサービスは、Extension.InitializeServices メソッドをオーバーライドすることで、インプロセス サービス コレクションに追加でき、拡張機能のコンポーネントのインスタンスが作成されるときに、コンストラクターの引数に基づいて注入されます。

サービスを追加するには、次の 3 つのオプションがあります。

  • AddTransient: サービスの新しいインスタンスが、それを取り込む各コンポーネントごとに作成されます。
  • AddScoped: サービスの新しいインスタンスが特定のスコープ内で作成されます。 Visual Studio の拡張機能のコンテキストでは、スコープは 1 つの拡張機能のコンポーネントを指します。
  • AddSingleton: サービスの 1 つの共有インスタンスが最初の取り込み時に作成されます。

VisualStudioExtensibility オブジェクトの有効期間は 1 つの拡張機能のコンポーネントのスコープに紐付けられているため、そのオブジェクトを注入するローカル サービスはスコープ付きか一時的なサービスであることが必要です。 VisualStudioExtensibility を注入するシングルトン サービスを作成しようとすると、エラーが発生します。

ローカル サービスの使用例については、「MarkdownLinter 拡張機能」を参照してください。

クライアント コンテキスト

新しい SDK ではすべての拡張機能はプロセス外で実行されるため、イベントやメソッドが呼び出された時点での IDE の状態を表すために、さまざまな拡張機能のコンポーネントに対してクライアント コンテキストという概念を導入しています。 このコンテキストは、SDK の IClientContext インスタンスによって表され、コマンド実行ハンドラーなどのさまざまな操作に渡されます。 SDK には、IClientContext の拡張メソッドが用意されており、これらのメソッドを使用してコンテキストからオブジェクトを取得できます。 たとえば、拡張機能は、IClientContext インスタンスを利用して、コマンド実行時にアクティブなテキスト ビューや選択したアイテムの URI を取得できます。

また、コマンドなどの一部のコンポーネントでは、どのコンテキストを使用するかを宣言することもできます。 これを行うのは、クライアント コンテキストが将来大きくなる可能性があるので、リモート実行ごとに転送されるデータ量を最適化するためです。 初期プレビューでは、利用可能なコンテキストは ShellEditor の 2 つのみであり、CommandAttribute を使用してコマンドを宣言すると、これらの両方が既定で含まれます。