Extensibility Language Server Provider
A Language Server Provider involves a process that is hosted outside of Visual Studio, and that provides language features not present in Visual Studio.
These servers must adhere to the Language Server Protocol, authored by an extension project, and implement LanguageServerProvider
.
Work with language server Providers
This overview covers these top scenarios for working with language server providers:
- Create a language server provider
- Send additional data when starting a language server
- Define custom document types
- Enable or disable a language server
- Use localized resources
Create a language server provider
Creating a language server provider involves adding a new class that extends Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider
and applying the VisualStudioContribution
attribute to it.
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
}
}
After you've defined your provider, you need to:
Configure your provider by overriding the
LanguageServerProviderConfiguration
property. This configuration property defines the server display name and applicable document types.LanguageServerBaseDocumentType
is available for all servers and triggers on all document types. See Define a custom Document Type.public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server", new[] { DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType), });
Override the
CreateServerConnectionAsync
method, which is called by Visual Studio to notify the extension that the LSP server should be started.// 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())); }
Override the
OnServerInitializationResultAsync
method, which is called by Visual Studio after the LSP server has completed its start-up and configuration steps.ServerInitializationResult
provides the resulting state of the server, andLanguageServerInitializationFailureInfo
provides an exception if any.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; }
Here's what our sample language server provider looks like after completing all of the steps:
[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;
}
}
Send additional data when starting a language server
LanguageServerOptions.InitializationOptions
can be set in the constructor for LanguageServerProvider
to send additional data to the server with the "initialize" protocol message.
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
this.LanguageServerOptions.InitializationOptions = JToken.Parse(@"[{""server"":""initialize""}]");
}
Define custom document types
When an extension supports file types that are not natively supported by Visual Studio, extension authors can implement custom document types. These types can be used when defining LanguageServerProviderConfiguration
to specify the supported document types.
[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,
};
This snippet defines two new document types: rust
and markdown
. These types contain a list of file extensions and a base type, which can be LanguageServerBaseDocumentType
to cover all types.
Use these types in LanguageServerProviderConfiguration
to activate your server when these document types are opened:
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("My Language Server",
new[]
{
DocumentFilter.FromDocumentType(RustDocumentType),
DocumentFilter.FromDocumentType(MarkdownDocumentType),
});
Enable or disable a language server
An enabled language server is allowed to "activate" once an applicable document type is opened. When disabled, a stop message is sent to any applicable active language server and prevent further activations.
[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;
}
}
}
This code snippet disables the language server by setting this.Enabled
to false
if ServerInitializationResult
gets set to Failed
after failing to initialize.
Note
This flag is public and if set to false, any running servers are stopped.
Use localized resources
We support using localization by defining a string-resources.json
file and using %tokens%
to specify localized content.
string-resources.json
{
{ "LocalizedResource": "LangaugeServerLocalized" }
}
Access a localized resource
[VisualStudioContribution]
public class MyLanguageServer : LanguageServerProvider
{
...
/// <inheritdoc/>
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("%LocalizedResource%",
new[]
{
DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType)
});
}
Next steps
- Follow the create your first extension tutorial to get started with creating an extension.
- See the Rust Language Server Provider sample for a full example of creating an extension with a language server provider.