当前位置: 首页
AI教程
VSCode插件快餐教程第7讲:从零构建完整LSP工程

VSCode插件快餐教程第7讲:从零构建完整LSP工程

热心网友 时间:2026-05-30
转载
```html

VSCode插件开发实战:从零构建完整LSP工程

经过前面基础知识的学习积累,现在正式开始搭建一个功能完整的LSP插件,采用经典的客户端与服务端分离架构。整个实现过程分为两大模块:首先完成服务端的核心逻辑,其次实现客户端的启动与通信,最后将两者无缝集成,形成可用插件。

服务端目录结构

首先聚焦服务端代码的构建。从package.json入手,微软提供的SDK已将大量底层细节封装完毕,我们仅需引入vscode-languageserver模块即可快速开发语言服务器。

package.json配置

{
    "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-jsonrpcvscode-languageservervscode-languageserver-protocolvscode-languageserver-typesvscode-uri,这些都是协议通信与类型定义的核心组件。

TypeScript编译配置

既然采用TypeScript编写服务端,就必须配置tsconfig.json,编译选项如下:

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

服务端核心逻辑 server.ts

接下来编写服务端的主入口——server.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
            }
        }
    };
});

初始化阶段的三次握手完成后,可以在VS Code界面中弹出欢迎消息:

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

最后,将第5节中实现的代码补全功能集成进来:

connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
    return [
        {
            label: 'TextView',
            kind: CompletionItemKind.Text,
            data: 1
        },
        {
            label: 'Button',
            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 = 'Ja vaScript documentation';
    } else if (item.data === 3) {
        item.detail = 'ListView';
        item.documentation = 'ListView documentation';
    }
    return item;
});

客户端目录结构

服务端核心代码完成后,接下来开发客户端的启动与通信部分。

客户端 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

客户端依然使用TypeScript,编译配置与服务端基本相同,直接复用:

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

客户端主入口 extension.ts

接下来编写extension.ts,客户端的主要职责是启动语言服务器,本身逻辑相对精简:

// 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 server
client.start();

serverOptions用于配置服务端的启动参数,其类型定义如下:

export type ServerOptions = Executable
    | { run: Executable; debug: Executable; }
    | { run: NodeModule; debug: NodeModule }
    | NodeModule
    | (() => Thenable);

相关类型的关系可参考下图:
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 | undefined {
    if (!client) {
        return undefined;
    }
    return client.stop();
}

整体组装与运行

服务端与客户端的代码均已完成,现在将它们整合在一起。核心步骤包括配置插件的package.json、总的tsconfig.json以及VS Code调试环境。

插件根目录 package.json

重点设置入口函数与激活事件:

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

完整配置如下:

{
    "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:ja vascript"],
    "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,通过references同时引用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" }
    ]
}

VS Code调试环境配置

至此,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,然后在VS Code中运行构建命令(mac下为cmd+shift+b),即可自动编译生成server与client的out目录下的js及map文件。此时按F5即可启动扩展调试窗口,体验完整的LSP插件功能。

本示例的完整源码已托管至 git@code.aliyun.com:lusinga/testlsp.git,读者可按需克隆参考。

```
来源:https://developer.aliyun.com/article/704465

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
AI高效生成大班科学实验教学PPT下载 提升课堂质量与趣味性

AI高效生成大班科学实验教学PPT下载 提升课堂质量与趣味性

```html 对于众多教师而言,大班教学既是日常挑战,也是必须面对的常态。当教室内坐着几十甚至上百名满怀期待的学生时,如何高效准备一堂既生动清晰又能牢牢抓住所有人注意力的课程?PPT 往往是那位不可或缺的“得力助手”。然而,难题也随之浮现:内容要充实,设计需美观,还要贴合不同教学主题——若从头自行

时间:2026-05-30 12:34
AI提升班会质量:PPT主题总结与未来计划范文

AI提升班会质量:PPT主题总结与未来计划范文

使用情景 在校园生活中,主题班会是班级凝聚力的重要体现,堪称一场“全员聚会”。同学们齐聚一堂,交流学习心得、分享生活体会,同时回顾和反思近期的整体表现。然而,每当提到“主题班会”,不少同学便会感到些许压力:如何准备?怎样才能将内容整理得既有条理、有深度,又不失趣味性?这时,PPT便成为了高效组织的得

时间:2026-05-30 12:33
实测ToDesk AI对比QClaw:更省额度回答更详细

实测ToDesk AI对比QClaw:更省额度回答更详细

前言 最近一段时间,我连续体验了几款主打“Claw”能力的桌面智能助手,最初只是想看看它们是否只是“披着AI外壳的聊天工具”。然而,真正上手体验后,感受非常明确:ToDesk AI(ToClaw)更像一个能够直接落地执行任务的桌面助手,而不只是一个会聊天、能生成内容的模型入口。 很多人在评估这类产品

时间:2026-05-30 12:32
大班幼儿教育PPT制作免费技巧轻松掌握告别烦恼

大班幼儿教育PPT制作免费技巧轻松掌握告别烦恼

使用情景 在幼儿园大班的教学场景中,PPT早已成为老师们不可或缺的课堂助手。无论是日常的课件讲解、主题活动的组织,还是家长会上的总结汇报,一份优质的PPT都能让信息传递更加直观,同时有效吸引孩子们的注意力。 不过,要想把大班PPT做得既美观又实用,确实需要花费不少心思。内容既要丰富有趣,视觉上又要具

时间:2026-05-30 12:32
2026最新版Claude Opus 4.7国内使用全攻略:价格不变能力翻倍

2026最新版Claude Opus 4.7国内使用全攻略:价格不变能力翻倍

比Opus 4 6更强的新一代模型Opus 4 7终于正式发布。就在OpenAI不断扩展Codex功能的同时,Anthropic迅速推出了Opus 4 7——而且这次带来的确实是实质性升级。(目前已经全量上线,用户可以直接上手体验。)那么,Opus 4 7究竟有哪些突破?先给个结论:这不是一次简单的

时间:2026-05-30 12:31
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程