test rust lsp

This commit is contained in:
2025-09-07 07:18:18 +02:00
parent a8bf55882e
commit 7a3e3ae9f7
330 changed files with 1714 additions and 70094 deletions
+56
View File
@@ -0,0 +1,56 @@
//@ts-check
const esbuild = require('esbuild');
/**
* @typedef {import('esbuild').BuildOptions} BuildOptions
*/
/** @type BuildOptions */
const sharedDesktopOptions = {
bundle: true,
external: ['vscode'],
target: 'es2020',
platform: 'node',
sourcemap: true,
};
/** @type BuildOptions */
const desktopOptions = {
entryPoints: ['src/extension.ts'],
outfile: 'dist/desktop/extension.js',
format: 'cjs',
...sharedDesktopOptions,
};
function createContexts() {
return Promise.all([esbuild.context(desktopOptions)]);
}
createContexts()
.then((contexts) => {
if (process.argv[2] === '--watch') {
const promises = [];
for (const context of contexts) {
promises.push(context.watch());
}
return Promise.all(promises).then(() => {
return undefined;
});
} else {
const promises = [];
for (const context of contexts) {
promises.push(context.rebuild());
}
Promise.all(promises)
.then(async () => {
for (const context of contexts) {
await context.dispose();
}
})
.then(() => {
return undefined;
})
.catch(console.error);
}
})
.catch(console.error);
+31 -23
View File
@@ -10,31 +10,31 @@
"license": "MIT",
"dependencies": {
"@vscode/python-extension": "^1.0.5",
"fs-extra": "^11.3.0",
"vscode-languageclient": "^9.0.1"
"fs-extra": "11.3.1"
},
"devDependencies": {
"@types/node": "^22.10.5",
"@types/vscode": "^1.96.0"
"@types/node": "^22.18.0",
"@types/vscode": "^1.96.0",
"vscode-languageclient": "^9.0.1"
},
"engines": {
"vscode": "^1.96.0"
}
},
"node_modules/@types/node": {
"version": "22.10.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
"integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
"version": "22.18.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz",
"integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
"undici-types": "~6.21.0"
}
},
"node_modules/@types/vscode": {
"version": "1.96.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
"integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==",
"version": "1.103.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.103.0.tgz",
"integrity": "sha512-o4hanZAQdNfsKecexq9L3eHICd0AAvdbLk6hA60UzGXbGH/q8b/9xv2RgR7vV3ZcHuyKVq7b37IGd/+gM4Tu+Q==",
"dev": true,
"license": "MIT"
},
@@ -52,21 +52,23 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/fs-extra": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
"version": "11.3.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
@@ -84,9 +86,9 @@
"license": "ISC"
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
@@ -99,6 +101,7 @@
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -108,9 +111,10 @@
}
},
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -120,9 +124,9 @@
}
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
@@ -139,6 +143,7 @@
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -148,6 +153,7 @@
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
"dev": true,
"license": "MIT",
"dependencies": {
"minimatch": "^5.1.0",
@@ -162,6 +168,7 @@
"version": "3.17.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
"dev": true,
"license": "MIT",
"dependencies": {
"vscode-jsonrpc": "8.2.0",
@@ -172,6 +179,7 @@
"version": "3.17.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
"dev": true,
"license": "MIT"
}
}
+11 -5
View File
@@ -8,11 +8,17 @@
},
"dependencies": {
"@vscode/python-extension": "^1.0.5",
"fs-extra": "^11.3.0",
"vscode-languageclient": "^9.0.1"
"fs-extra": "11.3.1"
},
"devDependencies": {
"@types/node": "^22.10.5",
"@types/vscode": "^1.96.0"
"@types/node": "^22.18.0",
"@types/vscode": "^1.96.0",
"vscode-languageclient": "^9.0.1"
},
"scripts": {
"compile": "tsc -b",
"watch": "tsc -b -w",
"lint": "eslint",
"esbuild": "^0.25.0"
}
}
}
-14
View File
@@ -1,14 +0,0 @@
import * as path from "path"
const folderName = path.basename(__dirname)
export const EXTENSION_ROOT_DIR =
folderName === "common"
? path.dirname(path.dirname(path.dirname(__dirname)))
: path.dirname(__dirname)
export const BUNDLED_PYTHON_SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, "server")
export const SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, "src", `lsp_server.py`)
export const DEBUG_SERVER_SCRIPT_PATH = path.join(
BUNDLED_PYTHON_SCRIPTS_DIR,
"src",
`_debug_server.py`
)
-86
View File
@@ -1,86 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/naming-convention */
import { commands, Disposable, Event, EventEmitter, Uri } from "vscode"
import { traceError, traceLog } from "./log/logging"
import { PythonExtension, ResolvedEnvironment } from "@vscode/python-extension"
export interface IInterpreterDetails {
path?: string[]
resource?: Uri
}
const onDidChangePythonInterpreterEvent = new EventEmitter<IInterpreterDetails>()
export const onDidChangePythonInterpreter: Event<IInterpreterDetails> =
onDidChangePythonInterpreterEvent.event
let _api: PythonExtension | undefined
async function getPythonExtensionAPI(): Promise<PythonExtension | undefined> {
if (_api) {
return _api
}
_api = await PythonExtension.api()
return _api
}
export async function initializePython(disposables: Disposable[]): Promise<void> {
try {
const api = await getPythonExtensionAPI()
if (api) {
disposables.push(
api.environments.onDidChangeActiveEnvironmentPath((e) => {
onDidChangePythonInterpreterEvent.fire({
path: [e.path],
resource: e.resource?.uri
})
})
)
traceLog("Waiting for interpreter from python extension.")
onDidChangePythonInterpreterEvent.fire(await getInterpreterDetails())
}
} catch (error) {
traceError("Error initializing python: ", error)
}
}
export async function resolveInterpreter(
interpreter: string[]
): Promise<ResolvedEnvironment | undefined> {
const api = await getPythonExtensionAPI()
return api?.environments.resolveEnvironment(interpreter[0])
}
export async function getInterpreterDetails(resource?: Uri): Promise<IInterpreterDetails> {
const api = await getPythonExtensionAPI()
const environment = await api?.environments.resolveEnvironment(
api?.environments.getActiveEnvironmentPath(resource)
)
if (environment?.executable.uri && checkVersion(environment)) {
return { path: [environment?.executable.uri.fsPath], resource }
}
return { path: undefined, resource }
}
export async function getDebuggerPath(): Promise<string | undefined> {
const api = await getPythonExtensionAPI()
return api?.debug.getDebuggerPackagePath()
}
export async function runPythonExtensionCommand(command: string, ...rest: any[]) {
await getPythonExtensionAPI()
return await commands.executeCommand(command, ...rest)
}
export function checkVersion(resolved: ResolvedEnvironment | undefined): boolean {
const version = resolved?.version
if (version?.major === 3 && version?.minor >= 11) {
return true
}
traceError(`Python version ${version?.major}.${version?.minor} is not supported.`)
traceError(`Selected python path: ${resolved?.executable.uri?.fsPath}`)
traceError("Supported versions are 3.11 and above.")
return false
}
-131
View File
@@ -1,131 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as fsapi from "fs-extra"
import { Disposable, env, LogOutputChannel } from "vscode"
import { State } from "vscode-languageclient"
import {
LanguageClient,
LanguageClientOptions,
RevealOutputChannelOn,
ServerOptions
} from "vscode-languageclient/node"
import { DEBUG_SERVER_SCRIPT_PATH, SERVER_SCRIPT_PATH } from "./constants"
import { traceError, traceInfo, traceVerbose } from "./log/logging"
import { getDebuggerPath } from "./python"
import {
getExtensionSettings,
getGlobalSettings,
getWorkspaceSettings,
ISettings
} from "./settings"
import { getLSClientTraceLevel, getProjectRoot } from "./utilities"
import { isVirtualWorkspace } from "./vscodeapi"
export type IInitOptions = { settings: ISettings[]; globalSettings: ISettings }
async function createServer(
settings: ISettings,
serverId: string,
serverName: string,
outputChannel: LogOutputChannel,
initializationOptions: IInitOptions
): Promise<LanguageClient> {
const command = settings.interpreter[0]
const cwd = settings.cwd
// Set debugger path needed for debugging python code.
const newEnv = { ...process.env }
const debuggerPath = await getDebuggerPath()
const isDebugScript = await fsapi.pathExists(DEBUG_SERVER_SCRIPT_PATH)
if (newEnv.USE_DEBUGPY && debuggerPath) {
newEnv.DEBUGPY_PATH = debuggerPath
} else {
newEnv.USE_DEBUGPY = "False"
}
// Set import strategy
newEnv.LS_IMPORT_STRATEGY = settings.importStrategy
// Set notification type
newEnv.LS_SHOW_NOTIFICATION = settings.showNotifications
const args =
newEnv.USE_DEBUGPY === "False" || !isDebugScript
? settings.interpreter.slice(1).concat([SERVER_SCRIPT_PATH])
: settings.interpreter.slice(1).concat([DEBUG_SERVER_SCRIPT_PATH])
traceInfo(`Server run command: ${[command, ...args].join(" ")}`)
const serverOptions: ServerOptions = {
command,
args,
options: { cwd, env: newEnv }
}
// Options to control the language client
const clientOptions: LanguageClientOptions = {
// Register the server for python documents
documentSelector: isVirtualWorkspace()
? [{ language: "tcl" }]
: [
{ scheme: "file", language: "tcl" },
{ scheme: "untitled", language: "tcl" },
{ scheme: "vscode-notebook", language: "tcl" },
{ scheme: "vscode-notebook-cell", language: "tcl" }
],
outputChannel: outputChannel,
traceOutputChannel: outputChannel,
revealOutputChannelOn: RevealOutputChannelOn.Never,
initializationOptions
}
return new LanguageClient(serverId, serverName, serverOptions, clientOptions)
}
let _disposables: Disposable[] = []
export async function restartServer(
serverId: string,
serverName: string,
outputChannel: LogOutputChannel,
lsClient?: LanguageClient
): Promise<LanguageClient | undefined> {
if (lsClient) {
traceInfo(`Server: Stop requested`)
await lsClient.stop()
_disposables.forEach((d) => d.dispose())
_disposables = []
}
const projectRoot = await getProjectRoot()
const workspaceSetting = await getWorkspaceSettings(serverId, projectRoot, true)
const newLSClient = await createServer(workspaceSetting, serverId, serverName, outputChannel, {
settings: await getExtensionSettings(serverId, true),
globalSettings: await getGlobalSettings(serverId, false)
})
traceInfo(`Server: Start requested.`)
_disposables.push(
newLSClient.onDidChangeState((e) => {
switch (e.newState) {
case State.Stopped:
traceVerbose(`Server State: Stopped`)
break
case State.Starting:
traceVerbose(`Server State: Starting`)
break
case State.Running:
traceVerbose(`Server State: Running`)
break
}
})
)
try {
await newLSClient.start()
} catch (ex) {
traceError(`Server: Start failed: ${ex}`)
return undefined
}
const level = getLSClientTraceLevel(outputChannel.logLevel, env.logLevel)
await newLSClient.setTrace(level)
return newLSClient
}
-136
View File
@@ -1,136 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {
ConfigurationChangeEvent,
ConfigurationScope,
WorkspaceConfiguration,
WorkspaceFolder
} from "vscode"
import { getInterpreterDetails } from "./python"
import { getConfiguration, getWorkspaceFolders } from "./vscodeapi"
export interface ISettings {
cwd: string
workspace: string
args: string[]
path: string[]
interpreter: string[]
importStrategy: string
showNotifications: string
}
export function getExtensionSettings(
namespace: string,
includeInterpreter?: boolean
): Promise<ISettings[]> {
return Promise.all(
getWorkspaceFolders().map((w) => getWorkspaceSettings(namespace, w, includeInterpreter))
)
}
function resolveVariables(value: string[], workspace?: WorkspaceFolder): string[] {
const substitutions = new Map<string, string>()
const home = process.env.HOME || process.env.USERPROFILE
if (home) {
substitutions.set("${userHome}", home)
}
if (workspace) {
substitutions.set("${workspaceFolder}", workspace.uri.fsPath)
}
substitutions.set("${cwd}", process.cwd())
getWorkspaceFolders().forEach((w) => {
substitutions.set("${workspaceFolder:" + w.name + "}", w.uri.fsPath)
})
return value.map((s) => {
for (const [key, value] of substitutions) {
s = s.replace(key, value)
}
return s
})
}
export function getInterpreterFromSetting(namespace: string, scope?: ConfigurationScope) {
const config = getConfiguration(namespace, scope)
return config.get<string[]>("interpreter")
}
export async function getWorkspaceSettings(
namespace: string,
workspace: WorkspaceFolder,
includeInterpreter?: boolean
): Promise<ISettings> {
const config = getConfiguration(namespace, workspace.uri)
let interpreter: string[] = []
if (includeInterpreter) {
interpreter = getInterpreterFromSetting(namespace, workspace) ?? []
if (interpreter.length === 0) {
interpreter = (await getInterpreterDetails(workspace.uri)).path ?? []
}
}
const workspaceSetting = {
cwd: workspace.uri.fsPath,
workspace: workspace.uri.toString(),
args: resolveVariables(config.get<string[]>(`args`) ?? [], workspace),
path: resolveVariables(config.get<string[]>(`path`) ?? [], workspace),
interpreter: resolveVariables(interpreter, workspace),
importStrategy: config.get<string>(`importStrategy`) ?? "useBundled",
showNotifications: config.get<string>(`showNotifications`) ?? "off",
formatter: config.get<boolean>(`formatter`) ?? true,
inlayHint: config.get<boolean>(`inlayHint`) ?? true
}
return workspaceSetting
}
function getGlobalValue<T>(config: WorkspaceConfiguration, key: string, defaultValue: T): T {
const inspect = config.inspect<T>(key)
return inspect?.globalValue ?? inspect?.defaultValue ?? defaultValue
}
export async function getGlobalSettings(
namespace: string,
includeInterpreter?: boolean
): Promise<ISettings> {
const config = getConfiguration(namespace)
let interpreter: string[] = []
if (includeInterpreter) {
interpreter = getGlobalValue<string[]>(config, "interpreter", [])
if (interpreter === undefined || interpreter.length === 0) {
interpreter = (await getInterpreterDetails()).path ?? []
}
}
const setting = {
cwd: process.cwd(),
workspace: process.cwd(),
args: getGlobalValue<string[]>(config, "args", []),
path: getGlobalValue<string[]>(config, "path", []),
interpreter: interpreter,
importStrategy: getGlobalValue<string>(config, "importStrategy", "useBundled"),
showNotifications: getGlobalValue<string>(config, "showNotifications", "off"),
formatter: config.get<boolean>(`formatter`) ?? true,
inlayHint: config.get<boolean>(`inlayHint`) ?? true
}
return setting
}
export function checkIfConfigurationChanged(
e: ConfigurationChangeEvent,
namespace: string
): boolean {
const settings = [
`${namespace}.args`,
`${namespace}.path`,
`${namespace}.interpreter`,
`${namespace}.importStrategy`,
`${namespace}.showNotifications`,
`${namespace}.formatter`,
`${namespace}.inlayHint`
]
const changed = settings.map((s) => e.affectsConfiguration(s))
return changed.includes(true)
}
-18
View File
@@ -1,18 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as path from "path"
import * as fs from "fs-extra"
import { EXTENSION_ROOT_DIR } from "./constants"
export interface IServerInfo {
name: string
module: string
}
export function loadServerDefaults(): IServerInfo {
const packageJson = path.join(EXTENSION_ROOT_DIR, "package.json")
const content = fs.readFileSync(packageJson).toString()
const config = JSON.parse(content)
return config.serverInfo as IServerInfo
}
-69
View File
@@ -1,69 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as fs from "fs-extra"
import * as path from "path"
import { LogLevel, Uri, WorkspaceFolder } from "vscode"
import { Trace } from "vscode-jsonrpc/node"
import { getWorkspaceFolders } from "./vscodeapi"
function logLevelToTrace(logLevel: LogLevel): Trace {
switch (logLevel) {
case LogLevel.Error:
case LogLevel.Warning:
case LogLevel.Info:
return Trace.Messages
case LogLevel.Debug:
case LogLevel.Trace:
return Trace.Verbose
case LogLevel.Off:
default:
return Trace.Off
}
}
export function getLSClientTraceLevel(channelLogLevel: LogLevel, globalLogLevel: LogLevel): Trace {
if (channelLogLevel === LogLevel.Off) {
return logLevelToTrace(globalLogLevel)
}
if (globalLogLevel === LogLevel.Off) {
return logLevelToTrace(channelLogLevel)
}
const level = logLevelToTrace(
channelLogLevel <= globalLogLevel ? channelLogLevel : globalLogLevel
)
return level
}
export async function getProjectRoot(): Promise<WorkspaceFolder> {
const workspaces: readonly WorkspaceFolder[] = getWorkspaceFolders()
if (workspaces.length === 0) {
return {
uri: Uri.file(process.cwd()),
name: path.basename(process.cwd()),
index: 0
}
} else if (workspaces.length === 1) {
return workspaces[0]
} else {
let rootWorkspace = workspaces[0]
let root = undefined
for (const w of workspaces) {
if (await fs.pathExists(w.uri.fsPath)) {
root = w.uri.fsPath
rootWorkspace = w
break
}
}
for (const w of workspaces) {
if (root && root.length > w.uri.fsPath.length && (await fs.pathExists(w.uri.fsPath))) {
root = w.uri.fsPath
rootWorkspace = w
}
}
return rootWorkspace
}
}
-52
View File
@@ -1,52 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
commands,
ConfigurationScope,
Disposable,
LogOutputChannel,
Uri,
window,
workspace,
WorkspaceConfiguration,
WorkspaceFolder
} from "vscode"
export function createOutputChannel(name: string): LogOutputChannel {
return window.createOutputChannel(name, { log: true })
}
export function getConfiguration(
config: string,
scope?: ConfigurationScope
): WorkspaceConfiguration {
return workspace.getConfiguration(config, scope)
}
export function registerCommand(
command: string,
callback: (...args: any[]) => any,
thisArg?: any
): Disposable {
return commands.registerCommand(command, callback, thisArg)
}
export const { onDidChangeConfiguration } = workspace
export function isVirtualWorkspace(): boolean {
const isVirtual =
workspace.workspaceFolders &&
workspace.workspaceFolders.every((f) => f.uri.scheme !== "file")
return !!isVirtual
}
export function getWorkspaceFolders(): readonly WorkspaceFolder[] {
return workspace.workspaceFolders ?? []
}
export function getWorkspaceFolder(uri: Uri): WorkspaceFolder | undefined {
return workspace.getWorkspaceFolder(uri)
}
+44 -90
View File
@@ -3,7 +3,7 @@ import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind
Executable
} from "vscode-languageclient/node"
import {
formatCdlFile,
@@ -15,105 +15,59 @@ import {
cdlDocumentSymbolProvider,
defDocumentSymbolProvider
} from "./common/handlers"
import { registerLogger, traceError, traceLog, traceVerbose } from "./common/log/logging"
import {
checkVersion,
getInterpreterDetails,
initializePython,
onDidChangePythonInterpreter,
resolveInterpreter
} from "./common/python"
import { restartServer } from "./common/server"
import { checkIfConfigurationChanged, getInterpreterFromSetting } from "./common/settings"
import { loadServerDefaults } from "./common/setup"
import { getLSClientTraceLevel } from "./common/utilities"
import { createOutputChannel, onDidChangeConfiguration, registerCommand } from "./common/vscodeapi"
let client: LanguageClient
interface InitializationOptions {
perFileParser: Record<string, string>
}
export async function activate(context: vscode.ExtensionContext) {
// This is required to get server name and module. This should be
// the first thing that we do in this extension.
const serverInfo = loadServerDefaults()
const serverName = serverInfo.name
const serverId = serverInfo.module
// Allow overriding the server executable path via environment variable so
// the extension can attach to a debug-run server started externally.
// If `TCL_LSP_SERVER_PATH` is set, use that path; otherwise fall back to
// the packaged server inside the extension.
let serverCommand: string
const serverModule = vscode.Uri.joinPath(
context.extensionUri,
"server",
"target",
...(process.platform === "win32"
? ["x86_64-pc-windows-gnu", "release", "lsp-main.exe"]
: ["x86_64-unknown-linux-gnu", "release", "lsp-main"])
)
serverCommand = serverModule.fsPath
// Setup logging
const outputChannel = createOutputChannel(serverName)
context.subscriptions.push(outputChannel, registerLogger(outputChannel))
const changeLogLevel = async (c: vscode.LogLevel, g: vscode.LogLevel) => {
const level = getLSClientTraceLevel(c, g)
await client?.setTrace(level)
const channel = vscode.window.createOutputChannel("TCL LSP Server", "log")
const run: Executable = {
command: serverCommand,
options: { env: process.env }
}
const serverOptions: ServerOptions = {
run,
debug: run
}
context.subscriptions.push(
outputChannel.onDidChangeLogLevel(async (e) => {
await changeLogLevel(e, vscode.env.logLevel)
}),
vscode.env.onDidChangeLogLevel(async (e) => {
await changeLogLevel(outputChannel.logLevel, e)
})
)
// Log Server information
traceLog(`Name: ${serverInfo.name}`)
traceLog(`Module: ${serverInfo.module}`)
traceVerbose(`Full Server Info: ${JSON.stringify(serverInfo)}`)
const runServer = async () => {
const interpreter = getInterpreterFromSetting(serverId)
if (interpreter && interpreter.length > 0) {
if (checkVersion(await resolveInterpreter(interpreter))) {
traceVerbose(
`Using interpreter from ${serverInfo.module}.interpreter: ${interpreter.join(" ")}`
)
client = await restartServer(serverId, serverName, outputChannel, client)
}
return
const initializationOptions: InitializationOptions = {
perFileParser: {
tcl: "tcl"
}
const interpreterDetails = await getInterpreterDetails()
if (interpreterDetails.path) {
traceVerbose(
`Using interpreter from Python extension: ${interpreterDetails.path.join(" ")}`
)
client = await restartServer(serverId, serverName, outputChannel, client)
return
}
traceError(
"Python interpreter missing:\r\n" +
"[Option 1] Select python interpreter using the ms-python.python.\r\n" +
`[Option 2] Set an interpreter using "${serverId}.interpreter" setting.\r\n` +
"Please use Python 3.8 or greater."
)
}
context.subscriptions.push(
onDidChangePythonInterpreter(async () => {
await runServer()
}),
onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => {
if (checkIfConfigurationChanged(e, serverId)) {
await runServer()
}
}),
registerCommand(`${serverId}.restart`, async () => {
await runServer()
})
)
const clientOptions: LanguageClientOptions = {
documentSelector: [{ language: "tcl" }],
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.tcl")
},
outputChannel: channel,
initializationOptions
}
setImmediate(async () => {
const interpreter = getInterpreterFromSetting(serverId)
if (interpreter === undefined || interpreter.length === 0) {
traceLog(`Python extension loading`)
await initializePython(context.subscriptions)
traceLog(`Python extension loaded`)
} else {
await runServer()
}
})
client = new LanguageClient("lspClient", "LSP Client", serverOptions, clientOptions)
try {
await client.start()
} catch (error) {
client.error(`Start failed`, error, "force")
}
//
const formatCdlProvider = vscode.languages.registerDocumentFormattingEditProvider(
+17 -8
View File
@@ -1,12 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"lib": ["ES2019"],
"rootDir": "./src",
"skipLibCheck": true,
"lib": ["ES2024", "webworker"],
"types": ["vscode"],
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./out",
"rootDir": "src",
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules", ".vscode-test"]
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"declaration": true,
"stripInternal": true,
"sourceMap": true,
"declarationMap": true,
"noUnusedLocals": false,
"noUnusedParameters": false
}
}
+1
View File
@@ -0,0 +1 @@
{"root":["./src/extension.ts","./src/common/handlers.ts","./src/common/log/logging.ts"],"version":"5.7.2"}