From 13070015bddbca61ad566d1abb8aa3ea95edf9cb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 11:52:11 +0100 Subject: [PATCH] expose context key info command, add first version of completion item provider for package.json and keybindings.json files, https://github.com/microsoft/vscode/issues/9303 --- .../src/configurationEditingMain.ts | 40 +++++++++++++++++++ .../contextkey/browser/contextKeyService.ts | 12 ++++++ .../platform/contextkey/common/contextkey.ts | 4 +- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index a3ef34f3d83..f4c2ab34174 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -22,6 +22,9 @@ export function activate(context: vscode.ExtensionContext): void { // task.json variable suggestions context.subscriptions.push(registerVariableCompletions('**/tasks.json')); + + // keybindings.json/package.json context key suggestions + context.subscriptions.push(registerContextKeyCompletions()); } function registerSettingsCompletions(): vscode.Disposable { @@ -136,3 +139,40 @@ vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', lan return result; } }, { label: 'Launch Targets' }); + +function registerContextKeyCompletions(): vscode.Disposable { + type ContextKeyInfo = { key: string, type?: string, description?: string }; + return vscode.languages.registerCompletionItemProvider( + [{ language: 'json', pattern: '**/package.json' }, { language: 'jsonc', pattern: '**/keybindings.json' }], + { + async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) { + + const replacing = document.getWordRangeAtPosition(position, /[\w.-\d]/); + const inserting = replacing?.with(undefined, position); + if (!replacing || !inserting) { + return; + } + + const location = getLocation(document.getText(), document.offsetAt(position)); + if (!location.matches(['*', 'when']) && !location.matches(['contributes', 'menus', '*', '*', 'when'])) { + return; + } + + const data = await vscode.commands.executeCommand('getContextKeyInfo'); + if (token.isCancellationRequested || !data) { + return; + } + + const result = new vscode.CompletionList(); + for (const item of data) { + const completion = new vscode.CompletionItem(item.key, vscode.CompletionItemKind.Constant); + completion.detail = item.type; + completion.range = { replacing, inserting }; + completion.documentation = item.description; + result.items.push(completion); + } + return result; + } + } + ); +} diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 00e2bde4875..e40016315b3 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -8,6 +8,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { distinct } from 'vs/base/common/objects'; +import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression, RawContextKey, ContextKeyInfo } from 'vs/platform/contextkey/common/contextkey'; @@ -578,6 +579,17 @@ CommandsRegistry.registerCommand(SET_CONTEXT_COMMAND_ID, function (accessor, con accessor.get(IContextKeyService).createKey(String(contextKey), contextValue); }); +CommandsRegistry.registerCommand({ + id: 'getContextKeyInfo', + handler() { + return [...RawContextKey.all()].sort((a, b) => a.key.localeCompare(b.key)); + }, + description: { + description: localize('getContextKeyInfo', "A command that returns information about context keys"), + args: [] + } +}); + CommandsRegistry.registerCommand('_generateContextKeyInfo', function () { const result: ContextKeyInfo[] = []; const seen = new Set(); diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 6ecb8a5dc76..af2326a7565 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -1259,7 +1259,7 @@ export class ContextKeyOrExpr implements IContextKeyExpression { export interface ContextKeyInfo { readonly key: string; - readonly type: string; + readonly type?: string; readonly description?: string; } @@ -1281,7 +1281,7 @@ export class RawContextKey extends ContextKeyDefinedExpr { if (typeof metaOrHide === 'object') { RawContextKey._info.push({ ...metaOrHide, key }); } else if (metaOrHide !== true) { - RawContextKey._info.push({ key, description: metaOrHide, type: typeof defaultValue }); + RawContextKey._info.push({ key, description: metaOrHide, type: defaultValue !== null && defaultValue !== undefined ? typeof defaultValue : undefined }); } }