扩展性语言服务器提供程序
语言服务器提供程序涉及托管在 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)
{
}
}
定义提供程序后,你需要:
通过替代
LanguageServerProviderConfiguration
属性来配置提供程序。 此配置属性定义服务器显示名称和适用的文档类型。LanguageServerBaseDocumentType
适用于所有服务器并在所有文档类型上触发。 请参阅定义自定义文档类型。public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server", new[] { DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType), });
替代
CreateServerConnectionAsync
方法,Visual Studio 会调用此方法,以通知扩展应启动 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())); }
替代
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
的构造函数中设置,以便通过“初始化”协议消息将其他数据发送到服务器。
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,
};
此代码片段定义以下两个新的文档类型:rust
和 markdown
。 这些类型包含文件扩展名列表和基类型,此基类型可能是用于涵盖所有类型的 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;
}
}
}
如果 ServerInitializationResult
在初始化失败后设置为 Failed
,则此代码片段会通过将 this.Enabled
设置为 false
来禁用语言服务器。
注意
此标志是公共标志,如果设置为 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)
});
}
后续步骤
- 按照创建第一个扩展教程开始创建扩展。
- 有关使用语言服务器提供程序创建扩展的完整示例,请参阅 Rust 语言服务器提供程序示例。