最近因为咱们公司的低代码开发渠道中的代码修改器的一些诟病,饱受开发同学吐槽,咱们决议基于LSP 办法增强已有的代码修改器功用,来提升开发同学们的用户体会。学习了一下LSP,整理总结一下如下
1. LSP 基本概念
LSP(Language Server Protocol)是一种言语服务器协议,它界说了一种通用的协议,使得不同的修改器或IDE能够运用相同的言语服务器进行代码修改和剖析。LSP的主要目的是进步修改器和IDE的代码智能化和跨渠道性。
LSP 的基本作业原理是:修改器或IDE与言语服务器建立衔接后,经过 LSP 协议进行通讯。当用户在修改器中输入代码时,修改器会将相关信息发送给言语服务器,言语服务器会进行代码剖析和补全等操作,并将成果回来给修改器,修改器再将成果展示给用户。
运用 LSP 能够实现多种功用,包含代码补全、代码格式化、语法查看、代码重构等。LSP 的优势在于其跨渠道性和灵活性,能够让不同的修改器或IDE运用相同的言语服务器,从而进步开发功率和代码质量。
先基于vscode 咱们实现一个简略的TypeScript 言语服务器,咱们需求装置 TypeScript 言语服务器和 VS Code 修改器。
新建 TypeScript 项目 在项目中创立一个名为server.ts的文件
咱们实现代码补全
// 导入了一些 LSP 相关的模块,然后创立了一个`connection`目标和一个`documents`目标。
import {
createConnection,
TextDocuments,
ProposedFeatures,
InitializeParams,
CompletionItem,
CompletionItemKind,
} from 'vscode-languageserver';
// connection目标用于与客户端建立衔接,并注册了一些事情处理函数。`documents`目标用于办理文档,并监听文档变化事情。
const connection = createConnection(ProposedFeatures.all);
const documents = new TextDocuments();
documents.listen(connection);
// 在`onInitialize`事情处理函数中,咱们回来了一些 LSP 支撑的才能,包含文档同步和代码补全。
connection.onInitialize((params: InitializeParams) => {
return {
capabilities: {
textDocumentSync: documents.syncKind,
completionProvider: {
resolveProvider: true,
triggerCharacters: ['.'],
},
},
};
});
// 在onCompletion事情处理函数中,咱们创立了两个CompletionItem 目标,并将它们作为代码补全的成果回来。
connection.onCompletion((textDocumentPosition) => {
const { line, character } = textDocumentPosition.position;
const document = documents.get(textDocumentPosition.textDocument.uri);
const completions: CompletionItem[] = [
{
label: 'Hello',
kind: CompletionItemKind.Text,
data: 1,
},
{
label: 'World',
kind: CompletionItemKind.Text,
data: 2,
},
];
return completions;
});
// 咱们调用`connection.listen()`办法来启动 LSP 服务器。
connection.listen();
在 VS Code 修改器中翻开client.ts
文件,并输入以下代码:
console.
当输入console.
时,修改器会主动调用 LSP 服务器,展示代码补全成果。
这个示例只是 LSP 的一个简略演示,实际上 LSP 还支撑更多的功用和事情,例如代码格式化、语法查看、代码重构等。
除了代码补全功用之外,LSP 还支撑以下功用和事情:
- 文档同步:当文档发生变化时,LSP 能够主动同步文档内容,以便进行语法查看、代码重构等操作。
- 代码格式化:LSP 能够依据编码标准主动格式化代码,以进步代码可读性和一致性。
- 语法查看:LSP 能够查看代码中的语法错误和潜在问题,并供给相应的修复主张。
- 代码重构:LSP 能够依据代码结构和语义,主动重构代码,以进步代码质量和可维护性。
- 调试支撑:LSP 能够与调试器集成,供给调试支撑,以便更方便地调试代码。
- 代码导航:LSP 能够供给代码导航功用,以便快速定位和浏览代码。
- 代码剖析:LSP 能够进行代码剖析,以供给更精确的代码补全和代码重构主张。
- 代码片段:LSP 能够供给代码片段功用,以便快速插入常用代码模板。
LSP 能够大大进步修改器和 IDE 的代码智能化和跨渠道性,让开发者愈加高效地编写和维护代码。
2. LSP 功用的代码示例:
1. 文档同步
import { createConnection, TextDocuments } from 'vscode-languageserver';
const connection = createConnection();
const documents = new TextDocuments();
documents.listen(connection);
documents.onDidChangeContent((change) => {
console.log(change.document.uri, change.document.getText());
});
connection.listen();
上述代码中,咱们创立了一个 documents
目标,并监听了 onDidChangeContent
事情。当文档发生变化时,事情处理函数会将文档的 URI 和文本内容打印出来。
2. 代码格式化
import { createConnection, TextDocuments } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { format } from 'prettier';
const connection = createConnection();
const documents = new TextDocuments(TextDocument);
documents.listen(connection);
connection.onRequest('textDocument/formatting', (params) => {
const document = documents.get(params.textDocument.uri);
const formatted = format(document.getText(), { parser: 'typescript' });
return [{ range: { start: { line: 0, character: 0 }, end: document.positionAt(document.getText().length) }, newText: formatted }];
});
connection.listen();
上述代码中,咱们运用了 prettier
模块来进行代码格式化。在 onRequest
事情处理函数中,咱们监听了 textDocument/formatting
恳求,并将恳求的文本进行格式化,然后将格式化后的文本回来给客户端。
3. 语法查看
import { createConnection, TextDocuments, Diagnostic, DiagnosticSeverity } from 'vscode-languageserver';
import * as ts from 'typescript';
const connection = createConnection();
const documents = new TextDocuments();
documents.listen(connection);
connection.onDidChangeWatchedFiles((change) => {
console.log('File change event received:', change);
});
connection.onDidChangeConfiguration((change) => {
console.log('Configuration change event received:', change);
});
documents.onDidChangeContent((change) => {
const diagnostics: Diagnostic[] = [];
const program = ts.createProgram({
rootNames: [change.document.uri],
options: {},
});
const diagnosticsHost: ts.FormatDiagnosticsHost = {
getCanonicalFileName: (path) => path,
getCurrentDirectory: ts.sys.getCurrentDirectory,
getNewLine: () => ts.sys.newLine,
};
const emitResult = program.emit();
const allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics)
.filter((diagnostic) => diagnostic.file !== undefined);
allDiagnostics.forEach((diagnostic) => {
const { file } = diagnostic;
if (file) {
const { line, character } = file.getLineAndCharacterOfPosition(diagnostic.start!);
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const severity = diagnostic.category === ts.DiagnosticCategory.Error ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning;
diagnostics.push({
severity,
range: {
start: { line, character },
end: { line, character: character + 1 },
},
message,
source: 'typescript',
});
}
});
connection.sendDiagnostics({ uri: change.document.uri, diagnostics });
});
connection.listen();
上述代码中,咱们运用了 TypeScript 编译器来进行语法查看。在 onDidChangeContent
事情处理函数中,咱们运用 ts.createProgram
办法来创立一个 TypeScript 编译器实例,并运用 ts.getPreEmitDiagnostics
办法来获取一切的确诊信息。然后,咱们将确诊信息转换成 LSP 的 Diagnostic
目标,并经过 connection.sendDiagnostics
办法将确诊信息发送给客户端。
4. 代码重构
import { createConnection, TextDocuments, Range, Position } from 'vscode-languageserver';
import * as ts from 'typescript';
const connection = createConnection();
const documents = new TextDocuments();
documents.listen(connection);
connection.onDidChangeWatchedFiles((change) => {
console.log('File change event received:', change);
});
connection.onDidChangeConfiguration((change) => {
console.log('Configuration change event received:', change);
});
documents.onDidChangeContent(async (change) => {
const refactorInfo = await connection.sendRequest('textDocument/codeAction', {
textDocument: change.document,
range: Range.create(Position.create(0, 0), Position.create(change.document.lineCount, 0)),
context: {
diagnostics: [],
only: ['refactor'],
},
});
if (refactorInfo) {
console.log(refactorInfo);
}
});
connection.listen();
上述代码中,咱们运用了 textDocument/codeAction
恳求来进行代码重构。在 onDidChangeContent
事情处理函数中,咱们向客户端发送了一条 textDocument/codeAction
恳求,并将恳求的规模设置为整个文档。然后,咱们将恳求的成果打印出来。
LSP 能够供给丰富的功用和事情,能够依据详细需求进行运用。