vscode插件快餐教程(7) - 从头开始写一个完整的lsp工程

2023-10-21 06:59

本文主要是介绍vscode插件快餐教程(7) - 从头开始写一个完整的lsp工程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

vscode插件快餐教程(7) - 从头开始写一个完整的lsp工程

有了一定的基础知识之后,我们就可以开始搭建一个client和server模式的lsp的插件了。

server目录

首先我们来写server端的代码。

package.json

首先我们来写package.json. 因为微软的sdk已经帮我们封装好了大部分细节,其实我们只要引用vscode-languageserver的模块就可以了:

{"name": "lsp-demo-server","description": "demo language server","version": "1.0.0","author": "Xulun","license": "MIT","engines": {"node": "*"},"repository": {"type": "git","url": "git@code.aliyun.com:lusinga/testlsp.git"},"dependencies": {"vscode-languageserver": "^4.1.3"},"scripts": {}
}

有了package.json之后,我们就可以在server目录下运行npm install命令将依赖安装进来。
安装之后会有下面的模块被引用进来:

  • vscode-jsonrpc
  • vscode-languageserver
  • vscode-languageserver-protocol
  • vscode-languageserver-types vscode-uri

tsconfig.json

因为我们是要用typescript来写server,所以我们用tsconfig.json来配置Typescript的选项:

{"compilerOptions": {"target": "es6","module": "commonjs","moduleResolution": "node","sourceMap": true,"outDir": "out","rootDir": "src","lib": ["es6"]},"include": ["src"],"exclude": ["node_modules", ".vscode-test"]
}

server.ts

下面我们开始写服务端的ts文件,首先我们要把vscode-languageserver和vscode-jsonrpc的依赖引入进来:

import {createConnection,TextDocuments,TextDocument,Diagnostic,DiagnosticSeverity,ProposedFeatures,InitializeParams,DidChangeConfigurationNotification,CompletionItem,CompletionItemKind,TextDocumentPositionParams,SymbolInformation,WorkspaceSymbolParams,WorkspaceEdit,WorkspaceFolder
} from 'vscode-languageserver';
import { HandlerResult } from 'vscode-jsonrpc';

下面,为了打印日志方便,我们使用log4js来打印日志,通过npm i log4js --save将其模块引入进来,然后对其进行初始化:

import { configure, getLogger } from "log4js";
configure({appenders: {lsp_demo: {type: "dateFile",filename: "/Users/ziyingliuziying/working/lsp_demo",pattern: "yyyy-MM-dd-hh.log",alwaysIncludePattern: true,},},categories: { default: { appenders: ["lsp_demo"], level: "debug" } }
});
const logger = getLogger("lsp_demo");

然后我们就可以调用createConnection来创建连接了:

let connection = createConnection(ProposedFeatures.all);

接着我们就可以处理一个个的事件啦,比如处理第6节介绍的初始化事件:

connection.onInitialize((params: InitializeParams) => {let capabilities = params.capabilities;return {capabilities: {completionProvider: {resolveProvider: true}}};
});

三次握手之后,我们可以在vscode上显示一条消息:

connection.onInitialized(() => {connection.window.showInformationMessage('Hello World! form server side');
});

最后,我们可以把第5节学过的代码补全的部分给加上:

connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {return [{label: 'TextView' + _textDocumentPosition.position.character,kind: CompletionItemKind.Text,data: 1},{label: 'Button' + _textDocumentPosition.position.line,kind: CompletionItemKind.Text,data: 2},{label: 'ListView',kind: CompletionItemKind.Text,data: 3}];}
);connection.onCompletionResolve((item: CompletionItem): CompletionItem => {if (item.data === 1) {item.detail = 'TextView';item.documentation = 'TextView documentation';} else if (item.data === 2) {item.detail = 'Button';item.documentation = 'JavaScript documentation';} else if (item.data === 3) {item.detail = 'ListView';item.documentation = 'ListView documentation';}return item;}
);

最后,我们需要让connection去监听:

documents.listen(connection);connection.listen();

client目录

服务端这就算开发就绪了,下面我们来开发客户端。

package.json

