From 28d8509020c69c31a5228fdb2b73079eb8519a78 Mon Sep 17 00:00:00 2001 From: Denis Loginov <137337+dinvlad@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:30:22 -0400 Subject: [PATCH] Use virtualenv for VS Code language server (#48) --- client/vscode/src/extension.ts | 93 +++++++++++++++------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/client/vscode/src/extension.ts b/client/vscode/src/extension.ts index 6fddc2e..68740ed 100644 --- a/client/vscode/src/extension.ts +++ b/client/vscode/src/extension.ts @@ -19,17 +19,17 @@ "use strict"; import { execFile } from "child_process"; -import * as net from "net"; +import * as fs from "fs"; import * as path from "path"; -import { promisify } from "util"; -import { ExtensionContext, workspace } from "vscode"; +import { ExtensionContext, window, workspace } from "vscode"; import { LanguageClient, LanguageClientOptions, ServerOptions } from "vscode-languageclient/node"; -const version: string = require("../package.json").version; - const language = "wdl"; -let client: LanguageClient; +const log = window.createOutputChannel("WDL Language Server"); + +const version: string = require("../package.json").version; +log.appendLine(`WDL Language Server version: ${version}`); function getClientOptions(): LanguageClientOptions { return { @@ -37,7 +37,7 @@ function getClientOptions(): LanguageClientOptions { documentSelector: [ { scheme: "file", language }, ], - outputChannelName: "WDL Language Server", + outputChannel: log, synchronize: { // Notify the server about changes to .wdl files contained in the workspace fileEvents: workspace.createFileSystemWatcher("**/*.wdl"), @@ -45,26 +45,6 @@ function getClientOptions(): LanguageClientOptions { }; } -function isStartedInDebugMode(): boolean { - return process.env.VSCODE_DEBUG_MODE === "true"; -} - -function startLangServerTCP(addr: number): LanguageClient { - const serverOptions: ServerOptions = () => { - return new Promise((resolve, reject) => { - const clientSocket = new net.Socket(); - clientSocket.connect(addr, "127.0.0.1", () => { - resolve({ - reader: clientSocket, - writer: clientSocket, - }); - }); - }); - }; - - return new LanguageClient(language, serverOptions, getClientOptions()); -} - function startLangServer( command: string, args: string[], cwd: string, ): LanguageClient { @@ -77,37 +57,44 @@ function startLangServer( return new LanguageClient(language, serverOptions, getClientOptions()); } -export async function activate(context: ExtensionContext) { - if (isStartedInDebugMode()) { - // Development - Run the server manually - client = startLangServerTCP(2087); - } else { - // Production - Client is going to run the server (for use within `.vsix` package) - const cwd = path.join(__dirname, "../"); - const pythonPath = workspace.getConfiguration(language).get("pythonPath"); +async function exec(cmd: string, ...args: string[]) { + return new Promise(resolve => { + execFile(cmd, args, (err, stdout, stderr) => { + log.appendLine(`${cmd} ${args.join(" ")}`); + if (err) { + log.appendLine(err.message); + } + log.appendLine(stdout); + log.appendLine(stderr); + if (err) { + log.show(); + } + resolve(); + }); + }); +} - if (!pythonPath) { - throw new Error(`${language}.pythonPath is not set`); - } +let client: LanguageClient; - try { - const { stdout } = await promisify(execFile)(pythonPath, ["-m", "pip", "show", "wdl-lsp"]).catch(err => { - console.log(err); - return { stdout: "" }; - }); - const versionStr = stdout.split('\n').find(line => line.startsWith('Version:')); - if (!versionStr || versionStr.split(':')[1].trim() !== version) { - await promisify(execFile)(pythonPath, [ - "-m", "pip", "install", "--user", "wdl-lsp==" + version, - ]); - } - } catch (e) { - console.error(e); - } +export async function activate(context: ExtensionContext) { + const cwd = path.join(__dirname, "../"); - client = startLangServer(pythonPath, ["-m", "wdl_lsp"], cwd); + const venvPath = path.join(__dirname, '.venv'); + if (!fs.existsSync(venvPath)) { + const pythonPath = workspace.getConfiguration(language).get("pythonPath")!; + await exec(pythonPath, "-m", "venv", venvPath); + } + + const installArgs: string[] = []; + if (process.env.VSCODE_DEBUG_MODE === "true") { + installArgs.push(path.join(__dirname, '..', '..', '..', 'server')); + } else { + installArgs.push("--upgrade", "wdl-lsp"); } + const pythonPath = path.join(venvPath, 'bin', 'python'); + await exec(pythonPath, "-m", "pip", "install", ...installArgs); + client = startLangServer(pythonPath, ["-m", "wdl_lsp"], cwd); client.registerProposedFeatures(); await client.start(); }