python server
This commit is contained in:
Generated
+56
-3
@@ -9,6 +9,8 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vscode/python-extension": "^1.0.5",
|
||||||
|
"fs-extra": "^11.3.0",
|
||||||
"vscode-languageclient": "^9.0.1"
|
"vscode-languageclient": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -36,6 +38,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@vscode/python-extension": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vscode/python-extension/-/python-extension-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-uYhXUrL/gn92mfqhjAwH2+yGOpjloBxj9ekoL4BhUsKcyJMpEg6WlNf3S3si+5x9zlbHHe7FYQNjZEbz1ymI9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.17.1",
|
||||||
|
"vscode": "^1.78.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -43,14 +55,46 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"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==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"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==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "5.1.6",
|
"version": "5.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||||
@@ -82,6 +126,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vscode-jsonrpc": {
|
"node_modules/vscode-jsonrpc": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
"vscode": "^1.96.0"
|
"vscode": "^1.96.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vscode/python-extension": "^1.0.5",
|
||||||
|
"fs-extra": "^11.3.0",
|
||||||
"vscode-languageclient": "^9.0.1"
|
"vscode-languageclient": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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(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`
|
||||||
|
)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
import * as util from "util"
|
||||||
|
import { Disposable, LogOutputChannel } from "vscode"
|
||||||
|
|
||||||
|
type Arguments = unknown[]
|
||||||
|
class OutputChannelLogger {
|
||||||
|
constructor(private readonly channel: LogOutputChannel) {}
|
||||||
|
|
||||||
|
public traceLog(...data: Arguments): void {
|
||||||
|
this.channel.appendLine(util.format(...data))
|
||||||
|
}
|
||||||
|
|
||||||
|
public traceError(...data: Arguments): void {
|
||||||
|
this.channel.error(util.format(...data))
|
||||||
|
}
|
||||||
|
|
||||||
|
public traceWarn(...data: Arguments): void {
|
||||||
|
this.channel.warn(util.format(...data))
|
||||||
|
}
|
||||||
|
|
||||||
|
public traceInfo(...data: Arguments): void {
|
||||||
|
this.channel.info(util.format(...data))
|
||||||
|
}
|
||||||
|
|
||||||
|
public traceVerbose(...data: Arguments): void {
|
||||||
|
this.channel.debug(util.format(...data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel: OutputChannelLogger | undefined
|
||||||
|
export function registerLogger(logChannel: LogOutputChannel): Disposable {
|
||||||
|
channel = new OutputChannelLogger(logChannel)
|
||||||
|
return {
|
||||||
|
dispose: () => {
|
||||||
|
channel = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function traceLog(...args: Arguments): void {
|
||||||
|
channel?.traceLog(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function traceError(...args: Arguments): void {
|
||||||
|
channel?.traceError(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function traceWarn(...args: Arguments): void {
|
||||||
|
channel?.traceWarn(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function traceInfo(...args: Arguments): void {
|
||||||
|
channel?.traceInfo(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function traceVerbose(...args: Arguments): void {
|
||||||
|
channel?.traceVerbose(...args)
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// 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"
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
return setting
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkIfConfigurationChanged(
|
||||||
|
e: ConfigurationChangeEvent,
|
||||||
|
namespace: string
|
||||||
|
): boolean {
|
||||||
|
const settings = [
|
||||||
|
`${namespace}.args`,
|
||||||
|
`${namespace}.path`,
|
||||||
|
`${namespace}.interpreter`,
|
||||||
|
`${namespace}.importStrategy`,
|
||||||
|
`${namespace}.showNotifications`
|
||||||
|
]
|
||||||
|
const changed = settings.map((s) => e.affectsConfiguration(s))
|
||||||
|
return changed.includes(true)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
+92
-30
@@ -14,43 +14,105 @@ import {
|
|||||||
diagnosticHandler,
|
diagnosticHandler,
|
||||||
tclDocumentSymbolProvider
|
tclDocumentSymbolProvider
|
||||||
} from "./common/handlers"
|
} from "./common/handlers"
|
||||||
import * as path from "path"
|
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
|
let client: LanguageClient
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
// The server is implemented in node
|
// This is required to get server name and module. This should be
|
||||||
const serverModule = context.asAbsolutePath(path.join("out", "server", "src", "server.js"))
|
// the first thing that we do in this extension.
|
||||||
console.log(context.asAbsolutePath(path.join("server", "src", "server.js")))
|
const serverInfo = loadServerDefaults()
|
||||||
// If the extension is launched in debug mode then the debug server options are used
|
const serverName = serverInfo.name
|
||||||
// Otherwise the run options are used
|
const serverId = serverInfo.module
|
||||||
const serverOptions: ServerOptions = {
|
|
||||||
run: { module: serverModule, transport: TransportKind.ipc },
|
// Setup logging
|
||||||
debug: {
|
const outputChannel = createOutputChannel(serverName)
|
||||||
module: serverModule,
|
context.subscriptions.push(outputChannel, registerLogger(outputChannel))
|
||||||
transport: TransportKind.ipc
|
|
||||||
}
|
const changeLogLevel = async (c: vscode.LogLevel, g: vscode.LogLevel) => {
|
||||||
}
|
const level = getLSClientTraceLevel(c, g)
|
||||||
// Options to control the language client
|
await client?.setTrace(level)
|
||||||
const clientOptions: LanguageClientOptions = {
|
|
||||||
// Register the server for plain text documents
|
|
||||||
documentSelector: [{ scheme: "file", language: "tcl" }],
|
|
||||||
synchronize: {
|
|
||||||
// Notify the server about file changes to '.clientrc files contained in the workspace
|
|
||||||
fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the language client and start the client.
|
context.subscriptions.push(
|
||||||
client = new LanguageClient(
|
outputChannel.onDidChangeLogLevel(async (e) => {
|
||||||
"languageServerExample",
|
await changeLogLevel(e, vscode.env.logLevel)
|
||||||
"Language Server Example",
|
}),
|
||||||
serverOptions,
|
vscode.env.onDidChangeLogLevel(async (e) => {
|
||||||
clientOptions
|
await changeLogLevel(outputChannel.logLevel, e)
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start the client. This will also launch the server
|
// Log Server information
|
||||||
client.start()
|
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 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()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
//
|
//
|
||||||
const formatCdlProvider = vscode.languages.registerDocumentFormattingEditProvider(
|
const formatCdlProvider = vscode.languages.registerDocumentFormattingEditProvider(
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
3.11
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
"""All the action we need during build"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import urllib.request as url_lib
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import nox # pylint: disable=import-error
|
||||||
|
|
||||||
|
|
||||||
|
@nox.session()
|
||||||
|
def setup(session: nox.Session) -> None:
|
||||||
|
"""Sets up the template for development."""
|
||||||
|
|
||||||
|
session.install(
|
||||||
|
"-t",
|
||||||
|
"./bundled/libs",
|
||||||
|
"--no-cache-dir",
|
||||||
|
"--implementation",
|
||||||
|
"py",
|
||||||
|
"--no-deps",
|
||||||
|
"--upgrade",
|
||||||
|
"-r",
|
||||||
|
"./requirements.txt",
|
||||||
|
)
|
||||||
Generated
-123
@@ -1,123 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "lsp-sample-server",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "lsp-sample-server",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"chevrotain": "^11.0.3",
|
|
||||||
"vscode-languageserver": "^9.0.1",
|
|
||||||
"vscode-languageserver-textdocument": "^1.0.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@chevrotain/cst-dts-gen": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@chevrotain/gast": "11.0.3",
|
|
||||||
"@chevrotain/types": "11.0.3",
|
|
||||||
"lodash-es": "4.17.21"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@chevrotain/gast": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@chevrotain/types": "11.0.3",
|
|
||||||
"lodash-es": "4.17.21"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@chevrotain/regexp-to-ast": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@chevrotain/types": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/@chevrotain/utils": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/chevrotain": {
|
|
||||||
"version": "11.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
|
|
||||||
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@chevrotain/cst-dts-gen": "11.0.3",
|
|
||||||
"@chevrotain/gast": "11.0.3",
|
|
||||||
"@chevrotain/regexp-to-ast": "11.0.3",
|
|
||||||
"@chevrotain/types": "11.0.3",
|
|
||||||
"@chevrotain/utils": "11.0.3",
|
|
||||||
"lodash-es": "4.17.21"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lodash-es": {
|
|
||||||
"version": "4.17.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/vscode-jsonrpc": {
|
|
||||||
"version": "8.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
|
||||||
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vscode-languageserver": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"vscode-languageserver-protocol": "3.17.5"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"installServerIntoExtension": "bin/installServerIntoExtension"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vscode-languageserver-protocol": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"vscode-jsonrpc": "8.2.0",
|
|
||||||
"vscode-languageserver-types": "3.17.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vscode-languageserver-textdocument": {
|
|
||||||
"version": "1.0.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
|
|
||||||
"integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/vscode-languageserver-types": {
|
|
||||||
"version": "3.17.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
|
||||||
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "lsp-sample-server",
|
|
||||||
"description": "Example implementation of a language server in node.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"author": "Microsoft Corporation",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"chevrotain": "^11.0.3",
|
|
||||||
"vscode-languageserver": "^9.0.1",
|
|
||||||
"vscode-languageserver-textdocument": "^1.0.12"
|
|
||||||
},
|
|
||||||
"scripts": {}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[project]
|
||||||
|
name = "server"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"pygls","ply"
|
||||||
|
]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
"""Debugging support for LSP."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import runpy
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def update_sys_path(path_to_add: str) -> None:
|
||||||
|
"""Add given path to `sys.path`."""
|
||||||
|
if path_to_add not in sys.path and os.path.isdir(path_to_add):
|
||||||
|
sys.path.append(path_to_add)
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure debugger is loaded before we load anything else, to debug initialization.
|
||||||
|
debugger_path = os.getenv("DEBUGPY_PATH", None)
|
||||||
|
if debugger_path:
|
||||||
|
if debugger_path.endswith("debugpy"):
|
||||||
|
debugger_path = os.fspath(pathlib.Path(debugger_path).parent)
|
||||||
|
|
||||||
|
update_sys_path(debugger_path)
|
||||||
|
|
||||||
|
# pylint: disable=wrong-import-position,import-error
|
||||||
|
import debugpy
|
||||||
|
|
||||||
|
# 5678 is the default port, If you need to change it update it here
|
||||||
|
# and in launch.json.
|
||||||
|
debugpy.connect(5678)
|
||||||
|
|
||||||
|
# This will ensure that execution is paused as soon as the debugger
|
||||||
|
# connects to VS Code. If you don't want to pause here comment this
|
||||||
|
# line and set breakpoints as appropriate.
|
||||||
|
debugpy.breakpoint()
|
||||||
|
|
||||||
|
SERVER_PATH = os.fspath(pathlib.Path(__file__).parent / "lsp_server.py")
|
||||||
|
# NOTE: Set breakpoint in `lsp_server.py` before continuing.
|
||||||
|
runpy.run_path(SERVER_PATH, run_name="__main__")
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""Implementation of tool support over LSP."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
import traceback
|
||||||
|
from typing import Any, Optional, Sequence
|
||||||
|
|
||||||
|
|
||||||
|
# **********************************************************
|
||||||
|
# Update sys.path before importing any bundled libraries.
|
||||||
|
# **********************************************************
|
||||||
|
def update_sys_path(path_to_add: str, strategy: str) -> None:
|
||||||
|
"""Add given path to `sys.path`."""
|
||||||
|
if path_to_add not in sys.path and os.path.isdir(path_to_add):
|
||||||
|
if strategy == "useBundled":
|
||||||
|
sys.path.insert(0, path_to_add)
|
||||||
|
elif strategy == "fromEnvironment":
|
||||||
|
sys.path.append(path_to_add)
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure that we can import LSP libraries, and other bundled libraries.
|
||||||
|
update_sys_path(
|
||||||
|
os.fspath(pathlib.Path(__file__).parent.parent / "libs"),
|
||||||
|
os.getenv("LS_IMPORT_STRATEGY", "useBundled"),
|
||||||
|
)
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import {
|
|
||||||
createConnection,
|
|
||||||
TextDocuments,
|
|
||||||
ProposedFeatures,
|
|
||||||
TextDocumentSyncKind,
|
|
||||||
InitializeParams,
|
|
||||||
InitializeResult,
|
|
||||||
CompletionItemKind,
|
|
||||||
TextDocumentPositionParams,
|
|
||||||
CompletionItem,
|
|
||||||
TextEdit,
|
|
||||||
Range,
|
|
||||||
Position
|
|
||||||
} from "vscode-languageserver/node"
|
|
||||||
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
||||||
|
|
||||||
// Create a connection for the server, using Node's IPC as a transport.
|
|
||||||
// Also include all preview / proposed LSP features.
|
|
||||||
let connection = createConnection(ProposedFeatures.all)
|
|
||||||
|
|
||||||
// Create a simple text document manager.
|
|
||||||
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument)
|
|
||||||
|
|
||||||
connection.onInitialize((params: InitializeParams) => {
|
|
||||||
let capabilities = params.capabilities
|
|
||||||
|
|
||||||
// Does the client support the `workspace/configuration` request?
|
|
||||||
// If not, we fall back using global settings.
|
|
||||||
const result: InitializeResult = {
|
|
||||||
capabilities: {
|
|
||||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
||||||
// Tell the client that this server supports code completion.
|
|
||||||
completionProvider: {
|
|
||||||
resolveProvider: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
// This handler provides the initial list of the completion items.
|
|
||||||
connection.onCompletion((params: TextDocumentPositionParams): CompletionItem[] => {
|
|
||||||
const doc = documents.get(params.textDocument.uri)
|
|
||||||
if (!doc) return []
|
|
||||||
|
|
||||||
const text = doc.getText()
|
|
||||||
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
connection.onDocumentFormatting(async (params, token) => {
|
|
||||||
const document = documents.get(params.textDocument.uri)
|
|
||||||
if (!document) return []
|
|
||||||
|
|
||||||
const originalText = document.getText()
|
|
||||||
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
documents.onDidChangeContent(async (change) => {
|
|
||||||
connection.console.log("Document has changed")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Make the text document manager listen on the connection
|
|
||||||
// for open, change and close text document events
|
|
||||||
documents.listen(connection)
|
|
||||||
|
|
||||||
// Listen on the connection
|
|
||||||
connection.listen()
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2020",
|
|
||||||
"lib": ["es2020"],
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"sourceMap": true,
|
|
||||||
"strict": true,
|
|
||||||
"outDir": "out",
|
|
||||||
"rootDir": "src"
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"exclude": ["node_modules", ".vscode-test"]
|
|
||||||
}
|
|
||||||
Generated
+83
@@ -0,0 +1,83 @@
|
|||||||
|
version = 1
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "attrs"
|
||||||
|
version = "25.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cattrs"
|
||||||
|
version = "25.1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "attrs" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/57/2b/561d78f488dcc303da4639e02021311728fb7fda8006dd2835550cddd9ed/cattrs-25.1.1.tar.gz", hash = "sha256:c914b734e0f2d59e5b720d145ee010f1fd9a13ee93900922a2f3f9d593b8382c", size = 435016 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/b0/215274ef0d835bbc1056392a367646648b6084e39d489099959aefcca2af/cattrs-25.1.1-py3-none-any.whl", hash = "sha256:1b40b2d3402af7be79a7e7e097a9b4cd16d4c06e6d526644b0b26a063a1cc064", size = 69386 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lsprotocol"
|
||||||
|
version = "2023.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "attrs" },
|
||||||
|
{ name = "cattrs" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9d/f6/6e80484ec078d0b50699ceb1833597b792a6c695f90c645fbaf54b947e6f/lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d", size = 69434 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/37/2351e48cb3309673492d3a8c59d407b75fb6630e560eb27ecd4da03adc9a/lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2", size = 70826 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ply"
|
||||||
|
version = "3.11"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygls"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cattrs" },
|
||||||
|
{ name = "lsprotocol" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/86/b9/41d173dad9eaa9db9c785a85671fc3d68961f08d67706dc2e79011e10b5c/pygls-1.3.1.tar.gz", hash = "sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018", size = 45527 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/19/b74a10dd24548e96e8c80226cbacb28b021bc3a168a7d2709fb0d0185348/pygls-1.3.1-py3-none-any.whl", hash = "sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e", size = 56031 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "server"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "ply" },
|
||||||
|
{ name = "pygls" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "ply" },
|
||||||
|
{ name = "pygls" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.14.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906 },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user