Skip to content

Commit 0b4261b

Browse files
committed
feat(vscode): add oxc.requireConfig configuration (#11700)
closes #11628 ![screen recording, showing that the language server will output diagnostics, when one `.oxlintrc.json` file is created](https://github.com/user-attachments/assets/7f4d6688-29da-479b-b19b-8cf25c82cac5)
1 parent f992737 commit 0b4261b

File tree

5 files changed

+96
-31
lines changed

5 files changed

+96
-31
lines changed

editors/vscode/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ This is the linter for Oxc. The currently supported features are listed below.
2727

2828
Following configuration are supported via `settings.json` and effect the window editor:
2929

30-
| Key | Default Value | Possible Values | Description |
31-
| ------------------ | ------------- | -------------------------------- | --------------------------------------------------------------------------- |
32-
| `oxc.enable` | `true` | `true` \| `false` | Enables the language server to receive lint diagnostics |
33-
| `oxc.trace.server` | `off` | `off` \| `messages` \| `verbose` | Traces the communication between VS Code and the language server. |
34-
| `oxc.path.server` | - | `<string>` | Path to Oxc language server binary. Mostly for testing the language server. |
30+
| Key | Default Value | Possible Values | Description |
31+
| ------------------- | ------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
32+
| `oxc.enable` | `true` | `true` \| `false` | Enables the language server to receive lint diagnostics |
33+
| `oxc.requireConfig` | `false` | `true` \| `false` | Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces. |
34+
| `oxc.trace.server` | `off` | `off` \| `messages` \| `verbose` | Traces the communication between VS Code and the language server. |
35+
| `oxc.path.server` | - | `<string>` | Path to Oxc language server binary. Mostly for testing the language server. |
3536

3637
### Workspace Configuration
3738

editors/vscode/client/VSCodeConfig.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export class VSCodeConfig implements VSCodeConfigInterface {
55
private _enable!: boolean;
66
private _trace!: TraceLevel;
77
private _binPath: string | undefined;
8+
private _requireConfig!: boolean;
89

910
constructor() {
1011
this.refresh();
@@ -18,6 +19,7 @@ export class VSCodeConfig implements VSCodeConfigInterface {
1819
this._enable = this.configuration.get<boolean>('enable') ?? true;
1920
this._trace = this.configuration.get<TraceLevel>('trace.server') || 'off';
2021
this._binPath = this.configuration.get<string>('path.server');
22+
this._requireConfig = this.configuration.get<boolean>('requireConfig') ?? false;
2123
}
2224

2325
get enable(): boolean {
@@ -46,9 +48,19 @@ export class VSCodeConfig implements VSCodeConfigInterface {
4648
this._binPath = value;
4749
return this.configuration.update('path.server', value);
4850
}
51+
52+
get requireConfig(): boolean {
53+
return this._requireConfig;
54+
}
55+
56+
updateRequireConfig(value: boolean): PromiseLike<void> {
57+
this._requireConfig = value;
58+
return this.configuration.update('requireConfig', value);
59+
}
4960
}
5061

5162
type TraceLevel = 'off' | 'messages' | 'verbose';
63+
5264
/**
5365
* See `"contributes.configuration"` in `package.json`
5466
*/
@@ -72,4 +84,10 @@ interface VSCodeConfigInterface {
7284
* @default undefined
7385
*/
7486
binPath: string | undefined;
87+
/**
88+
* Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces.
89+
* `oxc.requireConfig`
90+
* @default false
91+
*/
92+
requireConfig: boolean;
7593
}

editors/vscode/client/extension.ts

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from
2222

2323
import { join } from 'node:path';
2424
import { ConfigService } from './ConfigService';
25+
import { VSCodeConfig } from './VSCodeConfig';
2526

2627
const languageClientName = 'oxc';
2728
const outputChannelName = 'Oxc';
@@ -42,8 +43,16 @@ let client: LanguageClient | undefined;
4243

4344
let myStatusBarItem: StatusBarItem;
4445

46+
// Global flag to check if the user allows us to start the server.
47+
// When `oxc.requireConfig` is `true`, make sure one `.oxlintrc.json` file is present.
48+
let allowedToStartServer: boolean;
49+
4550
export async function activate(context: ExtensionContext) {
4651
const configService = new ConfigService();
52+
allowedToStartServer = configService.vsCodeConfig.requireConfig
53+
? (await workspace.findFiles(`**/.oxlintrc.json`, '**/node_modules/**', 1)).length > 0
54+
: true;
55+
4756
const restartCommand = commands.registerCommand(
4857
OxcCommands.RestartServer,
4958
async () => {
@@ -77,7 +86,7 @@ export async function activate(context: ExtensionContext) {
7786
async () => {
7887
await configService.vsCodeConfig.updateEnable(!configService.vsCodeConfig.enable);
7988

80-
if (client === undefined) {
89+
if (client === undefined || !allowedToStartServer) {
8190
return;
8291
}
8392

@@ -245,7 +254,7 @@ export async function activate(context: ExtensionContext) {
245254
context.subscriptions.push(onDidChangeWorkspaceFoldersDispose);
246255

247256
configService.onConfigChange = async function onConfigChange(event) {
248-
updateStatsBar(this.vsCodeConfig.enable);
257+
updateStatsBar(context, this.vsCodeConfig.enable);
249258

250259
if (client === undefined) {
251260
return;
@@ -264,29 +273,13 @@ export async function activate(context: ExtensionContext) {
264273
}
265274
};
266275

267-
function updateStatsBar(enable: boolean) {
268-
if (!myStatusBarItem) {
269-
myStatusBarItem = window.createStatusBarItem(
270-
StatusBarAlignment.Right,
271-
100,
272-
);
273-
myStatusBarItem.command = OxcCommands.ToggleEnable;
274-
context.subscriptions.push(myStatusBarItem);
275-
myStatusBarItem.show();
276+
updateStatsBar(context, configService.vsCodeConfig.enable);
277+
if (allowedToStartServer) {
278+
if (configService.vsCodeConfig.enable) {
279+
await client.start();
276280
}
277-
let bgColor = new ThemeColor(
278-
enable
279-
? 'statusBarItem.activeBackground'
280-
: 'statusBarItem.warningBackground',
281-
);
282-
myStatusBarItem.text = `oxc: ${enable ? '$(check-all)' : '$(check)'}`;
283-
284-
myStatusBarItem.backgroundColor = bgColor;
285-
}
286-
287-
updateStatsBar(configService.vsCodeConfig.enable);
288-
if (configService.vsCodeConfig.enable) {
289-
await client.start();
281+
} else {
282+
generateActivatorByConfig(configService.vsCodeConfig, context);
290283
}
291284
}
292285

@@ -297,3 +290,47 @@ export async function deactivate(): Promise<void> {
297290
await client.stop();
298291
client = undefined;
299292
}
293+
294+
function updateStatsBar(
295+
context: ExtensionContext,
296+
enable: boolean,
297+
) {
298+
if (!myStatusBarItem) {
299+
myStatusBarItem = window.createStatusBarItem(
300+
StatusBarAlignment.Right,
301+
100,
302+
);
303+
myStatusBarItem.command = OxcCommands.ToggleEnable;
304+
context.subscriptions.push(myStatusBarItem);
305+
myStatusBarItem.show();
306+
}
307+
let bgColor: string;
308+
let icon: string;
309+
if (!allowedToStartServer) {
310+
bgColor = 'statusBarItem.offlineBackground';
311+
icon = '$(circle-slash)';
312+
} else if (!enable) {
313+
bgColor = 'statusBarItem.warningBackground';
314+
icon = '$(check)';
315+
} else {
316+
bgColor = 'statusBarItem.activeBackground';
317+
icon = '$(check-all)';
318+
}
319+
320+
myStatusBarItem.text = `oxc: ${icon}`;
321+
myStatusBarItem.backgroundColor = new ThemeColor(bgColor);
322+
}
323+
324+
function generateActivatorByConfig(config: VSCodeConfig, context: ExtensionContext): void {
325+
const watcher = workspace.createFileSystemWatcher('**/.oxlintrc.json', false, true, true);
326+
watcher.onDidCreate(async () => {
327+
watcher.dispose();
328+
allowedToStartServer = true;
329+
updateStatsBar(context, config.enable);
330+
if (client && !client.isRunning() && config.enable) {
331+
await client.start();
332+
}
333+
});
334+
335+
context.subscriptions.push(watcher);
336+
}

editors/vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
"scope": "window",
8181
"description": "enable oxc language server"
8282
},
83+
"oxc.requireConfig": {
84+
"scope": "resource",
85+
"type": "boolean",
86+
"default": false,
87+
"description": "Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces."
88+
},
8389
"oxc.trace.server": {
8490
"type": "string",
8591
"scope": "window",

editors/vscode/tests/VSCodeConfig.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ const conf = workspace.getConfiguration('oxc');
77

88
suite('VSCodeConfig', () => {
99
setup(async () => {
10-
const keys = ['enable', 'trace.server', 'path.server'];
10+
const keys = ['enable', 'requireConfig', 'trace.server', 'path.server'];
1111

1212
await Promise.all(keys.map(key => conf.update(key, undefined)));
1313
});
1414

1515
teardown(async () => {
16-
const keys = ['enable', 'trace.server', 'path.server'];
16+
const keys = ['enable', 'requireConfig', 'trace.server', 'path.server'];
1717

1818
await Promise.all(keys.map(key => conf.update(key, undefined)));
1919
});
@@ -22,6 +22,7 @@ suite('VSCodeConfig', () => {
2222
const config = new VSCodeConfig();
2323

2424
strictEqual(config.enable, true);
25+
strictEqual(config.requireConfig, false);
2526
strictEqual(config.trace, 'off');
2627
strictEqual(config.binPath, '');
2728
});
@@ -31,13 +32,15 @@ suite('VSCodeConfig', () => {
3132

3233
await Promise.all([
3334
config.updateEnable(false),
35+
config.updateRequireConfig(true),
3436
config.updateTrace('messages'),
3537
config.updateBinPath('./binary'),
3638
]);
3739

3840
const wsConfig = workspace.getConfiguration('oxc');
3941

4042
strictEqual(wsConfig.get('enable'), false);
43+
strictEqual(wsConfig.get('requireConfig'), true);
4144
strictEqual(wsConfig.get('trace.server'), 'messages');
4245
strictEqual(wsConfig.get('path.server'), './binary');
4346
});

0 commit comments

Comments
 (0)