1313 *
1414 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
1515 ********************************************************************************/
16+ /*---------------------------------------------------------------------------------------------
17+ * Copyright (c) Microsoft Corporation. All rights reserved.
18+ * Licensed under the MIT License. See License.txt in the project root for license information.
19+ *--------------------------------------------------------------------------------------------*/
1620
1721import * as theia from '@theia/plugin' ;
1822import { CommandRegistryExt , PLUGIN_RPC_CONTEXT as Ext , CommandRegistryMain } from '../common/plugin-api-rpc' ;
1923import { RPCProtocol } from '../common/rpc-protocol' ;
2024import { Disposable } from './types-impl' ;
2125import { KnownCommands } from './type-converters' ;
26+ import { DisposableCollection } from '@theia/core' ;
2227
2328// tslint:disable-next-line:no-any
24- export type Handler = < T > ( ...args : any [ ] ) => T | PromiseLike < T > ;
29+ export type Handler = < T > ( ...args : any [ ] ) => T | PromiseLike < T | undefined > ;
2530
2631export interface ArgumentProcessor {
2732 // tslint:disable-next-line:no-any
@@ -34,10 +39,16 @@ export class CommandRegistryImpl implements CommandRegistryExt {
3439 private readonly commands = new Set < string > ( ) ;
3540 private readonly handlers = new Map < string , Handler > ( ) ;
3641 private readonly argumentProcessors : ArgumentProcessor [ ] ;
42+ private readonly commandsConverter : CommandsConverter ;
3743
3844 constructor ( rpc : RPCProtocol ) {
3945 this . proxy = rpc . getProxy ( Ext . COMMAND_REGISTRY_MAIN ) ;
4046 this . argumentProcessors = [ ] ;
47+ this . commandsConverter = new CommandsConverter ( this ) ;
48+ }
49+
50+ get converter ( ) : CommandsConverter {
51+ return this . commandsConverter ;
4152 }
4253
4354 // tslint:disable-next-line:no-any
@@ -78,7 +89,7 @@ export class CommandRegistryImpl implements CommandRegistryExt {
7889 }
7990
8091 // tslint:disable-next-line:no-any
81- $executeCommand < T > ( id : string , ...args : any [ ] ) : PromiseLike < T > {
92+ $executeCommand < T > ( id : string , ...args : any [ ] ) : PromiseLike < T | undefined > {
8293 if ( this . handlers . has ( id ) ) {
8394 return this . executeLocalCommand ( id , ...args ) ;
8495 } else {
@@ -102,7 +113,7 @@ export class CommandRegistryImpl implements CommandRegistryExt {
102113 }
103114
104115 // tslint:disable-next-line:no-any
105- private async executeLocalCommand < T > ( id : string , ...args : any [ ] ) : Promise < T > {
116+ private async executeLocalCommand < T > ( id : string , ...args : any [ ] ) : Promise < T | undefined > {
106117 const handler = this . handlers . get ( id ) ;
107118 if ( handler ) {
108119 return handler < T > ( ...args . map ( arg => this . argumentProcessors . reduce ( ( r , p ) => p . processArgument ( r ) , arg ) ) ) ;
@@ -123,3 +134,52 @@ export class CommandRegistryImpl implements CommandRegistryExt {
123134 this . argumentProcessors . push ( processor ) ;
124135 }
125136}
137+
138+ // copied and modified from https://github.com/microsoft/vscode/blob/1.37.1/src/vs/workbench/api/common/extHostCommands.ts#L217-L259
139+ export class CommandsConverter {
140+
141+ private readonly safeCommandId : string ;
142+ private readonly commands : CommandRegistryImpl ;
143+ private readonly commandsMap = new Map < number , theia . Command > ( ) ;
144+ private handle = 0 ;
145+ private isSafeCommandRegistered : boolean ;
146+
147+ constructor ( commands : CommandRegistryImpl ) {
148+ this . safeCommandId = `theia_safe_cmd_${ Date . now ( ) . toString ( ) } ` ;
149+ this . commands = commands ;
150+ this . isSafeCommandRegistered = false ;
151+ }
152+
153+ /**
154+ * Convert to a command that can be safely passed over JSON-RPC.
155+ */
156+ toSafeCommand ( command : theia . Command , disposables : DisposableCollection ) : theia . Command {
157+ if ( ! this . isSafeCommandRegistered ) {
158+ this . commands . registerCommand ( { id : this . safeCommandId } , this . executeSafeCommand , this ) ;
159+ this . isSafeCommandRegistered = true ;
160+ }
161+
162+ const result : theia . Command = { } ;
163+ Object . assign ( result , command ) ;
164+
165+ if ( command . command && command . arguments && command . arguments . length > 0 ) {
166+ const id = this . handle ++ ;
167+ this . commandsMap . set ( id , command ) ;
168+ disposables . push ( new Disposable ( ( ) => this . commandsMap . delete ( id ) ) ) ;
169+ result . command = this . safeCommandId ;
170+ result . arguments = [ id ] ;
171+ }
172+
173+ return result ;
174+ }
175+
176+ // tslint:disable-next-line:no-any
177+ private executeSafeCommand < R > ( ...args : any [ ] ) : PromiseLike < R | undefined > {
178+ const command = this . commandsMap . get ( args [ 0 ] ) ;
179+ if ( ! command || ! command . command ) {
180+ return Promise . reject ( 'command NOT FOUND' ) ;
181+ }
182+ return this . commands . executeCommand ( command . command , ...( command . arguments || [ ] ) ) ;
183+ }
184+
185+ }
0 commit comments