From 39e53c103f9a687a87fb6e5cd3d9b10ee42a865b Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Tue, 17 Oct 2023 12:27:15 +1100 Subject: [PATCH 01/12] feat(lsp): use standalone language server implementation --- client/src/extension.ts | 84 +++++++---------------------------- client/src/language-server.ts | 59 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 68 deletions(-) create mode 100644 client/src/language-server.ts diff --git a/client/src/extension.ts b/client/src/extension.ts index bca501b..c1d5c43 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,31 +1,21 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import * as path from "path"; -// import { workspace, ExtensionContext } from "vscode"; import * as vscode from "vscode"; import { - LanguageClient, - LanguageClientOptions, - ServerOptions, - TransportKind, -} from "vscode-languageclient/node"; - -let client: LanguageClient; + activate as activateLanguageServer, + deactivate as deactivateLanguageServer, +} from "./language-server"; -export function activate(context: vscode.ExtensionContext) { +export function activate(context: vscode.ExtensionContext): void { console.log("Terminals: " + (vscode.window).terminals.length); context.subscriptions.push( vscode.window.registerTerminalProfileProvider("nushell_default", { provideTerminalProfile( - token: vscode.CancellationToken + token: vscode.CancellationToken, ): vscode.ProviderResult { + /* eslint-disable @typescript-eslint/no-var-requires */ const which = require("which"); const path = require("path"); + /* eslint-enable @typescript-eslint/no-var-requires */ const PATH_FROM_ENV = process.env["PATH"]; const pathsToCheck = [ @@ -76,21 +66,21 @@ export function activate(context: vscode.ExtensionContext) { if (found_nushell_path == null) { console.log( - "Nushell not found in env:PATH or any of the heuristic locations." + "Nushell not found in env:PATH or any of the heuristic locations.", ); // use an async arrow funciton to use `await` inside return (async () => { if ( (await vscode.window.showErrorMessage( "We cannot find a nushell executable in your path or pre-defined locations", - "install from website" + "install from website", )) && (await vscode.env.openExternal( - vscode.Uri.parse("https://www.nushell.sh/") + vscode.Uri.parse("https://www.nushell.sh/"), )) && (await vscode.window.showInformationMessage( "after you install nushell, you might need to reload vscode", - "reload now" + "reload now", )) ) { vscode.commands.executeCommand("workbench.action.reloadWindow"); @@ -107,59 +97,17 @@ export function activate(context: vscode.ExtensionContext) { shellPath: found_nushell_path, iconPath: vscode.Uri.joinPath( context.extensionUri, - "assets/nu.svg" + "assets/nu.svg", ), }, }; }, - }) - ); - - // The server is implemented in node - const serverModule = context.asAbsolutePath( - path.join("out", "server", "src", "server.js") - ); - - // The debug options for the server - // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging - const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; - - // If the extension is launched in debug mode then the debug server options are used - // Otherwise the run options are used - const serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { - module: serverModule, - transport: TransportKind.ipc, - options: debugOptions, - }, - }; - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - 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. - client = new LanguageClient( - "nushellLanguageServer", - "Nushell Language Server", - serverOptions, - clientOptions + }), ); - // Start the client. This will also launch the server - client.start(); + activateLanguageServer(context); } -export function deactivate(): Thenable | undefined { - if (!client) { - return undefined; - } - return client.stop(); +export function deactivate(): void { + deactivateLanguageServer(); } diff --git a/client/src/language-server.ts b/client/src/language-server.ts new file mode 100644 index 0000000..4ae0daa --- /dev/null +++ b/client/src/language-server.ts @@ -0,0 +1,59 @@ +import { ExtensionContext, window } from "vscode"; +import * as vscode from "vscode"; +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, +} from "vscode-languageclient/node"; + +let client: LanguageClient | null = null; + +async function startClient(_context: ExtensionContext) { + const serverOptions: ServerOptions = { + command: "nuls", + args: [], + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "nushell" }], + 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. + client = new LanguageClient( + "nushellLanguageServer", + "Nushell Language Server", + serverOptions, + clientOptions, + ); + + return client.start().catch((reason) => { + window.showWarningMessage( + `Failed to run Nushell Language Server: ${reason}`, + ); + client = null; + }); +} + +async function stopClient(): Promise { + if (client) { + client.stop(); + } + client = null; +} + +export async function activate(context: ExtensionContext) { + // TODO: use configuration + // const configuration = workspace.getConfiguration("nushellLanguageServer", null); + + await startClient(context); +} + +export function deactivate(): Thenable { + return stopClient(); +} From 26cd39169772757bea779a4cec3f09692165e64f Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Thu, 19 Oct 2023 14:40:32 +1100 Subject: [PATCH 02/12] feat: new "implementation" setting to switch language servers --- client/src/extension.ts | 35 +++++++--- client/src/nu-ide.ts | 76 ++++++++++++++++++++++ client/src/nu-lsp.ts | 59 +++++++++++++++++ client/src/{language-server.ts => nuls.ts} | 2 +- package.json | 38 ++++------- 5 files changed, 174 insertions(+), 36 deletions(-) create mode 100644 client/src/nu-ide.ts create mode 100644 client/src/nu-lsp.ts rename client/src/{language-server.ts => nuls.ts} (95%) diff --git a/client/src/extension.ts b/client/src/extension.ts index c1d5c43..ff30bab 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,9 +1,16 @@ +import * as path from "path"; import * as vscode from "vscode"; +import * as which from "which"; import { - activate as activateLanguageServer, - deactivate as deactivateLanguageServer, -} from "./language-server"; + activate as activateExtension, + deactivate as deactivateExtension, +} from "./nu-ide"; +import { + activate as activateNuLsp, + deactivate as deactivateNuLsp, +} from "./nu-lsp"; +import { activate as activateNuls, deactivate as deactivateNuls } from "./nuls"; export function activate(context: vscode.ExtensionContext): void { console.log("Terminals: " + (vscode.window).terminals.length); @@ -12,11 +19,6 @@ export function activate(context: vscode.ExtensionContext): void { provideTerminalProfile( token: vscode.CancellationToken, ): vscode.ProviderResult { - /* eslint-disable @typescript-eslint/no-var-requires */ - const which = require("which"); - const path = require("path"); - /* eslint-enable @typescript-eslint/no-var-requires */ - const PATH_FROM_ENV = process.env["PATH"]; const pathsToCheck = [ PATH_FROM_ENV, @@ -105,9 +107,22 @@ export function activate(context: vscode.ExtensionContext): void { }), ); - activateLanguageServer(context); + const configuration = vscode.workspace.getConfiguration( + "nushellLanguageServer", + null, + ); + + if (configuration.implementation == "nu --lsp") { + activateNuLsp(context); + } else if (configuration.implementation == "nuls") { + activateNuls(context); + } else { + activateExtension(context); + } } export function deactivate(): void { - deactivateLanguageServer(); + deactivateExtension(); + deactivateNuLsp(); + deactivateNuls(); } diff --git a/client/src/nu-ide.ts b/client/src/nu-ide.ts new file mode 100644 index 0000000..2681848 --- /dev/null +++ b/client/src/nu-ide.ts @@ -0,0 +1,76 @@ +import * as path from "path"; +import { ExtensionContext, window } from "vscode"; +import * as vscode from "vscode"; +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind, +} from "vscode-languageclient/node"; + +let client: LanguageClient | null = null; + +async function startClient(context: ExtensionContext) { + // The server is implemented in node + const serverModule = context.asAbsolutePath( + path.join("out", "server", "src", "server.js"), + ); + + // The debug options for the server + // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging + const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + options: debugOptions, + }, + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "nushell" }], + 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. + client = new LanguageClient( + "nushellLanguageServer", + "Nushell Language Server", + serverOptions, + clientOptions, + ); + + return client.start().catch((reason) => { + window.showWarningMessage( + `Failed to run Nushell Language Server (extension): ${reason}`, + ); + client = null; + }); +} + +async function stopClient(): Promise { + if (client) { + client.stop(); + } + client = null; +} + +export async function activate(context: ExtensionContext) { + // TODO: use configuration + // const configuration = workspace.getConfiguration("nushellLanguageServer", null); + + await startClient(context); +} + +export function deactivate(): Thenable { + return stopClient(); +} diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts new file mode 100644 index 0000000..4ee0cb3 --- /dev/null +++ b/client/src/nu-lsp.ts @@ -0,0 +1,59 @@ +import { ExtensionContext, window } from "vscode"; +import * as vscode from "vscode"; +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, +} from "vscode-languageclient/node"; + +let client: LanguageClient | null = null; + +async function startClient(_context: ExtensionContext) { + const serverOptions: ServerOptions = { + command: "nu", + args: ["--lsp"], + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "nushell" }], + 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. + client = new LanguageClient( + "nushellLanguageServer", + "Nushell Language Server", + serverOptions, + clientOptions, + ); + + return client.start().catch((reason) => { + window.showWarningMessage( + `Failed to run Nushell Language Server (nu --lsp): ${reason}`, + ); + client = null; + }); +} + +async function stopClient(): Promise { + if (client) { + client.stop(); + } + client = null; +} + +export async function activate(context: ExtensionContext) { + // TODO: use configuration + // const configuration = workspace.getConfiguration("nushellLanguageServer", null); + + await startClient(context); +} + +export function deactivate(): Thenable { + return stopClient(); +} diff --git a/client/src/language-server.ts b/client/src/nuls.ts similarity index 95% rename from client/src/language-server.ts rename to client/src/nuls.ts index 4ae0daa..3f9fe29 100644 --- a/client/src/language-server.ts +++ b/client/src/nuls.ts @@ -34,7 +34,7 @@ async function startClient(_context: ExtensionContext) { return client.start().catch((reason) => { window.showWarningMessage( - `Failed to run Nushell Language Server: ${reason}`, + `Failed to run Nushell Language Server (nuls): ${reason}`, ); client = null; }); diff --git a/package.json b/package.json index e300074..e54ec76 100644 --- a/package.json +++ b/package.json @@ -19,25 +19,15 @@ "vscode": "^1.75.0" }, "icon": "assets/nushell.ico", - "categories": [ - "Programming Languages", - "Snippets" - ], - "activationEvents": [ - "onTerminalProfile:nushell_default" - ], + "categories": ["Programming Languages", "Snippets"], + "activationEvents": ["onTerminalProfile:nushell_default"], "main": "out/client/src/extension.js", "contributes": { "languages": [ { "id": "nushell", - "aliases": [ - "nushell", - "nu" - ], - "extensions": [ - ".nu" - ], + "aliases": ["nushell", "nu"], + "extensions": [".nu"], "icon": { "light": "assets/nu.svg", "dark": "assets/nu.svg" @@ -85,6 +75,13 @@ "type": "object", "title": "Nushell IDE Support", "properties": { + "nushellLanguageServer.implementation": { + "scope": "window", + "type": "string", + "enum": ["extension", "nu --lsp", "nuls"], + "default": "extension", + "description": "Switch between different language server implementations (requires restarting Visual Studio Code to take effect)." + }, "nushellLanguageServer.maxNumberOfProblems": { "scope": "resource", "type": "number", @@ -94,11 +91,7 @@ "nushellLanguageServer.trace.server": { "scope": "window", "type": "string", - "enum": [ - "off", - "messages", - "verbose" - ], + "enum": ["off", "messages", "verbose"], "default": "off", "description": "Traces the communication between VS Code and the language server." }, @@ -163,12 +156,7 @@ "webpack": "^5.70.0", "webpack-cli": "^4.9.2" }, - "keywords": [ - "nushell", - "nu", - "shell", - "scripting" - ], + "keywords": ["nushell", "nu", "shell", "scripting"], "galleryBanner": { "color": "#008000", "theme": "light" From 4a4c66c9cd978fcade382c1e4b7dca60226ad6f7 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Thu, 19 Oct 2023 15:34:48 +1100 Subject: [PATCH 03/12] chore: deduplicate LanguageClientOptions --- client/src/extension.ts | 17 ++++++++++++++--- client/src/nu-ide.ts | 26 +++++++++----------------- client/src/nu-lsp.ts | 23 +++++++++-------------- client/src/nuls.ts | 26 +++++++++----------------- 4 files changed, 41 insertions(+), 51 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index ff30bab..0d6a0df 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -11,6 +11,7 @@ import { deactivate as deactivateNuLsp, } from "./nu-lsp"; import { activate as activateNuls, deactivate as deactivateNuls } from "./nuls"; +import { LanguageClientOptions } from "vscode-languageclient"; export function activate(context: vscode.ExtensionContext): void { console.log("Terminals: " + (vscode.window).terminals.length); @@ -107,17 +108,27 @@ export function activate(context: vscode.ExtensionContext): void { }), ); + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "nushell" }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"), + }, + }; + const configuration = vscode.workspace.getConfiguration( "nushellLanguageServer", null, ); if (configuration.implementation == "nu --lsp") { - activateNuLsp(context); + activateNuLsp(context, clientOptions); } else if (configuration.implementation == "nuls") { - activateNuls(context); + activateNuls(context, clientOptions); } else { - activateExtension(context); + activateExtension(context, clientOptions); } } diff --git a/client/src/nu-ide.ts b/client/src/nu-ide.ts index 2681848..9e27075 100644 --- a/client/src/nu-ide.ts +++ b/client/src/nu-ide.ts @@ -1,6 +1,5 @@ import * as path from "path"; import { ExtensionContext, window } from "vscode"; -import * as vscode from "vscode"; import { LanguageClient, LanguageClientOptions, @@ -10,7 +9,10 @@ import { let client: LanguageClient | null = null; -async function startClient(context: ExtensionContext) { +async function startClient( + context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { // The server is implemented in node const serverModule = context.asAbsolutePath( path.join("out", "server", "src", "server.js"), @@ -31,16 +33,6 @@ async function startClient(context: ExtensionContext) { }, }; - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - 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. client = new LanguageClient( "nushellLanguageServer", @@ -64,11 +56,11 @@ async function stopClient(): Promise { client = null; } -export async function activate(context: ExtensionContext) { - // TODO: use configuration - // const configuration = workspace.getConfiguration("nushellLanguageServer", null); - - await startClient(context); +export async function activate( + context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { + await startClient(context, clientOptions); } export function deactivate(): Thenable { diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts index 4ee0cb3..b709cd9 100644 --- a/client/src/nu-lsp.ts +++ b/client/src/nu-lsp.ts @@ -1,5 +1,4 @@ import { ExtensionContext, window } from "vscode"; -import * as vscode from "vscode"; import { LanguageClient, LanguageClientOptions, @@ -8,22 +7,15 @@ import { let client: LanguageClient | null = null; -async function startClient(_context: ExtensionContext) { +async function startClient( + _context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { const serverOptions: ServerOptions = { command: "nu", args: ["--lsp"], }; - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - 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. client = new LanguageClient( "nushellLanguageServer", @@ -47,11 +39,14 @@ async function stopClient(): Promise { client = null; } -export async function activate(context: ExtensionContext) { +export async function activate( + context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { // TODO: use configuration // const configuration = workspace.getConfiguration("nushellLanguageServer", null); - await startClient(context); + await startClient(context, clientOptions); } export function deactivate(): Thenable { diff --git a/client/src/nuls.ts b/client/src/nuls.ts index 3f9fe29..1861f4c 100644 --- a/client/src/nuls.ts +++ b/client/src/nuls.ts @@ -1,5 +1,4 @@ import { ExtensionContext, window } from "vscode"; -import * as vscode from "vscode"; import { LanguageClient, LanguageClientOptions, @@ -8,22 +7,15 @@ import { let client: LanguageClient | null = null; -async function startClient(_context: ExtensionContext) { +async function startClient( + _context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { const serverOptions: ServerOptions = { command: "nuls", args: [], }; - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - 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. client = new LanguageClient( "nushellLanguageServer", @@ -47,11 +39,11 @@ async function stopClient(): Promise { client = null; } -export async function activate(context: ExtensionContext) { - // TODO: use configuration - // const configuration = workspace.getConfiguration("nushellLanguageServer", null); - - await startClient(context); +export async function activate( + context: ExtensionContext, + clientOptions: LanguageClientOptions, +) { + await startClient(context, clientOptions); } export function deactivate(): Thenable { From aec2b1608290e265a1a524e2e6def1e2a660ea67 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Thu, 19 Oct 2023 15:41:15 +1100 Subject: [PATCH 04/12] fix: `nu --lsp` uses "nushellExecutablePath" setting --- client/src/nu-lsp.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts index b709cd9..c4f2424 100644 --- a/client/src/nu-lsp.ts +++ b/client/src/nu-lsp.ts @@ -1,4 +1,5 @@ import { ExtensionContext, window } from "vscode"; +import * as vscode from "vscode"; import { LanguageClient, LanguageClientOptions, @@ -11,8 +12,13 @@ async function startClient( _context: ExtensionContext, clientOptions: LanguageClientOptions, ) { + const configuration = vscode.workspace.getConfiguration( + "nushellLanguageServer", + null, + ); + const serverOptions: ServerOptions = { - command: "nu", + command: configuration.nushellExecutablePath, args: ["--lsp"], }; From e932a35e479c6d8b3a5773d9481eac393d942b13 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Fri, 20 Oct 2023 15:41:22 +1100 Subject: [PATCH 05/12] fix: restart language server when user changes "implementation" --- client/src/extension.ts | 69 ++++++++++++++++++++++++++--------------- client/src/nu-lsp.ts | 14 +++------ client/src/nuls.ts | 14 +++------ package.json | 2 +- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 0d6a0df..2538439 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,4 +1,6 @@ import * as path from "path"; +import { LanguageClientOptions } from "vscode-languageclient"; +import { ExtensionContext } from "vscode"; import * as vscode from "vscode"; import * as which from "which"; @@ -11,7 +13,42 @@ import { deactivate as deactivateNuLsp, } from "./nu-lsp"; import { activate as activateNuls, deactivate as deactivateNuls } from "./nuls"; -import { LanguageClientOptions } from "vscode-languageclient"; + +function startLanguageServer(context: ExtensionContext) { + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "nushell" }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"), + }, + }; + + const configuration = vscode.workspace.getConfiguration( + "nushellLanguageServer", + null, + ); + + if (configuration.implementation == "nu --lsp") { + activateNuLsp(clientOptions); + } else if (configuration.implementation == "nuls") { + activateNuls(clientOptions); + } else { + activateExtension(context, clientOptions); + } +} + +function stopLanguageServers() { + deactivateExtension(); + deactivateNuLsp(); + deactivateNuls(); +} + +function handleDidChangeConfiguration(this: ExtensionContext) { + stopLanguageServers(); + startLanguageServer(this); +} export function activate(context: vscode.ExtensionContext): void { console.log("Terminals: " + (vscode.window).terminals.length); @@ -108,32 +145,14 @@ export function activate(context: vscode.ExtensionContext): void { }), ); - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - synchronize: { - // Notify the server about file changes to '.clientrc files contained in the workspace - fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"), - }, - }; - - const configuration = vscode.workspace.getConfiguration( - "nushellLanguageServer", - null, + startLanguageServer(context); + vscode.workspace.onDidChangeConfiguration( + handleDidChangeConfiguration, + context, + undefined, ); - - if (configuration.implementation == "nu --lsp") { - activateNuLsp(context, clientOptions); - } else if (configuration.implementation == "nuls") { - activateNuls(context, clientOptions); - } else { - activateExtension(context, clientOptions); - } } export function deactivate(): void { - deactivateExtension(); - deactivateNuLsp(); - deactivateNuls(); + stopLanguageServers(); } diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts index c4f2424..1934397 100644 --- a/client/src/nu-lsp.ts +++ b/client/src/nu-lsp.ts @@ -1,4 +1,4 @@ -import { ExtensionContext, window } from "vscode"; +import { window } from "vscode"; import * as vscode from "vscode"; import { LanguageClient, @@ -8,10 +8,7 @@ import { let client: LanguageClient | null = null; -async function startClient( - _context: ExtensionContext, - clientOptions: LanguageClientOptions, -) { +async function startClient(clientOptions: LanguageClientOptions) { const configuration = vscode.workspace.getConfiguration( "nushellLanguageServer", null, @@ -45,14 +42,11 @@ async function stopClient(): Promise { client = null; } -export async function activate( - context: ExtensionContext, - clientOptions: LanguageClientOptions, -) { +export async function activate(clientOptions: LanguageClientOptions) { // TODO: use configuration // const configuration = workspace.getConfiguration("nushellLanguageServer", null); - await startClient(context, clientOptions); + await startClient(clientOptions); } export function deactivate(): Thenable { diff --git a/client/src/nuls.ts b/client/src/nuls.ts index 1861f4c..e2c97a6 100644 --- a/client/src/nuls.ts +++ b/client/src/nuls.ts @@ -1,4 +1,4 @@ -import { ExtensionContext, window } from "vscode"; +import { window } from "vscode"; import { LanguageClient, LanguageClientOptions, @@ -7,10 +7,7 @@ import { let client: LanguageClient | null = null; -async function startClient( - _context: ExtensionContext, - clientOptions: LanguageClientOptions, -) { +async function startClient(clientOptions: LanguageClientOptions) { const serverOptions: ServerOptions = { command: "nuls", args: [], @@ -39,11 +36,8 @@ async function stopClient(): Promise { client = null; } -export async function activate( - context: ExtensionContext, - clientOptions: LanguageClientOptions, -) { - await startClient(context, clientOptions); +export async function activate(clientOptions: LanguageClientOptions) { + await startClient(clientOptions); } export function deactivate(): Thenable { diff --git a/package.json b/package.json index e54ec76..0f34694 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "type": "string", "enum": ["extension", "nu --lsp", "nuls"], "default": "extension", - "description": "Switch between different language server implementations (requires restarting Visual Studio Code to take effect)." + "description": "Switch between different language server implementations." }, "nushellLanguageServer.maxNumberOfProblems": { "scope": "resource", From 2fe5f61aa494c3e33884e5c5bd0f5172478287e5 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Sun, 22 Oct 2023 16:02:30 +1100 Subject: [PATCH 06/12] fix(lsp): await the `client.stop()` Promise --- client/src/extension.ts | 24 +++++++++++++----------- client/src/nu-ide.ts | 14 +++++++++----- client/src/nu-lsp.ts | 14 +++++++++----- client/src/nuls.ts | 14 +++++++++----- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 2538439..c5f8216 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -14,7 +14,7 @@ import { } from "./nu-lsp"; import { activate as activateNuls, deactivate as deactivateNuls } from "./nuls"; -function startLanguageServer(context: ExtensionContext) { +async function startLanguageServer(context: ExtensionContext) { // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for plain text documents @@ -31,23 +31,25 @@ function startLanguageServer(context: ExtensionContext) { ); if (configuration.implementation == "nu --lsp") { - activateNuLsp(clientOptions); + await activateNuLsp(clientOptions); } else if (configuration.implementation == "nuls") { - activateNuls(clientOptions); + await activateNuls(clientOptions); } else { - activateExtension(context, clientOptions); + await activateExtension(context, clientOptions); } } -function stopLanguageServers() { - deactivateExtension(); - deactivateNuLsp(); - deactivateNuls(); +async function stopLanguageServers() { + await Promise.all([ + deactivateExtension(), + deactivateNuLsp(), + deactivateNuls(), + ]); } -function handleDidChangeConfiguration(this: ExtensionContext) { - stopLanguageServers(); - startLanguageServer(this); +async function handleDidChangeConfiguration(this: ExtensionContext) { + await stopLanguageServers(); + await startLanguageServer(this); } export function activate(context: vscode.ExtensionContext): void { diff --git a/client/src/nu-ide.ts b/client/src/nu-ide.ts index 9e27075..a9484ad 100644 --- a/client/src/nu-ide.ts +++ b/client/src/nu-ide.ts @@ -41,9 +41,9 @@ async function startClient( clientOptions, ); - return client.start().catch((reason) => { + return client.start().catch((reason: unknown) => { window.showWarningMessage( - `Failed to run Nushell Language Server (extension): ${reason}`, + `Failed to start Nushell Language Server (extension): ${reason}`, ); client = null; }); @@ -51,7 +51,11 @@ async function startClient( async function stopClient(): Promise { if (client) { - client.stop(); + await client.stop().catch((reason: unknown) => { + window.showWarningMessage( + `Failed to stop Nushell Language Server (extension): ${reason}`, + ); + }); } client = null; } @@ -63,6 +67,6 @@ export async function activate( await startClient(context, clientOptions); } -export function deactivate(): Thenable { - return stopClient(); +export async function deactivate(): Promise { + await stopClient(); } diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts index 1934397..e6b86b9 100644 --- a/client/src/nu-lsp.ts +++ b/client/src/nu-lsp.ts @@ -27,9 +27,9 @@ async function startClient(clientOptions: LanguageClientOptions) { clientOptions, ); - return client.start().catch((reason) => { + return client.start().catch((reason: unknown) => { window.showWarningMessage( - `Failed to run Nushell Language Server (nu --lsp): ${reason}`, + `Failed to start Nushell Language Server (nu --lsp): ${reason}`, ); client = null; }); @@ -37,7 +37,11 @@ async function startClient(clientOptions: LanguageClientOptions) { async function stopClient(): Promise { if (client) { - client.stop(); + await client.stop().catch((reason: unknown) => { + window.showWarningMessage( + `Failed to stop Nushell Language Server (nu --lsp): ${reason}`, + ); + }); } client = null; } @@ -49,6 +53,6 @@ export async function activate(clientOptions: LanguageClientOptions) { await startClient(clientOptions); } -export function deactivate(): Thenable { - return stopClient(); +export async function deactivate(): Promise { + await stopClient(); } diff --git a/client/src/nuls.ts b/client/src/nuls.ts index e2c97a6..118d3e8 100644 --- a/client/src/nuls.ts +++ b/client/src/nuls.ts @@ -21,9 +21,9 @@ async function startClient(clientOptions: LanguageClientOptions) { clientOptions, ); - return client.start().catch((reason) => { + return client.start().catch((reason: unknown) => { window.showWarningMessage( - `Failed to run Nushell Language Server (nuls): ${reason}`, + `Failed to start Nushell Language Server (nuls): ${reason}`, ); client = null; }); @@ -31,7 +31,11 @@ async function startClient(clientOptions: LanguageClientOptions) { async function stopClient(): Promise { if (client) { - client.stop(); + await client.stop().catch((reason: unknown) => { + window.showWarningMessage( + `Failed to stop Nushell Language Server (nuls): ${reason}`, + ); + }); } client = null; } @@ -40,6 +44,6 @@ export async function activate(clientOptions: LanguageClientOptions) { await startClient(clientOptions); } -export function deactivate(): Thenable { - return stopClient(); +export async function deactivate(): Promise { + await stopClient(); } From eb01a1c9a202994a506195649d0519d49c554b05 Mon Sep 17 00:00:00 2001 From: Ron Waldon-Howe Date: Tue, 24 Oct 2023 15:57:35 +1100 Subject: [PATCH 07/12] fix(lsp): unique labels in Output pane, no pop-up for `.stop()` errors --- client/src/nu-ide.ts | 13 +++++-------- client/src/nu-lsp.ts | 13 +++++-------- client/src/nuls.ts | 13 +++++-------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/client/src/nu-ide.ts b/client/src/nu-ide.ts index a9484ad..ed634bd 100644 --- a/client/src/nu-ide.ts +++ b/client/src/nu-ide.ts @@ -8,6 +8,7 @@ import { } from "vscode-languageclient/node"; let client: LanguageClient | null = null; +const name = "Nushell Language Server (extension)"; async function startClient( context: ExtensionContext, @@ -35,16 +36,14 @@ async function startClient( // Create the language client and start the client. client = new LanguageClient( - "nushellLanguageServer", - "Nushell Language Server", + "nushellLanguageServer-ide", + name, serverOptions, clientOptions, ); return client.start().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to start Nushell Language Server (extension): ${reason}`, - ); + window.showWarningMessage(`Failed to start ${name}: ${reason}`); client = null; }); } @@ -52,9 +51,7 @@ async function startClient( async function stopClient(): Promise { if (client) { await client.stop().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to stop Nushell Language Server (extension): ${reason}`, - ); + console.error(`Failed to stop ${name}: ${reason}`); }); } client = null; diff --git a/client/src/nu-lsp.ts b/client/src/nu-lsp.ts index e6b86b9..92e22a2 100644 --- a/client/src/nu-lsp.ts +++ b/client/src/nu-lsp.ts @@ -7,6 +7,7 @@ import { } from "vscode-languageclient/node"; let client: LanguageClient | null = null; +const name = "Nushell Language Server (nu --lsp)"; async function startClient(clientOptions: LanguageClientOptions) { const configuration = vscode.workspace.getConfiguration( @@ -21,16 +22,14 @@ async function startClient(clientOptions: LanguageClientOptions) { // Create the language client and start the client. client = new LanguageClient( - "nushellLanguageServer", - "Nushell Language Server", + "nushellLanguageServer-lsp", + name, serverOptions, clientOptions, ); return client.start().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to start Nushell Language Server (nu --lsp): ${reason}`, - ); + window.showWarningMessage(`Failed to start ${name}: ${reason}`); client = null; }); } @@ -38,9 +37,7 @@ async function startClient(clientOptions: LanguageClientOptions) { async function stopClient(): Promise { if (client) { await client.stop().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to stop Nushell Language Server (nu --lsp): ${reason}`, - ); + console.error(`Failed to stop ${name}: ${reason}`); }); } client = null; diff --git a/client/src/nuls.ts b/client/src/nuls.ts index 118d3e8..b29df41 100644 --- a/client/src/nuls.ts +++ b/client/src/nuls.ts @@ -6,6 +6,7 @@ import { } from "vscode-languageclient/node"; let client: LanguageClient | null = null; +const name = "Nushell Language Server (nuls)"; async function startClient(clientOptions: LanguageClientOptions) { const serverOptions: ServerOptions = { @@ -15,16 +16,14 @@ async function startClient(clientOptions: LanguageClientOptions) { // Create the language client and start the client. client = new LanguageClient( - "nushellLanguageServer", - "Nushell Language Server", + "nushellLanguageServer-nuls", + name, serverOptions, clientOptions, ); return client.start().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to start Nushell Language Server (nuls): ${reason}`, - ); + window.showWarningMessage(`Failed to start ${name}: ${reason}`); client = null; }); } @@ -32,9 +31,7 @@ async function startClient(clientOptions: LanguageClientOptions) { async function stopClient(): Promise { if (client) { await client.stop().catch((reason: unknown) => { - window.showWarningMessage( - `Failed to stop Nushell Language Server (nuls): ${reason}`, - ); + console.error(`Failed to stop ${name}: ${reason}`); }); } client = null; From 1927b55b31d4f0221d1393a6f03b5a311548aeb6 Mon Sep 17 00:00:00 2001 From: Auca Maillot Date: Sat, 16 Dec 2023 21:27:33 -0300 Subject: [PATCH 08/12] remove `nuls` --- .gitignore | 3 ++- client/src/extension.ts | 4 ---- client/src/nuls.ts | 46 ----------------------------------------- package.json | 2 +- 4 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 client/src/nuls.ts diff --git a/.gitignore b/.gitignore index f35f04c..5f5b5b6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ client/tsconfig.tsbuildinfo .vscode-test out/ server/tsconfig.tsbuildinfo -tsconfig.tsbuildinfo \ No newline at end of file +tsconfig.tsbuildinfo +.pnpm-lock.yaml diff --git a/client/src/extension.ts b/client/src/extension.ts index c5f8216..993c924 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -12,7 +12,6 @@ import { activate as activateNuLsp, deactivate as deactivateNuLsp, } from "./nu-lsp"; -import { activate as activateNuls, deactivate as deactivateNuls } from "./nuls"; async function startLanguageServer(context: ExtensionContext) { // Options to control the language client @@ -32,8 +31,6 @@ async function startLanguageServer(context: ExtensionContext) { if (configuration.implementation == "nu --lsp") { await activateNuLsp(clientOptions); - } else if (configuration.implementation == "nuls") { - await activateNuls(clientOptions); } else { await activateExtension(context, clientOptions); } @@ -43,7 +40,6 @@ async function stopLanguageServers() { await Promise.all([ deactivateExtension(), deactivateNuLsp(), - deactivateNuls(), ]); } diff --git a/client/src/nuls.ts b/client/src/nuls.ts deleted file mode 100644 index b29df41..0000000 --- a/client/src/nuls.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { window } from "vscode"; -import { - LanguageClient, - LanguageClientOptions, - ServerOptions, -} from "vscode-languageclient/node"; - -let client: LanguageClient | null = null; -const name = "Nushell Language Server (nuls)"; - -async function startClient(clientOptions: LanguageClientOptions) { - const serverOptions: ServerOptions = { - command: "nuls", - args: [], - }; - - // Create the language client and start the client. - client = new LanguageClient( - "nushellLanguageServer-nuls", - name, - serverOptions, - clientOptions, - ); - - return client.start().catch((reason: unknown) => { - window.showWarningMessage(`Failed to start ${name}: ${reason}`); - client = null; - }); -} - -async function stopClient(): Promise { - if (client) { - await client.stop().catch((reason: unknown) => { - console.error(`Failed to stop ${name}: ${reason}`); - }); - } - client = null; -} - -export async function activate(clientOptions: LanguageClientOptions) { - await startClient(clientOptions); -} - -export async function deactivate(): Promise { - await stopClient(); -} diff --git a/package.json b/package.json index ae0e266..d225495 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "nushellLanguageServer.implementation": { "scope": "window", "type": "string", - "enum": ["extension", "nu --lsp", "nuls"], + "enum": ["extension", "nu --lsp"], "default": "extension", "description": "Switch between different language server implementations." }, From 5b0dc2f51f3252ad4c4d37ca0284834e64a3d068 Mon Sep 17 00:00:00 2001 From: Auca Coyan Date: Wed, 28 Feb 2024 17:57:56 -0300 Subject: [PATCH 09/12] :package: update the `package.lock` file --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16f0eb8..ee9d63b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-nushell-lang", - "version": "1.7.1", + "version": "1.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-nushell-lang", - "version": "1.7.1", + "version": "1.8.0", "hasInstallScript": true, "license": "MIT", "dependencies": { From 98ba7cb3982fccb5713d8cc2640a298b107a9b46 Mon Sep 17 00:00:00 2001 From: Auca Coyan Date: Sun, 24 Mar 2024 20:23:45 -0300 Subject: [PATCH 10/12] :coffin: remove unused definitions --- client/src/extension.ts | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 3274b2b..993c924 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -149,45 +149,6 @@ export function activate(context: vscode.ExtensionContext): void { context, undefined, ); - - // The debug options for the server - // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging - const debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; - - // If the extension is launched in debug mode then the debug server options are used - // Otherwise the run options are used - const serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { - module: serverModule, - transport: TransportKind.ipc, - options: debugOptions, - }, - }; - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for plain text documents - documentSelector: [{ scheme: "file", language: "nushell" }], - synchronize: { - // Notify the server about file changes to '.clientrc files contained in the workspace - fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"), - }, - markdown: { - isTrusted: true - } - }; - - // Create the language client and start the client. - client = new LanguageClient( - "nushellLanguageServer", - "Nushell Language Server", - serverOptions, - clientOptions - ); - - // Start the client. This will also launch the server - client.start(); } export function deactivate(): void { From 59df2a34aae7637fe0134d469b2928ecbae0710c Mon Sep 17 00:00:00 2001 From: Auca Coyan Date: Sun, 24 Mar 2024 20:28:04 -0300 Subject: [PATCH 11/12] :recycle: replace import * with import --- client/src/extension.ts | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 993c924..68dc3ad 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,8 +1,18 @@ import * as path from "path"; -import { LanguageClientOptions } from "vscode-languageclient"; -import { ExtensionContext } from "vscode"; -import * as vscode from "vscode"; +import { + commands, + Uri, + env, + ExtensionContext, + workspace, + window, + ProviderResult, + TerminalProfile, + CancellationToken, +} from "vscode"; + import * as which from "which"; +import { LanguageClientOptions } from "vscode-languageclient/node"; import { activate as activateExtension, @@ -20,11 +30,11 @@ async function startLanguageServer(context: ExtensionContext) { documentSelector: [{ scheme: "file", language: "nushell" }], synchronize: { // Notify the server about file changes to '.clientrc files contained in the workspace - fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"), + fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), }, }; - const configuration = vscode.workspace.getConfiguration( + const configuration = workspace.getConfiguration( "nushellLanguageServer", null, ); @@ -37,10 +47,7 @@ async function startLanguageServer(context: ExtensionContext) { } async function stopLanguageServers() { - await Promise.all([ - deactivateExtension(), - deactivateNuLsp(), - ]); + await Promise.all([deactivateExtension(), deactivateNuLsp()]); } async function handleDidChangeConfiguration(this: ExtensionContext) { @@ -48,13 +55,13 @@ async function handleDidChangeConfiguration(this: ExtensionContext) { await startLanguageServer(this); } -export function activate(context: vscode.ExtensionContext): void { - console.log("Terminals: " + (vscode.window).terminals.length); +export function activate(context: ExtensionContext): void { + console.log("Terminals: " + (window).terminals.length); context.subscriptions.push( - vscode.window.registerTerminalProfileProvider("nushell_default", { + window.registerTerminalProfileProvider("nushell_default", { provideTerminalProfile( - token: vscode.CancellationToken, - ): vscode.ProviderResult { + token: CancellationToken, + ): ProviderResult { const PATH_FROM_ENV = process.env["PATH"]; const pathsToCheck = [ PATH_FROM_ENV, @@ -109,19 +116,19 @@ export function activate(context: vscode.ExtensionContext): void { // use an async arrow funciton to use `await` inside return (async () => { if ( - (await vscode.window.showErrorMessage( + (await window.showErrorMessage( "We cannot find a nushell executable in your path or pre-defined locations", "install from website", )) && - (await vscode.env.openExternal( - vscode.Uri.parse("https://www.nushell.sh/"), + (await env.openExternal( + Uri.parse("https://www.nushell.sh/"), )) && - (await vscode.window.showInformationMessage( + (await window.showInformationMessage( "after you install nushell, you might need to reload vscode", "reload now", )) ) { - vscode.commands.executeCommand("workbench.action.reloadWindow"); + commands.executeCommand("workbench.action.reloadWindow"); } // user has already seen error messages, but they didn't click through // return a promise that never resolve to supress the confusing error @@ -133,7 +140,7 @@ export function activate(context: vscode.ExtensionContext): void { options: { name: "Nushell", shellPath: found_nushell_path, - iconPath: vscode.Uri.joinPath( + iconPath: Uri.joinPath( context.extensionUri, "assets/nu.svg", ), @@ -144,7 +151,7 @@ export function activate(context: vscode.ExtensionContext): void { ); startLanguageServer(context); - vscode.workspace.onDidChangeConfiguration( + workspace.onDidChangeConfiguration( handleDidChangeConfiguration, context, undefined, From 5b5bde884e2dd4f4377148c6b6ca7e4c1278129a Mon Sep 17 00:00:00 2001 From: Auca Coyan Date: Sun, 24 Mar 2024 20:31:53 -0300 Subject: [PATCH 12/12] :recycle: replace import * from which --- client/src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 68dc3ad..3ed3013 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -11,7 +11,7 @@ import { CancellationToken, } from "vscode"; -import * as which from "which"; +import {sync as which_sync} from "which"; import { LanguageClientOptions } from "vscode-languageclient/node"; import { @@ -104,7 +104,7 @@ export function activate(context: ExtensionContext): void { //"/usr/bin/nu", ]; - const found_nushell_path = which.sync("nu", { + const found_nushell_path = which_sync("nu", { nothrow: true, path: pathsToCheck.join(path.delimiter), });