Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions lib/adapters/code-action-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Range,
TextEditor,
} from 'atom';
import CommandExecutionAdapter from './command-execution-adapter';

export default class CodeActionAdapter {
/**
Expand Down Expand Up @@ -88,10 +89,7 @@ export default class CodeActionAdapter {
connection: LanguageClientConnection,
): Promise<void> {
if (Command.is(command)) {
await connection.executeCommand({
command: command.command,
arguments: command.arguments,
});
await CommandExecutionAdapter.executeCommand(connection, command.command, command.arguments);
}
}

Expand Down
30 changes: 30 additions & 0 deletions lib/adapters/command-execution-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ExecuteCommandParams, ServerCapabilities } from "../languageclient";
import { LanguageClientConnection } from "../main";

export type CommandCustomCallbackFunction = (command: ExecuteCommandParams) => Promise<any | void>;

export default class CommandExecutionAdapter {
private static commandsCustomCallbacks = new Map<string, CommandCustomCallbackFunction>();

public static canAdapt(serverCapabilities: ServerCapabilities): boolean {
return serverCapabilities.executeCommandProvider != null;
}

public static registerCustomCallbackForCommand(command: string, callback: CommandCustomCallbackFunction): void {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we exposing this function to the package author?

Copy link
Copy Markdown
Contributor Author

@deankevorkian deankevorkian Jan 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come to think about it, I just assumed one would invoke that method directly via the CommandExecutionAdapter static class, but I guess we could also have a method in AutoLanguageClient that simply calls that adapter. What'd you had in mind?

EDIT: Just realized you were referring to the fact I forgot to expose the Adapter itself out. Fixed it and added canAdapt and tests for it as well (I thought it is a necessity for all language servers for some reason). So do you think using CommandExecutionAdapter is fine?

this.commandsCustomCallbacks.set(command, callback);
}

public static async executeCommand(connection: LanguageClientConnection, command: string, commandArgs?: any[]): Promise<any | void> {
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
};
}
}
2 changes: 2 additions & 0 deletions lib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -18,4 +19,5 @@ export {
FilteredLogger,
DownloadFile,
LinterPushV2Adapter,
CommandExecutionAdapter
};
78 changes: 78 additions & 0 deletions test/adapters/command-execution-adapter.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
});