Extensibility 言語サーバー プロバイダー

言語サーバー プロバイダーには、Visual Studio の外部でホストされ、Visual Studio に存在しない言語機能を提供するプロセスが関係します。

これらのサーバーは、 拡張プロジェクト によって作成された言語サーバー プロトコルに準拠して LanguageServerProvider を実装する必要があります。

言語サーバー プロバイダーの操作

この概要では、言語サーバー プロバイダーを操作するための次の一般的なシナリオについて説明します。

言語サーバー プロバイダーの作成

言語サーバー プロバイダーを作成するには、Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider を拡張する新しいクラスを追加し、それに VisualStudioContribution 属性を適用することが関係します。

[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
    public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
        : base(container, extensibilityObject)
    {
    }
}

プロバイダーの定義後、次の手順を実行する必要があります。

  1. LanguageServerProviderConfiguration プロパティをオーバーライドして、プロバイダーを構成します。 この構成プロパティは、サーバーの表示名と適用可能なドキュメント タイプを定義します。 LanguageServerBaseDocumentType は、すべてのサーバーで利用でき、すべてのドキュメント タイプでトリガーされます。 「カスタム ドキュメント タイプの定義」を参照してください。

    public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server",
        new[]
        {
           DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType),
        });
    
  2. CreateServerConnectionAsync メソッドをオーバーライドします。このメソッドは、LSP サーバーを起動する必要があることを拡張機能に通知するために、Visual Studio によって呼び出されます。

    // Activate the language server and return a duplex pipe that communicates with the server. 
    public override Task<IDuplexPipe?> CreateServerConnectionAsync(CancellationToken cancellationToken)
    {
        (Stream PipeToServer, Stream PipeToVS) = FullDuplexStream.CreatePair();
    
        // Connect "PipeToServer" to the language server
    
        return Task.FromResult<IDuplexPipe?>(new DuplexPipe(PipeToVS.UsePipeReader(), PipeToVS.UsePipeWriter()));
    }
    
  3. OnServerInitializationResultAsync メソッドをオーバーライドします。このメソッドは、LSP サーバーが起動と構成の手順を完了した後に、Visual Studio によって呼び出されます。 ServerInitializationResult はサーバーの結果の状態を提供し、LanguageServerInitializationFailureInfo は例外を提供します (存在する場合)。

    public override Task OnServerInitializationResultAsync(ServerInitializationResult startState,LanguageServerInitializationFailureInfo?     initializationFailureInfo, CancellationToken cancellationToken)
    {
        // Method called when server activation was completed successfully or failed, denoted by "startState".
        return Task.CompletedTask;
    }
    

すべての手順を完了した後のサンプル言語サーバー プロバイダーはどうなっているのかを次に示します。

[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
    public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
        : base(container, extensibilityObject)
    {
    }

    public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
        new("My Language Server",
            new[]
            {
               DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType),
            });

    // Activate the language server and return a duplex pipe that communicates with the server. 
    public override Task<IDuplexPipe?> CreateServerConnectionAsync(CancellationToken cancellationToken)
    {
        (Stream PipeToServer, Stream PipeToVS) = FullDuplexStream.CreatePair();

        // Connect "PipeToServer" to the language server

        return Task.FromResult<IDuplexPipe?>(new DuplexPipe(PipeToVS.UsePipeReader(), PipeToVS.UsePipeWriter()));
    }

    public override Task OnServerInitializationResultAsync(ServerInitializationResult startState, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken)
    {
        // Method called when server activation was completed successfully or failed, denoted by "startState".
        return Task.CompletedTask;
    }
}

言語サーバー起動時の追加データの送信

LanguageServerOptions.InitializationOptions は、LanguageServerProvider が "initialize" プロトコル メッセージを添えてサーバーに追加データを送信するためのコンストラクターに設定できます。

public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
    : base(container, extensibilityObject)
{
    this.LanguageServerOptions.InitializationOptions = JToken.Parse(@"[{""server"":""initialize""}]");
}

カスタム ドキュメント タイプの定義

拡張機能が、Visual Studio でネイティブにはサポートされていないファイルの種類をサポートしている場合、拡張機能の作成者はカスタム ドキュメント タイプを実装できます。 これらのタイプは、サポートされているドキュメント タイプを指定するために LanguageServerProviderConfiguration を定義するときに使用できます。

[VisualStudioContribution]
internal static DocumentTypeConfiguration RustDocumentType => new("rust")
{
    FileExtensions = new[] { ".rs", ".rust" },
    BaseDocumentType = LanguageServerBaseDocumentType,
};

[VisualStudioContribution]
internal static DocumentTypeConfiguration MarkdownDocumentType => new("markdown")
{
    FileExtensions = new[] { ".md" },
    BaseDocumentType = LanguageServerBaseDocumentType,
};

このスニペットは、2 つの新しいドキュメント タイプ rustmarkdown を定義します。 これらのタイプには、ファイル拡張子と基本型の一覧が含まれています。これを LanguageServerBaseDocumentType にして、すべての型を対象にすることができます。

これらのタイプを LanguageServerProviderConfiguration で使用して、これらのドキュメント タイプを開いたときにサーバーをアクティブ化します。

public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
    new("My Language Server",
        new[]
        {
            DocumentFilter.FromDocumentType(RustDocumentType),
            DocumentFilter.FromDocumentType(MarkdownDocumentType),
        });

言語サーバーの有効化/無効化

有効化された言語サーバーは、該当するドキュメント タイプを開くと「アクティブ化」できます。 無効にすると、停止メッセージが該当するアクティブな言語サーバーに送信され、それ以上アクティブ化されなくなります。

[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
    ...

    public override Task OnServerInitializationResultAsync(ServerInitializationResult startState, LanguageServerInitializationException? initializationFailureInfo, CancellationToken cancellationToken)
    {
        if (startState == ServerInitializationResult.Failed)
        {
            Telemetry.LogEvent(initializationFailureInfo.StatusMessage, initializationFailureInfo.Exception)

            // Disable the language server.
            this.Enabled = false;
        }
    }
}

このコード スニペットは、初期化に失敗したら ServerInitializationResultFailed に設定されるように this.Enabledfalse に設定して、言語サーバーを無効にします。

Note

このフラグはパブリックであり、false に設定すると、実行中のすべてのサーバーが停止します。

ローカライズされたリソースの使用

string-resources.json ファイルを定義し、%tokens% を使用してローカライズされたコンテンツを指定して、ローカライズの使用をサポートします。

string-resources.json

{
  { "LocalizedResource": "LangaugeServerLocalized" }
}

ローカライズされたリソースへのアクセス

[VisualStudioContribution]
public class MyLanguageServer : LanguageServerProvider
{
    ...

    /// <inheritdoc/>
    public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
        new("%LocalizedResource%",
            new[]
            {
                DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType)
            });
}

次のステップ