Skip to content

Commit 4fd88d7

Browse files
committed
feat(vscode): add quick actions to status bar tooltip
1 parent fe28456 commit 4fd88d7

File tree

3 files changed

+88
-36
lines changed

3 files changed

+88
-36
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { MarkdownString, StatusBarAlignment, StatusBarItem, ThemeColor, window } from 'vscode';
2+
3+
type StatusBarTool = 'linter' | 'formatter';
4+
5+
export default class StatusBarItemHandler {
6+
private tooltipSections: Map<StatusBarTool, string> = new Map();
7+
8+
private statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 100);
9+
10+
private extensionVersion: string = '<unknown>';
11+
12+
constructor(extensionVersion?: string) {
13+
if (extensionVersion) {
14+
this.extensionVersion = extensionVersion;
15+
}
16+
}
17+
18+
public show(): void {
19+
this.statusBarItem.show();
20+
}
21+
22+
public setColorAndIcon(bgColor: string, icon: string): void {
23+
this.statusBarItem.backgroundColor = new ThemeColor(bgColor);
24+
this.statusBarItem.text = `$(${icon}) oxc`;
25+
}
26+
27+
/**
28+
* Updates the tooltip text for a specific tool section.
29+
* The tooltip can use markdown syntax and VSCode icons.
30+
*/
31+
public updateToolTooltip(toolId: StatusBarTool, text: string): void {
32+
this.tooltipSections.set(toolId, text);
33+
this.updateFullTooltip();
34+
}
35+
36+
private updateFullTooltip(): void {
37+
const text = Array.from(this.tooltipSections.values()).join('\n\n');
38+
39+
if (!(this.statusBarItem.tooltip instanceof MarkdownString)) {
40+
this.statusBarItem.tooltip = new MarkdownString('', true);
41+
this.statusBarItem.tooltip.isTrusted = true;
42+
}
43+
44+
this.statusBarItem.tooltip.value = `VSCode Extension v${this.extensionVersion}\n\n---\n\n${text}`;
45+
}
46+
47+
public dispose(): void {
48+
this.statusBarItem.dispose();
49+
}
50+
}

editors/vscode/client/extension.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
restartClient,
1010
toggleClient,
1111
} from './linter';
12+
import StatusBarItemHandler from './StatusBarItemHandler';
1213

1314
const outputChannelName = 'Oxc';
1415

@@ -42,20 +43,25 @@ export async function activate(context: ExtensionContext) {
4243
}
4344
});
4445

46+
const statusBarItemHandler = new StatusBarItemHandler(context.extension.packageJSON?.version);
47+
4548
context.subscriptions.push(
4649
restartCommand,
4750
showOutputCommand,
4851
toggleEnable,
4952
configService,
5053
outputChannel,
5154
onDidChangeWorkspaceFoldersDispose,
55+
statusBarItemHandler,
5256
);
5357

5458
configService.onConfigChange = async function onConfigChange(event) {
55-
await onConfigChangeLinter(context, event, configService);
59+
await onConfigChangeLinter(event, configService, statusBarItemHandler);
5660
};
5761

58-
await activateLinter(context, outputChannel, configService);
62+
await activateLinter(context, outputChannel, configService, statusBarItemHandler);
63+
// Show status bar item after activation
64+
statusBarItemHandler.show();
5965
}
6066

