diff --git a/lib/adapters/code-action-adapter.ts b/lib/adapters/code-action-adapter.ts index c01922ea..3ebc1aa8 100644 --- a/lib/adapters/code-action-adapter.ts +++ b/lib/adapters/code-action-adapter.ts @@ -15,6 +15,7 @@ import { Range, TextEditor, } from 'atom'; +import CommandExecutionAdapter from './command-execution-adapter'; export default class CodeActionAdapter { /** @@ -88,10 +89,7 @@ export default class CodeActionAdapter { connection: LanguageClientConnection, ): Promise { if (Command.is(command)) { - await connection.executeCommand({ - command: command.command, - arguments: command.arguments, - }); + await CommandExecutionAdapter.executeCommand(connection, command.command, command.arguments); } } diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts new file mode 100644 index 00000000..598f298f --- /dev/null +++ b/lib/adapters/command-execution-adapter.ts @@ -0,0 +1,30 @@ +import { ExecuteCommandParams, ServerCapabilities } from "../languageclient"; +import { LanguageClientConnection } from "../main"; + +export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; + +export default class CommandExecutionAdapter { + private static commandsCustomCallbacks = new Map(); + + public static canAdapt(serverCapabilities: ServerCapabilities): boolean { + return serverCapabilities.executeCommandProvider != null; + } + + public static registerCustomCallbackForCommand(command: string, callback: CommandCustomCallbackFunction): void { + this.commandsCustomCallbacks.set(command, callback); + } + + public static async executeCommand(connection: LanguageClientConnection, command: string, commandArgs?: any[]): Promise { + const executeCommandParams = CommandExecutionAdapter.createExecuteCommandParams(command, commandArgs); + const commandCustomCallback = this.commandsCustomCallbacks.get(command); + + return commandCustomCallback !== undefined ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); + } + + private static createExecuteCommandParams(command: string, commandArgs?: any[]): ExecuteCommandParams { + return { + command: command, + arguments: commandArgs + }; + } +} diff --git a/lib/main.ts b/lib/main.ts index a68eb876..3a4e2c43 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -8,6 +8,7 @@ import Convert from './convert'; import { Logger, ConsoleLogger, FilteredLogger } from './logger'; import DownloadFile from './download-file'; import LinterPushV2Adapter from './adapters/linter-push-v2-adapter'; +import CommandExecutionAdapter from './adapters/command-execution-adapter'; export * from './auto-languageclient'; export { @@ -18,4 +19,5 @@ export { FilteredLogger, DownloadFile, LinterPushV2Adapter, + CommandExecutionAdapter }; diff --git a/test/adapters/command-execution-adapter.test.ts b/test/adapters/command-execution-adapter.test.ts new file mode 100644 index 00000000..2315cb2d --- /dev/null +++ b/test/adapters/command-execution-adapter.test.ts @@ -0,0 +1,78 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import * as ls from '../../lib/languageclient'; +import CommandExecutionAdapter, { CommandCustomCallbackFunction } from '../../lib/adapters/command-execution-adapter'; +import { createSpyConnection } from '../helpers.js'; +import { ExecuteCommandParams } from '../../lib/languageclient'; + +describe('CommandExecutionAdapter', () => { + describe('canAdapt', () => { + it('returns true if command execution is supported', () => { + const result = CommandExecutionAdapter.canAdapt({ + executeCommandProvider: {commands: []}, + }); + expect(result).to.be.true; + }); + + it('returns false it no formatting supported', () => { + const result = CommandExecutionAdapter.canAdapt({}); + expect(result).to.be.false; + }); + }); + + describe('executeCommand', () => { + it('invokes an executeCommand object from given inputs', async () => { + const connection = createSpyConnection(); + const languageClient = new ls.LanguageClientConnection(connection); + const testCommand = { + command: 'testCommand', + arguments: ['a', 'b'], + }; + sinon.stub(languageClient, 'executeCommand').returns(Promise.resolve(testCommand)); + + const result = await CommandExecutionAdapter.executeCommand( + languageClient, + testCommand.command, + testCommand.arguments + ); + + expect(result.command).to.equal(testCommand.command); + expect(result.arguments).to.equal(testCommand.arguments); + + expect((languageClient as any).executeCommand.called).to.be.true; + expect((languageClient as any).executeCommand.getCalls()[0].args).to.deep.equal([{ + command: testCommand.command, + arguments: testCommand.arguments + } as ExecuteCommandParams]); + }); + }); + + describe('registerCustomCallbackForCommand', () => { + it('registers a custom callback for a command, to be executed on executeCommand', async () => { + const connection = createSpyConnection(); + const languageClient = new ls.LanguageClientConnection(connection); + const testCallback: CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise.resolve(command.command); + const testCommand = { + command: 'testCommand', + arguments: ['a', 'b'], + }; + + const spiedCallback = sinon.spy(testCallback); + sinon.spy(languageClient, 'executeCommand'); + + CommandExecutionAdapter.registerCustomCallbackForCommand(testCommand.command, spiedCallback); + + const result = await CommandExecutionAdapter.executeCommand( + languageClient, + testCommand.command, + testCommand.arguments + ); + + expect(spiedCallback.called).to.be.true; + + expect((languageClient as any).executeCommand.called).to.be.false; + + expect(result).to.equal(testCommand.command); + }); + }); +});