Skip to content

Commit 2ce3137

Browse files
committed
refactor(editor): flatten flags options
1 parent 1611b4f commit 2ce3137

File tree

6 files changed

+157
-60
lines changed

6 files changed

+157
-60
lines changed

editors/vscode/README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,22 @@ Following configuration are supported via `settings.json` and can be changed for
6464
| `oxc.tsConfigPath` | `null` | `null` \| `<string>` | Path to TypeScript configuration. If your `tsconfig.json` is not at the root, alias paths will not be resolve correctly for the `import` plugin. |
6565
| `oxc.unusedDisableDirectives` | `allow` | `allow` \| `warn` \| `deny` | Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway. |
6666
| `oxc.typeAware` | `false` | `false` \| `true` | Enable type aware linting. |
67-
| `oxc.flags` | - | `Record<string, string>` | Custom flags passed to the language server. |
67+
| `oxc.disableNestedConfig` | `false` | `false` \| `true` | Disable searching for nested configuration files. |
68+
| `oxc.fixKind` | `safe_fix` | [FixKind](#fixkind) | Specify the kind of fixes to suggest/apply. |
6869
| `oxc.fmt.experimental` | `false` | `false` \| `true` | Enable experimental formatting support. This feature is experimental and might not work as expected. |
6970
| `oxc.fmt.configPath` | `null` | `<string>` \| `null` | Path to an oxfmt configuration file. When `null`, the server will use `.oxfmtrc.json` at the workspace root. |
71+
| `oxc.flags` | - | `Record<string, string>` | (deprecated) Custom flags passed to the language server. |
7072

71-
#### Flags
73+
#### FixKind
74+
75+
- `"safe_fix"` (default)
76+
- `"safe_fix_or_suggestion"`
77+
- `"dangerous_fix"`
78+
- `"dangerous_fix_or_suggestion"`
79+
- `"none"`
80+
- `"all"`
81+
82+
#### Flags (deprecated)
7283

7384
- `key: disable_nested_config`: Disabled nested configuration and searches only for `configPath`
7485
- `key: fix_kind`: default: `"safe_fix"`, possible values `"safe_fix" | "safe_fix_or_suggestion" | "dangerous_fix" | "dangerous_fix_or_suggestion" | "none" | "all"`

editors/vscode/client/WorkspaceConfig.ts

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ export type Trigger = 'onSave' | 'onType';
77

88
type UnusedDisableDirectives = 'allow' | 'warn' | 'deny';
99

10+
export enum FixKind {
11+
SafeFix = 'safe_fix',
12+
SafeFixOrSuggestion = 'safe_fix_or_suggestion',
13+
DangerousFix = 'dangerous_fix',
14+
DangerousFixOrSuggestion = 'dangerous_fix_or_suggestion',
15+
None = 'none',
16+
All = 'all',
17+
}
18+
1019
/**
1120
* See `"contributes.configuration"` in `package.json`
1221
*/
@@ -54,6 +63,20 @@ export interface WorkspaceConfigInterface {
5463
*/
5564
typeAware: boolean;
5665

66+
/**
67+
* Disable nested config files detection
68+
* `oxc.disableNestedConfig`
69+
* @default false
70+
*/
71+
disableNestedConfig: boolean;
72+
73+
/**
74+
* Fix kind to use when applying fixes
75+
* `oxc.fixKind`
76+
* @default 'safe_fix'
77+
*/
78+
fixKind: FixKind;
79+
5780
/**
5881
* Additional flags to pass to the LSP binary
5982
* `oxc.flags`
@@ -83,7 +106,8 @@ export class WorkspaceConfig {
83106
private _runTrigger: Trigger = 'onType';
84107
private _unusedDisableDirectives: UnusedDisableDirectives = 'allow';
85108
private _typeAware: boolean = false;
86-
private _flags: Record<string, string> = {};
109+
private _disableNestedConfig: boolean = false;
110+
private _fixKind: FixKind = FixKind.SafeFix;
87111
private _formattingExperimental: boolean = false;
88112
private _formattingConfigPath: string | null = null;
89113

@@ -97,16 +121,28 @@ export class WorkspaceConfig {
97121

98122
public refresh(): void {
99123
const flags = this.configuration.get<Record<string, string>>('flags') ?? {};
100-
const useNestedConfigs = !('disable_nested_config' in flags);
124+
125+
// `configuration.get` takes the default value from the package.json, which is always `safe_fix`.
126+
// We need to check the deprecated flags.fix_kind for the real default value.
127+
let fixKind = this.configuration.get<FixKind>('fixKind');
128+
if (fixKind === FixKind.SafeFix && flags.fix_kind !== undefined && flags.fix_kind !== 'safe_fix') {
129+
fixKind = flags.fix_kind as FixKind;
130+
}
131+
132+
// the same for disabledNestedConfig
133+
let disableNestedConfig = this.configuration.get<boolean>('disableNestedConfig');
134+
if (disableNestedConfig === false && flags.disable_nested_config === 'true') {
135+
disableNestedConfig = true;
136+
}
101137

102138
this._runTrigger = this.configuration.get<Trigger>('lint.run') || 'onType';
103-
this._configPath =
104-
this.configuration.get<string | null>('configPath') || (useNestedConfigs ? null : oxlintConfigFileName);
139+
this._configPath = this.configuration.get<string | null>('configPath') ?? null;
105140
this._tsConfigPath = this.configuration.get<string | null>('tsConfigPath') ?? null;
106141
this._unusedDisableDirectives =
107142
this.configuration.get<UnusedDisableDirectives>('unusedDisableDirectives') ?? 'allow';
108143
this._typeAware = this.configuration.get<boolean>('typeAware') ?? false;
109-
this._flags = flags;
144+
this._disableNestedConfig = disableNestedConfig ?? false;
145+
this._fixKind = fixKind ?? FixKind.SafeFix;
110146
this._formattingExperimental = this.configuration.get<boolean>('fmt.experimental') ?? false;
111147
this._formattingConfigPath = this.configuration.get<string | null>('fmt.configPath') ?? null;
112148
}
@@ -127,7 +163,10 @@ export class WorkspaceConfig {
127163
if (event.affectsConfiguration(`${ConfigService.namespace}.typeAware`, this.workspace)) {
128164
return true;
129165
}
130-
if (event.affectsConfiguration(`${ConfigService.namespace}.flags`, this.workspace)) {
166+
if (event.affectsConfiguration(`${ConfigService.namespace}.disableNestedConfig`, this.workspace)) {
167+
return true;
168+
}
169+
if (event.affectsConfiguration(`${ConfigService.namespace}.fixKind`, this.workspace)) {
131170
return true;
132171
}
133172
if (event.affectsConfiguration(`${ConfigService.namespace}.fmt.experimental`, this.workspace)) {
@@ -136,6 +175,10 @@ export class WorkspaceConfig {
136175
if (event.affectsConfiguration(`${ConfigService.namespace}.fmt.configPath`, this.workspace)) {
137176
return true;
138177
}
178+
// deprecated settings in flags
179+
if (event.affectsConfiguration(`${ConfigService.namespace}.flags`, this.workspace)) {
180+
return true;
181+
}
139182
return false;
140183
}
141184

@@ -188,13 +231,22 @@ export class WorkspaceConfig {
188231
return this.configuration.update('typeAware', value, ConfigurationTarget.WorkspaceFolder);
189232
}
190233

191-
get flags(): Record<string, string> {
192-
return this._flags;
234+
get disableNestedConfig(): boolean {
235+
return this._disableNestedConfig;
236+
}
237+
238+
updateDisableNestedConfig(value: boolean): PromiseLike<void> {
239+
this._disableNestedConfig = value;
240+
return this.configuration.update('disableNestedConfig', value, ConfigurationTarget.WorkspaceFolder);
241+
}
242+
243+
get fixKind(): FixKind {
244+
return this._fixKind;
193245
}
194246

195-
updateFlags(value: Record<string, string>): PromiseLike<void> {
196-
this._flags = value;
197-
return this.configuration.update('flags', value, ConfigurationTarget.WorkspaceFolder);
247+
updateFixKind(value: FixKind): PromiseLike<void> {
248+
this._fixKind = value;
249+
return this.configuration.update('fixKind', value, ConfigurationTarget.WorkspaceFolder);
198250
}
199251

200252
get formattingExperimental(): boolean {
@@ -222,9 +274,15 @@ export class WorkspaceConfig {
222274
tsConfigPath: this.tsConfigPath ?? null,
223275
unusedDisableDirectives: this.unusedDisableDirectives,
224276
typeAware: this.typeAware,
225-
flags: this.flags,
277+
disableNestedConfig: this.disableNestedConfig,
278+
fixKind: this.fixKind,
226279
['fmt.experimental']: this.formattingExperimental,
227280
['fmt.configPath']: this.formattingConfigPath ?? null,
281+
// deprecated, kept for backward compatibility
282+
flags: {
283+
disable_nested_config: this.disableNestedConfig ? 'true' : 'false',
284+
...(this.fixKind ? { fix_kind: this.fixKind } : {}),
285+
},
228286
};
229287
}
230288
}

editors/vscode/package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,39 @@
147147
"default": false,
148148
"description": "Enable type-aware linting."
149149
},
150+
"oxc.disableNestedConfig": {
151+
"type": "boolean",
152+
"scope": "resource",
153+
"default": false,
154+
"description": "Disable searching for nested configuration files. When set to true, only the configuration file specified in `oxc.configPath` (if any) will be used."
155+
},
156+
"oxc.fixKind": {
157+
"type": "string",
158+
"scope": "resource",
159+
"enum": [
160+
"safe_fix",
161+
"safe_fix_or_suggestion",
162+
"dangerous_fix",
163+
"dangerous_fix_or_suggestion",
164+
"none",
165+
"all"
166+
],
167+
"enumDescriptions": [
168+
"Only safe fixes will be applied",
169+
"Safe fixes or suggestions will be applied",
170+
"Safe and dangerous fixes will be applied",
171+
"Safe and dangerous fixes or suggestions will be applied",
172+
"No fixes will be applied",
173+
"All fixes and suggestions will be applied"
174+
],
175+
"default": "safe_fix",
176+
"description": "Specify the kind of fixes to suggest/apply."
177+
},
150178
"oxc.flags": {
151179
"type": "object",
152180
"scope": "resource",
153181
"default": {},
182+
"deprecationMessage": "deprecated since 1.25.0, use `oxc.fixKind` or `oxc.disableNestedConfig` instead.",
154183
"description": "Specific Oxlint flags to pass to the language server."
155184
},
156185
"oxc.fmt.experimental": {

editors/vscode/tests/WorkspaceConfig.spec.ts

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
1-
import { deepStrictEqual, strictEqual } from 'assert';
1+
import { strictEqual } from 'assert';
22
import { ConfigurationTarget, workspace } from 'vscode';
3-
import { WorkspaceConfig } from '../client/WorkspaceConfig.js';
3+
import { FixKind, WorkspaceConfig } from '../client/WorkspaceConfig.js';
44
import { WORKSPACE_FOLDER } from './test-helpers.js';
55

6-
const keys = ['lint.run', 'configPath', 'tsConfigPath', 'flags', 'unusedDisableDirectives', 'typeAware', 'fmt.experimental', 'fmt.configPath'];
6+
const keys = [
7+
'lint.run',
8+
'configPath',
9+
'tsConfigPath',
10+
'unusedDisableDirectives',
11+
'typeAware',
12+
'disableNestedConfig',
13+
'fixKind',
14+
'fmt.experimental',
15+
'fmt.configPath',
16+
// deprecated
17+
'flags'
18+
];
719

820
suite('WorkspaceConfig', () => {
9-
setup(async () => {
21+
22+
const updateConfiguration = async (key: string, value: unknown) => {
1023
const workspaceConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER);
1124
const globalConfig = workspace.getConfiguration('oxc');
1225

26+
await Promise.all([
27+
workspaceConfig.update(key, value, ConfigurationTarget.WorkspaceFolder),
28+
// VSCode will not save different workspace configuration inside a `.code-workspace` file.
29+
// Do not fail, we will make sure the global config is empty too.
30+
globalConfig.update(key, value)
31+
]);
32+
};
1333

14-
await Promise.all(keys.map(key => workspaceConfig.update(key, undefined, ConfigurationTarget.WorkspaceFolder)));
15-
// VSCode will not save different workspace configuration inside a `.code-workspace` file.
16-
// Do not fail, we will make sure the global config is empty too.
17-
await Promise.all(keys.map(key => globalConfig.update(key, undefined)));
34+
setup(async () => {
35+
await Promise.all(keys.map(key => updateConfiguration(key, undefined)));
1836
});
1937

2038
teardown(async () => {
21-
const workspaceConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER);
22-
const globalConfig = workspace.getConfiguration('oxc');
23-
24-
await Promise.all(keys.map(key => workspaceConfig.update(key, undefined, ConfigurationTarget.WorkspaceFolder)));
25-
// VSCode will not save different workspace configuration inside a `.code-workspace` file.
26-
// Do not fail, we will make sure the global config is empty too.
27-
await Promise.all(keys.map(key => globalConfig.update(key, undefined)));
39+
await Promise.all(keys.map(key => updateConfiguration(key, undefined)));
2840
});
2941

3042
test('default values on initialization', () => {
@@ -34,31 +46,21 @@ suite('WorkspaceConfig', () => {
3446
strictEqual(config.tsConfigPath, null);
3547
strictEqual(config.unusedDisableDirectives, 'allow');
3648
strictEqual(config.typeAware, false);
37-
deepStrictEqual(config.flags, {});
49+
strictEqual(config.disableNestedConfig, false);
50+
strictEqual(config.fixKind, "safe_fix");
3851
strictEqual(config.formattingExperimental, false);
3952
strictEqual(config.formattingConfigPath, null);
4053
});
4154

42-
test('configPath defaults to null when using nested configs and configPath is empty', async () => {
43-
const wsConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER);
44-
await wsConfig.update('configPath', '', ConfigurationTarget.WorkspaceFolder);
45-
await wsConfig.update('flags', {}, ConfigurationTarget.WorkspaceFolder);
55+
test('deprecated values are respected', async () => {
56+
await updateConfiguration('flags', {
57+
disable_nested_config: 'true',
58+
fix_kind: 'dangerous_fix'
59+
});
4660

4761
const config = new WorkspaceConfig(WORKSPACE_FOLDER);
48-
49-
deepStrictEqual(config.flags, {});
50-
strictEqual(config.configPath, null);
51-
});
52-
53-
test('configPath defaults to .oxlintrc.json when not using nested configs and configPath is empty', async () => {
54-
const wsConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER);
55-
await wsConfig.update('configPath', undefined, ConfigurationTarget.WorkspaceFolder);
56-
await wsConfig.update('flags', { disable_nested_config: '' }, ConfigurationTarget.WorkspaceFolder);
57-
58-
const config = new WorkspaceConfig(WORKSPACE_FOLDER);
59-
60-
deepStrictEqual(config.flags, { disable_nested_config: '' });
61-
strictEqual(config.configPath, '.oxlintrc.json');
62+
strictEqual(config.disableNestedConfig, true);
63+
strictEqual(config.fixKind, "dangerous_fix");
6264
});
6365

6466
test('updating values updates the workspace configuration', async () => {
@@ -70,7 +72,8 @@ suite('WorkspaceConfig', () => {
7072
config.updateTsConfigPath('./tsconfig.json'),
7173
config.updateUnusedDisableDirectives('deny'),
7274
config.updateTypeAware(true),
73-
config.updateFlags({ test: 'value' }),
75+
config.updateDisableNestedConfig(true),
76+
config.updateFixKind(FixKind.DangerousFix),
7477
config.updateFormattingExperimental(true),
7578
config.updateFormattingConfigPath('./oxfmt.json'),
7679
]);
@@ -82,7 +85,8 @@ suite('WorkspaceConfig', () => {
8285
strictEqual(wsConfig.get('tsConfigPath'), './tsconfig.json');
8386
strictEqual(wsConfig.get('unusedDisableDirectives'), 'deny');
8487
strictEqual(wsConfig.get('typeAware'), true);
85-
deepStrictEqual(wsConfig.get('flags'), { test: 'value' });
88+
strictEqual(wsConfig.get('disableNestedConfig'), true);
89+
strictEqual(wsConfig.get('fixKind'), 'dangerous_fix');
8690
strictEqual(wsConfig.get('fmt.experimental'), true);
8791
strictEqual(wsConfig.get('fmt.configPath'), './oxfmt.json');
8892
});

editors/vscode/tests/code_actions.spec.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ teardown(async () => {
2929
const vsConfig = workspace.getConfiguration('oxc');
3030
const wsConfig = workspace.getConfiguration('oxc', fixturesWorkspaceUri());
3131
await vsConfig.update('unusedDisableDirectives', undefined);
32-
await wsConfig.update('flags', undefined, ConfigurationTarget.WorkspaceFolder);
33-
32+
await wsConfig.update('fixKind', undefined, ConfigurationTarget.WorkspaceFolder);
3433
await workspace.getConfiguration('editor').update('codeActionsOnSave', undefined);
3534
await workspace.saveAll();
3635
});
@@ -154,9 +153,7 @@ suite('code actions', () => {
154153
);
155154
strictEqual(quickFixesNoFix.length, 2);
156155

157-
await workspace.getConfiguration('oxc', fixturesWorkspaceUri()).update('flags', {
158-
'fix_kind': 'dangerous_fix',
159-
}, ConfigurationTarget.WorkspaceFolder);
156+
await workspace.getConfiguration('oxc', fixturesWorkspaceUri()).update('fixKind', 'dangerous_fix', ConfigurationTarget.WorkspaceFolder);
160157
await workspace.saveAll();
161158

162159
const codeActionsWithFix: ProviderResult<Array<CodeAction>> = await commands.executeCommand(

editors/vscode/tests/e2e_server.spec.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ suiteSetup(async () => {
3232
});
3333

3434
teardown(async () => {
35-
await workspace.getConfiguration('oxc').update('flags', undefined);
35+
await workspace.getConfiguration('oxc').update('fixKind', undefined);
3636
await workspace.getConfiguration('oxc').update('tsConfigPath', undefined);
3737
await workspace.getConfiguration('oxc').update('typeAware', undefined);
3838
await workspace.getConfiguration('oxc').update('fmt.experimental', undefined);
@@ -137,7 +137,7 @@ suite('E2E Diagnostics', () => {
137137
// We can check the changed with kind with `vscode.executeCodeActionProvider`
138138
// but to be safe that everything works, we will check the applied changes.
139139
// This way we can be sure that everything works as expected.
140-
test('auto detect changing `fix_kind` flag with fixAll command', async () => {
140+
test('auto detect changing `fixKind` with fixAll command', async () => {
141141
const originalContent = 'if (foo == null) { bar();}';
142142

143143
await createOxlintConfiguration({
@@ -163,9 +163,7 @@ suite('E2E Diagnostics', () => {
163163
const content = await workspace.fs.readFile(fileUri);
164164

165165
strictEqual(content.toString(), originalContent);
166-
await workspace.getConfiguration('oxc').update('flags', {
167-
fix_kind: 'all',
168-
});
166+
await workspace.getConfiguration('oxc').update('fixKind', 'all');
169167
// wait for server to update the internal linter
170168
await sleep(500);
171169
await workspace.saveAll();

0 commit comments

Comments
 (0)