From 5b6563c6e70376c194e928d043dbddf370019d3c Mon Sep 17 00:00:00 2001 From: Sysix Date: Fri, 31 Jan 2025 20:39:50 +0100 Subject: [PATCH] Revert "refactor(vscode): move commands and `findBinary` to separate files (#8605)" This reverts commit 259a47b2ac63b5f36f4f0910f8cc02d356a6dd52. --- editors/vscode/client/ConfigService.ts | 2 +- editors/vscode/client/commands.ts | 112 --------------- editors/vscode/client/extension.ts | 182 ++++++++++++++++++++++--- editors/vscode/client/findBinary.ts | 44 ------ editors/vscode/package.json | 2 +- 5 files changed, 164 insertions(+), 178 deletions(-) delete mode 100644 editors/vscode/client/commands.ts delete mode 100644 editors/vscode/client/findBinary.ts diff --git a/editors/vscode/client/ConfigService.ts b/editors/vscode/client/ConfigService.ts index f59d5fda22a1a..7d84f09708085 100644 --- a/editors/vscode/client/ConfigService.ts +++ b/editors/vscode/client/ConfigService.ts @@ -6,7 +6,7 @@ export class ConfigService implements IDisposable { private static readonly _namespace = 'oxc'; private readonly _disposables: IDisposable[] = []; - public readonly config: Config; + public config: Config; public onConfigChange: | ((this: ConfigService, config: ConfigurationChangeEvent) => void) diff --git a/editors/vscode/client/commands.ts b/editors/vscode/client/commands.ts deleted file mode 100644 index 3769f4200420b..0000000000000 --- a/editors/vscode/client/commands.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { CodeAction, Command, commands, Disposable, window, workspace } from 'vscode'; - -import { CodeActionRequest, CodeActionTriggerKind, LanguageClient, Position, Range } from 'vscode-languageclient/node'; -import { Config } from './Config'; - -const commandPrefix = 'oxc'; - -export const enum OxcCommands { - RestartServer = `${commandPrefix}.restartServer`, - ApplyAllFixesFile = `${commandPrefix}.applyAllFixesFile`, - ShowOutputChannel = `${commandPrefix}.showOutputChannel`, - ToggleEnable = `${commandPrefix}.toggleEnable`, -} - -export const restartServerCommand = (client: LanguageClient): Disposable => { - return commands.registerCommand( - OxcCommands.RestartServer, - async () => { - if (!client) { - window.showErrorMessage('oxc client not found'); - return; - } - - try { - if (client.isRunning()) { - await client.restart(); - - window.showInformationMessage('oxc server restarted.'); - } else { - await client.start(); - } - } catch (err) { - client.error('Restarting client failed', err, 'force'); - } - }, - ); -}; - -export const showOutputChannelCommand = (client: LanguageClient): Disposable => { - return commands.registerCommand( - OxcCommands.ShowOutputChannel, - () => { - client.outputChannel.show(); - }, - ); -}; - -export const toggleEnabledCommand = (config: Config): Disposable => { - return commands.registerCommand( - OxcCommands.ToggleEnable, - () => { - config.updateEnable(!config.enable); - }, - ); -}; - -export const applyAllFixesFileCommand = (client: LanguageClient): Disposable => { - return commands.registerCommand( - OxcCommands.ApplyAllFixesFile, - async () => { - if (!client) { - window.showErrorMessage('oxc client not found'); - return; - } - const textEditor = window.activeTextEditor; - if (!textEditor) { - window.showErrorMessage('active text editor not found'); - return; - } - - const lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1); - const codeActionResult = await client.sendRequest(CodeActionRequest.type, { - textDocument: { - uri: textEditor.document.uri.toString(), - }, - range: Range.create(Position.create(0, 0), lastLine.range.end), - context: { - diagnostics: [], - only: [], - triggerKind: CodeActionTriggerKind.Invoked, - }, - }); - const commandsOrCodeActions = await client.protocol2CodeConverter.asCodeActionResult(codeActionResult || []); - - await Promise.all( - commandsOrCodeActions - .map(async (codeActionOrCommand) => { - // Commands are always applied. Regardless of whether it's a Command or CodeAction#command. - if (isCommand(codeActionOrCommand)) { - await commands.executeCommand(codeActionOrCommand.command, codeActionOrCommand.arguments); - } else { - // Only preferred edits are applied - // LSP states edits must be run first, then commands - if (codeActionOrCommand.edit && codeActionOrCommand.isPreferred) { - await workspace.applyEdit(codeActionOrCommand.edit); - } - if (codeActionOrCommand.command) { - await commands.executeCommand( - codeActionOrCommand.command.command, - codeActionOrCommand.command.arguments, - ); - } - } - }), - ); - - function isCommand(codeActionOrCommand: CodeAction | Command): codeActionOrCommand is Command { - return typeof codeActionOrCommand.command === 'string'; - } - }, - ); -}; diff --git a/editors/vscode/client/extension.ts b/editors/vscode/client/extension.ts index d1f93f59ddfd8..896f2107b5e2d 100644 --- a/editors/vscode/client/extension.ts +++ b/editors/vscode/client/extension.ts @@ -1,26 +1,41 @@ -import { ExtensionContext, StatusBarAlignment, StatusBarItem, ThemeColor, window, workspace } from 'vscode'; +import { promises as fsPromises } from 'node:fs'; import { - Executable, - LanguageClient, - LanguageClientOptions, + CodeAction, + Command, + commands, + ExtensionContext, + StatusBarAlignment, + StatusBarItem, + ThemeColor, + window, + workspace, +} from 'vscode'; + +import { + CodeActionRequest, + CodeActionTriggerKind, MessageType, - ServerOptions, + Position, + Range, ShowMessageNotification, -} from 'vscode-languageclient/node'; +} from 'vscode-languageclient'; -import { - applyAllFixesFileCommand, - OxcCommands, - restartServerCommand, - showOutputChannelCommand, - toggleEnabledCommand, -} from './commands'; +import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; + +import { join } from 'node:path'; import { ConfigService } from './ConfigService'; -import findBinary from './findBinary'; const languageClientName = 'oxc'; const outputChannelName = 'Oxc'; +const commandPrefix = 'oxc'; + +const enum OxcCommands { + RestartServer = `${commandPrefix}.restartServer`, + ApplyAllFixesFile = `${commandPrefix}.applyAllFixesFile`, + ShowOutputChannel = `${commandPrefix}.showOutputChannel`, + ToggleEnable = `${commandPrefix}.toggleEnable`, +} let client: LanguageClient; @@ -28,18 +43,146 @@ let myStatusBarItem: StatusBarItem; export async function activate(context: ExtensionContext) { const configService = new ConfigService(); + const restartCommand = commands.registerCommand( + OxcCommands.RestartServer, + async () => { + if (!client) { + window.showErrorMessage('oxc client not found'); + return; + } + + try { + if (client.isRunning()) { + await client.restart(); + + window.showInformationMessage('oxc server restarted.'); + } else { + await client.start(); + } + } catch (err) { + client.error('Restarting client failed', err, 'force'); + } + }, + ); + + const showOutputCommand = commands.registerCommand( + OxcCommands.ShowOutputChannel, + () => { + client?.outputChannel?.show(); + }, + ); + + const toggleEnable = commands.registerCommand( + OxcCommands.ToggleEnable, + () => { + configService.config.updateEnable(!configService.config.enable); + }, + ); + + const applyAllFixesFile = commands.registerCommand( + OxcCommands.ApplyAllFixesFile, + async () => { + if (!client) { + window.showErrorMessage('oxc client not found'); + return; + } + const textEditor = window.activeTextEditor; + if (!textEditor) { + window.showErrorMessage('active text editor not found'); + return; + } + + const lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1); + const codeActionResult = await client.sendRequest(CodeActionRequest.type, { + textDocument: { + uri: textEditor.document.uri.toString(), + }, + range: Range.create(Position.create(0, 0), lastLine.range.end), + context: { + diagnostics: [], + only: [], + triggerKind: CodeActionTriggerKind.Invoked, + }, + }); + const commandsOrCodeActions = await client.protocol2CodeConverter.asCodeActionResult(codeActionResult || []); + + await Promise.all( + commandsOrCodeActions + .map(async (codeActionOrCommand) => { + // Commands are always applied. Regardless of whether it's a Command or CodeAction#command. + if (isCommand(codeActionOrCommand)) { + await commands.executeCommand(codeActionOrCommand.command, codeActionOrCommand.arguments); + } else { + // Only preferred edits are applied + // LSP states edits must be run first, then commands + if (codeActionOrCommand.edit && codeActionOrCommand.isPreferred) { + await workspace.applyEdit(codeActionOrCommand.edit); + } + if (codeActionOrCommand.command) { + await commands.executeCommand( + codeActionOrCommand.command.command, + codeActionOrCommand.command.arguments, + ); + } + } + }), + ); + + function isCommand(codeActionOrCommand: CodeAction | Command): codeActionOrCommand is Command { + return typeof codeActionOrCommand.command === 'string'; + } + }, + ); context.subscriptions.push( - applyAllFixesFileCommand(client), - restartServerCommand(client), - showOutputChannelCommand(client), - toggleEnabledCommand(configService.config), + applyAllFixesFile, + restartCommand, + showOutputCommand, + toggleEnable, configService, ); const outputChannel = window.createOutputChannel(outputChannelName, { log: true }); - const command = await findBinary(context, configService.config); + async function findBinary(): Promise { + let bin = configService.config.binPath; + if (bin) { + try { + await fsPromises.access(bin); + return bin; + } catch {} + } + + const workspaceFolders = workspace.workspaceFolders; + const isWindows = process.platform === 'win32'; + + if (workspaceFolders?.length && !isWindows) { + try { + return await Promise.any( + workspaceFolders.map(async (folder) => { + const binPath = join( + folder.uri.fsPath, + 'node_modules', + '.bin', + 'oxc_language_server', + ); + + await fsPromises.access(binPath); + return binPath; + }), + ); + } catch {} + } + + const ext = isWindows ? '.exe' : ''; + // NOTE: The `./target/release` path is aligned with the path defined in .github/workflows/release_vscode.yml + return ( + process.env.SERVER_PATH_DEV ?? + join(context.extensionPath, `./target/release/oxc_language_server${ext}`) + ); + } + + const command = await findBinary(); const run: Executable = { command: command!, options: { @@ -89,7 +232,6 @@ export async function activate(context: ExtensionContext) { serverOptions, clientOptions, ); - client.onNotification(ShowMessageNotification.type, (params) => { switch (params.type) { case MessageType.Debug: diff --git a/editors/vscode/client/findBinary.ts b/editors/vscode/client/findBinary.ts deleted file mode 100644 index 8e5d2323f05a6..0000000000000 --- a/editors/vscode/client/findBinary.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { promises as fsPromises } from 'node:fs'; -import { join } from 'node:path'; - -import { ExtensionContext, workspace } from 'vscode'; - -import { Config } from './Config'; - -export default async function findBinary(context: ExtensionContext, config: Config): Promise { - let bin = config.binPath; - if (bin) { - try { - await fsPromises.access(bin); - return bin; - } catch {} - } - - const workspaceFolders = workspace.workspaceFolders; - const isWindows = process.platform === 'win32'; - - if (workspaceFolders?.length && !isWindows) { - try { - return await Promise.any( - workspaceFolders.map(async (folder) => { - const binPath = join( - folder.uri.fsPath, - 'node_modules', - '.bin', - 'oxc_language_server', - ); - - await fsPromises.access(binPath); - return binPath; - }), - ); - } catch {} - } - - const ext = isWindows ? '.exe' : ''; - // NOTE: The `./target/release` path is aligned with the path defined in .github/workflows/release_vscode.yml - return ( - process.env.SERVER_PATH_DEV ?? - join(context.extensionPath, `./target/release/oxc_language_server${ext}`) - ); -} diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 1afc505aeb010..586782f640a1e 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -140,7 +140,7 @@ "server:build:debug": "cargo build -p oxc_language_server", "server:build:release": "cross-env CARGO_TARGET_DIR=./target cargo build -p oxc_language_server --release", "lint": "npx oxlint --config=oxlint.json --tsconfig=tsconfig.json", - "test": "esbuild \"client/*.spec.ts\" --bundle --outdir=out --external:vscode --format=cjs --platform=node --target=node16 --minify --sourcemap && vscode-test", + "test": "esbuild client/config.spec.ts --bundle --outfile=out/config.spec.js --external:vscode --format=cjs --platform=node --target=node16 --minify --sourcemap && vscode-test", "type-check": "tsc --noEmit" }, "devDependencies": {