尝试启用lsp

This commit is contained in:
Pan Qiancheng 2024-10-23 15:58:01 +08:00
parent 2901b86a45
commit 7add109ad5
6 changed files with 344 additions and 74 deletions

View File

@ -1,9 +1,9 @@
const esbuild = require("esbuild");
const esbuild = require('esbuild');
const production = process.argv.includes("--production");
const watch = process.argv.includes("--watch");
const path = require("path");
const fs = require("fs").promises;
const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');
const path = require('path');
const fs = require('fs').promises;
/**
* 递归复制目录
@ -11,92 +11,96 @@ const fs = require("fs").promises;
* @param {string} dest 目标目录
*/
async function copyDir(src, dest) {
await fs.mkdir(dest, { recursive: true });
let entries = await fs.readdir(src, { withFileTypes: true });
await fs.mkdir(dest, { recursive: true });
let entries = await fs.readdir(src, { withFileTypes: true });
for (let entry of entries) {
let srcPath = path.join(src, entry.name);
let destPath = path.join(dest, entry.name);
for (let entry of entries) {
let srcPath = path.join(src, entry.name);
let destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDir(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
console.log(`Copied ${srcPath} to ${destPath}`);
}
}
if (entry.isDirectory()) {
await copyDir(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
console.log(`Copied ${srcPath} to ${destPath}`);
}
}
}
/**
* @type {import('esbuild').Plugin}
*/
const copyTemplatesPlugin = {
name: "copy-templates",
setup(build) {
build.onEnd(async () => {
const srcDir = path.join(__dirname, "src", "templates");
const destDir = path.join(__dirname, "dist", "templates");
name: 'copy-templates',
setup(build) {
build.onEnd(async () => {
const srcDir = path.join(__dirname, 'src', 'templates');
const destDir = path.join(__dirname, 'dist', 'templates');
try {
await copyDir(srcDir, destDir);
console.log("Templates copied successfully");
} catch (err) {
console.error("Error copying templates:", err);
}
});
},
try {
await copyDir(srcDir, destDir);
console.log('Templates copied successfully');
} catch (err) {
console.error('Error copying templates:', err);
}
});
},
};
/**
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
name: "esbuild-problem-matcher",
name: 'esbuild-problem-matcher',
setup(build) {
build.onStart(() => {
console.log("[watch] build started");
});
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(
` ${location.file}:${location.line}:${location.column}:`
);
});
console.log("[watch] build finished");
});
},
setup(build) {
build.onStart(() => {
console.log('[watch] build started');
});
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(
` ${location.file}:${location.line}:${location.column}:`
);
});
console.log('[watch] build finished');
});
},
};
async function main() {
const ctx = await esbuild.context({
entryPoints: ["src/extension.ts", "src/utils/analyzeWorker.ts"],
bundle: true,
format: "cjs",
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: "node",
outdir: "dist",
entryNames: "[dir]/[name]",
external: ["vscode"],
logLevel: "silent",
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
copyTemplatesPlugin,
],
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
const ctx = await esbuild.context({
entryPoints: [
'src/extension.ts',
'src/utils/analyzeWorker.ts',
// 'src/server/xmlLanguageServer.ts',
],
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: 'node',
outdir: 'dist',
entryNames: '[dir]/[name]',
external: ['vscode'],
logLevel: 'silent',
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
copyTemplatesPlugin,
],
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
console.error(e);
process.exit(1);
});

View File

@ -117,7 +117,10 @@
"dependencies": {
"glob": "^11.0.0",
"handlebars": "^4.7.8",
"lodash": "^4.17.21"
"lodash": "^4.17.21",
"vscode-languageclient": "^9.0.1",
"vscode-languageserver": "^9.0.1",
"vscode-languageserver-textdocument": "^1.0.12"
},
"description": "OAK框架辅助开发插件",
"devDependencies": {

View File

@ -17,6 +17,15 @@ importers:
lodash:
specifier: ^4.17.21
version: 4.17.21
vscode-languageclient:
specifier: ^9.0.1
version: 9.0.1
vscode-languageserver:
specifier: ^9.0.1
version: 9.0.1
vscode-languageserver-textdocument:
specifier: ^1.0.12
version: 1.0.12
devDependencies:
'@types/lodash':
specifier: ^4.17.12
@ -1999,6 +2008,27 @@ packages:
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
vscode-jsonrpc@8.2.0:
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
engines: {node: '>=14.0.0'}
vscode-languageclient@9.0.1:
resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==}
engines: {vscode: ^1.82.0}
vscode-languageserver-protocol@3.17.5:
resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
vscode-languageserver-textdocument@1.0.12:
resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
vscode-languageserver-types@3.17.5:
resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
vscode-languageserver@9.0.1:
resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
hasBin: true
webidl-conversions@5.0.0:
resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
engines: {node: '>=8'}
@ -4267,6 +4297,27 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
vscode-jsonrpc@8.2.0: {}
vscode-languageclient@9.0.1:
dependencies:
minimatch: 5.1.6
semver: 7.6.3
vscode-languageserver-protocol: 3.17.5
vscode-languageserver-protocol@3.17.5:
dependencies:
vscode-jsonrpc: 8.2.0
vscode-languageserver-types: 3.17.5
vscode-languageserver-textdocument@1.0.12: {}
vscode-languageserver-types@3.17.5: {}
vscode-languageserver@9.0.1:
dependencies:
vscode-languageserver-protocol: 3.17.5
webidl-conversions@5.0.0: {}
whatwg-encoding@3.1.1:

View File

@ -0,0 +1,77 @@
import * as fs from 'fs';
import * as path from 'path';
import { workspace, ExtensionContext } from 'vscode';
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind,
} from 'vscode-languageclient/node';
let client: LanguageClient;
function createLanguageServer() {
// 服务器是在独立的进程中启动的
let serverModule = path.join(__dirname, 'server', 'xmlLanguageServer.js');
if (!fs.existsSync(serverModule)) {
console.error('Could not find server module');
return;
}
// 服务器的调试选项
// --inspect=6009: 在Node的调试器中运行服务器
// --nolazy: 不要延迟加载
let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
// 如果扩展在调试模式下运行,那么调试服务器选项
// 否则运行正常的服务器
let serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: {
module: serverModule,
transport: TransportKind.ipc,
options: debugOptions,
},
};
// 控制语言客户端的选项
let clientOptions: LanguageClientOptions = {
// 为语言服务器注册xml文件
documentSelector: [{ scheme: 'file', language: 'xml' }],
synchronize: {
// 通知服务器关于文件更改的事件
fileEvents: workspace.createFileSystemWatcher('**/.clientrc'),
},
};
// 创建语言客户端并启动
client = new LanguageClient(
'xmlLanguageServer',
'XML Language Server',
serverOptions,
clientOptions
);
}
export async function startAndWaitForReacy(): Promise<void> {
createLanguageServer();
return new Promise<void>((resolve) => {
if (client) {
client.onNotification('xmlLanguageServer/ready', () => {
console.log('xmlLanguageServer is ready');
resolve();
});
}
// 启动客户端。这也会启动服务器
client.start();
});
}
export function deactivateClient(): Thenable<void> | undefined {
if (!client) {
return undefined;
}
return client.stop();
}

