diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 4578e1e64f8..b6629ad43a7 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -86,7 +86,28 @@ export namespace EditorScroll_ { * 'value': Number of units to move. Default is '1'. * 'revealCursor': If 'true' reveals the cursor if it is outside view port. `, - constraint: isEditorScrollArgs + constraint: isEditorScrollArgs, + schema: { + 'type': 'object', + 'required': ['to'], + 'properties': { + 'to': { + 'type': 'string', + 'enum': ['up', 'down'] + }, + 'by': { + 'type': 'string', + 'enum': ['line', 'wrappedLine', 'page', 'halfPage'] + }, + 'value': { + 'type': 'number', + 'default': 1 + }, + 'revealCursor': { + 'type': 'boolean', + } + } + } } ] }; @@ -217,7 +238,20 @@ export namespace RevealLine_ { 'top', 'center', 'bottom' \`\`\` `, - constraint: isRevealLineArgs + constraint: isRevealLineArgs, + schema: { + 'type': 'object', + 'required': ['lineNumber'], + 'properties': { + 'lineNumber': { + 'type': 'number', + }, + 'at': { + 'type': 'string', + 'enum': ['top', 'center', 'bottom'] + } + } + } } ] }; @@ -1691,10 +1725,11 @@ class EditorHandlerCommand extends Command { private readonly _handlerId: string; - constructor(id: string, handlerId: string) { + constructor(id: string, handlerId: string, description?: ICommandHandlerDescription) { super({ id: id, - precondition: null + precondition: null, + description: description }); this._handlerId = handlerId; } @@ -1767,12 +1802,26 @@ registerCommand(new EditorOrNativeTextInputCommand({ })); registerCommand(new EditorHandlerCommand('default:' + Handler.Redo, Handler.Redo)); -function registerOverwritableCommand(handlerId: string): void { +function registerOverwritableCommand(handlerId: string, description?: ICommandHandlerDescription): void { registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId)); - registerCommand(new EditorHandlerCommand(handlerId, handlerId)); + registerCommand(new EditorHandlerCommand(handlerId, handlerId, description)); } -registerOverwritableCommand(Handler.Type); +registerOverwritableCommand(Handler.Type, { + description: `Type`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['text'], + 'properties': { + 'text': { + 'type': 'string' + } + }, + } + }] +}); registerOverwritableCommand(Handler.ReplacePreviousChar); registerOverwritableCommand(Handler.CompositionStart); registerOverwritableCommand(Handler.CompositionEnd); diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 493611967cd..938d59028a4 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -616,7 +616,29 @@ export namespace CursorMove { * 'value': Number of units to move. Default is '1'. * 'select': If 'true' makes the selection. Default is 'false'. `, - constraint: isCursorMoveArgs + constraint: isCursorMoveArgs, + schema: { + 'type': 'object', + 'required': ['to'], + 'properties': { + 'to': { + 'type': 'string', + 'enum': ['left', 'right', 'up', 'down', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'] + }, + 'by': { + 'type': 'string', + 'enum': ['line', 'wrappedLine', 'character', 'halfLine'] + }, + 'value': { + 'type': 'number', + 'default': 1 + }, + 'select': { + 'type': 'boolean', + 'default': false + } + } + } } ] }; diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 7777aded9f6..76e5dd2404f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -253,7 +253,27 @@ export class CodeActionCommand extends EditorCommand { constructor() { super({ id: CodeActionCommand.Id, - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider) + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), + description: { + description: `Trigger a code action`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['kind'], + 'properties': { + 'kind': { + 'type': 'string' + }, + 'apply': { + 'type': 'string', + 'default': 'ifSingle', + 'enum': ['first', 'ifSingle', 'never'] + } + } + } + }] + } }); } @@ -297,6 +317,25 @@ export class RefactorAction extends EditorAction { when: ContextKeyExpr.and( EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.Refactor)), + }, + description: { + description: 'Refactor...', + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'properties': { + 'kind': { + 'type': 'string' + }, + 'apply': { + 'type': 'string', + 'default': 'never', + 'enum': ['first', 'ifSingle', 'never'] + } + } + } + }] } }); } @@ -333,6 +372,25 @@ export class SourceAction extends EditorAction { when: ContextKeyExpr.and( EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.Source)), + }, + description: { + description: 'Source Action...', + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'properties': { + 'kind': { + 'type': 'string' + }, + 'apply': { + 'type': 'string', + 'default': 'never', + 'enum': ['first', 'ifSingle', 'never'] + } + } + } + }] } }); } diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index bf6adf0aced..d85f6bfbc75 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -519,7 +519,27 @@ class UnfoldAction extends FoldingAction { * 'direction': If 'up', unfold given number of levels up otherwise unfolds down. * 'selectionLines': The start lines (0-based) of the editor selections to apply the unfold action to. If not set, the active selection(s) will be used. `, - constraint: foldingArgumentsConstraint + constraint: foldingArgumentsConstraint, + schema: { + 'type': 'object', + 'properties': { + 'levels': { + 'type': 'number', + 'default': 1 + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'selectionLines': { + 'type': 'array', + 'items': { + 'type': 'number' + } + } + } + } } ] } @@ -584,7 +604,27 @@ class FoldAction extends FoldingAction { * 'direction': If 'up', folds given number of levels up otherwise folds down. * 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used. `, - constraint: foldingArgumentsConstraint + constraint: foldingArgumentsConstraint, + schema: { + 'type': 'object', + 'properties': { + 'levels': { + 'type': 'number', + 'default': 1 + }, + 'direction': { + 'type': 'string', + 'enum': ['up', 'down'], + 'default': 'down' + }, + 'selectionLines': { + 'type': 'array', + 'items': { + 'type': 'number' + } + } + } + } } ] } diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 16c3a764a02..91541847a17 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -8,6 +8,7 @@ import { TypeConstraint, validateConstraints } from 'vs/base/common/types'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { LinkedList } from 'vs/base/common/linkedList'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; export const ICommandService = createDecorator('commandService'); @@ -37,7 +38,7 @@ export interface ICommand { export interface ICommandHandlerDescription { description: string; - args: { name: string; description?: string; constraint?: TypeConstraint; }[]; + args: { name: string; description?: string; constraint?: TypeConstraint; schema?: IJSONSchema; }[]; returns?: string; } diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index ae2ee5b4c0e..01d1c77337c 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -65,7 +65,24 @@ export class OpenFolderAPICommand { return executor.executeCommand('_files.windowOpen', { urisToOpen: [{ uri }], forceNewWindow }); } } -CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute)); +CommandsRegistry.registerCommand({ + id: OpenFolderAPICommand.ID, + handler: adjustHandler(OpenFolderAPICommand.execute), + description: { + description: `Open a folder`, + args: [{ + name: 'uri', + schema: { + 'type': 'string' + } + }, { + name: 'forceNewWindow', + schema: { + 'type': 'boolean' + } + }] + } +}); export class DiffAPICommand { public static ID = 'vscode.diff'; @@ -126,7 +143,31 @@ export class SetEditorLayoutAPICommand { return executor.executeCommand('layoutEditorGroups', layout); } } -CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute)); +CommandsRegistry.registerCommand({ + id: SetEditorLayoutAPICommand.ID, + handler: adjustHandler(SetEditorLayoutAPICommand.execute), + description: { + description: 'Set Editor Layout', + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['groups'], + 'properties': { + 'orientation': { + 'type': 'number', + 'default': 0, + 'enum': [0, 1] + }, + 'groups': { + '$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ... + 'default': [{}, {}], + } + } + } + }] + } +}); CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) { const downloadService = accessor.get(IDownloadService); diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index ee3fb0e32ce..25e420329d5 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -90,7 +90,24 @@ function registerActiveEditorMoveCommand(): void { { name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"), description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."), - constraint: isActiveEditorMoveArg + constraint: isActiveEditorMoveArg, + schema: { + 'type': 'object', + 'required': ['to'], + 'properties': { + 'to': { + 'type': 'string', + 'enum': ['left', 'right'] + }, + 'by': { + 'type': 'string', + 'enum': ['tab', 'group'] + }, + 'value': { + 'type': 'number' + } + }, + } } ] } diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.ts b/src/vs/workbench/browser/parts/quickopen/quickopen.ts index 1c00cf57074..2a176b5726f 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickopen.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickopen.ts @@ -19,12 +19,24 @@ export const defaultQuickOpenContext = ContextKeyExpr.and(inQuickOpenContext, Co export const QUICKOPEN_ACTION_ID = 'workbench.action.quickOpen'; export const QUICKOPEN_ACION_LABEL = nls.localize('quickOpen', "Go to File..."); -CommandsRegistry.registerCommand(QUICKOPEN_ACTION_ID, function (accessor: ServicesAccessor, prefix: string | null = null) { - const quickOpenService = accessor.get(IQuickOpenService); +CommandsRegistry.registerCommand({ + id: QUICKOPEN_ACTION_ID, + handler: function (accessor: ServicesAccessor, prefix: string | null = null) { + const quickOpenService = accessor.get(IQuickOpenService); - return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => { - return undefined; - }); + return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => { + return undefined; + }); + }, + description: { + description: `Quick open`, + args: [{ + name: 'prefix', + schema: { + 'type': 'string' + } + }] + } }); export const QUICKOPEN_FOCUS_SECONDARY_ACTION_ID = 'workbench.action.quickOpenPreviousEditor'; diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index dc796e6acb7..bcf3f7a202f 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -54,7 +54,28 @@ class InsertSnippetAction extends EditorAction { id: 'editor.action.insertSnippet', label: nls.localize('snippet.suggestions.label', "Insert Snippet"), alias: 'Insert Snippet', - precondition: EditorContextKeys.writable + precondition: EditorContextKeys.writable, + description: { + description: `Insert Snippet`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'properties': { + 'snippet': { + 'type': 'string' + }, + 'langId': { + 'type': 'string', + + }, + 'name': { + 'type': 'string' + } + }, + } + }] + } }); } diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts index 25ea7c74e91..00c92544fb7 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts @@ -549,8 +549,20 @@ class TaskService extends Disposable implements ITaskService { } private registerCommands(): void { - CommandsRegistry.registerCommand('workbench.action.tasks.runTask', (accessor, arg) => { - this.runTaskCommand(arg); + CommandsRegistry.registerCommand({ + id: 'workbench.action.tasks.runTask', + handler: (accessor, arg) => { + this.runTaskCommand(arg); + }, + description: { + description: 'Run Task', + args: [{ + name: 'args', + schema: { + 'type': 'string', + } + }] + } }); CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', (accessor, arg) => { diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index 1f3cf1d3b57..b15ee85b95b 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -514,7 +514,22 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ id: SendSequenceTerminalCommand.ID, - precondition: null + precondition: null, + description: { + description: `Send Custom Sequence To Terminal`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['text'], + 'properties': { + 'text': { + 'type': 'string' + } + }, + } + }] + } }); sendSequenceTerminalCommand.register(); diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 817b087ec8e..6f7e51f4d98 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -15,7 +15,7 @@ import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { ConfigWatcher } from 'vs/base/node/config'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -280,6 +280,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { ) { super(contextKeyService, commandService, telemetryService, notificationService, statusBarService); + updateSchema(); + let dispatchConfig = getDispatchConfig(configurationService); configurationService.onDidChangeConfiguration((e) => { let newDispatchConfig = getDispatchConfig(configurationService); @@ -573,6 +575,24 @@ let schema: IJSONSchema = { 'id': schemaId, 'type': 'array', 'title': nls.localize('keybindings.json.title', "Keybindings configuration"), + 'definitions': { + 'editorGroupsSchema': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'groups': { + '$ref': '#/definitions/editorGroupsSchema', + 'default': [{}, {}] + }, + 'size': { + 'type': 'number', + 'default': 0.5 + } + } + } + } + }, 'items': { 'required': ['key'], 'type': 'object', @@ -593,13 +613,40 @@ let schema: IJSONSchema = { 'args': { 'description': nls.localize('keybindings.json.args', "Arguments to pass to the command to execute.") } - } + }, + 'allOf': [] } }; let schemaRegistry = Registry.as(Extensions.JSONContribution); schemaRegistry.registerSchema(schemaId, schema); +function updateSchema() { + const allCommands = CommandsRegistry.getCommands(); + for (let commandId in allCommands) { + const commandDescription = allCommands[commandId].description; + if (!commandDescription || !commandDescription.args || commandDescription.args.length !== 1 || !commandDescription.args[0].schema) { + continue; + } + + const argsSchema = commandDescription.args[0].schema; + const addition = { + 'if': { + 'properties': { + 'command': { 'const': commandId } + } + }, + 'then': { + 'properties': { + 'args': argsSchema + } + } + }; + + schema['items']['allOf'].push(addition); + } +} + const configurationRegistry = Registry.as(ConfigExtensions.Configuration); const keyboardConfiguration: IConfigurationNode = { 'id': 'keyboard',