Files
nx_post_support/client/src/common/server.ts
T

171 lines
5.8 KiB
TypeScript

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as fsapi from "fs-extra"
import { Disposable, env, LogOutputChannel, workspace } 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
// Folder for the server's persistent index cache; omitted without a workspace.
indexCachePath?: string
}
let _disposables: Disposable[] = []
export function disposeServerResources(): void {
_disposables.forEach((disposable) => disposable.dispose())
_disposables = []
}
async function createServer(
settings: ISettings,
serverId: string,
serverName: string,
outputChannel: LogOutputChannel,
initializationOptions: IInitOptions
): Promise<LanguageClient> {
const command = settings.interpreter[0]
if (!command) {
throw new Error("No Python interpreter is configured for the language server.")
}
const cwd = settings.cwd
// Set debugger path needed for debugging python code.
const newEnv = { ...process.env }
const isDebugScript = await fsapi.pathExists(DEBUG_SERVER_SCRIPT_PATH)
const debugRequested = newEnv.USE_DEBUGPY?.toLowerCase() === "true"
if (debugRequested && !isDebugScript) {
throw new Error(`Python debug bootstrap not found: ${DEBUG_SERVER_SCRIPT_PATH}`)
}
const debuggerPath = debugRequested ? await getDebuggerPath() : undefined
if (debugRequested && !debuggerPath) {
throw new Error(
"Python debugging was requested, but the Python Debugger extension did not provide debugpy."
)
}
if (debugRequested && 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 serverScript = debugRequested ? DEBUG_SERVER_SCRIPT_PATH : SERVER_SCRIPT_PATH
const interpreterArgs = settings.interpreter.slice(1)
if (debugRequested && !interpreterArgs.includes("-Xfrozen_modules=off")) {
interpreterArgs.push("-Xfrozen_modules=off")
}
const args = interpreterArgs.concat([serverScript])
traceInfo(`Python debug mode: ${debugRequested ? "enabled" : "disabled"}`)
traceInfo(`Server run command: ${[command, ...args].join(" ")}`)
const serverOptions: ServerOptions = {
command,
args,
options: { cwd, env: newEnv }
}
// Options to control the language client
const tclFileWatcher = workspace.createFileSystemWatcher("**/*.{tcl,psc,def}")
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,
synchronize: {
fileEvents: tclFileWatcher
},
initializationOptions
}
_disposables.push(tclFileWatcher)
return new LanguageClient(serverId, serverName, serverOptions, clientOptions)
}
export async function restartServer(
serverId: string,
serverName: string,
outputChannel: LogOutputChannel,
lsClient?: LanguageClient,
indexCachePath?: string
): Promise<LanguageClient | undefined> {
if (lsClient) {
traceInfo(`Server: Stop requested`)
await lsClient.stop()
disposeServerResources()
}
const projectRoot = await getProjectRoot()
const workspaceSetting = await getWorkspaceSettings(serverId, projectRoot, true)
try {
const newLSClient = await createServer(
workspaceSetting,
serverId,
serverName,
outputChannel,
{
settings: await getExtensionSettings(serverId, true),
globalSettings: await getGlobalSettings(serverId, false),
indexCachePath
}
)
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
}
})
)
await newLSClient.start()
const level = getLSClientTraceLevel(outputChannel.logLevel, env.logLevel)
await newLSClient.setTrace(level)
return newLSClient
} catch (ex) {
traceError(`Server: Start failed: ${ex}`)
disposeServerResources()
return undefined
}
}