Skip to content

Commit

Permalink
Use activated gem paths to register document selectors (#2718)
Browse files Browse the repository at this point in the history
* Return gem path from activation scripts

* Use activated gem paths to register document selectors
  • Loading branch information
vinistock authored Oct 17, 2024
1 parent 01a8ecf commit c5e248d
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 40 deletions.
36 changes: 16 additions & 20 deletions vscode/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ function collectClientOptions(
const enabledFeatures = Object.keys(features).filter((key) => features[key]);

const fsPath = workspaceFolder.uri.fsPath.replace(/\/$/, "");

// For each workspace, the language client is responsible for handling requests for:
// 1. Files inside of the workspace itself
// 2. Bundled gems
// 3. Default gems
let documentSelector: DocumentSelector = SUPPORTED_LANGUAGE_IDS.map(
(language) => {
return { language, pattern: `${fsPath}/**/*` };
Expand All @@ -143,27 +148,18 @@ function collectClientOptions(
});
}

// For each workspace, the language client is responsible for handling requests for:
// 1. Files inside of the workspace itself
// 2. Bundled gems
// 3. Default gems

if (ruby.env.GEM_PATH) {
const parts = ruby.env.GEM_PATH.split(path.delimiter);

// Because of how default gems are installed, the entry in the `GEM_PATH` is actually not exactly where the files
// are located. With the regex, we are correcting the default gem path from this (where the files are not located)
// /opt/rubies/3.3.1/lib/ruby/gems/3.3.0
//
// to this (where the files are actually stored)
// /opt/rubies/3.3.1/lib/ruby/3.3.0
parts.forEach((gemPath) => {
documentSelector.push({
language: "ruby",
pattern: `${gemPath.replace(/lib\/ruby\/gems\/(?=\d)/, "lib/ruby/")}/**/*`,
});
// Because of how default gems are installed, the entry in the `GEM_PATH` is actually not exactly where the files
// are located. With the regex, we are correcting the default gem path from this (where the files are not located)
// /opt/rubies/3.3.1/lib/ruby/gems/3.3.0
//
// to this (where the files are actually stored)
// /opt/rubies/3.3.1/lib/ruby/3.3.0
ruby.gemPath.forEach((gemPath) => {
documentSelector.push({
language: "ruby",
pattern: `${gemPath.replace(/lib\/ruby\/gems\/(?=\d)/, "lib/ruby/")}/**/*`,
});
}
});

// This is a temporary solution as an escape hatch for users who cannot upgrade the `ruby-lsp` gem to a version that
// supports ERB
Expand Down
4 changes: 3 additions & 1 deletion vscode/src/ruby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class Ruby implements RubyInterface {
// 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;
readonly gemPath: string[] = [];
private readonly workspaceFolder: vscode.WorkspaceFolder;
#versionManager: ManagerConfiguration = vscode.workspace
.getConfiguration("rubyLsp")
Expand Down Expand Up @@ -201,7 +202,7 @@ export class Ruby implements RubyInterface {
}

private async runActivation(manager: VersionManager) {
const { env, version, yjit } = await manager.activate();
const { env, version, yjit, gemPath } = await manager.activate();
const [major, minor, _patch] = version.split(".").map(Number);

this.sanitizeEnvironment(env);
Expand All @@ -211,6 +212,7 @@ export class Ruby implements RubyInterface {
this._env = env;
this.rubyVersion = version;
this.yjitEnabled = (yjit && major > 3) || (major === 3 && minor >= 2);
this.gemPath.push(...gemPath);
}

// Fetch information related to the Ruby version. This can only be invoked after activation, so that `rubyVersion` is
Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/asdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class Asdf extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}

Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/chruby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class Chruby extends VersionManager {
env: { ...process.env, ...rubyEnv },
yjit,
version,
gemPath: [gemHome, defaultGems],
};
}

Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class Custom extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}

Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/mise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class Mise extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}

Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/none.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class None extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}
}
1 change: 1 addition & 0 deletions vscode/src/ruby/rbenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Rbenv extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}
}
1 change: 1 addition & 0 deletions vscode/src/ruby/rvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class Rvm extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
}

