mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Merge pull request #66458 from usernamehw/json_if_validation
Autocomplete/validation `args` for keybindings.json
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -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']
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -519,7 +519,27 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
|
||||
* '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<FoldingArguments> {
|
||||
* '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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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<ICommandService>('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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
},
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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<IJSONContributionRegistry>(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<IConfigurationRegistry>(ConfigExtensions.Configuration);
|
||||
const keyboardConfiguration: IConfigurationNode = {
|
||||
'id': 'keyboard',
|
||||
|
||||
Reference in New Issue
Block a user