Provider del server del linguaggio di estendibilità

Un provider del server di linguaggio prevede un processo ospitato all'esterno di Visual Studio e che fornisce funzionalità del linguaggio non presenti in Visual Studio.

Questi server devono rispettare il protocollo del server di linguaggio, creato da un progetto di estensione e implementare LanguageServerProvider.

Usare i provider del server di linguaggio

Questa panoramica illustra questi scenari principali per l'uso dei provider di server di linguaggio:

Creare un provider di server di linguaggio

La creazione di un provider di server di linguaggio comporta l'aggiunta di una nuova classe che estende Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider e applica l'attributo VisualStudioContribution .

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

Dopo aver definito il provider, è necessario:

  1. Configurare il provider eseguendo l'override della LanguageServerProviderConfiguration proprietà . Questa proprietà di configurazione definisce il nome visualizzato del server e i tipi di documento applicabili. LanguageServerBaseDocumentType è disponibile per tutti i server e i trigger in tutti i tipi di documento. Vedere Definire un tipo di documento personalizzato.

    public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server",
        new[]
        {
           DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType),
        });
    
  2. Eseguire l'override del CreateServerConnectionAsync metodo , chiamato da Visual Studio per notificare all'estensione che il server LSP deve essere avviato.

    // 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. Eseguire l'override del OnServerInitializationResultAsync metodo , chiamato da Visual Studio dopo che il server LSP ha completato i passaggi di avvio e configurazione. ServerInitializationResult fornisce lo stato risultante del server e LanguageServerInitializationFailureInfo fornisce un'eccezione, se presente.

    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;
    }
    

Di seguito è riportato l'aspetto del provider di server di linguaggio di esempio dopo aver completato tutti i passaggi:

[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;
    }
}

Inviare dati aggiuntivi all'avvio di un server linguistico

LanguageServerOptions.InitializationOptions può essere impostato nel costruttore per LanguageServerProvider inviare dati aggiuntivi al server con il messaggio di protocollo "inizializza".

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

Definire tipi di documento personalizzati

Quando un'estensione supporta i tipi di file non supportati in modo nativo da Visual Studio, gli autori di estensioni possono implementare tipi di documento personalizzati. Questi tipi possono essere usati quando si definiscono LanguageServerProviderConfiguration per specificare i tipi di documento supportati.

[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,
};

Questo frammento di codice definisce due nuovi tipi di documento: rust e markdown. Questi tipi contengono un elenco di estensioni di file e un tipo di base, che può essere LanguageServerBaseDocumentType per coprire tutti i tipi.

Usare questi tipi in LanguageServerProviderConfiguration per attivare il server quando questi tipi di documento vengono aperti:

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

Abilitare o disabilitare un server di linguaggio

Un server di lingua abilitato può "attivare" una volta aperto un tipo di documento applicabile. Se disabilitato, un messaggio di arresto viene inviato a qualsiasi server di lingua attivo applicabile e impedisce ulteriori attivazioni.

[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;
        }
    }
}

Questo frammento di codice disabilita il server di linguaggio impostando this.Enabled su false se ServerInitializationResult viene impostato su Failed dopo che non è stato possibile inizializzare.

Nota

Questo flag è pubblico e, se impostato su false, tutti i server in esecuzione vengono arrestati.

Usare le risorse localizzate

È supportato l'uso della localizzazione definendo un string-resources.json file e usando %tokens% per specificare il contenuto localizzato.

string-resources.json

{
  { "LocalizedResource": "LangaugeServerLocalized" }
}

Accedere a una risorsa localizzata

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

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

Passaggi successivi

  • Seguire l'esercitazione creare la prima estensione per iniziare a creare un'estensione.
  • Per un esempio completo di creazione di un'estensione con un provider di server di linguaggio, vedere l'esempio del provider del server di linguaggio Rust.