Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions editors/vscode/client/StatusBarItemHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<StatusBarTool, string> = new Map();
private tooltipSections: Map<StatusBarTool, ToolState> = new Map([
["linter", { isEnabled: false, content: "", version: "unknown" }],
["formatter", { isEnabled: false, content: "", version: "unknown" }],
]);

private statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 100);

Expand All @@ -19,23 +28,45 @@ 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`;
}

/**
* 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)) {
Expand Down
45 changes: 14 additions & 31 deletions editors/vscode/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,42 +76,25 @@ export async function activate(context: ExtensionContext) {

await Promise.all(
tools.map((tool): Promise<void> => {
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();
}
Expand Down
2 changes: 1 addition & 1 deletion editors/vscode/client/tools/ToolInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export default interface ToolInterface {
*/
activate(
context: ExtensionContext,
binaryPath: string,
outputChannel: LogOutputChannel,
configService: ConfigService,
statusBarItemHandler: StatusBarItemHandler,
binaryPath?: string,
): Promise<void>;

/**
Expand Down
24 changes: 15 additions & 9 deletions editors/vscode/client/tools/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
);
}
}
50 changes: 28 additions & 22 deletions editors/vscode/client/tools/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
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;
Expand Down Expand Up @@ -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`;

Expand All @@ -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(
Expand Down
Loading