diff --git a/editors/vscode/client/StatusBarItemHandler.ts b/editors/vscode/client/StatusBarItemHandler.ts index b95c549d44c3e..03534464f8f8d 100644 --- a/editors/vscode/client/StatusBarItemHandler.ts +++ b/editors/vscode/client/StatusBarItemHandler.ts @@ -2,8 +2,17 @@ import { MarkdownString, StatusBarAlignment, StatusBarItem, ThemeColor, window } type StatusBarTool = "linter" | "formatter"; +type ToolState = { + isEnabled: boolean; + content: string; + version?: string; +}; + export default class StatusBarItemHandler { - private tooltipSections: Map = new Map(); + private tooltipSections: Map = new Map([ + ["linter", { isEnabled: false, content: "", version: "unknown" }], + ["formatter", { isEnabled: false, content: "", version: "unknown" }], + ]); private statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 100); @@ -19,8 +28,11 @@ export default class StatusBarItemHandler { this.statusBarItem.show(); } - public setColorAndIcon(bgColor: string, icon: string): void { - this.statusBarItem.backgroundColor = new ThemeColor(bgColor); + public setWarnBackground(): void { + this.statusBarItem.backgroundColor = new ThemeColor("statusBarItem.warningBackground"); + } + + public setIcon(icon: string): void { this.statusBarItem.text = `$(${icon}) oxc`; } @@ -28,14 +40,33 @@ export default class StatusBarItemHandler { * Updates the tooltip text for a specific tool section. * The tooltip can use markdown syntax and VSCode icons. */ - public updateToolTooltip(toolId: StatusBarTool, text: string): void { - this.tooltipSections.set(toolId, text); - this.updateFullTooltip(); + public updateTool( + toolId: StatusBarTool, + isEnabled: boolean, + text: string, + version?: string, + ): void { + const section = this.tooltipSections.get(toolId); + if (section) { + section.isEnabled = isEnabled; + section.content = text; + section.version = version ?? "unknown"; + this.updateFullTooltip(); + } } private updateFullTooltip(): void { - const text = [this.tooltipSections.get("linter"), this.tooltipSections.get("formatter")] - .filter(Boolean) + const sections: [string, ToolState][] = [ + ["oxlint", this.tooltipSections.get("linter")!], + ["oxfmt", this.tooltipSections.get("formatter")!], + ]; + + const text = sections + .map(([tool, section]) => { + const version = section.version ? ` v${section.version}` : "unknown version"; + const statusText = section.isEnabled ? `enabled (${version})` : "disabled"; + return `**${tool} is ${statusText}**\n\n${section.content}`; + }) .join("\n\n---\n\n"); if (!(this.statusBarItem.tooltip instanceof MarkdownString)) { diff --git a/editors/vscode/client/extension.ts b/editors/vscode/client/extension.ts index 6c48bf153effd..f411e1d5b59ce 100644 --- a/editors/vscode/client/extension.ts +++ b/editors/vscode/client/extension.ts @@ -76,42 +76,25 @@ export async function activate(context: ExtensionContext) { await Promise.all( tools.map((tool): Promise => { + const channel = tool instanceof Linter ? outputChannelLint : outputChannelFormat; const binaryPath = binaryPaths[tools.indexOf(tool)]; - if (!binaryPath && tool instanceof Linter) { - statusBarItemHandler.setColorAndIcon("statusBarItem.errorBackground", "error"); - statusBarItemHandler.updateToolTooltip( - "linter", - "**oxlint disabled**\n\nError: No valid oxlint binary found.", - ); - return Promise.resolve(); - } - - if (!binaryPath && tool instanceof Formatter) { - // No valid binary found for the formatter. - statusBarItemHandler.updateToolTooltip( - "formatter", - "**oxfmt disabled**\n\nNo valid oxfmt binary found.", - ); - outputChannelFormat.appendLine( - "No valid oxfmt binary found. Formatter will not be activated.", - ); - return Promise.resolve(); - } - - // binaryPath is guaranteed to be defined here. - const binaryPathResolved = binaryPath!; - - return tool.activate( - context, - binaryPathResolved, - tool instanceof Linter ? outputChannelLint : outputChannelFormat, - configService, - statusBarItemHandler, - ); + return tool.activate(context, channel, configService, statusBarItemHandler, binaryPath); }), ); + // No valid binaries found for any tool, show error background. + if (binaryPaths.every((path) => !path)) { + statusBarItemHandler.setWarnBackground(); + statusBarItemHandler.setIcon("circle-slash"); + // Every tool has a valid binary. + } else if (binaryPaths.every((path) => path)) { + statusBarItemHandler.setIcon("check-all"); + // Some tools are missing binaries. + } else { + statusBarItemHandler.setIcon("check"); + } + // Finally show the status bar item. statusBarItemHandler.show(); } diff --git a/editors/vscode/client/tools/ToolInterface.ts b/editors/vscode/client/tools/ToolInterface.ts index 75aff43305201..309e319f538e2 100644 --- a/editors/vscode/client/tools/ToolInterface.ts +++ b/editors/vscode/client/tools/ToolInterface.ts @@ -15,10 +15,10 @@ export default interface ToolInterface { */ activate( context: ExtensionContext, - binaryPath: string, outputChannel: LogOutputChannel, configService: ConfigService, statusBarItemHandler: StatusBarItemHandler, + binaryPath?: string, ): Promise; /** diff --git a/editors/vscode/client/tools/formatter.ts b/editors/vscode/client/tools/formatter.ts index 964ec1a75511c..f10db54634500 100644 --- a/editors/vscode/client/tools/formatter.ts +++ b/editors/vscode/client/tools/formatter.ts @@ -48,11 +48,18 @@ export default class FormatterTool implements ToolInterface { async activate( context: ExtensionContext, - binaryPath: string, outputChannel: LogOutputChannel, configService: ConfigService, statusBarItemHandler: StatusBarItemHandler, + binaryPath?: string, ) { + // No valid binary found for the formatter. + if (!binaryPath) { + statusBarItemHandler.updateTool("formatter", false, "No valid oxfmt binary found."); + outputChannel.appendLine("No valid oxfmt binary found. Formatter will not be activated."); + return Promise.resolve(); + } + const restartCommand = commands.registerCommand(OxcCommands.RestartServerFmt, async () => { await this.restartClient(); this.updateStatsBar(statusBarItemHandler, configService); @@ -234,16 +241,15 @@ export default class FormatterTool implements ToolInterface { } private updateStatsBar(statusBarItemHandler: StatusBarItemHandler, configService: ConfigService) { - const version = this.client?.initializeResult?.serverInfo?.version ?? "unknown"; - - let text = configService.vsCodeConfig.enable - ? `**oxfmt is enabled (v${version})**\n\n` - : `**oxfmt is disabled**\n\n`; - - text += + const text = `[$(terminal) Open Output](command:${OxcCommands.ShowOutputChannelFmt})\n\n` + `[$(refresh) Restart Server](command:${OxcCommands.RestartServerFmt})\n\n`; - statusBarItemHandler.updateToolTooltip("formatter", text); + statusBarItemHandler.updateTool( + "formatter", + configService.vsCodeConfig.enable, + text, + this.client?.initializeResult?.serverInfo?.version, + ); } } diff --git a/editors/vscode/client/tools/linter.ts b/editors/vscode/client/tools/linter.ts index 13c6f9bd19eb7..ae5a4f63206da 100644 --- a/editors/vscode/client/tools/linter.ts +++ b/editors/vscode/client/tools/linter.ts @@ -62,11 +62,17 @@ export default class LinterTool implements ToolInterface { async activate( context: ExtensionContext, - binaryPath: string, outputChannel: LogOutputChannel, configService: ConfigService, statusBarItemHandler: StatusBarItemHandler, + binaryPath?: string, ): Promise { + if (!binaryPath) { + statusBarItemHandler.updateTool("linter", false, "No valid oxlint binary found."); + outputChannel.appendLine("No valid oxlint binary found. Linter will not be activated."); + return Promise.resolve(); + } + this.allowedToStartServer = configService.vsCodeConfig.requireConfig ? (await workspace.findFiles(`**/.oxlintrc.json`, "**/node_modules/**", 1)).length > 0 : true; @@ -292,38 +298,30 @@ export default class LinterTool implements ToolInterface { * Get the status bar state based on whether oxc is enabled and allowed to start. */ getStatusBarState(enable: boolean): { - bgColor: string; - icon: string; - tooltipText: string; + isEnabled: boolean; + tooltipText?: string; } { if (!this.allowedToStartServer) { return { - bgColor: "statusBarItem.offlineBackground", - icon: "circle-slash", - tooltipText: "oxlint is disabled (no .oxlintrc.json found)", + isEnabled: false, + tooltipText: "no .oxlintrc.json found", }; } else if (!enable) { return { - bgColor: "statusBarItem.warningBackground", - icon: "check", - tooltipText: "oxlint is disabled", - }; - } else { - const version = this.client?.initializeResult?.serverInfo?.version ?? "unknown"; - - return { - bgColor: "statusBarItem.activeBackground", - icon: "check-all", - tooltipText: `oxlint is enabled (v${version})`, + isEnabled: false, + tooltipText: "`oxc.enable` is false", }; } + + return { + isEnabled: true, + }; } updateStatusBar(statusBarItemHandler: StatusBarItemHandler, enable: boolean) { - const { bgColor, icon, tooltipText } = this.getStatusBarState(enable); + const { isEnabled, tooltipText } = this.getStatusBarState(enable); let text = - `**${tooltipText}**\n\n` + `[$(terminal) Open Output](command:${OxcCommands.ShowOutputChannelLint})\n\n` + `[$(refresh) Restart Server](command:${OxcCommands.RestartServerLint})\n\n`; @@ -333,8 +331,16 @@ export default class LinterTool implements ToolInterface { text += `[$(play) Start Server](command:${OxcCommands.ToggleEnableLint})\n\n`; } - statusBarItemHandler.setColorAndIcon(bgColor, icon); - statusBarItemHandler.updateToolTooltip("linter", text); + if (tooltipText) { + text = `${tooltipText}\n\n` + text; + } + + statusBarItemHandler.updateTool( + "linter", + isEnabled, + text, + this.client?.initializeResult?.serverInfo?.version, + ); } generateActivatorByConfig(