提交 0ebecd5c 编写于 作者: A Alex Dima

Merge branch 'master' into alex/keybinding

......@@ -357,3 +357,110 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
// Default Filter
return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst);
}
export function matchesFuzzy2(pattern: string, word: string): number[] {
pattern = pattern.toLowerCase();
word = word.toLowerCase();
let matches: number[] = [];
let patternPos = 0;
let wordPos = 0;
while (patternPos < pattern.length && wordPos < word.length) {
if (pattern[patternPos] === word[wordPos]) {
patternPos += 1;
matches.push(wordPos);
}
wordPos += 1;
}
if (patternPos !== pattern.length) {
return undefined;
}
return matches;
}
export function createMatches(position: number[]): IMatch[] {
let ret: IMatch[] = [];
let last: IMatch;
for (const pos of position) {
if (last && last.end === pos) {
last.end += 1;
} else {
last = { start: pos, end: pos + 1 };
ret.push(last);
}
}
return ret;
}
export function fuzzyMatchAndScore(pattern: string, word: string): [number, number[]] {
if (!pattern) {
return [-1, []];
}
let matches: number[] = [];
let score = _matchRecursive(
pattern, pattern.toLowerCase(), pattern.toUpperCase(), 0,
word, word.toLowerCase(), 0,
matches
);
if (score <= 0) {
return undefined;
}
score -= Math.min(matches[0], 3) * 3; // penalty for first matching character
score -= (1 + matches[matches.length - 1]) - (pattern.length); // penalty for all non matching characters between first and last
return [score, matches];
}
export function _matchRecursive(
pattern: string, lowPattern: string, upPattern: string, patternPos: number,
word: string, lowWord: string, wordPos: number,
matches: number[]
): number {
if (patternPos >= lowPattern.length) {
return 0;
}
const lowPatternChar = lowPattern[patternPos];
let idx = -1;
let value = 0;
if ((patternPos === wordPos
&& lowPatternChar === lowWord[wordPos])
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, wordPos + 1, matches)) >= 0)
) {
matches.unshift(wordPos);
return (pattern[patternPos] === word[wordPos] ? 17 : 11) + value;
}
if ((idx = lowWord.indexOf(`_${lowPatternChar}`, wordPos)) >= 0
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 2, matches)) >= 0)
) {
matches.unshift(idx + 1);
return (pattern[patternPos] === word[idx + 1] ? 17 : 11) + value;
}
if ((idx = word.indexOf(upPattern[patternPos], wordPos)) >= 0
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0)
) {
matches.unshift(idx);
return (pattern[patternPos] === word[idx] ? 17 : 11) + value;
}
if (patternPos > 0
&& (idx = lowWord.indexOf(lowPatternChar, wordPos)) >= 0
&& ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0)
) {
matches.unshift(idx);
return 1 + value;
}
return -1;
}
因为 它太大了无法显示 source diff 。你可以改为 查看blob
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// import * as assert from 'assert';
import * as filters from 'vs/base/common/filters';
const data = <string[]>require.__$__nodeRequire(require.toUrl('./filters.perf.data.json'));
const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons'];
const _enablePerf = false;
function perfSuite(name: string, callback: (this: Mocha.ISuiteCallbackContext) => void) {
if (_enablePerf) {
suite(name, callback);
}
}
perfSuite('Performance - fuzzyMatch', function () {
console.log(`Matching ${data.length} items against ${patterns.length} patterns...`);
function perfTest(name: string, match: (pattern: string, word: string) => any) {
test(name, function () {
const t1 = Date.now();
let count = 0;
for (const pattern of patterns) {
for (const item of data) {
count += 1;
match(pattern, item);
}
}
console.log(name, Date.now() - t1, `${(count / (Date.now() - t1)).toPrecision(6)}/ms`);
});
}
perfTest('matchesFuzzy', filters.matchesFuzzy);
perfTest('fuzzyContiguousFilter', filters.fuzzyContiguousFilter);
perfTest('matchesFuzzy2', filters.matchesFuzzy2);
perfTest('fuzzyMatchAndScore', filters.fuzzyMatchAndScore);
});
......@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords } from 'vs/base/common/filters';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyMatchAndScore } from 'vs/base/common/filters';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst);
......@@ -192,4 +192,99 @@ suite('Filters', () => {
assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null);
assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]);
});
test('fuzzyMatchAndScore', function () {
function assertMatches(pattern: string, word: string, decoratedWord: string, filter: typeof fuzzyMatchAndScore) {
let r = filter(pattern, word);
assert.ok(Boolean(r) === Boolean(decoratedWord));
if (r) {
const [, matches] = r;
let pos = 0;
for (let i = 0; i < matches.length; i++) {
let actual = matches[i];
let expected = decoratedWord.indexOf('^', pos) - i;
assert.equal(actual, expected);
pos = expected + 1 + i;
}
}
}
assertMatches('no', 'match', undefined, fuzzyMatchAndScore);
assertMatches('no', '', undefined, fuzzyMatchAndScore);
assertMatches('BK', 'the_black_knight', 'the_^black_^knight', fuzzyMatchAndScore);
assertMatches('bkn', 'the_black_knight', 'the_^black_^k^night', fuzzyMatchAndScore);
assertMatches('bt', 'the_black_knight', 'the_^black_knigh^t', fuzzyMatchAndScore);
assertMatches('bti', 'the_black_knight', undefined, fuzzyMatchAndScore);
assertMatches('LLL', 'SVisualLoggerLogsList', 'SVisual^Logger^Logs^List', fuzzyMatchAndScore);
assertMatches('LLLL', 'SVisualLoggerLogsList', undefined, fuzzyMatchAndScore);
assertMatches('sllll', 'SVisualLoggerLogsList', '^SVisua^l^Logger^Logs^List', fuzzyMatchAndScore);
assertMatches('sl', 'SVisualLoggerLogsList', '^SVisual^LoggerLogsList', fuzzyMatchAndScore);
assertMatches('foobar', 'foobar', '^f^o^o^b^a^r', fuzzyMatchAndScore);
assertMatches('fob', 'foobar', '^f^oo^bar', fuzzyMatchAndScore);
assertMatches('ob', 'foobar', undefined, fuzzyMatchAndScore);
assertMatches('gp', 'Git: Pull', '^Git: ^Pull', fuzzyMatchAndScore);
assertMatches('gp', 'Git_Git_Pull', '^Git_Git_^Pull', fuzzyMatchAndScore);
assertMatches('g p', 'Git: Pull', '^Git:^ ^Pull', fuzzyMatchAndScore);
assertMatches('gip', 'Git: Pull', '^G^it: ^Pull', fuzzyMatchAndScore);
assertMatches('is', 'isValid', '^i^sValid', fuzzyMatchAndScore);
assertMatches('is', 'ImportStatement', '^Import^Statement', fuzzyMatchAndScore);
assertMatches('lowrd', 'lowWord', '^l^o^wWo^r^d', fuzzyMatchAndScore);
assertMatches('ccm', 'cacmelCase', '^ca^c^melCase', fuzzyMatchAndScore);
assertMatches('ccm', 'camelCase', undefined, fuzzyMatchAndScore);
assertMatches('ccm', 'camelCasecm', '^camel^Casec^m', fuzzyMatchAndScore);
assertMatches('myvable', 'myvariable', '^m^y^v^aria^b^l^e', fuzzyMatchAndScore);
assertMatches('fdm', 'findModel', '^fin^d^Model', fuzzyMatchAndScore);
});
test('topScore', function () {
function assertTopScore(pattern: string, expected: number, ...words: string[]) {
let topScore = Number.MIN_VALUE;
let topIdx = 0;
for (let i = 0; i < words.length; i++) {
const word = words[i];
const m = fuzzyMatchAndScore(pattern, word);
if (m) {
const [score] = m;
if (score > topScore) {
topScore = score;
topIdx = i;
}
}
}
assert.equal(topIdx, expected);
}
assertTopScore('cons', 2, 'ArrayBufferConstructor', 'Console', 'console');
assertTopScore('Foo', 1, 'foo', 'Foo', 'foo');
assertTopScore('CC', 1, 'camelCase', 'CamelCase');
assertTopScore('cC', 0, 'camelCase', 'CamelCase');
assertTopScore('cC', 1, 'ccfoo', 'camelCase');
assertTopScore('cC', 1, 'ccfoo', 'camelCase', 'foo-cC-bar');
// issue #17836
assertTopScore('p', 0, 'parse', 'posix', 'pafdsa', 'path', 'p');
assertTopScore('pa', 0, 'parse', 'pafdsa', 'path');
// issue #14583
assertTopScore('log', 3, 'HTMLOptGroupElement', 'ScrollLogicalPosition', 'SVGFEMorphologyElement', 'log');
assertTopScore('e', 2, 'AbstractWorker', 'ActiveXObject', 'else');
// issue #14446
assertTopScore('workbench.sideb', 1, 'workbench.editor.defaultSideBySideLayout', 'workbench.sideBar.location');
// issue #11423
assertTopScore('editor.r', 2, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore('editor.R', 1, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore('Editor.r', 0, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
assertTopScore('-mo', 1, '-ms-ime-mode', '-moz-columns');
// // dupe, issue #14861
assertTopScore('convertModelPosition', 0, 'convertModelPositionToViewPosition', 'convertViewToModelPosition');
// // dupe, issue #14942
assertTopScore('is', 0, 'isValidViewletId', 'import statement');
});
});
......@@ -387,16 +387,23 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
const item = items[i];
const when = (item.when ? item.when.normalize() : null);
const keybinding = item.keybinding;
const resolvedKeybinding = (keybinding ? this.resolveKeybinding(keybinding) : null);
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault);
if (!keybinding) {
// This might be a removal keybinding item in user settings => accept it
result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault);
} else {
const resolvedKeybindings = this.resolveKeybinding(keybinding);
for (let j = 0; j < resolvedKeybindings.length; j++) {
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault);
}
}
}
return result;
}
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding {
return new USLayoutResolvedKeybinding(kb, OS);
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {
return [new USLayoutResolvedKeybinding(kb, OS)];
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
......@@ -407,7 +414,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
keyboardEvent.metaKey,
keyboardEvent.keyCode
);
return this.resolveKeybinding(keybinding);
return this.resolveKeybinding(keybinding)[0];
}
}
......
......@@ -7,6 +7,7 @@
import 'vs/css!./suggest';
import * as nls from 'vs/nls';
import { createMatches } from 'vs/base/common/filters';
import * as strings from 'vs/base/common/strings';
import Event, { Emitter, chain } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
......@@ -136,7 +137,7 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
}
}
data.highlightedLabel.set(suggestion.label, element.highlights);
data.highlightedLabel.set(suggestion.label, createMatches(element.matches));
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
data.documentation.textContent = suggestion.documentation || '';
......@@ -234,7 +235,7 @@ class SuggestionDetails {
return;
}
this.titleLabel.set(item.suggestion.label, item.highlights);
this.titleLabel.set(item.suggestion.label, createMatches(item.matches));
this.type.innerText = item.suggestion.detail || '';
this.docs.textContent = item.suggestion.documentation;
this.back.onmousedown = e => {
......
......@@ -5,14 +5,13 @@
'use strict';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { indexOfIgnoreCase } from 'vs/base/common/strings';
import { IMatch, fuzzyContiguousFilter } from 'vs/base/common/filters';
import { fuzzyMatchAndScore } from 'vs/base/common/filters';
import { ISuggestSupport } from 'vs/editor/common/modes';
import { ISuggestionItem } from './suggest';
export interface ICompletionItem extends ISuggestionItem {
highlights?: IMatch[];
matches?: number[];
score?: number;
}
export interface ICompletionStats {
......@@ -100,17 +99,17 @@ export class CompletionModel {
private _createCachedState(): void {
this._filteredItems = [];
this._topScoreIdx = -1;
this._topScoreIdx = 0;
this._isIncomplete = false;
this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 };
const {leadingLineContent, characterCountDelta} = this._lineContext;
const { leadingLineContent, characterCountDelta } = this._lineContext;
let word = '';
let topScore = -1;
let topScore = Number.MIN_VALUE;
for (const item of this._items) {
const {suggestion, container} = item;
const { suggestion, container } = item;
// collect those supports that signaled having
// an incomplete result
......@@ -124,36 +123,32 @@ export class CompletionModel {
word = wordLen === 0 ? '' : leadingLineContent.slice(-wordLen);
}
let match = false;
// compute highlights based on 'label'
item.highlights = fuzzyContiguousFilter(word, suggestion.label);
match = item.highlights !== null;
// no match on label nor codeSnippet -> check on filterText
if (!match && typeof suggestion.filterText === 'string') {
if (!isFalsyOrEmpty(fuzzyContiguousFilter(word, suggestion.filterText))) {
match = true;
// try to compute highlights by stripping none-word
// characters from the end of the string
item.highlights = fuzzyContiguousFilter(word.replace(/^\W+|\W+$/, ''), suggestion.label);
let match = fuzzyMatchAndScore(word, suggestion.label);
if (!match) {
if (typeof suggestion.filterText === 'string') {
match = fuzzyMatchAndScore(word, suggestion.filterText);
} else {
continue;
}
if (match) {
match = fuzzyMatchAndScore(word.replace(/^\W+|\W+$/, ''), suggestion.label);
} else {
continue;
}
}
if (!match) {
continue;
if (match) {
item.score = match[0];
item.matches = match[1];
if (item.score > topScore) {
topScore = item.score;
this._topScoreIdx = this._filteredItems.length;
}
}
this._filteredItems.push(item);
// compute score against word
const score = CompletionModel._scoreByHighlight(item, word);
if (score > topScore) {
topScore = score;
this._topScoreIdx = this._filteredItems.length - 1;
}
// update stats
this._stats.suggestionCount++;
switch (suggestion.type) {
......@@ -162,63 +157,4 @@ export class CompletionModel {
}
}
}
private static _base = 100;
private static _scoreByHighlight(item: ICompletionItem, currentWord: string): number {
const {highlights, suggestion} = item;
if (isFalsyOrEmpty(highlights)) {
return 0;
}
let caseSensitiveMatches = 0;
let caseInsensitiveMatches = 0;
let firstMatchStart = 0;
const len = Math.min(CompletionModel._base, suggestion.label.length);
let currentWordOffset = 0;
for (let pos = 0, idx = 0; pos < len; pos++) {
const highlight = highlights[idx];
if (pos === highlight.start) {
// reached a highlight: find highlighted part
// and count case-sensitive /case-insensitive matches
const part = suggestion.label.substring(highlight.start, highlight.end);
currentWordOffset = indexOfIgnoreCase(currentWord, part, currentWordOffset);
if (currentWordOffset >= 0) {
do {
if (suggestion.label[pos] === currentWord[currentWordOffset]) {
caseSensitiveMatches += 1;
} else {
caseInsensitiveMatches += 1;
}
pos += 1;
currentWordOffset += 1;
} while (pos < highlight.end);
}
// proceed with next highlight, store first start,
// exit loop when no highlight is available
if (idx === 0) {
firstMatchStart = highlight.start;
}
idx += 1;
if (idx >= highlights.length) {
break;
}
}
}
// combine the 4 scoring values into one
// value using base_100. Values further left
// are more important
return (CompletionModel._base ** 3) * caseSensitiveMatches
+ (CompletionModel._base ** 2) * caseInsensitiveMatches
+ (CompletionModel._base ** 1) * (CompletionModel._base - firstMatchStart)
+ (CompletionModel._base ** 0) * (CompletionModel._base - highlights.length);
}
}
......@@ -93,7 +93,7 @@ suite('CompletionModel', function () {
const completeItem = createSuggestItem('foobar', 1, false, { lineNumber: 1, column: 2 });
const incompleteItem = createSuggestItem('foofoo', 1, true, { lineNumber: 1, column: 2 });
const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: 'foo', characterCountDelta: 0 });
const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: '', characterCountDelta: 0 });
assert.equal(model.incomplete, true);
assert.equal(model.items.length, 2);
......@@ -134,16 +134,16 @@ suite('CompletionModel', function () {
assertTopScore('pa', 0, 'parse', 'posix', 'sep', 'pafdsa', 'path', 'p');
// issue #14583
assertTopScore('log', 3, 'HTMLOptGroupElement', 'ScrollLogicalPosition', 'SVGFEMorphologyElement', 'log');
assertTopScore('e', 2, 'AbstractWorker', 'ActiveXObject', 'else');
assertTopScore('log', 2, 'HTMLOptGroupElement', 'ScrollLogicalPosition', 'log');
assertTopScore('e', 2, 'AbstractEorker', 'Activ_eXObject', 'else');
// issue #14446
assertTopScore('workbench.sideb', 1, 'workbench.editor.defaultSideBySideLayout', 'workbench.sideBar.location');
// issue #11423
assertTopScore('editor.r', 2, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
assertTopScore('editor.R', 1, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
assertTopScore('Editor.r', 0, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore('editor.R', 1, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore('Editor.r', 0, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
assertTopScore('-mo', 1, '-ms-ime-mode', '-moz-columns');
// dupe, issue #14861
......
......@@ -7,18 +7,19 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { IContextKey, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import Event, { Emitter, debounceEvent } from 'vs/base/common/event';
const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
export class ContextValuesContainer {
protected _parent: ContextValuesContainer;
export class Context implements IContext {
protected _parent: Context;
protected _value: { [key: string]: any; };
protected _id: number;
constructor(id: number, parent: ContextValuesContainer) {
constructor(id: number, parent: Context) {
this._id = id;
this._parent = parent;
this._value = Object.create(null);
......@@ -46,18 +47,9 @@ export class ContextValuesContainer {
}
return ret;
}
public fillInContext(bucket: any): void {
if (this._parent) {
this._parent.fillInContext(bucket);
}
for (let key in this._value) {
bucket[key] = this._value[key];
}
}
}
class ConfigAwareContextValuesContainer extends ContextValuesContainer {
class ConfigAwareContextValuesContainer extends Context {
private _emitter: Emitter<string>;
private _subscription: IDisposable;
......@@ -169,9 +161,8 @@ export abstract class AbstractContextKeyService {
}
public contextMatchesRules(rules: ContextKeyExpr): boolean {
const ctx = Object.create(null);
this.getContextValuesContainer(this._myContextId).fillInContext(ctx);
const result = KeybindingResolver.contextMatchesRules(ctx, rules);
const context = this.getContextValuesContainer(this._myContextId);
const result = KeybindingResolver.contextMatchesRules(context, rules);
// console.group(rules.serialize() + ' -> ' + result);
// rules.keys().forEach(key => { console.log(key, ctx[key]); });
// console.groupEnd();
......@@ -194,13 +185,11 @@ export abstract class AbstractContextKeyService {
}
}
public getContextValue(target: IContextKeyServiceTarget): any {
let res = Object.create(null);
this.getContextValuesContainer(findContextAttr(target)).fillInContext(res);
return res;
public getContext(target: IContextKeyServiceTarget): IContext {
return this.getContextValuesContainer(findContextAttr(target));
}
public abstract getContextValuesContainer(contextId: number): ContextValuesContainer;
public abstract getContextValuesContainer(contextId: number): Context;
public abstract createChildContext(parentContextId?: number): number;
public abstract disposeContext(contextId: number): void;
}
......@@ -209,7 +198,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
private _lastContextId: number;
private _contexts: {
[contextId: string]: ContextValuesContainer;
[contextId: string]: Context;
};
private _toDispose: IDisposable[] = [];
......@@ -239,13 +228,13 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
this._toDispose = dispose(this._toDispose);
}
public getContextValuesContainer(contextId: number): ContextValuesContainer {
public getContextValuesContainer(contextId: number): Context {
return this._contexts[String(contextId)];
}
public createChildContext(parentContextId: number = this._myContextId): number {
let id = (++this._lastContextId);
this._contexts[String(id)] = new ContextValuesContainer(id, this.getContextValuesContainer(parentContextId));
this._contexts[String(id)] = new Context(id, this.getContextValuesContainer(parentContextId));
return id;
}
......@@ -281,7 +270,7 @@ class ScopedContextKeyService extends AbstractContextKeyService {
return this._parent.onDidChangeContext;
}
public getContextValuesContainer(contextId: number): ContextValuesContainer {
public getContextValuesContainer(contextId: number): Context {
return this._parent.getContextValuesContainer(contextId);
}
......
......@@ -88,7 +88,7 @@ export abstract class ContextKeyExpr {
public abstract getType(): ContextKeyExprType;
public abstract equals(other: ContextKeyExpr): boolean;
public abstract evaluate(context: any): boolean;
public abstract evaluate(context: IContext): boolean;
public abstract normalize(): ContextKeyExpr;
public abstract serialize(): string;
public abstract keys(): string[];
......@@ -139,8 +139,8 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr {
return false;
}
public evaluate(context: any): boolean {
return (!!context[this.key]);
public evaluate(context: IContext): boolean {
return (!!context.getValue(this.key));
}
public normalize(): ContextKeyExpr {
......@@ -187,10 +187,10 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr {
return false;
}
public evaluate(context: any): boolean {
public evaluate(context: IContext): boolean {
/* tslint:disable:triple-equals */
// Intentional ==
return (context[this.key] == this.value);
return (context.getValue(this.key) == this.value);
/* tslint:enable:triple-equals */
}
......@@ -248,10 +248,10 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr {
return false;
}
public evaluate(context: any): boolean {
public evaluate(context: IContext): boolean {
/* tslint:disable:triple-equals */
// Intentional !=
return (context[this.key] != this.value);
return (context.getValue(this.key) != this.value);
/* tslint:enable:triple-equals */
}
......@@ -303,8 +303,8 @@ export class ContextKeyNotExpr implements ContextKeyExpr {
return false;
}
public evaluate(context: any): boolean {
return (!context[this.key]);
public evaluate(context: IContext): boolean {
return (!context.getValue(this.key));
}
public normalize(): ContextKeyExpr {
......@@ -346,7 +346,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr {
return false;
}
public evaluate(context: any): boolean {
public evaluate(context: IContext): boolean {
for (let i = 0, len = this.expr.length; i < len; i++) {
if (!this.expr[i].evaluate(context)) {
return false;
......@@ -441,6 +441,10 @@ export class RawContextKey<T> extends ContextKeyDefinedExpr {
}
}
export interface IContext {
getValue<T>(key: string): T;
}
export interface IContextKey<T> {
set(value: T): void;
reset(): void;
......@@ -467,7 +471,7 @@ export interface IContextKeyService {
getContextKeyValue<T>(key: string): T;
createScoped(target?: IContextKeyServiceTarget): IContextKeyService;
getContextValue(target: IContextKeyServiceTarget): any;
getContext(target: IContextKeyServiceTarget): IContext;
}
export const SET_CONTEXT_COMMAND_ID = 'setContext';
......@@ -7,6 +7,8 @@
import * as assert from 'assert';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
const createContext = ctx => ({ getValue: key => ctx[key] });
suite('ContextKeyExpr', () => {
test('ContextKeyExpr.equals', function () {
let a = ContextKeyExpr.and(
......@@ -48,11 +50,11 @@ suite('ContextKeyExpr', () => {
test('evaluate', function () {
/* tslint:disable:triple-equals */
let context = {
let context = createContext({
'a': true,
'b': false,
'c': '5'
};
});
function testExpression(expr: string, expected: boolean): void {
let rules = ContextKeyExpr.deserialize(expr);
assert.equal(rules.evaluate(context), expected, expr);
......
......@@ -62,7 +62,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
}
protected abstract _getResolver(): KeybindingResolver;
public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding;
public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
public getDefaultKeybindings(): string {
......@@ -101,7 +101,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
return null;
}
const contextValue = this._contextKeyService.getContextValue(target);
const contextValue = this._contextKeyService.getContext(target);
const currentChord = this._currentChord ? this._currentChord.keypress : null;
return this._getResolver().resolve(contextValue, currentChord, firstPart);
}
......@@ -120,7 +120,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
return shouldPreventDefault;
}
const contextValue = this._contextKeyService.getContextValue(target);
const contextValue = this._contextKeyService.getContext(target);
const currentChord = this._currentChord ? this._currentChord.keypress : null;
const keypressLabel = keybinding.getLabel();
const resolveResult = this._getResolver().resolve(contextValue, currentChord, firstPart);
......
......@@ -44,7 +44,10 @@ export interface IKeybindingService {
onDidUpdateKeybindings: Event<IKeybindingEvent>;
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding;
/**
* Returns none, one or many (depending on keyboard layout)!
*/
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
......
......@@ -5,7 +5,7 @@
'use strict';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
......@@ -234,7 +234,7 @@ export class KeybindingResolver {
return items[items.length - 1];
}
public resolve(context: any, currentChord: string, keypress: string): IResolveResult {
public resolve(context: IContext, currentChord: string, keypress: string): IResolveResult {
let lookupMap: ResolvedKeybindingItem[] = null;
if (currentChord !== null) {
......@@ -285,7 +285,7 @@ export class KeybindingResolver {
};
}
private _findCommand(context: any, matches: ResolvedKeybindingItem[]): ResolvedKeybindingItem {
private _findCommand(context: IContext, matches: ResolvedKeybindingItem[]): ResolvedKeybindingItem {
for (let i = matches.length - 1; i >= 0; i--) {
let k = matches[i];
......@@ -299,7 +299,7 @@ export class KeybindingResolver {
return null;
}
public static contextMatchesRules(context: any, rules: ContextKeyExpr): boolean {
public static contextMatchesRules(context: IContext, rules: ContextKeyExpr): boolean {
if (!rules) {
return true;
}
......
......@@ -12,7 +12,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { ContextKeyExpr, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
import { IContext, ContextKeyExpr, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IMessageService } from 'vs/platform/message/common/message';
import { TPromise } from 'vs/base/common/winjs.base';
......@@ -20,6 +20,8 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { OS } from 'vs/base/common/platform';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
const createContext = ctx => ({ getValue: key => ctx[key] });
suite('AbstractKeybindingService', () => {
class TestKeybindingService extends AbstractKeybindingService {
......@@ -40,8 +42,8 @@ suite('AbstractKeybindingService', () => {
return this._resolver;
}
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding {
return new USLayoutResolvedKeybinding(kb, OS);
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {
return [new USLayoutResolvedKeybinding(kb, OS)];
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
......@@ -52,7 +54,7 @@ suite('AbstractKeybindingService', () => {
keyboardEvent.metaKey,
keyboardEvent.keyCode
);
return this.resolveKeybinding(keybinding);
return this.resolveKeybinding(keybinding)[0];
}
public testDispatch(kb: number): boolean {
......@@ -69,7 +71,7 @@ suite('AbstractKeybindingService', () => {
}
let createTestKeybindingService: (items: ResolvedKeybindingItem[], contextValue?: any) => TestKeybindingService = null;
let currentContextValue: any = null;
let currentContextValue: IContext = null;
let executeCommandCalls: { commandId: string; args: any[]; }[] = null;
let showMessageCalls: { sev: Severity, message: any; }[] = null;
let statusMessageCalls: string[] = null;
......@@ -91,7 +93,7 @@ suite('AbstractKeybindingService', () => {
contextMatchesRules: undefined,
getContextKeyValue: undefined,
createScoped: undefined,
getContextValue: (target: IContextKeyServiceTarget): any => {
getContext: (target: IContextKeyServiceTarget): any => {
return currentContextValue;
}
};
......@@ -269,9 +271,9 @@ suite('AbstractKeybindingService', () => {
// send Ctrl/Cmd + K
currentContextValue = {
currentContextValue = createContext({
key1: true
};
});
let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_K);
assert.equal(shouldPreventDefault, true);
assert.deepEqual(executeCommandCalls, [{
......@@ -287,7 +289,7 @@ suite('AbstractKeybindingService', () => {
statusMessageCallsDisposed = [];
// send Ctrl/Cmd + K
currentContextValue = {};
currentContextValue = createContext({});
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_K);
assert.equal(shouldPreventDefault, true);
assert.deepEqual(executeCommandCalls, []);
......@@ -302,7 +304,7 @@ suite('AbstractKeybindingService', () => {
statusMessageCallsDisposed = [];
// send Ctrl/Cmd + X
currentContextValue = {};
currentContextValue = createContext({});
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_X);
assert.equal(shouldPreventDefault, true);
assert.deepEqual(executeCommandCalls, [{
......@@ -331,7 +333,7 @@ suite('AbstractKeybindingService', () => {
// send Ctrl/Cmd + K
currentContextValue = {};
currentContextValue = createContext({});
let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_K);
assert.equal(shouldPreventDefault, true);
assert.deepEqual(executeCommandCalls, [{
......@@ -347,9 +349,9 @@ suite('AbstractKeybindingService', () => {
statusMessageCallsDisposed = [];
// send Ctrl/Cmd + K
currentContextValue = {
currentContextValue = createContext({
key1: true
};
});
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_K);
assert.equal(shouldPreventDefault, true);
assert.deepEqual(executeCommandCalls, [{
......@@ -365,9 +367,9 @@ suite('AbstractKeybindingService', () => {
statusMessageCallsDisposed = [];
// send Ctrl/Cmd + X
currentContextValue = {
currentContextValue = createContext({
key1: true
};
});
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_X);
assert.equal(shouldPreventDefault, false);
assert.deepEqual(executeCommandCalls, []);
......@@ -389,7 +391,7 @@ suite('AbstractKeybindingService', () => {
]);
// send Ctrl/Cmd + K
currentContextValue = {};
currentContextValue = createContext({});
let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KEY_K);
assert.equal(shouldPreventDefault, false);
assert.deepEqual(executeCommandCalls, [{
......
......@@ -7,11 +7,13 @@
import * as assert from 'assert';
import { KeyCode, KeyMod, KeyChord, createKeybinding, KeybindingType, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { ContextKeyAndExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { OS } from 'vs/base/common/platform';
const createContext = ctx => ({ getValue: key => ctx[key] });
suite('KeybindingResolver', () => {
function kbItem(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr, isDefault: boolean): ResolvedKeybindingItem {
......@@ -35,12 +37,12 @@ suite('KeybindingResolver', () => {
let contextRules = ContextKeyExpr.equals('bar', 'baz');
let keybindingItem = kbItem(keybinding, 'yes', null, contextRules, true);
assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'baz' }, contextRules), true);
assert.equal(KeybindingResolver.contextMatchesRules({ bar: 'bz' }, contextRules), false);
assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'baz' }), contextRules), true);
assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'bz' }), contextRules), false);
let resolver = new KeybindingResolver([keybindingItem], []);
assert.equal(resolver.resolve({ bar: 'baz' }, null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)).commandId, 'yes');
assert.equal(resolver.resolve({ bar: 'bz' }, null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)), null);
assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)).commandId, 'yes');
assert.equal(resolver.resolve(createContext({ bar: 'bz' }), null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)), null);
});
test('resolve key with arguments', function () {
......@@ -51,7 +53,7 @@ suite('KeybindingResolver', () => {
let keybindingItem = kbItem(keybinding, 'yes', commandArgs, contextRules, true);
let resolver = new KeybindingResolver([keybindingItem], []);
assert.equal(resolver.resolve({ bar: 'baz' }, null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)).commandArgs, commandArgs);
assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, getDispatchStr(<SimpleKeybinding>runtimeKeybinding)).commandArgs, commandArgs);
});
test('KeybindingResolver.combine simple 1', function () {
......@@ -333,7 +335,7 @@ suite('KeybindingResolver', () => {
}
};
let testResolve = (ctx: any, _expectedKey: number, commandId: string) => {
let testResolve = (ctx: IContext, _expectedKey: number, commandId: string) => {
const expectedKey = createKeybinding(_expectedKey, OS);
if (expectedKey.type === KeybindingType.Chord) {
......@@ -360,30 +362,30 @@ suite('KeybindingResolver', () => {
testKey('first', []);
testKey('second', [KeyCode.KEY_Z, KeyCode.KEY_X]);
testResolve({ key2: true }, KeyCode.KEY_X, 'second');
testResolve({}, KeyCode.KEY_Z, 'second');
testResolve(createContext({ key2: true }), KeyCode.KEY_X, 'second');
testResolve(createContext({}), KeyCode.KEY_Z, 'second');
testKey('third', [KeyCode.KEY_X]);
testResolve({ key3: true }, KeyCode.KEY_X, 'third');
testResolve(createContext({ key3: true }), KeyCode.KEY_X, 'third');
testKey('fourth', []);
testKey('fifth', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z)]);
testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z), 'fifth');
testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z), 'fifth');
testKey('seventh', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K)]);
testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), 'seventh');
testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), 'seventh');
testKey('uncomment lines', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U)]);
testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), 'uncomment lines');
testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), 'uncomment lines');
testKey('comment lines', [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C)]);
testResolve({}, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), 'comment lines');
testResolve(createContext({}), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), 'comment lines');
testKey('unreachablechord', []);
testKey('eleven', [KeyMod.CtrlCmd | KeyCode.KEY_G]);
testResolve({}, KeyMod.CtrlCmd | KeyCode.KEY_G, 'eleven');
testResolve(createContext({}), KeyMod.CtrlCmd | KeyCode.KEY_G, 'eleven');
testKey('sixth', []);
});
......
......@@ -54,7 +54,7 @@ export class MockContextKeyService implements IContextKeyService {
public getContextKeyValue(key: string) {
return;
}
public getContextValue(domNode: HTMLElement): any {
public getContext(domNode: HTMLElement): any {
return null;
}
public createScoped(domNode: HTMLElement): IContextKeyService {
......@@ -77,8 +77,8 @@ export class MockKeybindingService implements IKeybindingService {
return [];
}
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding {
return new USLayoutResolvedKeybinding(keybinding, OS);
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
return [new USLayoutResolvedKeybinding(keybinding, OS)];
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
......@@ -89,7 +89,7 @@ export class MockKeybindingService implements IKeybindingService {
keyboardEvent.metaKey,
keyboardEvent.keyCode
);
return this.resolveKeybinding(keybinding);
return this.resolveKeybinding(keybinding)[0];
}
public lookupKeybindings(commandId: string): ResolvedKeybinding[] {
......
......@@ -669,8 +669,11 @@ export class ExtensionEditor extends BaseEditor {
}
const keyBinding = createKeybinding(KeybindingIO.readKeybinding(key || rawKeyBinding.key, OS), OS);
const resolvedKeybinding = this.keybindingService.resolveKeybinding(keyBinding);
const result = resolvedKeybinding.getLabel();
const resolvedKeybindings = this.keybindingService.resolveKeybinding(keyBinding);
if (resolvedKeybindings.length === 0) {
return null;
}
const result = resolvedKeybindings[0].getLabel();
return result === 'unknown' ? null : result;
}
......
......@@ -160,20 +160,21 @@ export class DefineKeybindingController implements editorCommon.IEditorContribut
var m = model.findMatches(regex, false, true, false, false, false).map(m => m.range);
let data = m.map((range) => {
let text = model.getValueInRange(range);
let strKeybinding = text.substring(1, text.length - 1);
strKeybinding = strKeybinding.replace(/\\\\/g, '\\');
let numKeybinding = KeybindingIO.readKeybinding(strKeybinding, OS);
const text = model.getValueInRange(range);
const strKeybinding = text.substring(1, text.length - 1).replace(/\\\\/g, '\\');
const numKeybinding = KeybindingIO.readKeybinding(strKeybinding, OS);
const keybinding = createKeybinding(numKeybinding, OS);
const usResolvedKeybinding = new USLayoutResolvedKeybinding(keybinding, OS);
let keybinding = createKeybinding(numKeybinding, OS);
let resolvedKeybinding = this._keybindingService.resolveKeybinding(keybinding);
let label: string = null;
const resolvedKeybindings = this._keybindingService.resolveKeybinding(keybinding);
if (resolvedKeybindings.length > 0) {
label = resolvedKeybindings[0].getLabel();
}
const usResolvedKeybinding = new USLayoutResolvedKeybinding(keybinding, OS);
return {
usLabel: usResolvedKeybinding.getLabel(),
label: resolvedKeybinding.getLabel(),
label: label,
range: range
};
});
......
......@@ -39,13 +39,16 @@ export function isSearchViewletFocussed(viewletService: IViewletService): boolea
}
export function appendKeyBindingLabel(label: string, keyBinding: number | ResolvedKeybinding, keyBindingService2: IKeybindingService): string {
let resolvedKb: ResolvedKeybinding;
if (typeof keyBinding === 'number') {
resolvedKb = keyBindingService2.resolveKeybinding(createKeybinding(keyBinding, OS));
const resolvedKeybindings = keyBindingService2.resolveKeybinding(createKeybinding(keyBinding, OS));
return doAppendKeyBindingLabel(label, resolvedKeybindings.length > 0 ? resolvedKeybindings[0] : null);
} else {
resolvedKb = keyBinding;
return doAppendKeyBindingLabel(label, keyBinding);
}
return resolvedKb ? label + ' (' + resolvedKb.getLabel() + ')' : label;
}
function doAppendKeyBindingLabel(label: string, keyBinding: ResolvedKeybinding): string {
return keyBinding ? label + ' (' + keyBinding.getLabel() + ')' : label;
}
export class ToggleCaseSensitiveAction extends Action {
......
......@@ -30,7 +30,7 @@ suite('Search Actions', () => {
instantiationService = new TestInstantiationService();
instantiationService.stub(IModelService, stubModelService(instantiationService));
instantiationService.stub(IKeybindingService, {});
instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => new USLayoutResolvedKeybinding(keybinding, OS));
instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => [new USLayoutResolvedKeybinding(keybinding, OS)]);
counter = 0;
});
......
......@@ -86,9 +86,13 @@ export function loadReleaseNotes(accessor: ServicesAccessor, version: string): T
return unassigned;
}
const resolvedKeybinding = keybindingService.resolveKeybinding(keybinding);
const resolvedKeybindings = keybindingService.resolveKeybinding(keybinding);
return resolvedKeybinding.getLabel();
if (resolvedKeybindings.length === 0) {
return unassigned;
}
return resolvedKeybindings[0].getLabel();
};
return text
......
......@@ -221,9 +221,15 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
const item = items[i];
const when = (item.when ? item.when.normalize() : null);
const keybinding = item.keybinding;
const resolvedKeybinding = (keybinding ? this.resolveKeybinding(keybinding) : null);
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault);
if (!keybinding) {
// This might be a removal keybinding item in user settings => accept it
result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault);
} else {
const resolvedKeybindings = this.resolveKeybinding(keybinding);
for (let j = 0; j < resolvedKeybindings.length; j++) {
result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault);
}
}
}
return result;
......@@ -242,14 +248,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
return extraUserKeybindings.map((k, i) => KeybindingIO.readKeybindingItem(k, i, OS));
}
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding {
// TODO@keybindings
let r = this._keyboardMapper.resolveKeybinding(kb);
if (r.length !== 1) {
console.warn('oh noes => I cannot fulfill API!!!');
return null;
}
return r[0];
public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {
return this._keyboardMapper.resolveKeybinding(kb);
}
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册