From 3d68ae19a02ebda4e84a790c546eced000346ef8 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 16 Aug 2024 21:46:09 +0100 Subject: [PATCH 1/2] Add command to collect Ruby LSP information for issue reporting - Implement `collectRubyLspInfo` function in new `infoCollector.ts` file - Add new command `rubyLsp.collectRubyLspInfo` in `package.json` - Register the new command in `rubyLsp.ts` - Collect and display information including: - VS Code version - Ruby LSP extension version - Ruby LSP server version - Installed Ruby LSP addons - Ruby version and version manager - Installed public VS Code extensions This feature will help users and maintainers quickly gather relevant information for reporting Ruby LSP related issues. The collected information is displayed in the output channel for easy copying and pasting into GitHub issues. --- vscode/package.json | 5 ++ vscode/src/common.ts | 1 + vscode/src/infoCollector.ts | 146 ++++++++++++++++++++++++++++++++++++ vscode/src/rubyLsp.ts | 5 ++ 4 files changed, 157 insertions(+) create mode 100644 vscode/src/infoCollector.ts diff --git a/vscode/package.json b/vscode/package.json index 21a7dfbb6..c4803d22f 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -140,6 +140,11 @@ "title": "Ruby file operations", "category": "Ruby LSP", "icon": "$(ruby)" + }, + { + "command": "rubyLsp.collectRubyLspInfo", + "title": "Collect Ruby LSP Information for Issue Reporting", + "category": "Ruby LSP" } ], "configuration": { diff --git a/vscode/src/common.ts b/vscode/src/common.ts index 9a868a135..ab6434592 100644 --- a/vscode/src/common.ts +++ b/vscode/src/common.ts @@ -26,6 +26,7 @@ export enum Command { RailsGenerate = "rubyLsp.railsGenerate", RailsDestroy = "rubyLsp.railsDestroy", NewMinitestFile = "rubyLsp.newMinitestFile", + CollectRubyLspInfo = "rubyLsp.collectRubyLspInfo", } export interface RubyInterface { diff --git a/vscode/src/infoCollector.ts b/vscode/src/infoCollector.ts new file mode 100644 index 000000000..238898725 --- /dev/null +++ b/vscode/src/infoCollector.ts @@ -0,0 +1,146 @@ +import * as vscode from "vscode"; + +import { Workspace } from "./workspace"; + +export async function collectRubyLspInfo(workspace: Workspace | undefined) { + if (!workspace) { + await vscode.window.showErrorMessage("No active Ruby workspace found."); + return; + } + + const lspInfo = await gatherLspInfo(workspace); + const panel = vscode.window.createWebviewPanel( + "rubyLspInfo", + "Ruby LSP Information", + vscode.ViewColumn.One, + { enableScripts: true }, + ); + + panel.webview.html = generateRubyLspInfoReport(lspInfo); +} + +async function gatherLspInfo( + workspace: Workspace, +): Promise> { + const vscodeVersion = vscode.version; + const rubyLspExtension = vscode.extensions.getExtension("Shopify.ruby-lsp")!; + const rubyLspExtensionVersion = rubyLspExtension.packageJSON.version; + const rubyLspVersion = workspace.lspClient?.serverVersion ?? "Unknown"; + const rubyLspAddons = + workspace.lspClient?.addons?.map((addon) => addon.name) ?? []; + const extensions = await getPublicExtensions(); + + return { + /* eslint-disable @typescript-eslint/naming-convention */ + "VS Code Version": vscodeVersion, + "Ruby LSP Extension Version": rubyLspExtensionVersion, + "Ruby LSP Server Version": rubyLspVersion, + "Ruby LSP Addons": rubyLspAddons, + "Ruby Version": workspace.ruby.rubyVersion ?? "Unknown", + "Ruby Version Manager": workspace.ruby.versionManager.identifier, + "Installed Extensions": extensions, + /* eslint-enable @typescript-eslint/naming-convention */ + }; +} + +async function getPublicExtensions(): Promise { + return vscode.extensions.all + .filter((ext) => { + // Filter out built-in extensions + if (ext.packageJSON.isBuiltin) { + return false; + } + + // Assume if an extension doesn't have a license, it's private and should not be listed + if ( + ext.packageJSON.license === "UNLICENSED" || + !ext.packageJSON.license + ) { + return false; + } + + return true; + }) + .map((ext) => `${ext.packageJSON.name} (${ext.packageJSON.version})`); +} + +function generateRubyLspInfoReport( + info: Record, +): string { + let markdown = "\n### Ruby LSP Information\n\n"; + + for (const [key, value] of Object.entries(info)) { + markdown += `#### ${key}\n\n`; + if (Array.isArray(value)) { + if (key === "Installed Extensions") { + markdown += + "<details>\n<summary>Click to expand</summary>\n\n"; + markdown += `${value.map((val) => `- ${val}`).join("\n")}\n`; + markdown += "</details>\n"; + } else { + markdown += `${value.map((val) => `- ${val}`).join("\n")}\n`; + } + } else { + markdown += `${value}\n`; + } + markdown += "\n"; + } + + const html = ` + + + + + + Ruby LSP Information + + + +

Ruby LSP Information

+

Please copy the content below and paste it into the issue you're opening:

+
${markdown}
+ + + + `; + + return html; +} diff --git a/vscode/src/rubyLsp.ts b/vscode/src/rubyLsp.ts index 148fa14a2..f83e6583f 100644 --- a/vscode/src/rubyLsp.ts +++ b/vscode/src/rubyLsp.ts @@ -17,6 +17,7 @@ import { Debugger } from "./debugger"; import { DependenciesTree } from "./dependenciesTree"; import { Rails } from "./rails"; import { ChatAgent } from "./chatAgent"; +import { collectRubyLspInfo } from "./infoCollector"; // The RubyLsp class represents an instance of the entire extension. This should only be instantiated once at the // activation event. One instance of this class controls all of the existing workspaces, telemetry and handles all @@ -570,6 +571,10 @@ export class RubyLsp { await vscode.commands.executeCommand(pick.command, ...pick.args); }), vscode.commands.registerCommand(Command.NewMinitestFile, newMinitestFile), + vscode.commands.registerCommand(Command.CollectRubyLspInfo, async () => { + const workspace = await this.showWorkspacePick(); + await collectRubyLspInfo(workspace); + }), ); } From 7b034e7e449906d1e14fb94c55f27f85c47c2563 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Tue, 20 Aug 2024 22:39:10 +0100 Subject: [PATCH 2/2] Collect rubyLsp settings as well --- vscode/src/infoCollector.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/vscode/src/infoCollector.ts b/vscode/src/infoCollector.ts index 238898725..4198a79e8 100644 --- a/vscode/src/infoCollector.ts +++ b/vscode/src/infoCollector.ts @@ -21,7 +21,7 @@ export async function collectRubyLspInfo(workspace: Workspace | undefined) { async function gatherLspInfo( workspace: Workspace, -): Promise> { +): Promise>> { const vscodeVersion = vscode.version; const rubyLspExtension = vscode.extensions.getExtension("Shopify.ruby-lsp")!; const rubyLspExtensionVersion = rubyLspExtension.packageJSON.version; @@ -30,6 +30,21 @@ async function gatherLspInfo( workspace.lspClient?.addons?.map((addon) => addon.name) ?? []; const extensions = await getPublicExtensions(); + // Fetch rubyLsp settings + const workspaceSettings = vscode.workspace.getConfiguration( + "rubyLsp", + workspace.workspaceFolder, + ); + const userSettings = vscode.workspace.getConfiguration("rubyLsp"); + + // Get only the workspace-specific settings + const workspaceSpecificSettings: Record = {}; + for (const key of Object.keys(workspaceSettings)) { + if (workspaceSettings.inspect(key)?.workspaceValue !== undefined) { + workspaceSpecificSettings[key] = workspaceSettings.get(key); + } + } + return { /* eslint-disable @typescript-eslint/naming-convention */ "VS Code Version": vscodeVersion, @@ -39,6 +54,10 @@ async function gatherLspInfo( "Ruby Version": workspace.ruby.rubyVersion ?? "Unknown", "Ruby Version Manager": workspace.ruby.versionManager.identifier, "Installed Extensions": extensions, + "Ruby LSP Settings": { + Workspace: workspaceSpecificSettings, + User: userSettings, + }, /* eslint-enable @typescript-eslint/naming-convention */ }; } @@ -65,7 +84,7 @@ async function getPublicExtensions(): Promise { } function generateRubyLspInfoReport( - info: Record, + info: Record>, ): string { let markdown = "\n### Ruby LSP Information\n\n"; @@ -80,6 +99,14 @@ function generateRubyLspInfoReport( } else { markdown += `${value.map((val) => `- ${val}`).join("\n")}\n`; } + } else if (typeof value === "object" && value !== null) { + markdown += + "<details>\n<summary>Click to expand</summary>\n\n"; + for (const [subKey, subValue] of Object.entries(value)) { + markdown += `##### ${subKey}\n\n`; + markdown += `\`\`\`json\n${JSON.stringify(subValue, null, 2)}\n\`\`\`\n\n`; + } + markdown += "</details>\n"; } else { markdown += `${value}\n`; }