Expand Down
1 change: 1 addition & 0 deletions vscode/src/ruby/shadowenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class Shadowenv extends VersionManager {
env: { ...process.env, ...parsedResult.env },
yjit: parsedResult.yjit,
version: parsedResult.version,
gemPath: parsedResult.gemPath,
};
} catch (error: any) {
const { stdout } = await this.runScript("command -v shadowenv");
Expand Down
3 changes: 2 additions & 1 deletion vscode/src/ruby/versionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ export interface ActivationResult {
env: NodeJS.ProcessEnv;
yjit: boolean;
version: string;
gemPath: string[];
}

export const ACTIVATION_SEPARATOR = "RUBY_LSP_ACTIVATION_SEPARATOR";

export abstract class VersionManager {
public activationScript = [
`STDERR.print("${ACTIVATION_SEPARATOR}" + `,
"{ env: ENV.to_h, yjit: !!defined?(RubyVM:: YJIT), version: RUBY_VERSION }.to_json + ",
"{ env: ENV.to_h, yjit: !!defined?(RubyVM:: YJIT), version: RUBY_VERSION, gemPath: Gem.path }.to_json + ",
`"${ACTIVATION_SEPARATOR}")`,
].join("");

Expand Down
66 changes: 48 additions & 18 deletions vscode/src/test/suite/ruby.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import sinon from "sinon";
import { Ruby, ManagerIdentifier } from "../../ruby";
import { WorkspaceChannel } from "../../workspaceChannel";
import { LOG_CHANNEL } from "../../common";
import * as common from "../../common";
import { ACTIVATION_SEPARATOR } from "../../ruby/versionManager";

suite("Ruby environment activation", () => {
const workspacePath = path.dirname(
Expand All @@ -18,6 +20,14 @@ suite("Ruby environment activation", () => {
name: path.basename(workspacePath),
index: 0,
};
const context = {
extensionMode: vscode.ExtensionMode.Test,
workspaceState: {
get: () => undefined,
update: () => undefined,
},
} as unknown as vscode.ExtensionContext;
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);

test("Activate fetches Ruby information when outside of Ruby LSP", async () => {
const manager = process.env.CI
Expand All @@ -38,15 +48,6 @@ suite("Ruby environment activation", () => {
},
} as unknown as vscode.WorkspaceConfiguration);

const context = {
extensionMode: vscode.ExtensionMode.Test,
workspaceState: {
get: () => undefined,
update: () => undefined,
},
} as unknown as vscode.ExtensionContext;
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);

const ruby = new Ruby(context, workspaceFolder, outputChannel);
await ruby.activateRuby();

Expand Down Expand Up @@ -79,15 +80,6 @@ suite("Ruby environment activation", () => {
},
} as unknown as vscode.WorkspaceConfiguration);

const context = {
extensionMode: vscode.ExtensionMode.Test,
workspaceState: {
get: () => undefined,
update: () => undefined,
},
} as unknown as vscode.ExtensionContext;
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);

const ruby = new Ruby(context, workspaceFolder, outputChannel);

process.env.VERBOSE = "1";
Expand All @@ -103,4 +95,42 @@ suite("Ruby environment activation", () => {
delete process.env.RUBY_GC_HEAP_GROWTH_FACTOR;
configStub.restore();
});

test("Sets gem path for version managers based on shims", async () => {
const configStub = sinon
.stub(vscode.workspace, "getConfiguration")
.returns({
get: (name: string) => {
if (name === "rubyVersionManager") {
return { identifier: ManagerIdentifier.Rbenv };
} else if (name === "bundleGemfile") {
return "";
}

return undefined;
},
} as unknown as vscode.WorkspaceConfiguration);

const envStub = {
env: { ANY: "true" },
yjit: true,
version: "3.3.5",
gemPath: ["~/.gem/ruby/3.3.5", "/opt/rubies/3.3.5/lib/ruby/gems/3.3.0"],
};

const execStub = sinon.stub(common, "asyncExec").resolves({
stdout: "",
stderr: `${ACTIVATION_SEPARATOR}${JSON.stringify(envStub)}${ACTIVATION_SEPARATOR}`,
});

const ruby = new Ruby(context, workspaceFolder, outputChannel);
await ruby.activateRuby();
execStub.restore();
configStub.restore();

assert.deepStrictEqual(ruby.gemPath, [
"~/.gem/ruby/3.3.5",
"/opt/rubies/3.3.5/lib/ruby/gems/3.3.0",
]);
});
});

0 comments on commit c5e248d

Please sign in to comment.