diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8f25b7a580d..b5c1275964b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Breaking changes: - [task] changed `TaskSchemaUpdater.update()` from asynchronous to synchronous [#6483](https://github.com/eclipse-theia/theia/pull/6483) +- [monaco] monaco prefix has been removed from commands [#5590](https://github.com/eclipse-theia/theia/pull/5590) ## v0.12.0 diff --git a/packages/monaco/src/browser/monaco-command-registry.ts b/packages/monaco/src/browser/monaco-command-registry.ts index e8faf0f237715..d51b7debdcf90 100644 --- a/packages/monaco/src/browser/monaco-command-registry.ts +++ b/packages/monaco/src/browser/monaco-command-registry.ts @@ -29,8 +29,6 @@ export interface MonacoEditorCommandHandler { @injectable() export class MonacoCommandRegistry { - public static MONACO_COMMAND_PREFIX = 'monaco.'; - @inject(MonacoEditorProvider) protected readonly monacoEditors: MonacoEditorProvider; @@ -38,19 +36,14 @@ export class MonacoCommandRegistry { @inject(SelectionService) protected readonly selectionService: SelectionService; - protected prefix(command: string): string { - return MonacoCommandRegistry.MONACO_COMMAND_PREFIX + command; - } - validate(command: string): string | undefined { - const monacoCommand = this.prefix(command); - return this.commands.commandIds.indexOf(monacoCommand) !== -1 ? monacoCommand : undefined; + return this.commands.commandIds.indexOf(command) !== -1 ? command : undefined; } registerCommand(command: Command, handler: MonacoEditorCommandHandler): void { this.commands.registerCommand({ ...command, - id: this.prefix(command.id) + id: command.id }, this.newHandler(handler)); } diff --git a/packages/monaco/src/browser/monaco-command-service.ts b/packages/monaco/src/browser/monaco-command-service.ts index 9821595d55861..f24c318d94489 100644 --- a/packages/monaco/src/browser/monaco-command-service.ts +++ b/packages/monaco/src/browser/monaco-command-service.ts @@ -57,6 +57,11 @@ export class MonacoCommandService implements ICommandService { this._onWillExecuteCommand.fire({ commandId }); return handler.execute(...args); } + return this.executeMonacoCommand(commandId, ...args); + } + + // tslint:disable-next-line:no-any + async executeMonacoCommand(commandId: any, ...args: any[]): Promise { if (this.delegate) { return this.delegate.executeCommand(commandId, ...args); } diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index 8d88c836d4c60..e4a05b30d7f67 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -25,6 +25,7 @@ import { EditorCommands } from '@theia/editor/lib/browser'; import { MonacoEditor } from './monaco-editor'; import { MonacoCommandRegistry, MonacoEditorCommandHandler } from './monaco-command-registry'; import MenuRegistry = monaco.actions.MenuRegistry; +import { MonacoCommandService } from './monaco-command-service'; export type MonacoCommand = Command & { delegate?: string }; export namespace MonacoCommands { @@ -118,7 +119,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { if (command.startsWith('_execute')) { this.commandRegistry.registerCommand( { - id: MonacoCommandRegistry.MONACO_COMMAND_PREFIX + command + id: command }, { // tslint:disable-next-line:no-any @@ -273,7 +274,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { } protected newMonacoActionHandler(action: MonacoCommand): MonacoEditorCommandHandler { const delegate = action.delegate; - return delegate ? this.newCommandHandler(delegate) : this.newActionHandler(action.id); + return delegate ? this.newDelegateHandler(delegate) : this.newActionHandler(action.id); } protected newKeyboardHandler(action: string): MonacoEditorCommandHandler { @@ -292,5 +293,10 @@ export class MonacoEditorCommandHandlers implements CommandContribution { isEnabled: editor => editor.isActionSupported(action) }; } + protected newDelegateHandler(action: string): MonacoEditorCommandHandler { + return { + execute: (editor, ...args) => (editor.commandService as MonacoCommandService).executeMonacoCommand(action, ...args) + }; + } } diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 957f7cac62d63..3efec40d79afc 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -199,7 +199,7 @@ export class MonacoEditorProvider { const formatOnSaveTimeout = this.editorPreferences.get({ preferenceName: 'editor.formatOnSaveTimeout', overrideIdentifier }, undefined, uri)!; await Promise.race([ new Promise(reject => setTimeout(() => reject(new Error(`Aborted format on save after ${formatOnSaveTimeout}ms`)), formatOnSaveTimeout)), - await editor.commandService.executeCommand('monaco.editor.action.formatDocument') + await editor.commandService.executeCommand('editor.action.formatDocument') ]); return []; } diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index 352b039696d05..94661977e454e 100644 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -323,7 +323,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution { id: 'vscode.executeDocumentSymbolProvider' }, { - execute: (resource: URI) => commands.executeCommand('monaco._executeDocumentSymbolProvider', + execute: (resource: URI) => commands.executeCommand('_executeDocumentSymbolProvider', { resource: monaco.Uri.parse(resource.toString()) } ).then((value: any) => { if (!Array.isArray(value) || value === undefined) { diff --git a/packages/plugin-ext/src/common/index.ts b/packages/plugin-ext/src/common/index.ts index 9d653b922fe83..70fe4d10aa5f9 100644 --- a/packages/plugin-ext/src/common/index.ts +++ b/packages/plugin-ext/src/common/index.ts @@ -18,3 +18,4 @@ export * from './plugin-protocol'; export * from './plugin-api-rpc'; export * from './plugin-ext-api-contribution'; +export * from './known-commands'; diff --git a/packages/plugin-ext/src/common/known-commands.ts b/packages/plugin-ext/src/common/known-commands.ts new file mode 100644 index 0000000000000..e26cfed226d76 --- /dev/null +++ b/packages/plugin-ext/src/common/known-commands.ts @@ -0,0 +1,325 @@ +/******************************************************************************** + * Copyright (C) 2019 Red Hat, Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { Range as R, Position as P, Location as L } from 'vscode-languageserver-types'; +import URI from 'vscode-uri'; +import * as theia from '@theia/plugin'; +import { cloneAndChange } from './objects'; +import { Position, Range, Location } from '../plugin/types-impl'; +import { fromPosition, fromRange, fromLocation } from '../plugin/type-converters'; + +// Here is a mapping of VSCode commands to monaco commands with their conversions +export namespace KnownCommands { + + /** + * Commands that you want to apply custom conversions to rather than pass through the automatic args converter. + * Would be useful in the case where theia provides some command and you need to provide custom conversions + */ + // tslint:disable-next-line:no-any + const mappings: { [id: string]: [string, (args: any[] | undefined) => any[] | undefined] } = {}; + mappings['editor.action.showReferences'] = ['textEditor.commands.showReferences', createConversionFunction( + (uri: URI) => uri.toString(), + fromPositionToP, + toArrayConversion(fromLocationToL))]; + + /** + * Mapping of all editor.action commands to their conversion function. + * executeCommand inside of the plugin command registry will automatically convert + * incoming arguments from vscode api types to monaco types + */ + + // tslint:disable-next-line:no-any + const MONACO_CONVERSION_IDENTITY = (args: any[] | undefined) => { + if (!args) { + return args; + } + const argStack: ConversionFunction[] = []; + args.forEach(_ => { + // tslint:disable-next-line:no-any + argStack.push((arg: any) => monacoArgsConverter(arg)); + }); + if (args) { + return createConversionFunction(...argStack)(args); + } + }; + + mappings['editor.action.select.all'] = ['editor.action.select.all', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.toggleHighContrast'] = ['editor.action.toggleHighContrast', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveCarretLeftAction'] = ['editor.action.moveCarretLeftAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveCarretRightAction'] = ['editor.action.moveCarretRightAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.transposeLetters'] = ['editor.action.transposeLetters', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.clipboardCopyWithSyntaxHighlightingAction'] = ['editor.action.clipboardCopyWithSyntaxHighlightingAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.commentLine'] = ['editor.action.commentLine', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.addCommentLine'] = ['editor.action.addCommentLine', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.removeCommentLine'] = ['editor.action.removeCommentLine', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.blockComment'] = ['editor.action.blockComment', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.showContextMenu'] = ['editor.action.showContextMenu', MONACO_CONVERSION_IDENTITY]; + mappings['cursorUndo'] = ['cursorUndo', MONACO_CONVERSION_IDENTITY]; + mappings['editor.unfold'] = ['editor.unfold', MONACO_CONVERSION_IDENTITY]; + mappings['editor.unfoldRecursively'] = ['editor.unfoldRecursively', MONACO_CONVERSION_IDENTITY]; + mappings['editor.fold'] = ['editor.fold', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldRecursively'] = ['editor.foldRecursively', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldAll'] = ['editor.foldAll', MONACO_CONVERSION_IDENTITY]; + mappings['editor.unfoldAll'] = ['editor.unfoldAll', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldAllBlockComments'] = ['editor.foldAllBlockComments', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldAllMarkerRegions'] = ['editor.foldAllMarkerRegions', MONACO_CONVERSION_IDENTITY]; + mappings['editor.unfoldAllMarkerRegions'] = ['editor.unfoldAllMarkerRegions', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel1'] = ['editor.foldLevel1', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel2'] = ['editor.foldLevel2', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel3'] = ['editor.foldLevel3', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel4'] = ['editor.foldLevel4', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel5'] = ['editor.foldLevel5', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel6'] = ['editor.foldLevel6', MONACO_CONVERSION_IDENTITY]; + mappings['editor.foldLevel7'] = ['editor.foldLevel7', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.fontZoomIn'] = ['editor.action.fontZoomIn', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.fontZoomOut'] = ['editor.action.fontZoomOut', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.fontZoomReset'] = ['editor.action.fontZoomReset', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.formatDocument'] = ['editor.action.formatDocument', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.formatSelection'] = ['editor.action.formatSelection', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.copyLinesUpAction'] = ['editor.action.copyLinesUpAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.copyLinesDownAction'] = ['editor.action.copyLinesDownAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveLinesUpAction'] = ['editor.action.moveLinesUpAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveLinesDownAction'] = ['editor.action.moveLinesDownAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.sortLinesAscending'] = ['editor.action.sortLinesAscending', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.sortLinesDescending'] = ['editor.action.sortLinesDescending', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.trimTrailingWhitespace'] = ['editor.action.trimTrailingWhitespace', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.deleteLines'] = ['editor.action.deleteLines', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.indentLines'] = ['editor.action.indentLines', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.outdentLines'] = ['editor.action.outdentLines', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.insertLineBefore'] = ['editor.action.insertLineBefore', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.insertLineAfter'] = ['editor.action.insertLineAfter', MONACO_CONVERSION_IDENTITY]; + mappings['deleteAllLeft'] = ['deleteAllLeft', MONACO_CONVERSION_IDENTITY]; + mappings['deleteAllRight'] = ['deleteAllRight', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.joinLines'] = ['editor.action.joinLines', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.transpose'] = ['editor.action.transpose', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.transformToUppercase'] = ['editor.action.transformToUppercase', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.transformToLowercase'] = ['editor.action.transformToLowercase', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.transformToTitlecase'] = ['editor.action.transformToTitlecase', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.smartSelect.expand'] = ['editor.action.smartSelect.expand', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.smartSelect.shrink'] = ['editor.action.smartSelect.shrink', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.forceRetokenize'] = ['editor.action.forceRetokenize', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.toggleTabFocusMode'] = ['editor.action.toggleTabFocusMode', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.gotoLine'] = ['editor.action.gotoLine', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.quickOutline'] = ['editor.action.quickOutline', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.inPlaceReplace.up'] = ['editor.action.inPlaceReplace.up', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.inPlaceReplace.down'] = ['editor.action.inPlaceReplace.down', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.diffReview.next'] = ['editor.action.diffReview.next', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.diffReview.prev'] = ['editor.action.diffReview.prev', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.selectToBracket'] = ['editor.action.selectToBracket', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.jumpToBracket'] = ['editor.action.jumpToBracket', MONACO_CONVERSION_IDENTITY]; + mappings['actions.findWithSelection'] = ['actions.findWithSelection', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.nextMatchFindAction'] = ['editor.action.nextMatchFindAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.previousMatchFindAction'] = ['editor.action.previousMatchFindAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.nextSelectionMatchFindAction'] = ['editor.action.nextSelectionMatchFindAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.previousSelectionMatchFindAction'] = ['editor.action.previousSelectionMatchFindAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.openLink'] = ['editor.action.openLink', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.quickFix'] = ['editor.action.quickFix', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.refactor'] = ['editor.action.refactor', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.sourceAction'] = ['editor.action.sourceAction', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.organizeImports'] = ['editor.action.organizeImports', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.autoFix'] = ['editor.action.autoFix', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.fixAll'] = ['editor.action.fixAll', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.rename'] = ['editor.action.rename', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.insertCursorAbove'] = ['editor.action.insertCursorAbove', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.insertCursorBelow'] = ['editor.action.insertCursorBelow', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.insertCursorAtEndOfEachLineSelected'] = ['editor.action.insertCursorAtEndOfEachLineSelected', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.addSelectionToNextFindMatch'] = ['editor.action.addSelectionToNextFindMatch', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.addSelectionToPreviousFindMatch'] = ['editor.action.addSelectionToPreviousFindMatch', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveSelectionToNextFindMatch'] = ['editor.action.moveSelectionToNextFindMatch', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.moveSelectionToPreviousFindMatch'] = ['editor.action.moveSelectionToPreviousFindMatch', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.selectHighlights'] = ['editor.action.selectHighlights', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.changeAll'] = ['editor.action.changeAll', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.addCursorsToBottom'] = ['editor.action.addCursorsToBottom', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.addCursorsToTop'] = ['editor.action.addCursorsToTop', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.triggerParameterHints'] = ['editor.action.triggerParameterHints', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.wordHighlight.next'] = ['editor.action.wordHighlight.next', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.wordHighlight.prev'] = ['editor.action.wordHighlight.prev', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.wordHighlight.trigger'] = ['editor.action.wordHighlight.trigger', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.showAccessibilityHelp'] = ['editor.action.showAccessibilityHelp', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.inspectTokens'] = ['editor.action.inspectTokens', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.marker.next'] = ['editor.action.marker.next', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.marker.prev'] = ['editor.action.marker.prev', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.marker.nextInFiles'] = ['editor.action.marker.nextInFiles', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.marker.prevInFiles'] = ['editor.action.marker.prevInFiles', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.showHover'] = ['editor.action.showHover', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.revealDefinition'] = ['editor.action.revealDefinition', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.revealDefinitionAside'] = ['editor.action.revealDefinitionAside', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.peekDefinition'] = ['editor.action.peekDefinition', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.revealDeclaration'] = ['editor.action.revealDeclaration', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.peekDeclaration'] = ['editor.action.peekDeclaration', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.goToImplementation'] = ['editor.action.goToImplementation', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.peekImplementation'] = ['editor.action.peekImplementation', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.goToTypeDefinition'] = ['editor.action.goToTypeDefinition', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.peekTypeDefinition'] = ['editor.action.peekTypeDefinition', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.referenceSearch.trigger'] = ['editor.action.referenceSearch.trigger', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.triggerSuggest'] = ['editor.action.triggerSuggest', MONACO_CONVERSION_IDENTITY]; + mappings['closeReferenceSearchEditor'] = ['closeReferenceSearchEditor', MONACO_CONVERSION_IDENTITY]; + mappings['cancelSelection'] = ['cancelSelection', MONACO_CONVERSION_IDENTITY]; + mappings['cursorBottom'] = ['cursorBottom', MONACO_CONVERSION_IDENTITY]; + mappings['cursorBottomSelect'] = ['cursorBottomSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorDown'] = ['cursorDown', MONACO_CONVERSION_IDENTITY]; + mappings['cursorDownSelect'] = ['cursorDownSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorEnd'] = ['cursorEnd', MONACO_CONVERSION_IDENTITY]; + mappings['cursorEndSelect'] = ['cursorEndSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorHome'] = ['cursorHome', MONACO_CONVERSION_IDENTITY]; + mappings['cursorHomeSelect'] = ['cursorHomeSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorLeft'] = ['cursorLeft', MONACO_CONVERSION_IDENTITY]; + mappings['cursorLeftSelect'] = ['cursorLeftSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorPageDown'] = ['cursorPageDown', MONACO_CONVERSION_IDENTITY]; + mappings['cursorPageDownSelect'] = ['cursorPageDownSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorPageUp'] = ['cursorPageUp', MONACO_CONVERSION_IDENTITY]; + mappings['cursorPageUpSelect'] = ['cursorPageUpSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorRight'] = ['cursorRight', MONACO_CONVERSION_IDENTITY]; + mappings['cursorRightSelect'] = ['cursorRightSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorTop'] = ['cursorTop', MONACO_CONVERSION_IDENTITY]; + mappings['cursorTopSelect'] = ['cursorTopSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorUp'] = ['cursorUp', MONACO_CONVERSION_IDENTITY]; + mappings['cursorUpSelect'] = ['cursorUpSelect', MONACO_CONVERSION_IDENTITY]; + mappings['deleteLeft'] = ['deleteLeft', MONACO_CONVERSION_IDENTITY]; + mappings['deleteRight'] = ['deleteRight', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.selectAll'] = ['editor.action.selectAll', MONACO_CONVERSION_IDENTITY]; + mappings['expandLineSelection'] = ['expandLineSelection', MONACO_CONVERSION_IDENTITY]; + mappings['outdent'] = ['outdent', MONACO_CONVERSION_IDENTITY]; + mappings['scrollLineDown'] = ['scrollLineDown', MONACO_CONVERSION_IDENTITY]; + mappings['scrollLineUp'] = ['scrollLineUp', MONACO_CONVERSION_IDENTITY]; + mappings['scrollPageDown'] = ['scrollPageDown', MONACO_CONVERSION_IDENTITY]; + mappings['scrollPageUp'] = ['scrollPageUp', MONACO_CONVERSION_IDENTITY]; + mappings['tab'] = ['tab', MONACO_CONVERSION_IDENTITY]; + mappings['removeSecondaryCursors'] = ['removeSecondaryCursors', MONACO_CONVERSION_IDENTITY]; + mappings['cursorWordEndRight'] = ['cursorWordEndRight', MONACO_CONVERSION_IDENTITY]; + mappings['cursorWordEndRightSelect'] = ['cursorWordEndRightSelect', MONACO_CONVERSION_IDENTITY]; + mappings['cursorWordStartLeft'] = ['cursorWordStartLeft', MONACO_CONVERSION_IDENTITY]; + mappings['cursorWordStartLeftSelect'] = ['cursorWordStartLeftSelect', MONACO_CONVERSION_IDENTITY]; + mappings['deleteWordLeft'] = ['deleteWordLeft', MONACO_CONVERSION_IDENTITY]; + mappings['deleteWordRight'] = ['deleteWordRight', MONACO_CONVERSION_IDENTITY]; + mappings['editor.cancelOperation'] = ['editor.cancelOperation', MONACO_CONVERSION_IDENTITY]; + mappings['editor.gotoNextSymbolFromResult'] = ['editor.gotoNextSymbolFromResult', MONACO_CONVERSION_IDENTITY]; + mappings['editor.gotoNextSymbolFromResult.cancel'] = ['editor.gotoNextSymbolFromResult.cancel', MONACO_CONVERSION_IDENTITY]; + mappings['openReferenceToSide'] = ['openReferenceToSide', MONACO_CONVERSION_IDENTITY]; + mappings['toggleExplainMode'] = ['toggleExplainMode', MONACO_CONVERSION_IDENTITY]; + mappings['closeFindWidget'] = ['closeFindWidget', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.replaceAll'] = ['editor.action.replaceAll', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.replaceOne'] = ['editor.action.replaceOne', MONACO_CONVERSION_IDENTITY]; + mappings['editor.action.selectAllMatches'] = ['editor.action.selectAllMatches', MONACO_CONVERSION_IDENTITY]; + mappings['toggleFindCaseSensitive'] = ['toggleFindCaseSensitive', MONACO_CONVERSION_IDENTITY]; + mappings['toggleFindInSelection'] = ['toggleFindInSelection', MONACO_CONVERSION_IDENTITY]; + mappings['toggleFindRegex'] = ['toggleFindRegex', MONACO_CONVERSION_IDENTITY]; + mappings['toggleFindWholeWord'] = ['toggleFindWholeWord', MONACO_CONVERSION_IDENTITY]; + mappings['jumpToNextSnippetPlaceholder'] = ['jumpToNextSnippetPlaceholder', MONACO_CONVERSION_IDENTITY]; + mappings['jumpToPrevSnippetPlaceholder'] = ['jumpToPrevSnippetPlaceholder', MONACO_CONVERSION_IDENTITY]; + mappings['leaveEditorMessage'] = ['leaveEditorMessage', MONACO_CONVERSION_IDENTITY]; + mappings['leaveSnippet'] = ['leaveSnippet', MONACO_CONVERSION_IDENTITY]; + mappings['closeMarkersNavigation'] = ['closeMarkersNavigation', MONACO_CONVERSION_IDENTITY]; + mappings['goToNextReferenceFromEmbeddedEditor'] = ['goToNextReferenceFromEmbeddedEditor', MONACO_CONVERSION_IDENTITY]; + mappings['goToPreviousReferenceFromEmbeddedEditor'] = ['goToPreviousReferenceFromEmbeddedEditor', MONACO_CONVERSION_IDENTITY]; + mappings['closeParameterHints'] = ['closeParameterHints', MONACO_CONVERSION_IDENTITY]; + mappings['showNextParameterHint'] = ['showNextParameterHint', MONACO_CONVERSION_IDENTITY]; + mappings['showPrevParameterHint'] = ['showPrevParameterHint', MONACO_CONVERSION_IDENTITY]; + mappings['acceptSelectedSuggestion'] = ['acceptSelectedSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['acceptSelectedSuggestionOnEnter'] = ['acceptSelectedSuggestionOnEnter', MONACO_CONVERSION_IDENTITY]; + mappings['hideSuggestWidget'] = ['hideSuggestWidget', MONACO_CONVERSION_IDENTITY]; + mappings['insertBestCompletion'] = ['insertBestCompletion', MONACO_CONVERSION_IDENTITY]; + mappings['insertNextSuggestion'] = ['insertNextSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['insertPrevSuggestion'] = ['insertPrevSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['selectNextPageSuggestion'] = ['selectNextPageSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['selectNextSuggestion'] = ['selectNextSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['selectPrevPageSuggestion'] = ['selectPrevPageSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['selectPrevSuggestion'] = ['selectPrevSuggestion', MONACO_CONVERSION_IDENTITY]; + mappings['toggleSuggestionDetails'] = ['toggleSuggestionDetails', MONACO_CONVERSION_IDENTITY]; + mappings['toggleSuggestionFocus'] = ['toggleSuggestionFocus', MONACO_CONVERSION_IDENTITY]; + mappings['acceptRenameInput'] = ['acceptRenameInput', MONACO_CONVERSION_IDENTITY]; + mappings['cancelRenameInput'] = ['cancelRenameInput', MONACO_CONVERSION_IDENTITY]; + mappings['closeAccessibilityHelp'] = ['closeAccessibilityHelp', MONACO_CONVERSION_IDENTITY]; + mappings['history.showNext'] = ['history.showNext', MONACO_CONVERSION_IDENTITY]; + mappings['history.showPrevious'] = ['history.showPrevious', MONACO_CONVERSION_IDENTITY]; + mappings['closeReferenceSearch'] = ['closeReferenceSearch', MONACO_CONVERSION_IDENTITY]; + mappings['goToNextReference'] = ['goToNextReference', MONACO_CONVERSION_IDENTITY]; + mappings['goToPreviousReference'] = ['goToPreviousReference', MONACO_CONVERSION_IDENTITY]; + + // tslint:disable-next-line:no-any + export function map(id: string, args: any[] | undefined, toDo: (mappedId: string, mappedArgs: any[] | undefined) => T): T { + if (mappings[id]) { + return toDo(mappings[id][0], mappings[id][1](args)); + } else { + return toDo(id, args); + } + } + + export function mapped(id: string): boolean { + return !!mappings[id]; + } + + // tslint:disable-next-line:no-any + type ConversionFunction = ((parameter: any) => any) | undefined; + // tslint:disable-next-line:no-any + function createConversionFunction(...conversions: ConversionFunction[]): (args: any[] | undefined) => any[] | undefined { + // tslint:disable-next-line:no-any + return function (args: any[] | undefined): any[] | undefined { + if (!args) { + return args; + } + // tslint:disable-next-line:no-any + return args.map(function (arg: any, index: number): any { + if (index < conversions.length) { + const conversion = conversions[index]; + if (conversion) { + return conversion(arg); + } + } + return arg; + }); + }; + } + + function fromPositionToP(p: theia.Position): P { + return P.create(p.line, p.character); + } + + function fromRangeToR(r: theia.Range): R { + return R.create(fromPositionToP(r.start), fromPositionToP(r.end)); + } + + function fromLocationToL(l: theia.Location): L { + return L.create(l.uri.toString(), fromRangeToR(l.range)); + } + + // tslint:disable-next-line:no-any typedef + function monacoArgsConverter(args: any[]) { + // tslint:disable-next-line:typedef + return cloneAndChange(args, function (value) { + if (Position.isPosition(value)) { + return fromPosition(value); + } + if (Range.isRange(value)) { + return fromRange(value); + } + if (Location.isLocation(value)) { + return fromLocation(value); + } + if (!Array.isArray(value)) { + return value; + } + }); + } + +} + +function toArrayConversion(f: (a: T) => U): (a: T[]) => U[] { + // tslint:disable-next-line:typedef + return function (a: T[]) { + return a.map(f); + }; +} diff --git a/packages/plugin-ext/src/common/objects.ts b/packages/plugin-ext/src/common/objects.ts new file mode 100644 index 0000000000000..958f6ec8365e6 --- /dev/null +++ b/packages/plugin-ext/src/common/objects.ts @@ -0,0 +1,50 @@ +// tslint:disable +// copied from https://github.com/microsoft/vscode/blob/1.37.0/src/vs/base/common/objects.ts +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { isUndefinedOrNull, isArray, isObject } from './types'; + +const _hasOwnProperty = Object.prototype.hasOwnProperty; + +export function cloneAndChange(obj: any, changer: (orig: any) => any): any { + return _cloneAndChange(obj, changer, new Set()); +} + +function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set): any { + if (isUndefinedOrNull(obj)) { + return obj; + } + + const changed = changer(obj); + if (typeof changed !== 'undefined') { + return changed; + } + + if (isArray(obj)) { + const r1: any[] = []; + for (const e of obj) { + r1.push(_cloneAndChange(e, changer, seen)); + } + return r1; + } + + if (isObject(obj)) { + if (seen.has(obj)) { + throw new Error('Cannot clone recursive data-structure'); + } + seen.add(obj); + const r2 = {}; + for (let i2 in obj) { + if (_hasOwnProperty.call(obj, i2)) { + (r2 as any)[i2] = _cloneAndChange(obj[i2], changer, seen); + } + } + seen.delete(obj); + return r2; + } + + return obj; +} diff --git a/packages/plugin-ext/src/common/types.ts b/packages/plugin-ext/src/common/types.ts index d902cfaad592c..74fc2e3f6784b 100644 --- a/packages/plugin-ext/src/common/types.ts +++ b/packages/plugin-ext/src/common/types.ts @@ -13,6 +13,11 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +// copied from https://github.com/microsoft/vscode/blob/1.37.0/src/vs/base/common/types.ts +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ /** * Returns `true` if the parameter has type "object" and not null, an array, a regexp, a date. @@ -75,3 +80,39 @@ export function es5ClassCompat(target: T): T { return _ as unknown as T; } // tslint:enable:no-any +const _typeof = { + number: 'number', + string: 'string', + undefined: 'undefined', + object: 'object', + function: 'function' +}; +// tslint:disable:no-any +/** + * @returns whether the provided parameter is a JavaScript Array or not. + */ +export function isArray(array: any): array is any[] { + if (Array.isArray) { + return Array.isArray(array); + } + + if (array && typeof (array.length) === _typeof.number && array.constructor === Array) { + return true; + } + + return false; +} + +/** + * @returns whether the provided parameter is undefined. + */ +export function isUndefined(obj: any): obj is undefined { + return typeof (obj) === _typeof.undefined; +} + +/** + * @returns whether the provided parameter is undefined or null. + */ +export function isUndefinedOrNull(obj: any): obj is undefined | null { + return isUndefined(obj) || obj === null; +} diff --git a/packages/plugin-ext/src/plugin/command-registry.ts b/packages/plugin-ext/src/plugin/command-registry.ts index 60b801dbe05f5..75c88503b66df 100644 --- a/packages/plugin-ext/src/plugin/command-registry.ts +++ b/packages/plugin-ext/src/plugin/command-registry.ts @@ -23,8 +23,8 @@ import * as model from '../common/plugin-api-rpc-model'; import { CommandRegistryExt, PLUGIN_RPC_CONTEXT as Ext, CommandRegistryMain } from '../common/plugin-api-rpc'; import { RPCProtocol } from '../common/rpc-protocol'; import { Disposable } from './types-impl'; -import { KnownCommands } from './type-converters'; import { DisposableCollection } from '@theia/core'; +import { KnownCommands } from '../common/known-commands'; // tslint:disable-next-line:no-any export type Handler = (...args: any[]) => T | PromiseLike; @@ -102,9 +102,12 @@ export class CommandRegistryImpl implements CommandRegistryExt { executeCommand(id: string, ...args: any[]): PromiseLike { if (this.handlers.has(id)) { return this.executeLocalCommand(id, ...args); - } else { + } else if (KnownCommands.mapped(id)) { + // Using the KnownCommand exclusions, convert the commands manually return KnownCommands.map(id, args, (mappedId: string, mappedArgs: any[] | undefined) => this.proxy.$executeCommand(mappedId, ...mappedArgs)); + } else { + return this.proxy.$executeCommand(id, args); } } // tslint:enable:no-any diff --git a/packages/plugin-ext/src/plugin/known-commands.spec.ts b/packages/plugin-ext/src/plugin/known-commands.spec.ts new file mode 100644 index 0000000000000..569fc4f289c05 --- /dev/null +++ b/packages/plugin-ext/src/plugin/known-commands.spec.ts @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (C) 2019 Red Hat, Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import * as assert from 'assert'; +import { KnownCommands } from '../common/known-commands'; +import URI from 'vscode-uri'; +import { Position } from './types-impl'; +import { fromPosition } from './type-converters'; + +describe('Known Command Conversions', () => { + + it('Should convert position correctly', () => { + + // given + const commandID = 'editor.action.rename'; + const uri = URI.parse('file://my_theia_location'); + const line = 61; + const character = 22; + const position = new Position(line, character); // vscode type position + + assert.ok(KnownCommands.mapped(commandID)); + + // when + // tslint:disable-next-line:no-any + KnownCommands.map(commandID, [uri, position], (mappedID: string, mappedArgs: any[]) => { + + // then + assert.strictEqual(commandID, mappedID); + assert.strictEqual(mappedArgs.length, 2); + assert.deepStrictEqual(uri, mappedArgs[0]); + + const expectedMonacoPosition = fromPosition(position); + assert.deepStrictEqual(expectedMonacoPosition, mappedArgs[1]); + }); + + }); + +}); diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 3034c5654e722..13b40b56e0572 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -37,7 +37,7 @@ import URI from 'vscode-uri'; const SIDE_GROUP = -2; const ACTIVE_GROUP = -1; -import { SymbolInformation, Range as R, Position as P, SymbolKind as S, Location as L } from 'vscode-languageserver-types'; +import { SymbolInformation, Range as R, Position as P, SymbolKind as S } from 'vscode-languageserver-types'; import { Item } from './quick-open'; export function toViewColumn(ep?: EditorPosition): theia.ViewColumn | undefined { @@ -507,64 +507,6 @@ export namespace SignatureHelp { } } -export namespace KnownCommands { - // tslint:disable: no-any - const mappings: { [id: string]: [string, (args: any[] | undefined) => any[] | undefined] } = {}; - mappings['editor.action.showReferences'] = ['textEditor.commands.showReferences', createConversionFunction( - (uri: URI) => uri.toString(), - fromPositionToP, - toArrayConversion(fromLocationToL))]; - - export function mapped(id: string): boolean { - return !!mappings[id]; - } - - export function map(id: string, args: any[] | undefined, toDo: (mappedId: string, mappedArgs: any[] | undefined) => T): T { - if (mappings[id]) { - return toDo(mappings[id][0], mappings[id][1](args)); - } else { - return toDo(id, args); - } - } - - type conversionFunction = ((parameter: any) => any) | undefined; - function createConversionFunction(...conversions: conversionFunction[]): (args: any[] | undefined) => any[] | undefined { - return function (args: any[] | undefined): any[] | undefined { - if (!args) { - return args; - } - return args.map(function (arg: any, index: number): any { - if (index < conversions.length) { - const conversion = conversions[index]; - if (conversion) { - return conversion(arg); - } - } - return arg; - }); - }; - } - // tslint:enable: no-any - function fromPositionToP(p: theia.Position): P { - return P.create(p.line, p.character); - } - - function fromRangeToR(r: theia.Range): R { - return R.create(fromPositionToP(r.start), fromPositionToP(r.end)); - } - - function fromLocationToL(l: theia.Location): L { - return L.create(l.uri.toString(), fromRangeToR(l.range)); - } - -} - -function toArrayConversion(f: (a: T) => U): (a: T[]) => U[] { - return function (a: T[]): U[] { - return a.map(f); - }; -} - // tslint:disable-next-line:no-any export function fromWorkspaceEdit(value: theia.WorkspaceEdit, documents?: any): WorkspaceEditDto { const result: WorkspaceEditDto = { diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 83f564b11b784..f2a730f123965 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -13,6 +13,11 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +// copied from https://github.com/microsoft/vscode/blob/1.37.0/src/vs/workbench/api/common/extHostTypes.ts +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ import { UUID } from '@phosphor/coreutils/lib/uuid'; import { illegalArgument } from '../common/errors'; @@ -785,6 +790,17 @@ export class Location { this.range = new Range(rangeOrPosition, rangeOrPosition); } } + + static isLocation(thing: {}): thing is theia.Location { + if (thing instanceof Location) { + return true; + } + if (!thing) { + return false; + } + return Range.isRange((thing).range) + && URI.isUri((thing).uri); + } } export enum DiagnosticTag {