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

相关文章

使用Nginx来共享文件的详细教程

《使用Nginx来共享文件的详细教程》有时我们想共享电脑上的某些文件,一个比较方便的做法是,开一个HTTP服务,指向文件所在的目录,这次我们用nginx来实现这个需求,本文将通过代码示例一步步教你使用... 在本教程中,我们将向您展示如何使用开源 Web 服务器 Nginx 设置文件共享服务器步骤 0 —

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

mysql重置root密码的完整步骤(适用于5.7和8.0)

《mysql重置root密码的完整步骤(适用于5.7和8.0)》:本文主要介绍mysql重置root密码的完整步骤,文中描述了如何停止MySQL服务、以管理员身份打开命令行、替换配置文件路径、修改... 目录第一步:先停止mysql服务,一定要停止!方式一:通过命令行关闭mysql服务方式二:通过服务项关闭

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

IDEA常用插件之代码扫描SonarLint详解

《IDEA常用插件之代码扫描SonarLint详解》SonarLint是一款用于代码扫描的插件,可以帮助查找隐藏的bug,下载并安装插件后,右键点击项目并选择“Analyze”、“Analyzewit... 目录SonajavascriptrLint 查找隐藏的bug下载安装插件扫描代码查看结果总结Sona

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.

Python使用pysmb库访问Windows共享文件夹的详细教程

《Python使用pysmb库访问Windows共享文件夹的详细教程》本教程旨在帮助您使用pysmb库,通过SMB(ServerMessageBlock)协议,轻松连接到Windows共享文件夹,并列... 目录前置条件步骤一:导入必要的模块步骤二:配置连接参数步骤三:实例化SMB连接对象并尝试连接步骤四:

Linux使用粘滞位 (t-bit)共享文件的方法教程

《Linux使用粘滞位(t-bit)共享文件的方法教程》在Linux系统中,共享文件是日常管理和协作中的常见任务,而粘滞位(StickyBit或t-bit)是实现共享目录安全性的重要工具之一,本文将... 目录文件共享的常见场景基础概念linux 文件权限粘滞位 (Sticky Bit)设置共享目录并配置粘