首先还是先写package.json,依赖于vscode-languageclient,不要跟服务端用的库vscode-languageserver搞混了哈。

{"name": "lspdemo-client","description": "demo language server client","author": "Xulun","license": "MIT","version": "0.0.1","publisher": "Xulun","repository": {"type": "git","url": "git@code.aliyun.com:lusinga/testlsp.git"},"engines": {"vscode": "^1.33.1"},"scripts": {"update-vscode": "vscode-install","postinstall": "vscode-install"},"dependencies": {"path": "^0.12.7","vscode-languageclient": "^4.1.4"},"devDependencies": {"vscode": "^1.1.30"}
}

tsconfig.json

反正都是ts,客户端与服务端比也没有增加啥特别的,于是照抄一份:

{"compilerOptions": {"module": "commonjs","target": "es6","outDir": "out","rootDir": "src","lib": ["es6"],"sourceMap": true},"include": ["src"],"exclude": ["node_modules", ".vscode-test"]
}

extension.ts

下面我们来写extension.ts。

其实客户端要做的事情比server还少,本质上就是启动server就好:

	// Create the language client and start the client.client = new LanguageClient('DemoLanguageServer','Demo Language Server',serverOptions,clientOptions);// Start the client. This will also launch the serverclient.start();

serverOptions用来配置服务端的参数,其定义为:

export type ServerOptions = 
Executable | 
{ run: Executable; debug: Executable; } | 
{ run: NodeModule; debug: NodeModule } | 
NodeModule | 
(() => Thenable<ChildProcess | StreamInfo | MessageTransports | ChildProcessInfo>);

相关类型的简图如下:
ServerOptions

下面我们来配置一下:

	// 服务端配置let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));let serverOptions: ServerOptions = {module: serverModule, transport: TransportKind.ipc};// 客户端配置let clientOptions: LanguageClientOptions = {// js代码触发事情documentSelector: [{ scheme: 'file', language: 'js' }],};

extension.ts的完整代码如下:

import * as path from 'path';
import { workspace, ExtensionContext } from 'vscode';import {LanguageClient,LanguageClientOptions,ServerOptions,TransportKind
} from 'vscode-languageclient';let client: LanguageClient;export function activate(context: ExtensionContext) {// 服务端配置let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));let serverOptions: ServerOptions = {module: serverModule, transport: TransportKind.ipc};// 客户端配置let clientOptions: LanguageClientOptions = {// js代码触发事情documentSelector: [{ scheme: 'file', language: 'js' }],};client = new LanguageClient('DemoLanguageServer','Demo Language Server',serverOptions,clientOptions);// 启动客户端,同时启动语言服务器client.start();
}export function deactivate(): Thenable<void> | undefined {if (!client) {return undefined;}return client.stop();
}

组装运行

万事俱备,只欠包装,下面我们将上面的客户端和服务器组装一下。

插件配置 - package.json

我们关注点主要是入口函数和触发事件:

    "activationEvents": ["onLanguage:javascript"],"main": "./client/out/extension",

完整的package.json如下:

{"name": "lsp_demo_server","description": "A demo language server","author": "Xulun","license": "MIT","version": "1.0.0","repository": {"type": "git","url": "git@code.aliyun.com:lusinga/testlsp.git"},"publisher": "Xulun","categories": [],"keywords": [],"engines": {"vscode": "^1.33.1"},"activationEvents": ["onLanguage:javascript"],"main": "./client/out/extension","contributes": {},"scripts": {"vscode:prepublish": "cd client && npm run update-vscode && cd .. && npm run compile","compile": "tsc -b","watch": "tsc -b -w","postinstall": "cd client && npm install && cd ../server && npm install && cd ..","test": "sh ./scripts/e2e.sh"},"devDependencies": {"@types/mocha": "^5.2.0","@types/node": "^8.0.0","tslint": "^5.11.0","typescript": "^3.1.3"}
}

装配tsconfig.json

我们还需要一个总的tsconfig.json,引用client和server两个目录:

