import * as path from 'path' import * as vscode from 'vscode' import * as fs from 'fs' import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node' let client: LanguageClient | undefined function findInSystemPath(binary: string): string | null { const pathDirs = process.env.PATH?.split(path.delimiter) || [] for (const dir of pathDirs) { const fullPath = path.join(dir, binary) if (fs.existsSync(fullPath)) { return fullPath } } return null } function findExecutable(context: vscode.ExtensionContext, configPath: string | undefined, bundledRelativePath: string, systemBinaryName: string): string | null { // 1. 优先使用用户配置的路径 if (configPath) { return configPath } // 2. 查找插件自带的可执行文件 const bundledPath = context.asAbsolutePath(bundledRelativePath) if (fs.existsSync(bundledPath)) { return bundledPath } // 3. 从系统 PATH 查找 return findInSystemPath(systemBinaryName) } export function activate(context: vscode.ExtensionContext) { const config = vscode.workspace.getConfiguration('tsl') let serverArguments = config.get('server.arguments') || [] context.subscriptions.push( vscode.commands.registerCommand('tsl.showReferences', async (data?: any) => { if (!data || typeof data !== 'object') { vscode.window.showErrorMessage('TSL: invalid reference payload') return } const uriValue = (data as any).uri const positionValue = (data as any).position if (typeof uriValue !== 'string' || !positionValue || typeof positionValue !== 'object') { vscode.window.showErrorMessage('TSL: missing uri/position in reference payload') return } const line = (positionValue as any).line const character = (positionValue as any).character if (typeof line !== 'number' || typeof character !== 'number') { vscode.window.showErrorMessage('TSL: invalid position in reference payload') return } if (client && !client.isRunning()) { await client.start() } const uri = vscode.Uri.parse(uriValue) const position = new vscode.Position(line, character) const locations = await vscode.commands.executeCommand( 'vscode.executeReferenceProvider', uri, position ) if (!locations || locations.length === 0) { vscode.window.showInformationMessage('TSL: no references found') return } await vscode.commands.executeCommand('editor.action.showReferences', uri, position, locations) }) ) const serverBinary = process.platform === 'win32' ? 'tsl-server.exe' : 'tsl-server' let serverExe = findExecutable( context, config.get('server.executable'), path.join('bin', serverBinary), serverBinary ) if (!serverExe) { vscode.window.showErrorMessage( "Cannot find tsl-server. Please install it globally or configure 'tsl.server.executable' in settings." ) return } const interpreterBinary = process.platform === 'win32' ? 'tsl.exe' : 'tsl' let interpreterExe = findExecutable( context, config.get('interpreter.executable'), "", interpreterBinary ) if (!interpreterExe) { vscode.window.showWarningMessage( "Cannot find TSL interpreter. Some features may not work. Please install it globally or configure 'tsl.interpreter.executable' in settings." ) } if (interpreterExe) { let interpreterDir = path.dirname(interpreterExe) interpreterDir = interpreterDir.replace(/\\/g, '/') + '/' serverArguments = serverArguments.filter(arg => !arg.startsWith('--interpreter=')) serverArguments.push(`--interpreter=${interpreterDir}`) } const runArgs = [...serverArguments] const debugArgs = serverArguments.map(arg => arg.startsWith('--log=') ? '--log=trace' : arg) if (!debugArgs.some(arg => arg.startsWith('--log='))) debugArgs.push('--log=trace') if (!debugArgs.includes('--log-stderr')) debugArgs.push('--log-stderr') const serverOptions: ServerOptions = { run: { command: serverExe, transport: TransportKind.stdio, args: runArgs }, debug: { command: serverExe, transport: TransportKind.stdio, args: debugArgs } } const clientOptions: LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'tsl' }], synchronize: { fileEvents: [ vscode.workspace.createFileSystemWatcher('**/*.tsl'), vscode.workspace.createFileSystemWatcher('**/*.tsf') ] } } client = new LanguageClient( 'tslLanguageServer', 'TSL Language Server', serverOptions, clientOptions ) // 保存解释器路径到上下文,供其他命令使用 if (interpreterExe) { context.globalState.update('tsl.interpreterPath', interpreterExe) } client.start() } export function deactivate(): Thenable | undefined { return client ? client.stop() : undefined }