From fc6e182bcc86af5ae59840d5b374ea40992140d4 Mon Sep 17 00:00:00 2001 From: Dean Kevorkian Date: Sat, 16 Jan 2021 19:07:12 +0200 Subject: [PATCH 1/6] feat: introduce an Adapter for `executeCommand` that also allows registering custom callbacks --- lib/adapters/code-action-adapter.ts | 6 ++---- lib/adapters/command-execution-adapter.ts | 26 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 lib/adapters/command-execution-adapter.ts 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..f5dc38c5 --- /dev/null +++ b/lib/adapters/command-execution-adapter.ts @@ -0,0 +1,26 @@ +import { Command, ExecuteCommandParams } from "../languageclient"; +import { LanguageClientConnection } from "../main"; + +export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; + +export default class CommandExecutionAdapter { + private static commandsCustomCallbacks: Map = new Map(); + + public static registerCustomCallbackForCommand(command: string, callback: CommandCustomCallbackFunction) { + this.commandsCustomCallbacks.set(command, callback); + } + + public static async executeCommand(connection: LanguageClientConnection, command: string, commandArgs?: any[] | undefined): Promise { + const executeCommandParams = CommandExecutionAdapter.createExecuteCommandParams(command, commandArgs); + const commandCustomCallback = this.commandsCustomCallbacks.get(command); + + return commandCustomCallback != null ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); + } + + public static createExecuteCommandParams(command: string, commandArgs?: any[] | undefined): ExecuteCommandParams { + return { + command: command, + arguments: commandArgs + }; + } +} From 4b54305c413b48a309ec6a79485cb669d59588c7 Mon Sep 17 00:00:00 2001 From: Dean Kevorkian Date: Sun, 17 Jan 2021 03:02:44 +0200 Subject: [PATCH 2/6] test: addede tests for CommandExecutionAdapter --- lib/adapters/command-execution-adapter.ts | 6 +- .../command-execution-adapter.test.ts | 64 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 test/adapters/command-execution-adapter.test.ts diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts index f5dc38c5..a9182464 100644 --- a/lib/adapters/command-execution-adapter.ts +++ b/lib/adapters/command-execution-adapter.ts @@ -1,7 +1,7 @@ -import { Command, ExecuteCommandParams } from "../languageclient"; +import { ExecuteCommandParams } from "../languageclient"; import { LanguageClientConnection } from "../main"; -export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; +export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; export default class CommandExecutionAdapter { private static commandsCustomCallbacks: Map = new Map(); @@ -17,7 +17,7 @@ export default class CommandExecutionAdapter { return commandCustomCallback != null ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); } - public static createExecuteCommandParams(command: string, commandArgs?: any[] | undefined): ExecuteCommandParams { + private static createExecuteCommandParams(command: string, commandArgs?: any[] | undefined): ExecuteCommandParams { return { command: command, arguments: commandArgs diff --git a/test/adapters/command-execution-adapter.test.ts b/test/adapters/command-execution-adapter.test.ts new file mode 100644 index 00000000..7bcab3c7 --- /dev/null +++ b/test/adapters/command-execution-adapter.test.ts @@ -0,0 +1,64 @@ +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('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); + }); + }); +}); From 672bd411a02ee477887507d36a0c276615a5881e Mon Sep 17 00:00:00 2001 From: Dean Kevorkian Date: Sun, 17 Jan 2021 03:29:12 +0200 Subject: [PATCH 3/6] chore: lint --- lib/adapters/command-execution-adapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts index a9182464..878ff9f9 100644 --- a/lib/adapters/command-execution-adapter.ts +++ b/lib/adapters/command-execution-adapter.ts @@ -6,7 +6,7 @@ export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => P export default class CommandExecutionAdapter { private static commandsCustomCallbacks: Map = new Map(); - public static registerCustomCallbackForCommand(command: string, callback: CommandCustomCallbackFunction) { + public static registerCustomCallbackForCommand(command: string, callback: CommandCustomCallbackFunction): void { this.commandsCustomCallbacks.set(command, callback); } From eb1cc74709ed65925bc8b15b4636c96f5982e1b2 Mon Sep 17 00:00:00 2001 From: Dean Kevorkian Date: Sun, 17 Jan 2021 22:41:40 +0200 Subject: [PATCH 4/6] feat: added canAdapt & tests, exposed CommandExecutionAdapter --- lib/adapters/command-execution-adapter.ts | 6 +++++- lib/main.ts | 2 ++ test/adapters/command-execution-adapter.test.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts index 878ff9f9..b4d45896 100644 --- a/lib/adapters/command-execution-adapter.ts +++ b/lib/adapters/command-execution-adapter.ts @@ -1,4 +1,4 @@ -import { ExecuteCommandParams } from "../languageclient"; +import { ExecuteCommandParams, ServerCapabilities } from "../languageclient"; import { LanguageClientConnection } from "../main"; export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; @@ -6,6 +6,10 @@ export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => P export default class CommandExecutionAdapter { private static commandsCustomCallbacks: Map = 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); } 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 index 7bcab3c7..2315cb2d 100644 --- a/test/adapters/command-execution-adapter.test.ts +++ b/test/adapters/command-execution-adapter.test.ts @@ -6,6 +6,20 @@ 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(); From 46a486d7e31cdde144a8fe2ca0c02007b6d09221 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Sat, 20 Feb 2021 08:50:24 -0600 Subject: [PATCH 5/6] chore: remove redundant types --- lib/adapters/command-execution-adapter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts index b4d45896..69186cc1 100644 --- a/lib/adapters/command-execution-adapter.ts +++ b/lib/adapters/command-execution-adapter.ts @@ -4,7 +4,7 @@ import { LanguageClientConnection } from "../main"; export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise; export default class CommandExecutionAdapter { - private static commandsCustomCallbacks: Map = new Map(); + private static commandsCustomCallbacks = new Map(); public static canAdapt(serverCapabilities: ServerCapabilities): boolean { return serverCapabilities.executeCommandProvider != null; @@ -14,14 +14,14 @@ export default class CommandExecutionAdapter { this.commandsCustomCallbacks.set(command, callback); } - public static async executeCommand(connection: LanguageClientConnection, command: string, commandArgs?: any[] | undefined): Promise { + 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 != null ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); } - private static createExecuteCommandParams(command: string, commandArgs?: any[] | undefined): ExecuteCommandParams { + private static createExecuteCommandParams(command: string, commandArgs?: any[]): ExecuteCommandParams { return { command: command, arguments: commandArgs From d569a460b38d704bd545279beefd9f9d596871c4 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Sat, 20 Feb 2021 08:53:44 -0600 Subject: [PATCH 6/6] chore: use undefined for checking key existence --- lib/adapters/command-execution-adapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/adapters/command-execution-adapter.ts b/lib/adapters/command-execution-adapter.ts index 69186cc1..598f298f 100644 --- a/lib/adapters/command-execution-adapter.ts +++ b/lib/adapters/command-execution-adapter.ts @@ -18,7 +18,7 @@ export default class CommandExecutionAdapter { const executeCommandParams = CommandExecutionAdapter.createExecuteCommandParams(command, commandArgs); const commandCustomCallback = this.commandsCustomCallbacks.get(command); - return commandCustomCallback != null ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); + return commandCustomCallback !== undefined ? await commandCustomCallback(executeCommandParams) : await connection.executeCommand(executeCommandParams); } private static createExecuteCommandParams(command: string, commandArgs?: any[]): ExecuteCommandParams {