{"compilerOptions": {"module": "commonjs","target": "es6","outDir": "out","rootDir": "src","lib": [ "es6" ],"sourceMap": true},"include": ["src"],"exclude": ["node_modules",".vscode-test"],"references": [{ "path": "./client" },{ "path": "./server" }]
}

配置vscode

上面,我们就将client, server和整合它们的代码全部写完了。
下面我们在.vscode目录中写两个配置文件,使我们可以更方便地调试和运行。

.vscode/launch.json

有了这个文件之后,我们就有了运行的配置,可以通过F5来启动。

// A launch configuration that compiles the extension and then opens it inside a new window
{"version": "0.2.0","configurations": [{"type": "extensionHost","request": "launch","name": "Launch Client","runtimeExecutable": "${execPath}","args": ["--extensionDevelopmentPath=${workspaceRoot}"],"outFiles": ["${workspaceRoot}/client/out/**/*.js"],"preLaunchTask": {"type": "npm","script": "watch"}},{"type": "node","request": "attach","name": "Attach to Server","port": 6009,"restart": true,"outFiles": ["${workspaceRoot}/server/out/**/*.js"]},],"compounds": [{"name": "Client + Server","configurations": ["Launch Client", "Attach to Server"]}]
}

.vscode/tasks.json

配置npm compile和npm watch两个脚本。

{"version": "2.0.0","tasks": [{"type": "npm","script": "compile","group": "build","presentation": {"panel": "dedicated","reveal": "never"},"problemMatcher": ["$tsc"]},{"type": "npm","script": "watch","isBackground": true,"group": {"kind": "build","isDefault": true},"presentation": {"panel": "dedicated","reveal": "never"},"problemMatcher": ["$tsc-watch"]}]
}

一切就绪之后,在插件根目录下运行下npm install。
然后在vscode中运行build命令,比如mac下是cmd-shift-b,于是就构建生成了server和client的out目录下的js和map。
现在就可以通过F5键运行啦。

本示例的源代码放在code.aliyun.com:lusinga/testlsp.git中。

这篇关于vscode插件快餐教程(7) - 从头开始写一个完整的lsp工程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/252699

相关文章

Spring Security中用户名和密码的验证完整流程

《SpringSecurity中用户名和密码的验证完整流程》本文给大家介绍SpringSecurity中用户名和密码的验证完整流程,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 首先创建了一个UsernamePasswordAuthenticationTChina编程oken对象,这是S

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机

java向微信服务号发送消息的完整步骤实例

《java向微信服务号发送消息的完整步骤实例》:本文主要介绍java向微信服务号发送消息的相关资料,包括申请测试号获取appID/appsecret、关注公众号获取openID、配置消息模板及代码... 目录步骤1. 申请测试系统2. 公众号账号信息3. 关注测试号二维码4. 消息模板接口5. Java测试

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

SpringBoot集成LiteFlow工作流引擎的完整指南

《SpringBoot集成LiteFlow工作流引擎的完整指南》LiteFlow作为一款国产轻量级规则引擎/流程引擎,以其零学习成本、高可扩展性和极致性能成为微服务架构下的理想选择,本文将详细讲解Sp... 目录一、LiteFlow核心优势二、SpringBoot集成实战三、高级特性应用1. 异步并行执行2

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

SpringBoot连接Redis集群教程

《SpringBoot连接Redis集群教程》:本文主要介绍SpringBoot连接Redis集群教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 依赖2. 修改配置文件3. 创建RedisClusterConfig4. 测试总结1. 依赖 <de

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

Nexus安装和启动的实现教程

《Nexus安装和启动的实现教程》:本文主要介绍Nexus安装和启动的实现教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Nexus下载二、Nexus安装和启动三、关闭Nexus总结一、Nexus下载官方下载链接:DownloadWindows系统根

java对接海康摄像头的完整步骤记录

《java对接海康摄像头的完整步骤记录》在Java中调用海康威视摄像头通常需要使用海康威视提供的SDK,下面这篇文章主要给大家介绍了关于java对接海康摄像头的完整步骤,文中通过代码介绍的非常详细,需... 目录一、开发环境准备二、实现Java调用设备接口(一)加载动态链接库(二)结构体、接口重定义1.类型