6167
export async function deactivate(): Promise<void> {

editors/vscode/client/linter.ts

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
11
import { promises as fsPromises } from 'node:fs';
22

3-
import {
4-
commands,
5-
ConfigurationChangeEvent,
6-
ExtensionContext,
7-
LogOutputChannel,
8-
StatusBarAlignment,
9-
StatusBarItem,
10-
ThemeColor,
11-
Uri,
12-
window,
13-
workspace,
14-
} from 'vscode';
3+
import { commands, ConfigurationChangeEvent, ExtensionContext, LogOutputChannel, Uri, window, workspace } from 'vscode';
154

165
import { ConfigurationParams, ExecuteCommandRequest, ShowMessageNotification } from 'vscode-languageclient';
176

187
import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
198

209
import { join } from 'node:path';
21-
import { ConfigService } from './ConfigService';
22-
import { VSCodeConfig } from './VSCodeConfig';
2310
import { OxcCommands } from './commands';
11+
import { ConfigService } from './ConfigService';
2412
import { onClientNotification, runExecutable } from './lsp_helper';
13+
import StatusBarItemHandler from './StatusBarItemHandler';
14+
import { VSCodeConfig } from './VSCodeConfig';
2515

2616
const languageClientName = 'oxc';
2717

@@ -31,8 +21,6 @@ const enum LspCommands {
3121

3222
let client: LanguageClient | undefined;
3323

34-
let oxcStatusBarItem: StatusBarItem;
35-
3624
// Global flag to check if the user allows us to start the server.
3725
// When `oxc.requireConfig` is `true`, make sure one `.oxlintrc.json` file is present.
3826
let allowedToStartServer: boolean;
@@ -41,6 +29,7 @@ export async function activate(
4129
context: ExtensionContext,
4230
outputChannel: LogOutputChannel,
4331
configService: ConfigService,
32+
statusBarItemHandler: StatusBarItemHandler,
4433
) {
4534
allowedToStartServer = configService.vsCodeConfig.requireConfig
4635
? (await workspace.findFiles(`**/.oxlintrc.json`, '**/node_modules/**', 1)).length > 0
@@ -157,13 +146,13 @@ export async function activate(
157146

158147
context.subscriptions.push(onDeleteFilesDispose);
159148

160-
updateStatusBar(context, configService.vsCodeConfig.enable);
149+
updateStatusBar(statusBarItemHandler, configService.vsCodeConfig.enable);
161150
if (allowedToStartServer) {
162151
if (configService.vsCodeConfig.enable) {
163152
await client.start();
164153
}
165154
} else {
166-
generateActivatorByConfig(configService.vsCodeConfig, context);
155+
generateActivatorByConfig(configService.vsCodeConfig, context, statusBarItemHandler);
167156
}
168157
}
169158

@@ -192,26 +181,33 @@ function getStatusBarState(enable: boolean): { bgColor: string; icon: string; to
192181
}
193182
}
194183

195-
function updateStatusBar(context: ExtensionContext, enable: boolean) {
196-
if (!oxcStatusBarItem) {
197-
oxcStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 100);
198-
oxcStatusBarItem.command = OxcCommands.ToggleEnable;
199-
context.subscriptions.push(oxcStatusBarItem);
200-
oxcStatusBarItem.show();
201-
}
202-
184+
function updateStatusBar(statusBarItemHandler: StatusBarItemHandler, enable: boolean) {
203185
const { bgColor, icon, tooltipText } = getStatusBarState(enable);
204186

205-
oxcStatusBarItem.text = `$(${icon}) oxc`;
206-
oxcStatusBarItem.backgroundColor = new ThemeColor(bgColor);
207-
oxcStatusBarItem.tooltip = tooltipText;
187+
let text =
188+
`**${tooltipText}**\n\n` +
189+
`[$(terminal) Open Output](command:${OxcCommands.ShowOutputChannel})\n\n` +
190+
`[$(refresh) Restart Server](command:${OxcCommands.RestartServer})\n\n`;
191+
192+
if (enable) {
193+
text += `[$(stop) Stop Server](command:${OxcCommands.ToggleEnable})\n\n`;
194+
} else {
195+
text += `[$(play) Start Server](command:${OxcCommands.ToggleEnable})\n\n`;
196+
}
197+
198+
statusBarItemHandler.setColorAndIcon(bgColor, icon);
199+
statusBarItemHandler.updateToolTooltip('linter', text);
208200
}
209201

210-
function generateActivatorByConfig(config: VSCodeConfig, context: ExtensionContext): void {
202+
function generateActivatorByConfig(
203+
config: VSCodeConfig,
204+
context: ExtensionContext,
205+
statusBarItemHandler: StatusBarItemHandler,
206+
): void {
211207
const watcher = workspace.createFileSystemWatcher('**/.oxlintrc.json', false, true, !config.requireConfig);
212208
watcher.onDidCreate(async () => {
213209
allowedToStartServer = true;
214-
updateStatusBar(context, config.enable);
210+
updateStatusBar(statusBarItemHandler, config.enable);
215211
if (client && !client.isRunning() && config.enable) {
216212
await client.start();
217213
}
@@ -221,7 +217,7 @@ function generateActivatorByConfig(config: VSCodeConfig, context: ExtensionConte
221217
// only can be called when config.requireConfig
222218
allowedToStartServer = (await workspace.findFiles(`**/.oxlintrc.json`, '**/node_modules/**', 1)).length > 0;
223219
if (!allowedToStartServer) {
224-
updateStatusBar(context, false);
220+
updateStatusBar(statusBarItemHandler, false);
225221
if (client && client.isRunning()) {
226222
await client.stop();
227223
}
@@ -266,11 +262,11 @@ export async function toggleClient(configService: ConfigService): Promise<void>
266262
}
267263

268264
export async function onConfigChange(
269-
context: ExtensionContext,
270265
event: ConfigurationChangeEvent,
271266
configService: ConfigService,
267+
statusBarItemHandler: StatusBarItemHandler,
272268
): Promise<void> {
273-
updateStatusBar(context, configService.vsCodeConfig.enable);
269+
updateStatusBar(statusBarItemHandler, configService.vsCodeConfig.enable);
274270

275271
if (client === undefined) {
276272
return;

0 commit comments

Comments
 (0)