编写一个 Vscode 语言插件
语言插件能做什么?
-
语法提示 -
错误标注 -
代码格式化 -
...
我们要做什么?
处理输入的文本,调用VSCode的API来实现具体的功能。 例如:我们要实现代码格式化,可以分为三步。 第一步:获取用户输入的文本。 第二步:格式化用户输入的文本。 第三步,调用VSCode的API来替换文本。
VSCode将提供处理用户输入,格式化文本的部分称为 Language Server。我们的各种语言功能都在Language Server中实现。 如下图所示:
在上图中,Language Server 直接调用VSCode的API来操作编辑器,两者之间是强耦合的。这样就产生了一个新的问题,假如我们想要在Sublime也实现相同功能,就要再写一个以来Sublime API的Server。所以,VSCode进一步改进了这个方案,提供了基于LSP(Language Server Protocol)的Client-Server模式。在这个模式中,Language Server 和 编辑器只需要各自实现 LSP 协议就可以了。
LSP 主要结构如下
Content-Length: ...\r\n
\r\n
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/didOpen",
"params": {
...
}
}
可以看到,主体部分是一个 JSON,这个是基于 JSON-RPC 协议的 JSON 结构。 其中 jsonrpc 代表 JSON-RPC 的协议版本,LSP 是基于2.0的。 id 代表一个消息的表示 method 代表消息的功能类型 params 表示消息的具体参数
基于这一套结构。我们需要做两件事情。
- 实现一个基于 LSP 与外界通信的 Language Server。
- 实现一个 VSCode 插件来注册具体语言,并且和 Language Server 通信。
介绍完基础内容,下面我们从官方的例子来讲解如何实现一个插件。 官方例子:github.com/Microsoft/v…
1. 注册插件事件
在 package.json 中添加
"activationEvents": [
"onLanguage:plaintext"
]
这个配置使得插件在 plaintext 语言被使用的时候被激活。
2. 实现Server。
VSCode 提供了 NodeJs 平台的 Server 包 vscode-languageserver,这个包封装了很多工具类,可以节约大量时间。
2.1 创建 Connection 和 Documens
let connection = createConnection(ProposedFeatures.all);// 采用node ipc通信
let documents: TextDocuments = new TextDocuments();
connection 负责网络之间的通信 documents 负责用户文档的管理。
2.2 监听初始化事件
connection.onInitialize((params: InitializeParams) => {
let capabilities = params.capabilities;
// Does the client support the `workspace/configuration` request?
// If not, we will fall back using global settings
hasConfigurationCapability =
capabilities.workspace && !!capabilities.workspace.configuration;
hasWorkspaceFolderCapability =
capabilities.workspace && !!capabilities.workspace.workspaceFolders;
hasDiagnosticRelatedInformationCapability =
capabilities.textDocument &&
capabilities.textDocument.publishDiagnostics &&
capabilities.textDocument.publishDiagnostics.relatedInformation;
return {
capabilities: {
textDocumentSync: documents.syncKind,
// Tell the client that the server supports code completion
completionProvider: {
resolveProvider: true
}
}
};
});
这边获取客户端的一些基本信息,并且返回服务器自身的一些信息。
2.3 监听文档事件
documents.onDidClose(e => {
...
});
documents.onDidChangeContent(change => {
// 可以在这边检查文档的语法
...
});
2.4 实现代码补全功能
connection.onCompletion(
(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
return [
{
label: 'TypeScript',
kind: CompletionItemKind.Text,
data: 1
},
{
label: 'JavaScript',
kind: CompletionItemKind.Text,
data: 2
}
];
}
);
这边我们实现了一个简单的补全功能,只返回两个补全项。
2.5 启动服务
documents.listen(connection);// 监听connection中的文档打开,关闭事件等
connection.listen();// 监听客户端的请求
// 两者关系:你保护世界,我保护你。:)
3. 实现客户端
客户端就是VSCode的插件,所以基本内容和插件是一样的,只是里面功能不一样。 和服务器一样,客户端也有一个工具包 vscode-languageclient
3.1 创建client对象,设置server地址和一些配置,启动并监听 Server。
export fucntion activate () {
// Create the language client and start the client.
client = new LanguageClient(
'languageServerExample',
'Language Server Example',
serverOptions,
clientOptions
);
// Start the client. This will also launch the server
client.start();
}
3.2 注册销毁操作
export fucntion activate () {
// Create the language client and start the client.
client = new LanguageClient(
'languageServerExample',
'Language Server Example',
serverOptions,
clientOptions
);
// Start the client. This will also launch the server
client.start();
}
一个简单的语言插件到此就完成了,详细的内容可以查看 VSCode 提供的例子。