Dostawca serwera języka rozszerzalności
Dostawca serwera językowego obejmuje proces, który jest hostowany poza programem Visual Studio i udostępnia funkcje językowe, które nie są obecne w programie Visual Studio.
Te serwery muszą być zgodne z protokołem Language Server Protocol, utworzonym przez projekt rozszerzenia i zaimplementować .LanguageServerProvider
Praca z dostawcami serwerów językowych
To omówienie obejmuje następujące najważniejsze scenariusze pracy z dostawcami serwerów językowych:
- Tworzenie dostawcy serwera językowego
- Wysyłanie dodatkowych danych podczas uruchamiania serwera językowego
- Definiowanie niestandardowych typów dokumentów
- Włączanie lub wyłączanie serwera językowego
- Korzystanie z zlokalizowanych zasobów
Tworzenie dostawcy serwera językowego
Tworzenie dostawcy serwera językowego obejmuje dodanie nowej klasy, która rozszerza Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider
i stosuje VisualStudioContribution
do niego atrybut.
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
}
}
Po zdefiniowaniu dostawcy należy wykonać następujące kroki:
Skonfiguruj dostawcę
LanguageServerProviderConfiguration
, przesłaniając właściwość . Ta właściwość konfiguracji definiuje nazwę wyświetlaną serwera i odpowiednie typy dokumentów.LanguageServerBaseDocumentType
jest dostępny dla wszystkich serwerów i wyzwalaczy we wszystkich typach dokumentów. Zobacz Definiowanie niestandardowego typu dokumentu.public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server", new[] { DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType), });
Zastąpi metodę
CreateServerConnectionAsync
, która jest wywoływana przez program Visual Studio, aby powiadomić rozszerzenie o konieczności uruchomienia serwera LSP.// 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())); }
Zastąpi metodę
OnServerInitializationResultAsync
, która jest wywoływana przez program Visual Studio po ukończeniu kroków uruchamiania i konfiguracji serwera LSP.ServerInitializationResult
udostępnia wynikowy stan serwera iLanguageServerInitializationFailureInfo
udostępnia wyjątek, jeśli istnieje.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; }
Oto, jak wygląda nasz przykładowy dostawca serwera językowego po wykonaniu wszystkich kroków:
[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;
}
}
Wysyłanie dodatkowych danych podczas uruchamiania serwera językowego
LanguageServerOptions.InitializationOptions
Można ustawić w konstruktorze LanguageServerProvider
, aby wysyłać dodatkowe dane do serwera za pomocą komunikatu protokołu "inicjowanie".
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
this.LanguageServerOptions.InitializationOptions = JToken.Parse(@"[{""server"":""initialize""}]");
}
Definiowanie niestandardowych typów dokumentów
Jeśli rozszerzenie obsługuje typy plików, które nie są natywnie obsługiwane przez program Visual Studio, autorzy rozszerzeń mogą implementować niestandardowe typy dokumentów. Tego typu można użyć podczas definiowania LanguageServerProviderConfiguration
w celu określenia obsługiwanych typów dokumentów.
[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,
};
Ten fragment kodu definiuje dwa nowe typy dokumentów: rust
i markdown
. Te typy zawierają listę rozszerzeń plików i typ podstawowy, który może obejmować LanguageServerBaseDocumentType
wszystkie typy.
Użyj tych typów w programie LanguageServerProviderConfiguration
, aby aktywować serwer po otwarciu tych typów dokumentów:
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("My Language Server",
new[]
{
DocumentFilter.FromDocumentType(RustDocumentType),
DocumentFilter.FromDocumentType(MarkdownDocumentType),
});
Włączanie lub wyłączanie serwera językowego
Włączony serwer językowy może "aktywować" po otwarciu odpowiedniego typu dokumentu. Po wyłączeniu komunikat zatrzymania jest wysyłany do dowolnego odpowiedniego aktywnego serwera językowego i zapobiega dalszej aktywacji.
[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;
}
}
}
Ten fragment kodu wyłącza serwer językowy, ustawiając wartość this.Enabled
false
, jeśli ServerInitializationResult
zostanie ustawiona na Failed
wartość po niepowodzeniu inicjowania.
Uwaga
Ta flaga jest publiczna i jeśli ustawiono wartość false, wszystkie uruchomione serwery zostaną zatrzymane.
Korzystanie z zlokalizowanych zasobów
Obsługujemy używanie lokalizacji, definiując string-resources.json
plik i używając %tokens%
do określania zlokalizowanej zawartości.
string-resources.json
{
{ "LocalizedResource": "LangaugeServerLocalized" }
}
Uzyskiwanie dostępu do zlokalizowanego zasobu
[VisualStudioContribution]
public class MyLanguageServer : LanguageServerProvider
{
...
/// <inheritdoc/>
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("%LocalizedResource%",
new[]
{
DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType)
});
}
Następne kroki
- Postępuj zgodnie z samouczkiem dotyczącym tworzenia pierwszego rozszerzenia , aby rozpocząć tworzenie rozszerzenia.
- Zobacz przykład dostawcy serwera języka Rust, aby zapoznać się z pełnym przykładem tworzenia rozszerzenia za pomocą dostawcy serwera językowego.