View File

@ -16,7 +16,10 @@ import entityProviders from './plugins/entityJump';
import { activateOakLocale, deactivateOakLocale } from './plugins/oakLocale';
import { startWorker, stopWorker, waitWorkerReady } from './utils/workers';
import { loadComponents } from './utils/components';
import { activateOakComponentPropsLinkProvider, deactivateOakComponentPropsLinkProvider } from './plugins/oakComponent';
import {
activateOakComponentPropsLinkProvider,
deactivateOakComponentPropsLinkProvider,
} from './plugins/oakComponent';
// 初始化配置
// 查找工作区的根目录中的oak.config.json文件排除src和node_modules目录
@ -123,7 +126,7 @@ export async function activate(context: vscode.ExtensionContext) {
oakPathCompletion.oakPathCompletion,
oakPathCompletion.oakPathDocumentLinkProvider,
...oakPathHighlighter,
entityProviders.documentLinkProvider,
entityProviders.documentLinkProvider
);
createFileWatcher(context);
} catch (error) {

View File

@ -0,0 +1,132 @@
import {
createConnection,
TextDocuments,
ProposedFeatures,
InitializeParams,
TextDocumentPositionParams,
CompletionItem,
CompletionItemKind,
TextDocumentSyncKind,
DidChangeConfigurationNotification,
Hover,
} from 'vscode-languageserver/node';
import { TextDocument } from 'vscode-languageserver-textdocument';
// 创建一个连接来使用Node的IPC作为传输
let connection = createConnection(ProposedFeatures.all);
// 创建一个简单的文本文档管理器
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
let hasConfigurationCapability: boolean = false;
let hasWorkspaceFolderCapability: boolean = false;
connection.onInitialize((params: InitializeParams) => {
let capabilities = params.capabilities;
hasConfigurationCapability = !!(
capabilities.workspace && !!capabilities.workspace.configuration
);
hasWorkspaceFolderCapability = !!(
capabilities.workspace && !!capabilities.workspace.workspaceFolders
);
return {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: {
resolveProvider: true,
},
hoverProvider: true,
},
};
});
connection.onInitialized(() => {
if (hasConfigurationCapability) {
connection.client.register(
DidChangeConfigurationNotification.type,
undefined
);
}
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders((_event) => {
connection.console.log('Workspace folder change event received.');
});
}
// 发送服务器就绪通知
connection.sendNotification("xmlLanguageServer/ready");
});
// 这个处理程序提供初始补全项。
connection.onCompletion(
(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
return [
{
label: 'TypeScript',
kind: CompletionItemKind.Text,
data: 1,
},
{
label: 'JavaScript',
kind: CompletionItemKind.Text,
data: 2,
},
];
}
);
// 这个处理程序解析补全项的附加信息。
connection.onCompletionResolve((item: CompletionItem): CompletionItem => {
if (item.data === 1) {
item.detail = 'TypeScript details';
item.documentation = 'TypeScript documentation';
} else if (item.data === 2) {
item.detail = 'JavaScript details';
item.documentation = 'JavaScript documentation';
}
return item;
});
connection.onHover((params: TextDocumentPositionParams): Hover | null => {
const document = documents.get(params.textDocument.uri);
if (!document) {
return null;
}
const text = document.getText();
const position = params.position;
const offset = document.offsetAt(position);
// 查找 {{attr}} 模式
const regex = /{{(\w+)}}/g;
let match;
while ((match = regex.exec(text)) !== null) {
if (match.index <= offset && offset <= match.index + match[0].length) {
const attr = match[1];
const type = getTypeForAttr(attr);
return {
contents: {
kind: 'markdown',
value: `Type of \`${attr}\`: \`${type}\``,
},
};
}
}
return null;
});
function getTypeForAttr(attr: string): string {
// 这里应该实现实际的类型推断逻辑
// 为了示例,我们返回一个模拟的类型
return `MockType<${attr}>`;
}
// 使文档管理器监听连接的所有相关事件
documents.listen(connection);
// 监听连接
connection.listen();