Skip to content

Commit

Permalink
Merge pull request #72345 from hedgerh/command-execute-events
Browse files Browse the repository at this point in the history
onDidExecuteCommand API
  • Loading branch information
jrieken authored Jul 24, 2019
2 parents e0b6026 + 7beb320 commit 6a25cfa
Show file tree
Hide file tree
Showing 13 changed files with 76 additions and 6 deletions.
6 changes: 5 additions & 1 deletion src/vs/editor/standalone/browser/simpleServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ export class StandaloneCommandService implements ICommandService {
private readonly _dynamicCommands: { [id: string]: ICommand; };

private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;

constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService;
Expand All @@ -256,8 +258,10 @@ export class StandaloneCommandService implements ICommandService {
}

try {
this._onWillExecuteCommand.fire({ commandId: id });
this._onWillExecuteCommand.fire({ commandId: id, args });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;

this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
Expand Down
6 changes: 5 additions & 1 deletion src/vs/editor/test/browser/editorTestServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export class TestCommandService implements ICommandService {
private readonly _onWillExecuteCommand = new Emitter<ICommandEvent>();
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;

private readonly _onDidExecuteCommand = new Emitter<ICommandEvent>();
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;

constructor(instantiationService: IInstantiationService) {
this._instantiationService = instantiationService;
}
Expand All @@ -43,8 +46,9 @@ export class TestCommandService implements ICommandService {
}

try {
this._onWillExecuteCommand.fire({ commandId: id });
this._onWillExecuteCommand.fire({ commandId: id, args });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T;
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
Expand Down
1 change: 1 addition & 0 deletions src/vs/editor/test/browser/services/openerService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ suite('OpenerService', function () {
const commandService = new class implements ICommandService {
_serviceBrand: any;
onWillExecuteCommand = () => ({ dispose: () => { } });
onDidExecuteCommand = () => ({ dispose: () => { } });
executeCommand(id: string, ...args: any[]): Promise<any> {
lastCommand = { id, args };
return Promise.resolve(undefined);
Expand Down
3 changes: 3 additions & 0 deletions src/vs/platform/commands/common/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ export const ICommandService = createDecorator<ICommandService>('commandService'

export interface ICommandEvent {
commandId: string;
args: any[];
}

export interface ICommandService {
_serviceBrand: any;
onWillExecuteCommand: Event<ICommandEvent>;
onDidExecuteCommand: Event<ICommandEvent>;
executeCommand<T = any>(commandId: string, ...args: any[]): Promise<T | undefined>;
}

Expand Down Expand Up @@ -135,6 +137,7 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR
export const NullCommandService: ICommandService = {
_serviceBrand: undefined,
onWillExecuteCommand: () => ({ dispose: () => { } }),
onDidExecuteCommand: () => ({ dispose: () => { } }),
executeCommand() {
return Promise.resolve(undefined);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ suite('AbstractKeybindingService', () => {
let commandService: ICommandService = {
_serviceBrand: undefined,
onWillExecuteCommand: () => ({ dispose: () => { } }),
onDidExecuteCommand: () => ({ dispose: () => { } }),
executeCommand: (commandId: string, ...args: any[]): Promise<any> => {
executeCommandCalls.push({
commandId: commandId,
Expand Down
16 changes: 16 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,22 @@ declare module 'vscode' {

//#endregion

//#region Joh: onDidExecuteCommand

export interface CommandExecutionEvent {
command: string;
arguments: any[];
}

export namespace commands {
/**
* An event that is emitted when a [command](#Command) is executed.
*/
export const onDidExecuteCommand: Event<CommandExecutionEvent>;
}

//#endregion

//#region Joh: decorations

//todo@joh -> make class
Expand Down
14 changes: 14 additions & 0 deletions src/vs/workbench/api/browser/mainThreadCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _commandRegistrations = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
private _onDidExecuteCommandListener?: IDisposable;

constructor(
extHostContext: IExtHostContext,
Expand Down Expand Up @@ -77,6 +78,19 @@ export class MainThreadCommands implements MainThreadCommandsShape {
return this._commandService.executeCommand<T>(id, ...args);
}

$registerCommandListener() {
if (!this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener = this._commandService.onDidExecuteCommand(command => this._proxy.$handleDidExecuteCommand(command));
}
}

$unregisterCommandListener() {
if (this._onDidExecuteCommandListener) {
this._onDidExecuteCommandListener.dispose();
this._onDidExecuteCommandListener = undefined;
}
}

$getCommands(): Promise<string[]> {
return Promise.resolve([...CommandsRegistry.getCommands().keys()]);
}
Expand Down
5 changes: 4 additions & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model'
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
Expand Down Expand Up @@ -115,6 +115,8 @@ export interface MainThreadClipboardShape extends IDisposable {

export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
$registerCommandListener(): void;
$unregisterCommandListener(): void;
$unregisterCommand(id: string): void;
$executeCommand<T>(id: string, args: any[]): Promise<T | undefined>;
$getCommands(): Promise<string[]>;
Expand Down Expand Up @@ -736,6 +738,7 @@ export interface MainThreadWindowShape extends IDisposable {
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
$handleDidExecuteCommand(command: ICommandEvent): void;
}

export interface ExtHostConfigurationShape {
Expand Down
16 changes: 15 additions & 1 deletion src/vs/workbench/api/common/extHostCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { validateConstraint } from 'vs/base/common/types';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
Expand All @@ -17,6 +17,7 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';

interface CommandHandler {
Expand All @@ -31,6 +32,9 @@ export interface ArgumentProcessor {

export class ExtHostCommands implements ExtHostCommandsShape {

private readonly _onDidExecuteCommand: Emitter<vscode.CommandExecutionEvent>;
readonly onDidExecuteCommand: Event<vscode.CommandExecutionEvent>;

private readonly _commands = new Map<string, CommandHandler>();
private readonly _proxy: MainThreadCommandsShape;
private readonly _converter: CommandsConverter;
Expand All @@ -42,6 +46,11 @@ export class ExtHostCommands implements ExtHostCommandsShape {
logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadCommands);
this._onDidExecuteCommand = new Emitter<vscode.CommandExecutionEvent>({
onFirstListenerDidAdd: () => this._proxy.$registerCommandListener(),
onLastListenerRemove: () => this._proxy.$unregisterCommandListener(),
});
this.onDidExecuteCommand = this._onDidExecuteCommand.event;
this._logService = logService;
this._converter = new CommandsConverter(this);
this._argumentProcessors = [
Expand Down Expand Up @@ -106,6 +115,10 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}

$handleDidExecuteCommand(command: ICommandEvent): void {
this._onDidExecuteCommand.fire({ command: command.commandId, arguments: command.args });
}

executeCommand<T>(id: string, ...args: any[]): Promise<T> {
this._logService.trace('ExtHostCommands#executeCommand', id);

Expand Down Expand Up @@ -154,6 +167,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {

try {
const result = callback.apply(thisArg, args);
this._onDidExecuteCommand.fire({ command: id, arguments: args });
return Promise.resolve(result);
} catch (err) {
this._logService.error(err, id);
Expand Down
6 changes: 5 additions & 1 deletion src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ export function createApiFactory(
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
}
},
onDidExecuteCommand: proposedApiFunction(extension, (listener, thisArgs?, disposables?) => {
checkProposedApiEnabled(extension);
return extHostCommands.onDidExecuteCommand(listener, thisArgs, disposables);
}),
};

// namespace: env
Expand Down
6 changes: 5 additions & 1 deletion src/vs/workbench/services/commands/common/commandService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export class CommandService extends Disposable implements ICommandService {
private readonly _onWillExecuteCommand: Emitter<ICommandEvent> = this._register(new Emitter<ICommandEvent>());
public readonly onWillExecuteCommand: Event<ICommandEvent> = this._onWillExecuteCommand.event;

private readonly _onDidExecuteCommand: Emitter<ICommandEvent> = new Emitter<ICommandEvent>();
public readonly onDidExecuteCommand: Event<ICommandEvent> = this._onDidExecuteCommand.event;

constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IExtensionService private readonly _extensionService: IExtensionService,
Expand Down Expand Up @@ -77,8 +80,9 @@ export class CommandService extends Disposable implements ICommandService {
return Promise.reject(new Error(`command '${id}' not found`));
}
try {
this._onWillExecuteCommand.fire({ commandId: id });
this._onWillExecuteCommand.fire({ commandId: id, args });
const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]);
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ class MockCommandService implements ICommandService {
public callCount = 0;

onWillExecuteCommand = () => Disposable.None;
onDidExecuteCommand = () => Disposable.None;
public executeCommand(commandId: string, ...args: any[]): Promise<any> {
this.callCount++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const emptyDialogService = new class implements IDialogService {
const emptyCommandService: ICommandService = {
_serviceBrand: undefined,
onWillExecuteCommand: () => ({ dispose: () => { } }),
onDidExecuteCommand: () => ({ dispose: () => { } }),
executeCommand: (commandId: string, ...args: any[]): Promise<any> => {
return Promise.resolve(undefined);
}
Expand Down

0 comments on commit 6a25cfa

Please sign in to comment.