diff --git a/vscode/src/ruby.ts b/vscode/src/ruby.ts index 3405a1973..b628c8233 100644 --- a/vscode/src/ruby.ts +++ b/vscode/src/ruby.ts @@ -17,25 +17,6 @@ import { Custom } from "./ruby/custom"; import { Asdf } from "./ruby/asdf"; import { Rv } from "./ruby/rv"; -async function detectMise() { - const possiblePaths = [ - vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise"), - vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin", "mise"), - vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "bin", "mise"), - ]; - - for (const possiblePath of possiblePaths) { - try { - await vscode.workspace.fs.stat(possiblePath); - return true; - } catch (_error: any) { - // Continue looking - } - } - - return false; -} - export enum ManagerIdentifier { Asdf = "asdf", Auto = "auto", @@ -374,6 +355,7 @@ export class Ruby implements RubyInterface { ManagerIdentifier.Rvm, ManagerIdentifier.Asdf, ManagerIdentifier.Rv, + ManagerIdentifier.Mise, ]; for (const tool of managers) { @@ -385,11 +367,6 @@ export class Ruby implements RubyInterface { } } - if (await detectMise()) { - this.versionManager = ManagerIdentifier.Mise; - return; - } - if (os.platform() === "win32") { this.versionManager = ManagerIdentifier.RubyInstaller; return; diff --git a/vscode/src/ruby/mise.ts b/vscode/src/ruby/mise.ts index aa65f1866..6479e07da 100644 --- a/vscode/src/ruby/mise.ts +++ b/vscode/src/ruby/mise.ts @@ -9,10 +9,10 @@ import { VersionManager, ActivationResult, NonReportableError } from "./versionM // Learn more: https://github.com/jdx/mise export class Mise extends VersionManager { async activate(): Promise { - const miseUri = await this.findMiseUri(); + const miseExec = await this.findMise(); // The exec command in Mise is called `x` - const parsedResult = await this.runEnvActivationScript(`${miseUri.fsPath} x -- ruby`); + const parsedResult = await this.runEnvActivationScript(`${miseExec} x -- ruby`); return { env: { ...process.env, ...parsedResult.env }, @@ -22,46 +22,37 @@ export class Mise extends VersionManager { }; } - async findMiseUri(): Promise { + async findMise(): Promise { const config = vscode.workspace.getConfiguration("rubyLsp"); - const misePath = config.get("rubyVersionManager.miseExecutablePath"); + const configuredMisePath = config.get("rubyVersionManager.miseExecutablePath"); - if (misePath) { - const configuredPath = vscode.Uri.file(misePath); - - try { - await vscode.workspace.fs.stat(configuredPath); - return configuredPath; - } catch (_error: any) { - throw new NonReportableError( - `Mise executable configured as ${configuredPath.fsPath}, but that file doesn't exist`, - ); - } + if (configuredMisePath) { + return this.ensureMiseExistsAt(configuredMisePath); } - // Possible mise installation paths + // Possible mise installation directories. If none match, fall back to the PATH. // // 1. Installation from curl | sh (per mise.jdx.dev Getting Started) // 2. Homebrew M series - // 3. Installation from `apt install mise` + // 3. Homebrew Intel / Linuxbrew + // 4. Linuxbrew (legacy) + // 5. Installation from `apt install mise` const possiblePaths = [ - vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise"), - vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin", "mise"), - vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "bin", "mise"), + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "local", "bin"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "home", "linuxbrew", ".linuxbrew", "bin"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "bin"), ]; + return this.findExec(possiblePaths, "mise"); + } - for (const possiblePath of possiblePaths) { - try { - await vscode.workspace.fs.stat(possiblePath); - return possiblePath; - } catch (_error: any) { - // Continue looking - } + private async ensureMiseExistsAt(path: string): Promise { + try { + await vscode.workspace.fs.stat(vscode.Uri.file(path)); + return path; + } catch (_error: any) { + throw new NonReportableError(`The Ruby LSP version manager is configured to be Mise, but ${path} does not exist`); } - - throw new NonReportableError( - `The Ruby LSP version manager is configured to be Mise, but could not find Mise installation. Searched in - ${possiblePaths.join(", ")}`, - ); } } diff --git a/vscode/src/test/suite/ruby/mise.test.ts b/vscode/src/test/suite/ruby/mise.test.ts index a37d5ee0c..53b39e181 100644 --- a/vscode/src/test/suite/ruby/mise.test.ts +++ b/vscode/src/test/suite/ruby/mise.test.ts @@ -35,7 +35,7 @@ suite("Mise", () => { context.dispose(); }); - test("Finds Ruby only binary path is appended to PATH", async () => { + test("Activates with auto-detected mise", async () => { const workspacePath = process.env.PWD!; const workspaceFolder = { uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), @@ -51,31 +51,25 @@ suite("Mise", () => { stdout: "", stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, }); - const findStub = sandbox - .stub(mise, "findMiseUri") - .resolves(vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise")); + + // Stub findMise to return the executable name, simulating PATH fallback + sandbox.stub(mise, "findMise" as any).resolves("mise"); const { env, version, yjit } = await mise.activate(); assert.ok( - execStub.calledOnceWithExactly( - `${os.homedir()}/.local/bin/mise x -- ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, - { - cwd: workspacePath, - shell: vscode.env.shell, - - env: process.env, - encoding: "utf-8", - }, - ), + execStub.calledOnceWithExactly(`mise x -- ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), ); assert.strictEqual(version, "3.0.0"); assert.strictEqual(yjit, true); assert.deepStrictEqual(env.ANY, "true"); - - execStub.restore(); - findStub.restore(); }); test("Allows configuring where Mise is installed", async () => {