From b9e537c09441ecede2b7d939c6297b2eab291bd2 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Wed, 6 Nov 2024 13:56:45 -0500 Subject: [PATCH] Allow individual version managers to trigger manual Ruby selection --- vscode/src/ruby.ts | 68 ++++++++++++++++--- vscode/src/ruby/chruby.ts | 3 +- vscode/src/ruby/none.ts | 3 +- vscode/src/ruby/versionManager.ts | 3 + vscode/src/test/suite/ruby/asdf.test.ts | 4 +- vscode/src/test/suite/ruby/chruby.test.ts | 18 ++--- vscode/src/test/suite/ruby/custom.test.ts | 2 +- vscode/src/test/suite/ruby/mise.test.ts | 4 +- vscode/src/test/suite/ruby/none.test.ts | 2 +- vscode/src/test/suite/ruby/rbenv.test.ts | 4 +- .../src/test/suite/ruby/rubyInstaller.test.ts | 18 ++++- vscode/src/test/suite/ruby/rvm.test.ts | 2 +- vscode/src/test/suite/ruby/shadowenv.test.ts | 30 ++++++-- 13 files changed, 122 insertions(+), 39 deletions(-) diff --git a/vscode/src/ruby.ts b/vscode/src/ruby.ts index f06786d83..fad69bb63 100644 --- a/vscode/src/ruby.ts +++ b/vscode/src/ruby.ts @@ -114,7 +114,12 @@ export class Ruby implements RubyInterface { if (workspaceRubyPath) { // If a workspace specific Ruby path is configured, then we use that to activate the environment await this.runActivation( - new None(this.workspaceFolder, this.outputChannel, workspaceRubyPath), + new None( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + workspaceRubyPath, + ), ); } else { // If the version manager is auto, discover the actual manager before trying to activate anything @@ -139,7 +144,12 @@ export class Ruby implements RubyInterface { if (globalRubyPath) { await this.runActivation( - new None(this.workspaceFolder, this.outputChannel, globalRubyPath), + new None( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + globalRubyPath, + ), ); } else { this._error = true; @@ -266,47 +276,83 @@ export class Ruby implements RubyInterface { switch (this.versionManager.identifier) { case ManagerIdentifier.Asdf: await this.runActivation( - new Asdf(this.workspaceFolder, this.outputChannel), + new Asdf( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.Chruby: await this.runActivation( - new Chruby(this.workspaceFolder, this.outputChannel), + new Chruby( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.Rbenv: await this.runActivation( - new Rbenv(this.workspaceFolder, this.outputChannel), + new Rbenv( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.Rvm: await this.runActivation( - new Rvm(this.workspaceFolder, this.outputChannel), + new Rvm( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.Mise: await this.runActivation( - new Mise(this.workspaceFolder, this.outputChannel), + new Mise( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.RubyInstaller: await this.runActivation( - new RubyInstaller(this.workspaceFolder, this.outputChannel), + new RubyInstaller( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.Custom: await this.runActivation( - new Custom(this.workspaceFolder, this.outputChannel), + new Custom( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; case ManagerIdentifier.None: await this.runActivation( - new None(this.workspaceFolder, this.outputChannel), + new None( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; default: await this.runActivation( - new Shadowenv(this.workspaceFolder, this.outputChannel), + new Shadowenv( + this.workspaceFolder, + this.outputChannel, + this.manuallySelectRuby.bind(this), + ), ); break; } diff --git a/vscode/src/ruby/chruby.ts b/vscode/src/ruby/chruby.ts index cabb26ee0..81a8d9915 100644 --- a/vscode/src/ruby/chruby.ts +++ b/vscode/src/ruby/chruby.ts @@ -29,8 +29,9 @@ export class Chruby extends VersionManager { constructor( workspaceFolder: vscode.WorkspaceFolder, outputChannel: WorkspaceChannel, + manuallySelectRuby: () => Promise, ) { - super(workspaceFolder, outputChannel); + super(workspaceFolder, outputChannel, manuallySelectRuby); const configuredRubies = vscode.workspace .getConfiguration("rubyLsp") diff --git a/vscode/src/ruby/none.ts b/vscode/src/ruby/none.ts index faea1690f..13d65facb 100644 --- a/vscode/src/ruby/none.ts +++ b/vscode/src/ruby/none.ts @@ -19,9 +19,10 @@ export class None extends VersionManager { constructor( workspaceFolder: vscode.WorkspaceFolder, outputChannel: WorkspaceChannel, + manuallySelectRuby: () => Promise, rubyPath?: string, ) { - super(workspaceFolder, outputChannel); + super(workspaceFolder, outputChannel, manuallySelectRuby); this.rubyPath = rubyPath ?? "ruby"; } diff --git a/vscode/src/ruby/versionManager.ts b/vscode/src/ruby/versionManager.ts index 506fc8a0c..f24837490 100644 --- a/vscode/src/ruby/versionManager.ts +++ b/vscode/src/ruby/versionManager.ts @@ -26,15 +26,18 @@ export abstract class VersionManager { protected readonly outputChannel: WorkspaceChannel; protected readonly workspaceFolder: vscode.WorkspaceFolder; protected readonly bundleUri: vscode.Uri; + protected readonly manuallySelectRuby: () => Promise; private readonly customBundleGemfile?: string; constructor( workspaceFolder: vscode.WorkspaceFolder, outputChannel: WorkspaceChannel, + manuallySelectRuby: () => Promise, ) { this.workspaceFolder = workspaceFolder; this.outputChannel = outputChannel; + this.manuallySelectRuby = manuallySelectRuby; const customBundleGemfile: string = vscode.workspace .getConfiguration("rubyLsp") .get("bundleGemfile")!; diff --git a/vscode/src/test/suite/ruby/asdf.test.ts b/vscode/src/test/suite/ruby/asdf.test.ts index e5e8aeab5..46a4449ad 100644 --- a/vscode/src/test/suite/ruby/asdf.test.ts +++ b/vscode/src/test/suite/ruby/asdf.test.ts @@ -26,7 +26,7 @@ suite("Asdf", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const asdf = new Asdf(workspaceFolder, outputChannel); + const asdf = new Asdf(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, yjit: true, @@ -75,7 +75,7 @@ suite("Asdf", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const asdf = new Asdf(workspaceFolder, outputChannel); + const asdf = new Asdf(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, yjit: true, diff --git a/vscode/src/test/suite/ruby/chruby.test.ts b/vscode/src/test/suite/ruby/chruby.test.ts index 8e1339457..d65f45b57 100644 --- a/vscode/src/test/suite/ruby/chruby.test.ts +++ b/vscode/src/test/suite/ruby/chruby.test.ts @@ -67,14 +67,14 @@ suite("Chruby", () => { outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); }); - after(() => { + afterEach(() => { fs.rmSync(rootPath, { recursive: true, force: true }); }); test("Finds Ruby when .ruby-version is inside workspace", async () => { fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, "opt", "rubies")), ]; @@ -90,7 +90,7 @@ suite("Chruby", () => { test("Finds Ruby when .ruby-version is inside on parent directories", async () => { fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, "opt", "rubies")), ]; @@ -126,7 +126,7 @@ suite("Chruby", () => { fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, "opt", "rubies")), ]; @@ -171,7 +171,7 @@ suite("Chruby", () => { `${RUBY_VERSION}-rc1`, ); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, "opt", "rubies")), ]; @@ -201,7 +201,7 @@ suite("Chruby", () => { fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [vscode.Uri.file(rubyHome)]; const { env, version, yjit } = await chruby.activate(); @@ -223,7 +223,7 @@ suite("Chruby", () => { : "", } as any); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); configStub.restore(); const { env, version, yjit } = await chruby.activate(); @@ -247,7 +247,7 @@ suite("Chruby", () => { `${major}.${minor}`, ); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, "opt", "rubies")), ]; @@ -278,7 +278,7 @@ suite("Chruby", () => { `${major}.${minor}`, ); - const chruby = new Chruby(workspaceFolder, outputChannel); + const chruby = new Chruby(workspaceFolder, outputChannel, async () => {}); chruby.rubyInstallationUris = [ vscode.Uri.file(path.join(rootPath, ".rubies")), vscode.Uri.file(path.join(rootPath, "opt", "rubies")), diff --git a/vscode/src/test/suite/ruby/custom.test.ts b/vscode/src/test/suite/ruby/custom.test.ts index 27bc824f1..5e1482d1c 100644 --- a/vscode/src/test/suite/ruby/custom.test.ts +++ b/vscode/src/test/suite/ruby/custom.test.ts @@ -23,7 +23,7 @@ suite("Custom", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const custom = new Custom(workspaceFolder, outputChannel); + const custom = new Custom(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, diff --git a/vscode/src/test/suite/ruby/mise.test.ts b/vscode/src/test/suite/ruby/mise.test.ts index b2e6a1f2c..5d31fa244 100644 --- a/vscode/src/test/suite/ruby/mise.test.ts +++ b/vscode/src/test/suite/ruby/mise.test.ts @@ -27,7 +27,7 @@ suite("Mise", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const mise = new Mise(workspaceFolder, outputChannel); + const mise = new Mise(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, @@ -82,7 +82,7 @@ suite("Mise", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const mise = new Mise(workspaceFolder, outputChannel); + const mise = new Mise(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, diff --git a/vscode/src/test/suite/ruby/none.test.ts b/vscode/src/test/suite/ruby/none.test.ts index 1b5bbaf3e..f42fbaf64 100644 --- a/vscode/src/test/suite/ruby/none.test.ts +++ b/vscode/src/test/suite/ruby/none.test.ts @@ -23,7 +23,7 @@ suite("None", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const none = new None(workspaceFolder, outputChannel); + const none = new None(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, diff --git a/vscode/src/test/suite/ruby/rbenv.test.ts b/vscode/src/test/suite/ruby/rbenv.test.ts index db33705f8..99569a921 100644 --- a/vscode/src/test/suite/ruby/rbenv.test.ts +++ b/vscode/src/test/suite/ruby/rbenv.test.ts @@ -26,7 +26,7 @@ suite("Rbenv", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const rbenv = new Rbenv(workspaceFolder, outputChannel); + const rbenv = new Rbenv(workspaceFolder, outputChannel, async () => {}); const envStub = { env: { ANY: "true" }, @@ -68,7 +68,7 @@ suite("Rbenv", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const rbenv = new Rbenv(workspaceFolder, outputChannel); + const rbenv = new Rbenv(workspaceFolder, outputChannel, async () => {}); const execStub = sinon.stub(common, "asyncExec").resolves({ stdout: "", diff --git a/vscode/src/test/suite/ruby/rubyInstaller.test.ts b/vscode/src/test/suite/ruby/rubyInstaller.test.ts index 6e478da36..ca773618d 100644 --- a/vscode/src/test/suite/ruby/rubyInstaller.test.ts +++ b/vscode/src/test/suite/ruby/rubyInstaller.test.ts @@ -60,7 +60,11 @@ suite("RubyInstaller", () => { fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); - const windows = new RubyInstaller(workspaceFolder, outputChannel); + const windows = new RubyInstaller( + workspaceFolder, + outputChannel, + async () => {}, + ); const { env, version, yjit } = await windows.activate(); assert.match(env.GEM_PATH!, /ruby\/3\.3\.0/); @@ -90,7 +94,11 @@ suite("RubyInstaller", () => { fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); - const windows = new RubyInstaller(workspaceFolder, outputChannel); + const windows = new RubyInstaller( + workspaceFolder, + outputChannel, + async () => {}, + ); const { env, version, yjit } = await windows.activate(); assert.match(env.GEM_PATH!, /ruby\/3\.3\.0/); @@ -120,7 +128,11 @@ suite("RubyInstaller", () => { fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); - const windows = new RubyInstaller(workspaceFolder, outputChannel); + const windows = new RubyInstaller( + workspaceFolder, + outputChannel, + async () => {}, + ); const result = ["/fake/dir", "/other/fake/dir", true, RUBY_VERSION].join( ACTIVATION_SEPARATOR, ); diff --git a/vscode/src/test/suite/ruby/rvm.test.ts b/vscode/src/test/suite/ruby/rvm.test.ts index ff6bcba41..a1ff3c5b4 100644 --- a/vscode/src/test/suite/ruby/rvm.test.ts +++ b/vscode/src/test/suite/ruby/rvm.test.ts @@ -26,7 +26,7 @@ suite("RVM", () => { index: 0, }; const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); - const rvm = new Rvm(workspaceFolder, outputChannel); + const rvm = new Rvm(workspaceFolder, outputChannel, async () => {}); const installationPathStub = sinon .stub(rvm, "findRvmInstallation") diff --git a/vscode/src/test/suite/ruby/shadowenv.test.ts b/vscode/src/test/suite/ruby/shadowenv.test.ts index 8d1666c8e..0ba34ceab 100644 --- a/vscode/src/test/suite/ruby/shadowenv.test.ts +++ b/vscode/src/test/suite/ruby/shadowenv.test.ts @@ -121,7 +121,11 @@ suite("Shadowenv", () => { `(env/prepend-to-pathlist "PATH" "${rubyBinPath}")`, ); - const shadowenv = new Shadowenv(workspaceFolder, outputChannel); + const shadowenv = new Shadowenv( + workspaceFolder, + outputChannel, + async () => {}, + ); const { env, version, yjit } = await shadowenv.activate(); assert.match(env.PATH!, new RegExp(rubyBinPath)); @@ -137,7 +141,11 @@ suite("Shadowenv", () => { shadowLispFile, ); - const shadowenv = new Shadowenv(workspaceFolder, outputChannel); + const shadowenv = new Shadowenv( + workspaceFolder, + outputChannel, + async () => {}, + ); const { env, version, yjit } = await shadowenv.activate(); assert.match(env.PATH!, new RegExp(rubyBinPath)); @@ -159,7 +167,11 @@ suite("Shadowenv", () => { .stub(vscode.window, "showErrorMessage") .resolves("Trust workspace" as any); - const shadowenv = new Shadowenv(workspaceFolder, outputChannel); + const shadowenv = new Shadowenv( + workspaceFolder, + outputChannel, + async () => {}, + ); const { env, version, yjit } = await shadowenv.activate(); assert.match(env.PATH!, new RegExp(rubyBinPath)); @@ -185,7 +197,11 @@ suite("Shadowenv", () => { .stub(vscode.window, "showErrorMessage") .resolves("Cancel" as any); - const shadowenv = new Shadowenv(workspaceFolder, outputChannel); + const shadowenv = new Shadowenv( + workspaceFolder, + outputChannel, + async () => {}, + ); await assert.rejects(async () => { await shadowenv.activate(); @@ -204,7 +220,11 @@ suite("Shadowenv", () => { shadowLispFile, ); - const shadowenv = new Shadowenv(workspaceFolder, outputChannel); + const shadowenv = new Shadowenv( + workspaceFolder, + outputChannel, + async () => {}, + ); // First, reject the call to `shadowenv exec`. Then resolve the call to `which shadowenv` to return nothing const execStub = sinon