From 031d839e760e8bd85a3b0a03270c97826f51f589 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Mon, 25 Mar 2024 19:35:26 -0400 Subject: [PATCH] Enhance version manager config to accept more fields --- vscode/README.md | 7 ++++- vscode/VERSION_MANAGERS.md | 20 ++++---------- vscode/package.json | 36 ++++++++++++++++---------- vscode/src/common.ts | 2 +- vscode/src/extension.ts | 34 ++++++++++++++++++++++++ vscode/src/ruby.ts | 30 ++++++++++++++------- vscode/src/rubyLsp.ts | 17 ++++++++---- vscode/src/status.ts | 2 +- vscode/src/telemetry.ts | 2 +- vscode/src/test/suite/client.test.ts | 7 ++++- vscode/src/test/suite/debugger.test.ts | 7 ++++- vscode/src/test/suite/ruby.test.ts | 8 +++--- vscode/src/test/suite/status.test.ts | 5 +++- 13 files changed, 124 insertions(+), 53 deletions(-) diff --git a/vscode/README.md b/vscode/README.md index 4929e642d8..dde8f7d887 100644 --- a/vscode/README.md +++ b/vscode/README.md @@ -89,7 +89,12 @@ by clicking `Change version manager` in the language status center or by changin // "rbenv" // "rvm" // "shadowenv" -"rubyLsp.rubyVersionManager": "chruby" +// "mise" +{ + "rubyLsp.rubyVersionManager": { + "identifier": "chruby" + } +} ``` To make sure that the Ruby LSP can find the version manager scripts, make sure that they are loaded in the shell's diff --git a/vscode/VERSION_MANAGERS.md b/vscode/VERSION_MANAGERS.md index 02dd25fba8..22cb2239c8 100644 --- a/vscode/VERSION_MANAGERS.md +++ b/vscode/VERSION_MANAGERS.md @@ -10,13 +10,15 @@ If you're using a different version manager that's not supported by this extensi executable into the PATH, you will probably need to define custom activation so that the extension can find the correct Ruby. -For these cases, set `rubyLsp.rubyVersionManager` to `"custom"` and then set `rubyLsp.customRubyCommand` to a shell -command that will activate the right Ruby version or add the Ruby `bin` folder to the `PATH`. Some examples: +For these cases, set `rubyLsp.rubyVersionManager.identifier` to `"custom"` and then set `rubyLsp.customRubyCommand` to a +shell command that will activate the right Ruby version or add the Ruby `bin` folder to the `PATH`. Some examples: ```jsonc { // Don't forget to set the manager to custom when using this option - "rubyLsp.rubyVersionManager": "custom", + "rubyLsp.rubyVersionManager": { + "identifier": "custom", + }, // Using a different version manager than the ones included by default "rubyLsp.customRubyCommand": "my_custom_version_manager activate", @@ -25,15 +27,3 @@ command that will activate the right Ruby version or add the Ruby `bin` folder t "rubyLsp.customRubyCommand": "PATH=/path/to/ruby/bin:$PATH", } ``` - -### mise (formerly rtx) - -[mise](https://github.com/jdx/mise) is a Rust clone compatible with asdf. You can use it by adding the following -snippet to your user configuration JSON - -```json -{ - "rubyLsp.rubyVersionManager": "custom", - "rubyLsp.customRubyCommand": "eval \"$(mise env -s zsh)\"" // Instructions for zsh, change for bash or fish -} -``` diff --git a/vscode/package.json b/vscode/package.json index 77954dbdda..74310c5301 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -232,20 +232,28 @@ } }, "rubyLsp.rubyVersionManager": { - "description": "The Ruby version manager to use", - "type": "string", - "enum": [ - "asdf", - "auto", - "chruby", - "none", - "rbenv", - "rvm", - "shadowenv", - "mise", - "custom" - ], - "default": "auto" + "type": "object", + "properties": { + "identifier": { + "description": "The Ruby version manager to use", + "type": "string", + "enum": [ + "asdf", + "auto", + "chruby", + "none", + "rbenv", + "rvm", + "shadowenv", + "mise", + "custom" + ], + "default": "auto" + } + }, + "default": { + "identifier": "auto" + } }, "rubyLsp.customRubyCommand": { "description": "A shell command to activate the right Ruby version or add a custom Ruby bin folder to the PATH. Only used if rubyVersionManager is set to 'custom'", diff --git a/vscode/src/common.ts b/vscode/src/common.ts index 9f5129feff..bb125a2096 100644 --- a/vscode/src/common.ts +++ b/vscode/src/common.ts @@ -22,7 +22,7 @@ export enum Command { export interface RubyInterface { error: boolean; - versionManager?: string; + versionManager: { identifier: string }; rubyVersion?: string; } diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 2b737f08f1..53b81ee499 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -11,8 +11,42 @@ export async function activate(context: vscode.ExtensionContext) { extension = new RubyLsp(context); await extension.activate(); + + await migrateManagerConfigurations(); } export async function deactivate(): Promise { await extension.deactivate(); } + +type InspectKeys = + | "globalValue" + | "workspaceValue" + | "workspaceFolderValue" + | "globalLanguageValue" + | "workspaceLanguageValue" + | "workspaceFolderLanguageValue"; +// Function to migrate the old version manager configuration to the new format. Remove this after a few months +async function migrateManagerConfigurations() { + const configuration = vscode.workspace.getConfiguration("rubyLsp"); + const currentManagerSettings = + configuration.inspect("rubyVersionManager")!; + let identifier: string | undefined; + + const targetMap: Record = { + globalValue: vscode.ConfigurationTarget.Global, + globalLanguageValue: vscode.ConfigurationTarget.Global, + workspaceFolderLanguageValue: vscode.ConfigurationTarget.WorkspaceFolder, + workspaceFolderValue: vscode.ConfigurationTarget.WorkspaceFolder, + workspaceLanguageValue: vscode.ConfigurationTarget.Workspace, + workspaceValue: vscode.ConfigurationTarget.Workspace, + }; + + for (const [key, target] of Object.entries(targetMap)) { + identifier = currentManagerSettings[key as InspectKeys]; + + if (identifier && typeof identifier === "string") { + await configuration.update("rubyVersionManager", { identifier }, target); + } + } +} diff --git a/vscode/src/ruby.ts b/vscode/src/ruby.ts index 85721fbbdf..9319d959e5 100644 --- a/vscode/src/ruby.ts +++ b/vscode/src/ruby.ts @@ -26,13 +26,19 @@ export enum ManagerIdentifier { Custom = "custom", } +export interface ManagerConfiguration { + identifier: ManagerIdentifier; +} + export class Ruby implements RubyInterface { public rubyVersion?: string; // This property indicates that Ruby has been compiled with YJIT support and that we're running on a Ruby version // where it will be activated, either by the extension or by the server public yjitEnabled?: boolean; private readonly workspaceFolder: vscode.WorkspaceFolder; - #versionManager?: ManagerIdentifier; + #versionManager: ManagerConfiguration = vscode.workspace + .getConfiguration("rubyLsp") + .get("rubyVersionManager")!; private readonly shell = process.env.SHELL?.replace(/(\s+)/g, "\\$1"); private _env: NodeJS.ProcessEnv = {}; @@ -68,12 +74,18 @@ export class Ruby implements RubyInterface { : this.workspaceFolder.uri.fsPath; } - get versionManager() { + get versionManager(): ManagerConfiguration { return this.#versionManager; } - private set versionManager(versionManager: ManagerIdentifier | undefined) { - this.#versionManager = versionManager; + private set versionManager( + versionManager: ManagerConfiguration | ManagerIdentifier, + ) { + if (typeof versionManager === "string") { + this.#versionManager.identifier = versionManager; + } else { + this.#versionManager = versionManager; + } } get env() { @@ -85,14 +97,14 @@ export class Ruby implements RubyInterface { } async activateRuby( - versionManager: ManagerIdentifier = vscode.workspace + versionManager: ManagerConfiguration = vscode.workspace .getConfiguration("rubyLsp") - .get("rubyVersionManager")!, + .get("rubyVersionManager")!, ) { this.versionManager = versionManager; // If the version manager is auto, discover the actual manager before trying to activate anything - if (this.versionManager === ManagerIdentifier.Auto) { + if (this.versionManager.identifier === ManagerIdentifier.Auto) { await this.discoverVersionManager(); this.outputChannel.info( `Discovered version manager ${this.versionManager}`, @@ -100,7 +112,7 @@ export class Ruby implements RubyInterface { } try { - switch (this.versionManager) { + switch (this.versionManager.identifier) { case ManagerIdentifier.Asdf: await this.activate("asdf exec ruby"); break; @@ -263,7 +275,7 @@ export class Ruby implements RubyInterface { await vscode.workspace.fs.stat( vscode.Uri.joinPath(this.workspaceFolder.uri, ".shadowenv.d"), ); - this.versionManager = ManagerIdentifier.Shadowenv; + this.versionManager.identifier = ManagerIdentifier.Shadowenv; return; } catch (error: any) { // If .shadowenv.d doesn't exist, then we check the other version managers diff --git a/vscode/src/rubyLsp.ts b/vscode/src/rubyLsp.ts index d017125d9b..a53a70fee6 100644 --- a/vscode/src/rubyLsp.ts +++ b/vscode/src/rubyLsp.ts @@ -5,7 +5,7 @@ import { Telemetry } from "./telemetry"; import DocumentProvider from "./documentProvider"; import { Workspace } from "./workspace"; import { Command, STATUS_EMITTER } from "./common"; -import { ManagerIdentifier } from "./ruby"; +import { ManagerIdentifier, ManagerConfiguration } from "./ruby"; import { StatusItems } from "./status"; import { TestController } from "./testController"; import { Debugger } from "./debugger"; @@ -281,13 +281,20 @@ export class RubyLsp { Command.SelectVersionManager, async () => { const configuration = vscode.workspace.getConfiguration("rubyLsp"); + const managerConfig = + configuration.get("rubyVersionManager")!; const options = Object.values(ManagerIdentifier); - const manager = await vscode.window.showQuickPick(options, { - placeHolder: `Current: ${configuration.get("rubyVersionManager")}`, - }); + const manager = (await vscode.window.showQuickPick(options, { + placeHolder: `Current: ${managerConfig.identifier}`, + })) as ManagerIdentifier | undefined; if (manager !== undefined) { - configuration.update("rubyVersionManager", manager, true, true); + managerConfig.identifier = manager; + await configuration.update( + "rubyVersionManager", + managerConfig, + true, + ); } }, ), diff --git a/vscode/src/status.ts b/vscode/src/status.ts index 97c471fe73..701c883336 100644 --- a/vscode/src/status.ts +++ b/vscode/src/status.ts @@ -49,7 +49,7 @@ export class RubyVersionStatus extends StatusItem { this.item.text = "Failed to activate Ruby"; this.item.severity = vscode.LanguageStatusSeverity.Error; } else { - this.item.text = `Using Ruby ${workspace.ruby.rubyVersion} with ${workspace.ruby.versionManager}`; + this.item.text = `Using Ruby ${workspace.ruby.rubyVersion} with ${workspace.ruby.versionManager.identifier}`; this.item.severity = vscode.LanguageStatusSeverity.Information; } } diff --git a/vscode/src/telemetry.ts b/vscode/src/telemetry.ts index 4c2e00cc11..7553dd070f 100644 --- a/vscode/src/telemetry.ts +++ b/vscode/src/telemetry.ts @@ -74,7 +74,7 @@ export class Telemetry { const promises: Promise[] = [ { namespace: "workbench", field: "colorTheme" }, { namespace: "rubyLsp", field: "enableExperimentalFeatures" }, - { namespace: "rubyLsp", field: "rubyVersionManager" }, + { namespace: "rubyLsp", field: "rubyVersionManager.identifier" }, { namespace: "rubyLsp", field: "formatter" }, ].map(({ namespace, field }) => { return this.sendEvent({ diff --git a/vscode/src/test/suite/client.test.ts b/vscode/src/test/suite/client.test.ts index 4a07d59847..4426f6a89b 100644 --- a/vscode/src/test/suite/client.test.ts +++ b/vscode/src/test/suite/client.test.ts @@ -116,7 +116,12 @@ suite("Client", () => { if (process.env.CI) { await vscode.workspace .getConfiguration("rubyLsp") - .update("rubyVersionManager", ManagerIdentifier.None, true, true); + .update( + "rubyVersionManager", + { identifier: ManagerIdentifier.None }, + true, + true, + ); } client = await launchClient(workspaceUri); }); diff --git a/vscode/src/test/suite/debugger.test.ts b/vscode/src/test/suite/debugger.test.ts index 08e0628eb6..1603465dfa 100644 --- a/vscode/src/test/suite/debugger.test.ts +++ b/vscode/src/test/suite/debugger.test.ts @@ -161,7 +161,12 @@ suite("Debugger", () => { if (process.env.CI) { await vscode.workspace .getConfiguration("rubyLsp") - .update("rubyVersionManager", ManagerIdentifier.None, true, true); + .update( + "rubyVersionManager", + { identifier: ManagerIdentifier.None }, + true, + true, + ); } // By default, VS Code always saves all open files when launching a debugging session. This is a problem for tests diff --git a/vscode/src/test/suite/ruby.test.ts b/vscode/src/test/suite/ruby.test.ts index 33d983272e..0c3a6c9381 100644 --- a/vscode/src/test/suite/ruby.test.ts +++ b/vscode/src/test/suite/ruby.test.ts @@ -33,10 +33,12 @@ suite("Ruby environment activation", () => { } as vscode.WorkspaceFolder, outputChannel, ); - await ruby.activateRuby( + await ruby.activateRuby({ // eslint-disable-next-line no-process-env - process.env.CI ? ManagerIdentifier.None : ManagerIdentifier.Chruby, - ); + identifier: process.env.CI + ? ManagerIdentifier.None + : ManagerIdentifier.Chruby, + }); assert.ok(ruby.rubyVersion, "Expected Ruby version to be set"); assert.notStrictEqual( diff --git a/vscode/src/test/suite/status.test.ts b/vscode/src/test/suite/status.test.ts index 19f83de306..1ba43bda86 100644 --- a/vscode/src/test/suite/status.test.ts +++ b/vscode/src/test/suite/status.test.ts @@ -28,7 +28,10 @@ suite("StatusItems", () => { suite("RubyVersionStatus", () => { beforeEach(() => { - ruby = { rubyVersion: "3.2.0", versionManager: "shadowenv" } as Ruby; + ruby = { + rubyVersion: "3.2.0", + versionManager: { identifier: "shadowenv" }, + } as Ruby; workspace = { ruby, lspClient: {