156 lines
5.4 KiB
TypeScript
156 lines
5.4 KiB
TypeScript
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<string[]>('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.Location[]>(
|
|
'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<string>('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<string>('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<void> | undefined {
|
|
return client ? client.stop() : undefined
|
|
}
|