mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(editor/vscode)!: unify configuration logic (#6630)
> Closes https://github.com/oxc-project/backlog/issues/132 Unify configuration logic into `ConfigService`. This allows for type-checked config settings. It also lets us make sure config values are synced with what the user has set in their vscode settings. This PR is a breaking change since it also unifies config key prefixes (`oxc` and `oxc_language_server` -> `oxc` only)
This commit is contained in:
parent
cf9273090a
commit
7f6b21925d
4 changed files with 185 additions and 32 deletions
161
editors/vscode/client/config.ts
Normal file
161
editors/vscode/client/config.ts
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { ConfigurationChangeEvent, workspace, WorkspaceConfiguration } from 'vscode';
|
||||||
|
import { IDisposable } from './types';
|
||||||
|
|
||||||
|
export class ConfigService implements Config, IDisposable {
|
||||||
|
private static readonly _namespace = 'oxc';
|
||||||
|
private readonly _disposables: IDisposable[] = [];
|
||||||
|
private _inner: WorkspaceConfiguration;
|
||||||
|
private _runTrigger: 'onSave' | 'onType';
|
||||||
|
private _enable: boolean;
|
||||||
|
private _trace: 'off' | 'messages' | 'verbose';
|
||||||
|
private _configPath: string;
|
||||||
|
private _binPath: string | undefined;
|
||||||
|
|
||||||
|
public onConfigChange:
|
||||||
|
| ((this: ConfigService, config: ConfigurationChangeEvent) => void)
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._inner = workspace.getConfiguration(ConfigService._namespace);
|
||||||
|
this._runTrigger = this._inner.get<Trigger>('lint.run') || 'onType';
|
||||||
|
this._enable = this._inner.get<boolean>('enable') ?? true;
|
||||||
|
this._trace = this._inner.get<TraceLevel>('trace.server') || 'off';
|
||||||
|
this._configPath = this._inner.get<string>('configPath') || '.eslintrc';
|
||||||
|
this._binPath = this._inner.get<string>('path.server');
|
||||||
|
this.onConfigChange = undefined;
|
||||||
|
|
||||||
|
const disposeChangeListener = workspace.onDidChangeConfiguration(
|
||||||
|
this.onVscodeConfigChange.bind(this),
|
||||||
|
);
|
||||||
|
this._disposables.push(disposeChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawConfig(): WorkspaceConfiguration {
|
||||||
|
return this._inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
get runTrigger(): Trigger {
|
||||||
|
return this._runTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
set runTrigger(value: Trigger) {
|
||||||
|
this._runTrigger = value;
|
||||||
|
workspace
|
||||||
|
.getConfiguration(ConfigService._namespace)
|
||||||
|
.update('lint.run', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get enable(): boolean {
|
||||||
|
return this._enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
set enable(value: boolean) {
|
||||||
|
this._enable = value;
|
||||||
|
workspace
|
||||||
|
.getConfiguration(ConfigService._namespace)
|
||||||
|
.update('enable', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get trace(): TraceLevel {
|
||||||
|
return this._trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
set trace(value: TraceLevel) {
|
||||||
|
this._trace = value;
|
||||||
|
workspace
|
||||||
|
.getConfiguration(ConfigService._namespace)
|
||||||
|
.update('trace.server', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get configPath(): string {
|
||||||
|
return this._configPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
set configPath(value: string) {
|
||||||
|
this._configPath = value;
|
||||||
|
workspace
|
||||||
|
.getConfiguration(ConfigService._namespace)
|
||||||
|
.update('configPath', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get binPath(): string | undefined {
|
||||||
|
return this._binPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
set binPath(value: string | undefined) {
|
||||||
|
this._binPath = value;
|
||||||
|
workspace
|
||||||
|
.getConfiguration(ConfigService._namespace)
|
||||||
|
.update('path.server', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onVscodeConfigChange(event: ConfigurationChangeEvent): void {
|
||||||
|
if (event.affectsConfiguration(ConfigService._namespace)) {
|
||||||
|
this._runTrigger = this._inner.get<Trigger>('lint.run') || 'onType';
|
||||||
|
this._enable = this._inner.get<boolean>('enable') ?? true;
|
||||||
|
this._trace = this._inner.get<TraceLevel>('trace.server') || 'off';
|
||||||
|
this._configPath = this._inner.get<string>('configPath') || '.eslintrc';
|
||||||
|
this._binPath = this._inner.get<string>('path.server');
|
||||||
|
this.onConfigChange?.call(this, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
for (const disposable of this._disposables) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): Config {
|
||||||
|
return {
|
||||||
|
runTrigger: this.runTrigger,
|
||||||
|
enable: this.enable,
|
||||||
|
trace: this.trace,
|
||||||
|
configPath: this.configPath,
|
||||||
|
binPath: this.binPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Trigger = 'onSave' | 'onType';
|
||||||
|
type TraceLevel = 'off' | 'messages' | 'verbose';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See `"contributes.configuration"` in `package.json`
|
||||||
|
*/
|
||||||
|
interface Config {
|
||||||
|
/**
|
||||||
|
* When to run the linter and generate diagnostics
|
||||||
|
* `oxc.lint.run`
|
||||||
|
*
|
||||||
|
* @default 'onType'
|
||||||
|
*/
|
||||||
|
runTrigger: Trigger;
|
||||||
|
/**
|
||||||
|
* `oxc.enable`
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
enable: boolean;
|
||||||
|
/**
|
||||||
|
* Trace VSCode <-> Oxc Language Server communication
|
||||||
|
* `oxc.trace.server`
|
||||||
|
*
|
||||||
|
* @default 'off'
|
||||||
|
*/
|
||||||
|
trace: TraceLevel;
|
||||||
|
/**
|
||||||
|
* oxlint config path
|
||||||
|
*
|
||||||
|
* `oxc.configPath`
|
||||||
|
*
|
||||||
|
* @default ".eslintrc"
|
||||||
|
*/
|
||||||
|
configPath: string;
|
||||||
|
/**
|
||||||
|
* Path to LSP binary
|
||||||
|
* `oxc.path.server`
|
||||||
|
* @default undefined
|
||||||
|
*/
|
||||||
|
binPath: string | undefined;
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,11 @@
|
||||||
import { promises as fsPromises } from 'node:fs';
|
import { promises as fsPromises } from 'node:fs';
|
||||||
|
|
||||||
import {
|
import { commands, ExtensionContext, StatusBarAlignment, StatusBarItem, ThemeColor, window, workspace } from 'vscode';
|
||||||
commands,
|
|
||||||
ConfigurationTarget,
|
|
||||||
ExtensionContext,
|
|
||||||
StatusBarAlignment,
|
|
||||||
StatusBarItem,
|
|
||||||
ThemeColor,
|
|
||||||
window,
|
|
||||||
workspace,
|
|
||||||
} from 'vscode';
|
|
||||||
|
|
||||||
import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
|
import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
|
||||||
|
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
import { ConfigService } from './config';
|
||||||
|
|
||||||
const languageClientId = 'oxc-vscode';
|
const languageClientId = 'oxc-vscode';
|
||||||
const languageClientName = 'oxc';
|
const languageClientName = 'oxc';
|
||||||
|
|
@ -34,6 +26,7 @@ let client: LanguageClient;
|
||||||
let myStatusBarItem: StatusBarItem;
|
let myStatusBarItem: StatusBarItem;
|
||||||
|
|
||||||
export async function activate(context: ExtensionContext) {
|
export async function activate(context: ExtensionContext) {
|
||||||
|
const config = new ConfigService();
|
||||||
const restartCommand = commands.registerCommand(
|
const restartCommand = commands.registerCommand(
|
||||||
OxcCommands.RestartServer,
|
OxcCommands.RestartServer,
|
||||||
async () => {
|
async () => {
|
||||||
|
|
@ -73,13 +66,7 @@ export async function activate(context: ExtensionContext) {
|
||||||
const toggleEnable = commands.registerCommand(
|
const toggleEnable = commands.registerCommand(
|
||||||
OxcCommands.ToggleEnable,
|
OxcCommands.ToggleEnable,
|
||||||
() => {
|
() => {
|
||||||
let enabled = workspace
|
config.enable = !config.enable;
|
||||||
.getConfiguration('oxc_language_server')
|
|
||||||
.get('enable');
|
|
||||||
let nextState = !enabled;
|
|
||||||
workspace
|
|
||||||
.getConfiguration('oxc_language_server')
|
|
||||||
.update('enable', nextState, ConfigurationTarget.Global);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -88,15 +75,14 @@ export async function activate(context: ExtensionContext) {
|
||||||
showOutputCommand,
|
showOutputCommand,
|
||||||
showTraceOutputCommand,
|
showTraceOutputCommand,
|
||||||
toggleEnable,
|
toggleEnable,
|
||||||
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
const outputChannel = window.createOutputChannel(outputChannelName);
|
const outputChannel = window.createOutputChannel(outputChannelName);
|
||||||
const traceOutputChannel = window.createOutputChannel(traceOutputChannelName);
|
const traceOutputChannel = window.createOutputChannel(traceOutputChannelName);
|
||||||
|
|
||||||
async function findBinary(): Promise<string> {
|
async function findBinary(): Promise<string> {
|
||||||
const cfg = workspace.getConfiguration('oxc');
|
let bin = config.binPath;
|
||||||
|
|
||||||
let bin = cfg.get<string>('binPath', '');
|
|
||||||
if (bin) {
|
if (bin) {
|
||||||
try {
|
try {
|
||||||
await fsPromises.access(bin);
|
await fsPromises.access(bin);
|
||||||
|
|
@ -150,9 +136,7 @@ export async function activate(context: ExtensionContext) {
|
||||||
// If the extension is launched in debug mode then the debug server options are used
|
// If the extension is launched in debug mode then the debug server options are used
|
||||||
// Otherwise the run options are used
|
// Otherwise the run options are used
|
||||||
// Options to control the language client
|
// Options to control the language client
|
||||||
let clientConfig: any = JSON.parse(
|
let clientConfig: any = JSON.parse(JSON.stringify(config.rawConfig));
|
||||||
JSON.stringify(workspace.getConfiguration('oxc_language_server')),
|
|
||||||
);
|
|
||||||
let clientOptions: LanguageClientOptions = {
|
let clientOptions: LanguageClientOptions = {
|
||||||
// Register the server for plain text documents
|
// Register the server for plain text documents
|
||||||
documentSelector: [
|
documentSelector: [
|
||||||
|
|
@ -191,17 +175,11 @@ export async function activate(context: ExtensionContext) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
workspace.onDidChangeConfiguration((e) => {
|
config.onConfigChange = function onConfigChange() {
|
||||||
let isAffected = e.affectsConfiguration('oxc_language_server');
|
let settings: any = JSON.parse(JSON.stringify(this));
|
||||||
if (!isAffected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let settings: any = JSON.parse(
|
|
||||||
JSON.stringify(workspace.getConfiguration('oxc_language_server')),
|
|
||||||
);
|
|
||||||
updateStatsBar(settings.enable);
|
updateStatsBar(settings.enable);
|
||||||
client.sendNotification('workspace/didChangeConfiguration', { settings });
|
client.sendNotification('workspace/didChangeConfiguration', { settings });
|
||||||
});
|
};
|
||||||
|
|
||||||
function updateStatsBar(enable: boolean) {
|
function updateStatsBar(enable: boolean) {
|
||||||
if (!myStatusBarItem) {
|
if (!myStatusBarItem) {
|
||||||
|
|
|
||||||
9
editors/vscode/client/types.ts
Normal file
9
editors/vscode/client/types.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* A type with a destructor for releasing resources when de-registered by an LSP client.
|
||||||
|
*
|
||||||
|
* There's a newer {@link Disposable} interface that works with `using`, but
|
||||||
|
* VSCode uses this in its APIs.
|
||||||
|
*/
|
||||||
|
export interface IDisposable {
|
||||||
|
dispose(): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
@ -100,6 +100,11 @@
|
||||||
"scope": "window",
|
"scope": "window",
|
||||||
"default": ".eslintrc",
|
"default": ".eslintrc",
|
||||||
"description": "Path to ESlint configuration."
|
"description": "Path to ESlint configuration."
|
||||||
|
},
|
||||||
|
"oxc.path.server": {
|
||||||
|
"type": "string",
|
||||||
|
"scope": "window",
|
||||||
|
"description": "Path to Oxc language server binary."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue