Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -207,7 +207,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
}
getChannel<T extends IChannel>(channelName: string): T {
const call = (command, arg) => this.request(channelName, command, arg);
const call = (command: string, arg: any) => this.request(channelName, command, arg);
return { call } as T;
}
@@ -411,8 +411,8 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos
}
dispose(): void {
this.channels = null;
this.channelClients = null;
this.channels = Object.create(null);
this.channelClients = Object.create(null);
this.onClientAdded.dispose();
}
}
@@ -452,14 +452,14 @@ export class IPCClient implements IChannelClient, IChannelServer, IDisposable {
}
export function getDelayedChannel<T extends IChannel>(promise: TPromise<T>): T {
const call = (command, arg) => promise.then(c => c.call(command, arg));
const call = (command: string, arg: any) => promise.then(c => c.call(command, arg));
return { call } as T;
}
export function getNextTickChannel<T extends IChannel>(channel: T): T {
let didTick = false;
const call = (command, arg) => {
const call = (command: string, arg: any) => {
if (didTick) {
return channel.call(command, arg);
}

View File

@@ -12,6 +12,7 @@ import { Emitter } from 'vs/base/common/event';
import { fromEventEmitter } from 'vs/base/node/event';
import { createQueuedSender } from 'vs/base/node/processes';
import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { isRemoteConsoleLog, log } from 'vs/base/node/console';
export class Server extends IPCServer {
constructor() {
@@ -89,7 +90,7 @@ export class Client implements IChannelClient, IDisposable {
}
getChannel<T extends IChannel>(channelName: string): T {
const call = (command, arg) => this.request(channelName, command, arg);
const call = (command: string, arg: any) => this.request(channelName, command, arg);
return { call } as T;
}
@@ -151,24 +152,15 @@ export class Client implements IChannelClient, IDisposable {
const onRawMessage = fromEventEmitter(this.child, 'message', msg => msg);
onRawMessage(msg => {
// Handle console logs specially
if (msg && msg.type === '__$console') {
let args = ['%c[IPC Library: ' + this.options.serverName + ']', 'color: darkgreen'];
try {
const parsed = JSON.parse(msg.arguments);
args = args.concat(Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
} catch (error) {
args.push(msg.arguments);
}
console[msg.severity].apply(console, args);
// Handle remote console logs specially
if (isRemoteConsoleLog(msg)) {
log(msg, `IPC Library: ${this.options.serverName}`);
return null;
}
// Anything else goes to the outside
else {
onMessageEmitter.fire(msg);
}
onMessageEmitter.fire(msg);
});
const sender = this.options.useQueue ? createQueuedSender(this.child) : this.child;

View File

@@ -34,7 +34,7 @@ export class Protocol implements IMessagePassingProtocol {
constructor(private _socket: Socket) {
let chunks = [];
let chunks: Buffer[] = [];
let totalLength = 0;
const state = {

View File

@@ -10,13 +10,10 @@ import { TPromise } from 'vs/base/common/winjs.base';
import types = require('vs/base/common/types');
import URI from 'vs/base/common/uri';
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import filters = require('vs/base/common/filters');
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import { IconLabel, IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen';
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
import { compareAnything, compareByScore as doCompareByScore } from 'vs/base/common/comparers';
import { compareAnything } from 'vs/base/common/comparers';
import { ActionBar, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import DOM = require('vs/base/browser/dom');
@@ -24,6 +21,7 @@ import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidge
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { OS } from 'vs/base/common/platform';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IItemAccessor } from 'vs/base/parts/quickopen/common/quickOpenScorer';
export interface IContext {
event: any;
@@ -37,18 +35,25 @@ export interface IHighlight {
let IDS = 0;
class EntryAccessor {
export class QuickOpenItemAccessorClass implements IItemAccessor<QuickOpenEntry> {
public static getLabel(entry: QuickOpenEntry) {
public getItemLabel(entry: QuickOpenEntry): string {
return entry.getLabel();
}
public static getResourcePath(entry: QuickOpenEntry) {
public getItemDescription(entry: QuickOpenEntry): string {
return entry.getDescription();
}
public getItemPath(entry: QuickOpenEntry): string {
const resource = entry.getResource();
return resource && resource.fsPath;
return resource ? resource.fsPath : void 0;
}
}
export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass();
export class QuickOpenEntry {
private id: string;
private labelHighlights: IHighlight[];
@@ -166,116 +171,6 @@ export class QuickOpenEntry {
return false;
}
/**
* A good default sort implementation for quick open entries respecting highlight information
* as well as associated resources.
*/
public static compare(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
// Give matches with label highlights higher priority over
// those with only description highlights
const labelHighlightsA = elementA.getHighlights()[0] || [];
const labelHighlightsB = elementB.getHighlights()[0] || [];
if (labelHighlightsA.length && !labelHighlightsB.length) {
return -1;
} else if (!labelHighlightsA.length && labelHighlightsB.length) {
return 1;
}
// Fallback to the full path if labels are identical and we have associated resources
let nameA = elementA.getLabel();
let nameB = elementB.getLabel();
if (nameA === nameB) {
const resourceA = elementA.getResource();
const resourceB = elementB.getResource();
if (resourceA && resourceB) {
nameA = resourceA.fsPath;
nameB = resourceB.fsPath;
}
}
return compareAnything(nameA, nameB, lookFor);
}
public static compareByScore(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number {
return doCompareByScore(elementA, elementB, EntryAccessor, lookFor, lookForNormalizedLower, scorerCache);
}
/**
* A good default highlight implementation for an entry with label and description.
*/
public static highlight(entry: QuickOpenEntry, lookFor: string, fuzzyHighlight = false): { labelHighlights: IHighlight[], descriptionHighlights: IHighlight[] } {
let labelHighlights: IHighlight[] = [];
const descriptionHighlights: IHighlight[] = [];
const normalizedLookFor = strings.stripWildcards(lookFor);
const label = entry.getLabel();
const description = entry.getDescription();
// Highlight file aware
if (entry.getResource()) {
// Highlight entire label and description if searching for full absolute path
const fsPath = entry.getResource().fsPath;
if (lookFor.length === fsPath.length && lookFor.toLowerCase() === fsPath.toLowerCase()) {
labelHighlights.push({ start: 0, end: label.length });
descriptionHighlights.push({ start: 0, end: description.length });
}
// Fuzzy/Full-Path: Highlight is special
else if (fuzzyHighlight || lookFor.indexOf(paths.nativeSep) >= 0) {
const candidateLabelHighlights = filters.matchesFuzzy(lookFor, label, fuzzyHighlight);
if (!candidateLabelHighlights) {
const pathPrefix = description ? (description + paths.nativeSep) : '';
const pathPrefixLength = pathPrefix.length;
// If there are no highlights in the label, build a path out of description and highlight and match on both,
// then extract the individual label and description highlights back to the original positions
let pathHighlights = filters.matchesFuzzy(lookFor, pathPrefix + label, fuzzyHighlight);
if (!pathHighlights && lookFor !== normalizedLookFor) {
pathHighlights = filters.matchesFuzzy(normalizedLookFor, pathPrefix + label, fuzzyHighlight);
}
if (pathHighlights) {
pathHighlights.forEach(h => {
// Match overlaps label and description part, we need to split it up
if (h.start < pathPrefixLength && h.end > pathPrefixLength) {
labelHighlights.push({ start: 0, end: h.end - pathPrefixLength });
descriptionHighlights.push({ start: h.start, end: pathPrefixLength });
}
// Match on label part
else if (h.start >= pathPrefixLength) {
labelHighlights.push({ start: h.start - pathPrefixLength, end: h.end - pathPrefixLength });
}
// Match on description part
else {
descriptionHighlights.push(h);
}
});
}
} else {
labelHighlights = candidateLabelHighlights;
}
}
// Highlight only inside label
else {
labelHighlights = filters.matchesFuzzy(lookFor, label);
}
}
// Highlight by label otherwise
else {
labelHighlights = filters.matchesFuzzy(lookFor, label);
}
return { labelHighlights, descriptionHighlights };
}
public isFile(): boolean {
return false; // TODO@Ben debt with editor history merging
}
@@ -686,3 +581,37 @@ export class QuickOpenModel implements
return entry.run(mode, context);
}
}
/**
* A good default sort implementation for quick open entries respecting highlight information
* as well as associated resources.
*/
export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
// Give matches with label highlights higher priority over
// those with only description highlights
const labelHighlightsA = elementA.getHighlights()[0] || [];
const labelHighlightsB = elementB.getHighlights()[0] || [];
if (labelHighlightsA.length && !labelHighlightsB.length) {
return -1;
}
if (!labelHighlightsA.length && labelHighlightsB.length) {
return 1;
}
// Fallback to the full path if labels are identical and we have associated resources
let nameA = elementA.getLabel();
let nameB = elementB.getLabel();
if (nameA === nameB) {
const resourceA = elementA.getResource();
const resourceB = elementB.getResource();
if (resourceA && resourceB) {
nameA = resourceA.fsPath;
nameB = resourceB.fsPath;
}
}
return compareAnything(nameA, nameB, lookFor);
}

View File

@@ -532,6 +532,13 @@ export class QuickOpenWidget implements IModelProvider {
if (this.usageLogger) {
const indexOfAcceptedElement = this.model.entries.indexOf(value);
const entriesCount = this.model.entries.length;
/* __GDPR__
"quickOpenWidgetItemAccepted" : {
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"count": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isQuickNavigate": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.usageLogger.publicLog('quickOpenWidgetItemAccepted', { index: indexOfAcceptedElement, count: entriesCount, isQuickNavigate: this.quickNavigateConfiguration ? true : false });
}
@@ -773,6 +780,12 @@ export class QuickOpenWidget implements IModelProvider {
if (this.model) {
const entriesCount = this.model.entries.filter(e => this.isElementVisible(this.model, e)).length;
if (this.usageLogger) {
/* __GDPR__
"quickOpenWidgetCancelled" : {
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isQuickNavigate": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.usageLogger.publicLog('quickOpenWidgetCancelled', { count: entriesCount, isQuickNavigate: this.quickNavigateConfiguration ? true : false });
}
}

View File

@@ -6,6 +6,11 @@
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
/* __GDPR__FRAGMENT__
"IQuickNavigateConfiguration" : {
"keybindings" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IQuickNavigateConfiguration {
keybindings: ResolvedKeybinding[];
}

View File

@@ -0,0 +1,614 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { compareAnything } from 'vs/base/common/comparers';
import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isUpper } from 'vs/base/common/filters';
import { isEqual, nativeSep } from 'vs/base/common/paths';
import { isWindows } from 'vs/base/common/platform';
import { stripWildcards } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
export type Score = [number /* score */, number[] /* match positions */];
export type ScorerCache = { [key: string]: IItemScore };
const NO_MATCH = 0;
const NO_SCORE: Score = [NO_MATCH, []];
// const DEBUG = false;
// const DEBUG_MATRIX = false;
export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
if (!target || !query) {
return NO_SCORE; // return early if target or query are undefined
}
const targetLength = target.length;
const queryLength = query.length;
if (targetLength < queryLength) {
return NO_SCORE; // impossible for query to be contained in target
}
// if (DEBUG) {
// console.group(`Target: ${target}, Query: ${query}`);
// }
const targetLower = target.toLowerCase();
// When not searching fuzzy, we require the query to be contained fully
// in the target string contiguously.
if (!fuzzy) {
const indexOfQueryInTarget = targetLower.indexOf(queryLower);
if (indexOfQueryInTarget === -1) {
// if (DEBUG) {
// console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`);
// }
return NO_SCORE;
}
}
// When searching fuzzy, we require the query to be contained fully
// in the target string as separate substrings
else {
let targetOffset = 0;
for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) {
targetOffset = targetLower.indexOf(queryLower[queryIndex], targetOffset);
if (targetOffset === -1) {
return NO_SCORE;
}
}
}
const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength);
// if (DEBUG) {
// console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold');
// console.groupEnd();
// }
return res;
}
function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] {
const scores = [];
const matches = [];
//
// Build Scorer Matrix
// The matrix is composed of query q and target t. For each index we score
// q[i] with t[i] and compare that with the previous score. If the score is
// equal or larger, we keep the match. In addition to the score, we also keep
// the length of the consecutive matches to use as boost for the score.
//
// t a r g e t
// q
// u
// e
// r
// y
//
for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) {
for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) {
const currentIndex = queryIndex * targetLength + targetIndex;
const leftIndex = currentIndex - 1;
const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1;
const leftScore = targetIndex > 0 ? scores[leftIndex] : 0;
const diagScore = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0;
const matchesSequenceLength = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0;
const score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength);
// We have a score and its equal or larger than the left score
// Match: sequence continues growing from previous diag value
// Score: increases by diag score value
if (score && diagScore + score >= leftScore) {
matches[currentIndex] = matchesSequenceLength + 1;
scores[currentIndex] = diagScore + score;
}
// We either have no score or the score is lower than the left score
// Match: reset to 0
// Score: pick up from left hand side
else {
matches[currentIndex] = NO_MATCH;
scores[currentIndex] = leftScore;
}
}
}
// Restore Positions (starting from bottom right of matrix)
const positions = [];
let queryIndex = queryLength - 1;
let targetIndex = targetLength - 1;
while (queryIndex >= 0 && targetIndex >= 0) {
const currentIndex = queryIndex * targetLength + targetIndex;
const match = matches[currentIndex];
if (match === NO_MATCH) {
targetIndex--; // go left
} else {
positions.push(targetIndex);
// go up and left
queryIndex--;
targetIndex--;
}
}
// Print matrix
// if (DEBUG_MATRIX) {
// printMatrix(query, target, matches, scores);
// }
return [scores[queryLength * targetLength - 1], positions.reverse()];
}
function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number {
let score = 0;
if (queryLower[queryIndex] !== targetLower[targetIndex]) {
return score; // no match of characters
}
// Character match bonus
score += 1;
// if (DEBUG) {
// console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal');
// }
// Consecutive match bonus
if (matchesSequenceLength > 0) {
score += (matchesSequenceLength * 5);
// if (DEBUG) {
// console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5));
// }
}
// Same case bonus
if (query[queryIndex] === target[targetIndex]) {
score += 1;
// if (DEBUG) {
// console.log('Same case bonus: +1');
// }
}
// Start of word bonus
if (targetIndex === 0) {
score += 8;
// if (DEBUG) {
// console.log('Start of word bonus: +8');
// }
}
else {
// After separator bonus
const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1));
if (separatorBonus) {
score += separatorBonus;
// if (DEBUG) {
// console.log('After separtor bonus: +4');
// }
}
// Inside word upper case bonus (camel case)
else if (isUpper(target.charCodeAt(targetIndex))) {
score += 1;
// if (DEBUG) {
// console.log('Inside word upper case bonus: +1');
// }
}
}
// if (DEBUG) {
// console.groupEnd();
// }
return score;
}
function scoreSeparatorAtPos(charCode: number): number {
switch (charCode) {
case CharCode.Slash:
case CharCode.Backslash:
return 5; // prefer path separators...
case CharCode.Underline:
case CharCode.Dash:
case CharCode.Period:
case CharCode.Space:
case CharCode.SingleQuote:
case CharCode.DoubleQuote:
case CharCode.Colon:
return 4; // ...over other separators
default:
return 0;
}
}
// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void {
// console.log('\t' + target.split('').join('\t'));
// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) {
// let line = query[queryIndex] + '\t';
// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) {
// const currentIndex = queryIndex * target.length + targetIndex;
// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t';
// }
// console.log(line);
// }
// }
/**
* Scoring on structural items that have a label and optional description.
*/
export interface IItemScore {
/**
* Overall score.
*/
score: number;
/**
* Matches within the label.
*/
labelMatch?: IMatch[];
/**
* Matches within the description.
*/
descriptionMatch?: IMatch[];
}
const NO_ITEM_SCORE: IItemScore = Object.freeze({ score: 0 });
export interface IItemAccessor<T> {
/**
* Just the label of the item to score on.
*/
getItemLabel(item: T): string;
/**
* The optional description of the item to score on. Can be null.
*/
getItemDescription(item: T): string;
/**
* If the item is a file, the path of the file to score on. Can be null.
*/
getItemPath(file: T): string;
}
const PATH_IDENTITY_SCORE = 1 << 18;
const LABEL_PREFIX_SCORE = 1 << 17;
const LABEL_CAMELCASE_SCORE = 1 << 16;
const LABEL_SCORE_THRESHOLD = 1 << 15;
export interface IPreparedQuery {
value: string;
lowercase: string;
containsPathSeparator: boolean;
}
/**
* Helper function to prepare a search value for scoring in quick open by removing unwanted characters.
*/
export function prepareQuery(value: string): IPreparedQuery {
let lowercase: string;
let containsPathSeparator: boolean;
if (value) {
value = stripWildcards(value).replace(/\s/g, ''); // get rid of all wildcards and whitespace
if (isWindows) {
value = value.replace(/\//g, '\\'); // Help Windows users to search for paths when using slash
}
lowercase = value.toLowerCase();
containsPathSeparator = value.indexOf(nativeSep) >= 0;
}
return { value, lowercase, containsPathSeparator };
}
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
if (!item || !query.value) {
return NO_ITEM_SCORE; // we need an item and query to score on at least
}
const label = accessor.getItemLabel(item);
if (!label) {
return NO_ITEM_SCORE; // we need a label at least
}
const description = accessor.getItemDescription(item);
let cacheHash: string;
if (description) {
cacheHash = `${label}${description}${query.value}${fuzzy}`;
} else {
cacheHash = `${label}${query.value}${fuzzy}`;
}
const cached = cache[cacheHash];
if (cached) {
return cached;
}
const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy);
cache[cacheHash] = itemScore;
return itemScore;
}
function doScoreItem<T>(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore {
// 1.) treat identity matches on full path highest
if (path && isEqual(query.value, path, true)) {
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : void 0 };
}
// We only consider label matches if the query is not including file path separators
const preferLabelMatches = !path || !query.containsPathSeparator;
if (preferLabelMatches) {
// 2.) treat prefix matches on the label second highest
const prefixLabelMatch = matchesPrefix(query.value, label);
if (prefixLabelMatch) {
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
}
// 3.) treat camelcase matches on the label third highest
const camelcaseLabelMatch = matchesCamelCase(query.value, label);
if (camelcaseLabelMatch) {
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
}
// 4.) prefer scores on the label if any
const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy);
if (labelScore) {
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
}
}
// 5.) finally compute description + label scores if we have a description
if (description) {
let descriptionPrefix = description;
if (!!path) {
descriptionPrefix = `${description}${nativeSep}`; // assume this is a file path
}
const descriptionPrefixLength = descriptionPrefix.length;
const descriptionAndLabel = `${descriptionPrefix}${label}`;
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy);
if (labelDescriptionScore) {
const labelDescriptionMatches = createMatches(labelDescriptionPositions);
const labelMatch: IMatch[] = [];
const descriptionMatch: IMatch[] = [];
// We have to split the matches back onto the label and description portions
labelDescriptionMatches.forEach(h => {
// Match overlaps label and description part, we need to split it up
if (h.start < descriptionPrefixLength && h.end > descriptionPrefixLength) {
labelMatch.push({ start: 0, end: h.end - descriptionPrefixLength });
descriptionMatch.push({ start: h.start, end: descriptionPrefixLength });
}
// Match on label part
else if (h.start >= descriptionPrefixLength) {
labelMatch.push({ start: h.start - descriptionPrefixLength, end: h.end - descriptionPrefixLength });
}
// Match on description part
else {
descriptionMatch.push(h);
}
});
return { score: labelDescriptionScore, labelMatch, descriptionMatch };
}
}
return NO_ITEM_SCORE;
}
export function compareItemsByScore<T>(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache, fallbackComparer = fallbackCompare): number {
const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache);
const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache);
const scoreA = itemScoreA.score;
const scoreB = itemScoreB.score;
// 1.) prefer identity matches
if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) {
if (scoreA !== scoreB) {
return scoreA === PATH_IDENTITY_SCORE ? -1 : 1;
}
}
// 2.) prefer label prefix matches
if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_PREFIX_SCORE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA);
const labelB = accessor.getItemLabel(itemB);
// prefer shorter names when both match on label prefix
if (labelA.length !== labelB.length) {
return labelA.length - labelB.length;
}
}
// 3.) prefer camelcase matches
if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA);
const labelB = accessor.getItemLabel(itemB);
// prefer more compact camel case matches over longer
const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch);
if (comparedByMatchLength !== 0) {
return comparedByMatchLength;
}
// prefer shorter names when both match on label camelcase
if (labelA.length !== labelB.length) {
return labelA.length - labelB.length;
}
}
// 4.) prefer label scores
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
if (scoreB < LABEL_SCORE_THRESHOLD) {
return -1;
}
if (scoreA < LABEL_SCORE_THRESHOLD) {
return 1;
}
}
// 5.) compare by score
if (scoreA !== scoreB) {
return scoreA > scoreB ? -1 : 1;
}
// 6.) scores are identical, prefer more compact matches (label and description)
const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor);
const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor);
if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) {
return itemBMatchDistance > itemAMatchDistance ? -1 : 1;
}
// 7.) at this point, scores are identical and match compactness as well
// for both items so we start to use the fallback compare
return fallbackComparer(itemA, itemB, query, accessor);
}
function computeLabelAndDescriptionMatchDistance<T>(item: T, score: IItemScore, accessor: IItemAccessor<T>): number {
const hasLabelMatches = (score.labelMatch && score.labelMatch.length);
const hasDescriptionMatches = (score.descriptionMatch && score.descriptionMatch.length);
let matchStart: number = -1;
let matchEnd: number = -1;
// If we have description matches, the start is first of description match
if (hasDescriptionMatches) {
matchStart = score.descriptionMatch[0].start;
}
// Otherwise, the start is the first label match
else if (hasLabelMatches) {
matchStart = score.labelMatch[0].start;
}
// If we have label match, the end is the last label match
// If we had a description match, we add the length of the description
// as offset to the end to indicate this.
if (hasLabelMatches) {
matchEnd = score.labelMatch[score.labelMatch.length - 1].end;
if (hasDescriptionMatches) {
const itemDescription = accessor.getItemDescription(item);
if (itemDescription) {
matchEnd += itemDescription.length;
}
}
}
// If we have just a description match, the end is the last description match
else if (hasDescriptionMatches) {
matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end;
}
return matchEnd - matchStart;
}
function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number {
if ((!matchesA && !matchesB) || (!matchesA.length && !matchesB.length)) {
return 0; // make sure to not cause bad comparing when matches are not provided
}
if (!matchesB || !matchesB.length) {
return -1;
}
if (!matchesA || !matchesA.length) {
return 1;
}
// Compute match length of A (first to last match)
const matchStartA = matchesA[0].start;
const matchEndA = matchesA[matchesA.length - 1].end;
const matchLengthA = matchEndA - matchStartA;
// Compute match length of B (first to last match)
const matchStartB = matchesB[0].start;
const matchEndB = matchesB[matchesB.length - 1].end;
const matchLengthB = matchEndB - matchStartB;
// Prefer shorter match length
return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1;
}
export function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor<T>): number {
// check for label + description length and prefer shorter
const labelA = accessor.getItemLabel(itemA);
const labelB = accessor.getItemLabel(itemB);
const descriptionA = accessor.getItemDescription(itemA);
const descriptionB = accessor.getItemDescription(itemB);
const labelDescriptionALength = labelA.length + (descriptionA ? descriptionA.length : 0);
const labelDescriptionBLength = labelB.length + (descriptionB ? descriptionB.length : 0);
if (labelDescriptionALength !== labelDescriptionBLength) {
return labelDescriptionALength - labelDescriptionBLength;
}
// check for path length and prefer shorter
const pathA = accessor.getItemPath(itemA);
const pathB = accessor.getItemPath(itemB);
if (pathA && pathB && pathA.length !== pathB.length) {
return pathA.length - pathB.length;
}
// 7.) finally we have equal scores and equal length, we fallback to comparer
// compare by label
if (labelA !== labelB) {
return compareAnything(labelA, labelB, query.value);
}
// compare by description
if (descriptionA && descriptionB && descriptionA !== descriptionB) {
return compareAnything(descriptionA, descriptionB, query.value);
}
// compare by path
if (pathA && pathB && pathA !== pathB) {
return compareAnything(pathA, pathB, query.value);
}
// equal
return 0;
}

View File

@@ -0,0 +1,778 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer';
import URI from 'vs/base/common/uri';
import { basename, dirname, nativeSep } from 'vs/base/common/paths';
import { isWindows } from 'vs/base/common/platform';
class ResourceAccessorClass implements scorer.IItemAccessor<URI> {
getItemLabel(resource: URI): string {
return basename(resource.fsPath);
}
getItemDescription(resource: URI): string {
return dirname(resource.fsPath);
}
getItemPath(resource: URI): string {
return resource.fsPath;
}
}
const ResourceAccessor = new ResourceAccessorClass();
class NullAccessorClass implements scorer.IItemAccessor<URI> {
getItemLabel(resource: URI): string {
return void 0;
}
getItemDescription(resource: URI): string {
return void 0;
}
getItemPath(resource: URI): string {
return void 0;
}
}
function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score {
return scorer.score(target, query, query.toLowerCase(), fuzzy);
}
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache): scorer.IItemScore {
return scorer.scoreItem(item, scorer.prepareQuery(query), fuzzy, accessor, cache);
}
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache, fallbackComparer = scorer.fallbackCompare): number {
return scorer.compareItemsByScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache, fallbackComparer);
}
const NullAccessor = new NullAccessorClass();
let cache: scorer.ScorerCache = Object.create(null);
suite('Quick Open Scorer', () => {
setup(() => {
cache = Object.create(null);
});
test('score (fuzzy)', function () {
const target = 'HeLlo-World';
const scores: scorer.Score[] = [];
scores.push(_doScore(target, 'HelLo-World', true)); // direct case match
scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match
scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple)
scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple)
scores.push(_doScore(target, 'H', true)); // direct case prefix
scores.push(_doScore(target, 'h', true)); // direct mix-case prefix
scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit)
scores.push(_doScore(target, 'W', true)); // direct case word prefix
scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix
scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple)
scores.push(_doScore(target, 'L', true)); // in-string case match
scores.push(_doScore(target, 'l', true)); // in-string mix-case match
scores.push(_doScore(target, '4', true)); // no match
// Assert scoring order
let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]);
assert.deepEqual(scores, sortedScores);
// Assert scoring positions
let positions = scores[0][1];
assert.equal(positions.length, 'HelLo-World'.length);
positions = scores[2][1];
assert.equal(positions.length, 'HW'.length);
assert.equal(positions[0], 0);
assert.equal(positions[1], 6);
});
test('score (non fuzzy)', function () {
const target = 'HeLlo-World';
assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0);
assert.equal(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length);
assert.ok(_doScore(target, 'hello-world', false)[0] > 0);
assert.equal(_doScore(target, 'HW', false)[0], 0);
assert.ok(_doScore(target, 'h', false)[0] > 0);
assert.ok(_doScore(target, 'ello', false)[0] > 0);
assert.ok(_doScore(target, 'ld', false)[0] > 0);
assert.equal(_doScore(target, 'eo', false)[0], 0);
});
test('scoreItem - matches are proper', function () {
let res = scoreItem(null, 'something', true, ResourceAccessor, cache);
assert.ok(!res.score);
const resource = URI.file('/xyz/some/path/someFile123.txt');
res = scoreItem(resource, 'something', true, NullAccessor, cache);
assert.ok(!res.score);
// Path Identity
const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache);
assert.ok(identityRes.score);
assert.equal(identityRes.descriptionMatch.length, 1);
assert.equal(identityRes.labelMatch.length, 1);
assert.equal(identityRes.descriptionMatch[0].start, 0);
assert.equal(identityRes.descriptionMatch[0].end, ResourceAccessor.getItemDescription(resource).length);
assert.equal(identityRes.labelMatch[0].start, 0);
assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length);
// Basename Prefix
const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache);
assert.ok(basenamePrefixRes.score);
assert.ok(!basenamePrefixRes.descriptionMatch);
assert.equal(basenamePrefixRes.labelMatch.length, 1);
assert.equal(basenamePrefixRes.labelMatch[0].start, 0);
assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length);
// Basename Camelcase
const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache);
assert.ok(basenameCamelcaseRes.score);
assert.ok(!basenameCamelcaseRes.descriptionMatch);
assert.equal(basenameCamelcaseRes.labelMatch.length, 2);
assert.equal(basenameCamelcaseRes.labelMatch[0].start, 0);
assert.equal(basenameCamelcaseRes.labelMatch[0].end, 1);
assert.equal(basenameCamelcaseRes.labelMatch[1].start, 4);
assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5);
// Basename Match
const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache);
assert.ok(basenameRes.score);
assert.ok(!basenameRes.descriptionMatch);
assert.equal(basenameRes.labelMatch.length, 2);
assert.equal(basenameRes.labelMatch[0].start, 1);
assert.equal(basenameRes.labelMatch[0].end, 2);
assert.equal(basenameRes.labelMatch[1].start, 4);
assert.equal(basenameRes.labelMatch[1].end, 5);
// Path Match
const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
assert.equal(pathRes.labelMatch.length, 1);
assert.equal(pathRes.labelMatch[0].start, 8);
assert.equal(pathRes.labelMatch[0].end, 11);
assert.equal(pathRes.descriptionMatch.length, 1);
assert.equal(pathRes.descriptionMatch[0].start, 1);
assert.equal(pathRes.descriptionMatch[0].end, 4);
// No Match
const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache);
assert.ok(!noRes.score);
assert.ok(!noRes.labelMatch);
assert.ok(!noRes.descriptionMatch);
// Verify Scores
assert.ok(identityRes.score > basenamePrefixRes.score);
assert.ok(basenamePrefixRes.score > basenameRes.score);
assert.ok(basenameRes.score > pathRes.score);
assert.ok(pathRes.score > noRes.score);
});
test('scoreItem - invalid input', function () {
let res = scoreItem(null, null, true, ResourceAccessor, cache);
assert.equal(res.score, 0);
res = scoreItem(null, 'null', true, ResourceAccessor, cache);
assert.equal(res.score, 0);
});
test('scoreItem - optimize for file paths', function () {
const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt');
// xsp is more relevant to the end of the file path even though it matches
// fuzzy also in the beginning. we verify the more relevant match at the
// end gets returned.
const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
assert.equal(pathRes.labelMatch.length, 1);
assert.equal(pathRes.labelMatch[0].start, 0);
assert.equal(pathRes.labelMatch[0].end, 7);
assert.equal(pathRes.descriptionMatch.length, 1);
assert.equal(pathRes.descriptionMatch[0].start, 23);
assert.equal(pathRes.descriptionMatch[0].end, 26);
});
test('scoreItem - avoid match scattering (bug #36119)', function () {
const resource = URI.file('projects/ui/cula/ats/target.mk');;
const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor, cache);
assert.ok(pathRes.score);
assert.ok(pathRes.descriptionMatch);
assert.ok(pathRes.labelMatch);
assert.equal(pathRes.labelMatch.length, 1);
assert.equal(pathRes.labelMatch[0].start, 0);
assert.equal(pathRes.labelMatch[0].end, 9);
});
test('scoreItem - prefers more compact matches', function () {
const resource = URI.file('/1a111d1/11a1d1/something.txt');
// expect "ad" to be matched towards the end of the file because the
// match is more compact
const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache);
assert.ok(res.score);
assert.ok(res.descriptionMatch);
assert.ok(!res.labelMatch.length);
assert.equal(res.descriptionMatch.length, 2);
assert.equal(res.descriptionMatch[0].start, 11);
assert.equal(res.descriptionMatch[0].end, 12);
assert.equal(res.descriptionMatch[1].start, 13);
assert.equal(res.descriptionMatch[1].end, 14);
});
test('compareItemsByScore - identity', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// Full resource A path
let query = ResourceAccessor.getItemPath(resourceA);
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
// Full resource B path
query = ResourceAccessor.getItemPath(resourceB);
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - basename prefix', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// Full resource A basename
let query = ResourceAccessor.getItemLabel(resourceA);
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
// Full resource B basename
query = ResourceAccessor.getItemLabel(resourceB);
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - basename camelcase', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// resource A camelcase
let query = 'fA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
// resource B camelcase
query = 'fB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - basename scores', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// Resource A part of basename
let query = 'fileA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
// Resource B part of basename
query = 'fileB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - path scores', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// Resource A part of path
let query = 'pathfileA';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
// Resource B part of path
query = 'pathfileB';
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - prefer shorter basenames', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileBLonger.txt');
const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');
// Resource A part of path
let query = 'somepath';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - prefer shorter basenames (match on basename)', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileBLonger.txt');
const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');
// Resource A part of path
let query = 'file';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
});
test('compareFilesByScore - prefer shorter paths', function () {
const resourceA = URI.file('/some/path/fileA.txt');
const resourceB = URI.file('/some/path/other/fileB.txt');
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
// Resource A part of path
let query = 'somepath';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - prefer shorter paths (bug #17443)', function () {
const resourceA = URI.file('config/test/t1.js');
const resourceB = URI.file('config/test.js');
const resourceC = URI.file('config/test/t2.js');
let query = 'co/te';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
assert.equal(res[2], resourceC);
});
test('compareFilesByScore - allow to provide fallback sorter (bug #31591)', function () {
const resourceA = URI.file('virtual/vscode.d.ts');
const resourceB = URI.file('vscode/src/vs/vscode.d.ts');
let query = 'vscode';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
test('compareFilesByScore - prefer more compact camel case matches', function () {
const resourceA = URI.file('config/test/openthisAnythingHandler.js');
const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js');
let query = 'AH';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
test('compareFilesByScore - prefer more compact matches (label)', function () {
const resourceA = URI.file('config/test/examasdaple.js');
const resourceB = URI.file('config/test/exampleasdaasd.ts');
let query = 'xp';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
test('compareFilesByScore - prefer more compact matches (path)', function () {
const resourceA = URI.file('config/test/examasdaple/file.js');
const resourceB = URI.file('config/test/exampleasdaasd/file.ts');
let query = 'xp';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
test('compareFilesByScore - prefer more compact matches (label and path)', function () {
const resourceA = URI.file('config/example/thisfile.ts');
const resourceB = URI.file('config/24234243244/example/file.js');
let query = 'exfile';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
assert.equal(res[1], resourceA);
});
test('compareFilesByScore - avoid match scattering (bug #34210)', function () {
const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js');
const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js');
const resourceC = URI.file('node_modules1/bundle/lib/model/modules/modu1/index.js');
const resourceD = URI.file('node_modules1/bundle/lib/model/modules/oddl1/index.js');
let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js';
let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
query = isWindows ? 'un1\\index.js' : 'un1/index.js';
res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () {
const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js');
const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js');
const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js');
let query = 'StatVideoindex';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
});
test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () {
const resourceA = URI.file('src/build-helper/store/redux.ts');
const resourceB = URI.file('src/repository/store/redux.ts');
let query = 'reproreduxts';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #26649)', function () {
const resourceA = URI.file('photobook/src/components/AddPagesButton/index.js');
const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js');
const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js');
let query = 'bookpageIndex';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceC);
});
test('compareFilesByScore - avoid match scattering (bug #33247)', function () {
const resourceA = URI.file('ui/src/utils/constants.js');
const resourceB = URI.file('ui/src/ui/Icons/index.js');
let query = isWindows ? 'ui\\icons' : 'ui/icons';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #33247 comment)', function () {
const resourceA = URI.file('ui/src/components/IDInput/index.js');
const resourceB = URI.file('ui/src/ui/Input/index.js');
let query = isWindows ? 'ui\\input\\index' : 'ui/input/index';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #36166)', function () {
const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo');
const resourceB = URI.file('django/core/signals.py');
let query = 'djancosig';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #32918)', function () {
const resourceA = URI.file('adsys/protected/config.php');
const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php');
const resourceC = URI.file('duowanVideo/wap/protected/config.php');
let query = 'protectedconfig.php';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceA);
assert.equal(res[1], resourceC);
assert.equal(res[2], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #14879)', function () {
const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml');
const resourceB = URI.file('cmd/gradient/main.go');
let query = 'gradientmain';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #14727 1)', function () {
const resourceA = URI.file('alpha-beta-cappa.txt');
const resourceB = URI.file('abc.txt');
let query = 'abc';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #14727 2)', function () {
const resourceA = URI.file('xerxes-yak-zubba/index.js');
const resourceB = URI.file('xyz/index.js');
let query = 'xyz';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #18381)', function () {
const resourceA = URI.file('AssymblyInfo.cs');
const resourceB = URI.file('IAsynchronousTask.java');
let query = 'async';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #35572)', function () {
const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js');
const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js');
let query = 'partisettings';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #36810)', function () {
const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml');
const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml');
let query = 'tipsindex.cshtml';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - prefer shorter hit (bug #20546)', function () {
const resourceA = URI.file('editor/core/components/tests/list-view-spec.js');
const resourceB = URI.file('editor/core/components/list-view.js');
let query = 'listview';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - avoid match scattering (bug #12095)', function () {
const resourceA = URI.file('src/vs/workbench/parts/files/common/explorerViewModel.ts');
const resourceB = URI.file('src/vs/workbench/parts/files/browser/views/explorerView.ts');
const resourceC = URI.file('src/vs/workbench/parts/files/browser/views/explorerViewer.ts');
let query = 'filesexplorerview.ts';
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
assert.equal(res[0], resourceB);
});
test('prepareSearchForScoring', function () {
assert.equal(scorer.prepareQuery(' f*a ').value, 'fa');
assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts');
assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts');
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
assert.equal(scorer.prepareQuery('Model' + nativeSep + 'Tester.ts').containsPathSeparator, true);
});
});

View File

@@ -131,7 +131,7 @@ export class DefaultController implements _.IController {
}
public onClick(tree: _.ITree, element: any, event: mouse.IMouseEvent): boolean {
var isMac = platform.isMacintosh;
const isMac = platform.isMacintosh;
// A Ctrl click on the Mac is a context menu event
if (isMac && event.ctrlKey) {
@@ -152,13 +152,13 @@ export class DefaultController implements _.IController {
}
protected onLeftClick(tree: _.ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
var payload = { origin: origin, originalEvent: eventish };
const payload = { origin: origin, originalEvent: eventish };
if (tree.getInput() === element) {
tree.clearFocus(payload);
tree.clearSelection(payload);
} else {
var isMouseDown = eventish && (<mouse.IMouseEvent>eventish).browserEvent && (<mouse.IMouseEvent>eventish).browserEvent.type === 'mousedown';
const isMouseDown = eventish && (<mouse.IMouseEvent>eventish).browserEvent && (<mouse.IMouseEvent>eventish).browserEvent.type === 'mousedown';
if (!isMouseDown) {
eventish.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise
}
@@ -193,7 +193,7 @@ export class DefaultController implements _.IController {
}
public onTap(tree: _.ITree, element: any, event: touch.GestureEvent): boolean {
var target = <HTMLElement>event.initialTarget;
const target = <HTMLElement>event.initialTarget;
if (target && target.tagName && target.tagName.toLowerCase() === 'input') {
return false; // Ignore event if target is a form input field (avoids browser specific issues)
@@ -211,7 +211,7 @@ export class DefaultController implements _.IController {
}
private onKey(bindings: KeybindingDispatcher, tree: _.ITree, event: IKeyboardEvent): boolean {
var handler = bindings.dispatch(event.toKeybinding());
const handler = bindings.dispatch(event.toKeybinding());
if (handler) {
if (handler(tree, event)) {
event.preventDefault();
@@ -223,7 +223,7 @@ export class DefaultController implements _.IController {
}
protected onUp(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -235,7 +235,7 @@ export class DefaultController implements _.IController {
}
protected onPageUp(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -247,7 +247,7 @@ export class DefaultController implements _.IController {
}
protected onDown(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -259,7 +259,7 @@ export class DefaultController implements _.IController {
}
protected onPageDown(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -271,7 +271,7 @@ export class DefaultController implements _.IController {
}
protected onHome(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -283,7 +283,7 @@ export class DefaultController implements _.IController {
}
protected onEnd(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
@@ -295,12 +295,12 @@ export class DefaultController implements _.IController {
}
protected onLeft(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
} else {
var focus = tree.getFocus();
const focus = tree.getFocus();
tree.collapse(focus).then(didCollapse => {
if (focus && !didCollapse) {
tree.focusParent(payload);
@@ -313,12 +313,12 @@ export class DefaultController implements _.IController {
}
protected onRight(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);
} else {
var focus = tree.getFocus();
const focus = tree.getFocus();
tree.expand(focus).then(didExpand => {
if (focus && !didExpand) {
tree.focusFirstChild(payload);
@@ -331,12 +331,12 @@ export class DefaultController implements _.IController {
}
protected onEnter(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
return false;
}
var focus = tree.getFocus();
const focus = tree.getFocus();
if (focus) {
tree.setSelection([focus], payload);
}
@@ -347,7 +347,7 @@ export class DefaultController implements _.IController {
if (tree.getHighlight()) {
return false;
}
var focus = tree.getFocus();
const focus = tree.getFocus();
if (focus) {
tree.toggleExpansion(focus);
}
@@ -355,7 +355,7 @@ export class DefaultController implements _.IController {
}
protected onEscape(tree: _.ITree, event: IKeyboardEvent): boolean {
var payload = { origin: 'keyboard', originalEvent: event };
const payload = { origin: 'keyboard', originalEvent: event };
if (tree.getHighlight()) {
tree.clearHighlight(payload);

View File

@@ -52,12 +52,12 @@ export class TreeContext implements _.ITreeContext {
const defaultStyles: _.ITreeStyles = {
listFocusBackground: Color.fromHex('#073655'),
listActiveSelectionBackground: Color.fromHex('#3062D6'),
listActiveSelectionBackground: Color.fromHex('#0E639C'),
listActiveSelectionForeground: Color.fromHex('#FFFFFF'),
listFocusAndSelectionBackground: Color.fromHex('#094771'),
listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'),
listInactiveSelectionBackground: Color.fromHex('#C8C8C8'),
listHoverBackground: Color.fromHex('#DCDCDC'),
listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
listHoverBackground: Color.fromHex('#2A2D2E'),
listDropBackground: Color.fromHex('#383B3D')
};

View File

@@ -556,7 +556,7 @@ export class Item extends Events.EventEmitter {
}
private mapEachChild<T>(fn: (child: Item) => T): T[] {
var result = [];
var result: T[] = [];
this.forEachChild((child) => {
result.push(fn(child));
});

View File

@@ -1550,7 +1550,6 @@ export class TreeView extends HeightMap {
// {{SQL CARBON EDIT}}
this.context.dnd.dropAbort(this.context.tree, this.currentDragAndDropData);
}
this.cancelDragAndDropScrollInterval();
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { EventEmitter } from 'vs/base/common/eventEmitter';
import { IIterator, ArrayIterator } from 'vs/base/common/iterator';
import { INextIterator, ArrayIterator } from 'vs/base/common/iterator';
import { Item } from './treeModel';
export interface IViewItem {
@@ -30,7 +30,7 @@ export class HeightMap extends EventEmitter {
return !last ? 0 : last.top + last.height;
}
public onInsertItems(iterator: IIterator<Item>, afterItemId: string = null): number {
public onInsertItems(iterator: INextIterator<Item>, afterItemId: string = null): number {
var item: Item;
var viewItem: IViewItem;
var i: number, j: number;
@@ -90,7 +90,7 @@ export class HeightMap extends EventEmitter {
}
// Contiguous items
public onRemoveItems(iterator: IIterator<string>): void {
public onRemoveItems(iterator: INextIterator<string>): void {
var itemId: string;
var viewItem: IViewItem;
var startIndex: number = null;
@@ -139,7 +139,7 @@ export class HeightMap extends EventEmitter {
}
// Ordered, but not necessarily contiguous items
public onRefreshItems(iterator: IIterator<Item>): void {
public onRefreshItems(iterator: INextIterator<Item>): void {
var item: Item;
var viewItem: IViewItem;
var newHeight: number;
@@ -240,4 +240,4 @@ export class HeightMap extends EventEmitter {
this.heightMap = null;
this.indexes = null;
}
}
}

View File

@@ -7,7 +7,6 @@
import assert = require('assert');
import lifecycle = require('vs/base/common/lifecycle');
import ee = require('vs/base/common/eventEmitter');
import _ = require('vs/base/parts/tree/browser/tree');
import WinJS = require('vs/base/common/winjs.base');
import Events = require('vs/base/common/eventEmitter');
@@ -75,7 +74,7 @@ class EventCounter {
this._count = 0;
}
public listen(emitter: ee.IEventEmitter, event: string, fn: (e) => void = null): () => void {
public listen(emitter: Events.IEventEmitter, event: string, fn: (e) => void = null): () => void {
let r = emitter.addListener(event, (e) => {
this._count++;
if (fn) {