test rust lsp
This commit is contained in:
@@ -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);
|
||||
Generated
+31
-23
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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`
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"root":["./src/extension.ts","./src/common/handlers.ts","./src/common/log/logging.ts"],"version":"5.7.2"}
|
||||
Reference in New Issue
Block a user