mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
@@ -1,30 +0,0 @@
|
||||
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
|
||||
[{
|
||||
"name": "string_scorer",
|
||||
"version": "0.1.20",
|
||||
"license": "MIT",
|
||||
"repositoryURL": "https://github.com/joshaven/string_score",
|
||||
"description": "The file scorer.ts was inspired by the string_score algorithm from Joshaven Potter.",
|
||||
"licenseDetail": [
|
||||
"This software is released under the Source EULA:",
|
||||
"",
|
||||
"Copyright (c) Joshaven Potter",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy of",
|
||||
"this software and associated documentation files (the \"Software\"), to deal in",
|
||||
"the Software without restriction, including without limitation the rights to",
|
||||
"use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of",
|
||||
"the Software, and to permit persons to whom the Software is furnished to do so,",
|
||||
"subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in all",
|
||||
"copies or substantial portions of the Software.",
|
||||
"",
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS",
|
||||
"FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR",
|
||||
"COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER",
|
||||
"IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN",
|
||||
"CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
|
||||
]
|
||||
}]
|
||||
@@ -4,6 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
/**
|
||||
* Returns the last element of an array.
|
||||
* @param array The array.
|
||||
@@ -111,7 +113,7 @@ function _divideAndMerge<T>(data: T[], compare: (a: T, b: T) => number): void {
|
||||
export function groupBy<T>(data: T[], compare: (a: T, b: T) => number): T[][] {
|
||||
const result: T[][] = [];
|
||||
let currentGroup: T[];
|
||||
for (const element of data.slice(0).sort(compare)) {
|
||||
for (const element of mergeSort(data.slice(0), compare)) {
|
||||
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
|
||||
currentGroup = [element];
|
||||
result.push(currentGroup);
|
||||
@@ -122,28 +124,43 @@ export function groupBy<T>(data: T[], compare: (a: T, b: T) => number): T[][] {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes two *sorted* arrays and computes their delta (removed, added elements).
|
||||
* Finishes in `Math.min(before.length, after.length)` steps.
|
||||
* @param before
|
||||
* @param after
|
||||
* @param compare
|
||||
*/
|
||||
export function delta<T>(before: T[], after: T[], compare: (a: T, b: T) => number) {
|
||||
export interface Splice<T> {
|
||||
start: number;
|
||||
deleteCount: number;
|
||||
inserted: T[];
|
||||
}
|
||||
|
||||
const removed: T[] = [];
|
||||
const added: T[] = [];
|
||||
/**
|
||||
* Diffs two *sorted* arrays and computes the splices which apply the diff.
|
||||
*/
|
||||
export function sortedDiff<T>(before: T[], after: T[], compare: (a: T, b: T) => number): Splice<T>[] {
|
||||
const result: Splice<T>[] = [];
|
||||
|
||||
function pushSplice(start: number, deleteCount: number, inserted: T[]): void {
|
||||
if (deleteCount === 0 && inserted.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const latest = result[result.length - 1];
|
||||
|
||||
if (latest && latest.start + latest.deleteCount === start) {
|
||||
latest.deleteCount += deleteCount;
|
||||
latest.inserted.push(...inserted);
|
||||
} else {
|
||||
result.push({ start, deleteCount, inserted });
|
||||
}
|
||||
}
|
||||
|
||||
let beforeIdx = 0;
|
||||
let afterIdx = 0;
|
||||
|
||||
while (true) {
|
||||
if (beforeIdx === before.length) {
|
||||
added.push(...after.slice(afterIdx));
|
||||
pushSplice(beforeIdx, 0, after.slice(afterIdx));
|
||||
break;
|
||||
}
|
||||
if (afterIdx === after.length) {
|
||||
removed.push(...before.slice(beforeIdx));
|
||||
pushSplice(beforeIdx, before.length - beforeIdx, []);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -156,15 +173,35 @@ export function delta<T>(before: T[], after: T[], compare: (a: T, b: T) => numbe
|
||||
afterIdx += 1;
|
||||
} else if (n < 0) {
|
||||
// beforeElement is smaller -> before element removed
|
||||
removed.push(beforeElement);
|
||||
pushSplice(beforeIdx, 1, []);
|
||||
beforeIdx += 1;
|
||||
} else if (n > 0) {
|
||||
// beforeElement is greater -> after element added
|
||||
added.push(afterElement);
|
||||
pushSplice(beforeIdx, 0, [afterElement]);
|
||||
afterIdx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes two *sorted* arrays and computes their delta (removed, added elements).
|
||||
* Finishes in `Math.min(before.length, after.length)` steps.
|
||||
* @param before
|
||||
* @param after
|
||||
* @param compare
|
||||
*/
|
||||
export function delta<T>(before: T[], after: T[], compare: (a: T, b: T) => number): { removed: T[], added: T[] } {
|
||||
const splices = sortedDiff(before, after, compare);
|
||||
const removed: T[] = [];
|
||||
const added: T[] = [];
|
||||
|
||||
for (const splice of splices) {
|
||||
removed.push(...before.slice(splice.start, splice.start + splice.deleteCount));
|
||||
added.push(...splice.inserted);
|
||||
}
|
||||
|
||||
return { removed, added };
|
||||
}
|
||||
|
||||
@@ -183,7 +220,51 @@ export function top<T>(array: T[], compare: (a: T, b: T) => number, n: number):
|
||||
return [];
|
||||
}
|
||||
const result = array.slice(0, n).sort(compare);
|
||||
for (let i = n, m = array.length; i < m; i++) {
|
||||
topStep(array, compare, result, n, array.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run.
|
||||
*
|
||||
* Returns the top N elements from the array.
|
||||
*
|
||||
* Faster than sorting the entire array when the array is a lot larger than N.
|
||||
*
|
||||
* @param array The unsorted array.
|
||||
* @param compare A sort function for the elements.
|
||||
* @param n The number of elements to return.
|
||||
* @param batch The number of elements to examine before yielding to the event loop.
|
||||
* @return The first n elemnts from array when sorted with compare.
|
||||
*/
|
||||
export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number): TPromise<T[]> {
|
||||
if (n === 0) {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
let canceled = false;
|
||||
return new TPromise((resolve, reject) => {
|
||||
(async () => {
|
||||
const o = array.length;
|
||||
const result = array.slice(0, n).sort(compare);
|
||||
for (let i = n, m = Math.min(n + batch, o); i < o; i = m, m = Math.min(m + batch, o)) {
|
||||
if (i > n) {
|
||||
await new Promise(resolve => setTimeout(resolve)); // nextTick() would starve I/O.
|
||||
}
|
||||
if (canceled) {
|
||||
throw new Error('canceled');
|
||||
}
|
||||
topStep(array, compare, result, i, m);
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
.then(resolve, reject);
|
||||
}, () => {
|
||||
canceled = true;
|
||||
});
|
||||
}
|
||||
|
||||
function topStep<T>(array: T[], compare: (a: T, b: T) => number, result: T[], i: number, m: number): void {
|
||||
for (const n = result.length; i < m; i++) {
|
||||
const element = array[i];
|
||||
if (compare(element, result[n - 1]) < 0) {
|
||||
result.pop();
|
||||
@@ -191,7 +272,6 @@ export function top<T>(array: T[], compare: (a: T, b: T) => number, n: number):
|
||||
result.splice(j, 0, element);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,14 +367,46 @@ export function commonPrefixLength<T>(one: T[], other: T[], equals: (a: T, b: T)
|
||||
}
|
||||
|
||||
export function flatten<T>(arr: T[][]): T[] {
|
||||
return arr.reduce((r, v) => r.concat(v), []);
|
||||
return [].concat(...arr);
|
||||
}
|
||||
|
||||
export function range(to: number, from = 0): number[] {
|
||||
export function range(to: number): number[];
|
||||
export function range(from: number, to: number): number[];
|
||||
export function range(arg: number, to?: number): number[] {
|
||||
let from = typeof to === 'number' ? arg : 0;
|
||||
|
||||
if (typeof to === 'number') {
|
||||
from = arg;
|
||||
} else {
|
||||
from = 0;
|
||||
to = arg;
|
||||
}
|
||||
|
||||
const result: number[] = [];
|
||||
|
||||
for (let i = from; i < to; i++) {
|
||||
result.push(i);
|
||||
if (from <= to) {
|
||||
for (let i = from; i < to; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
} else {
|
||||
for (let i = from; i > to; i--) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function weave<T>(a: T[], b: T[]): T[] {
|
||||
const result: T[] = [];
|
||||
let ai = 0, bi = 0;
|
||||
|
||||
for (let i = 0, length = a.length + b.length; i < length; i++) {
|
||||
if ((i % 2 === 0 && ai < a.length) || bi >= b.length) {
|
||||
result.push(a[ai++]);
|
||||
} else {
|
||||
result.push(b[bi++]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -11,8 +11,9 @@ import { Promise, TPromise, ValueCallback, ErrorCallback, ProgressCallback } fro
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
function isThenable<T>(obj: any): obj is Thenable<T> {
|
||||
export function isThenable<T>(obj: any): obj is Thenable<T> {
|
||||
return obj && typeof (<Thenable<any>>obj).then === 'function';
|
||||
}
|
||||
|
||||
@@ -451,6 +452,10 @@ export class Limiter<T> {
|
||||
return this._onFinished.event;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this.runningPromises + this.outstandingPromises.length;
|
||||
}
|
||||
|
||||
queue(promiseFactory: ITask<Promise>): Promise;
|
||||
queue(promiseFactory: ITask<TPromise<T>>): TPromise<T> {
|
||||
return new TPromise<T>((c, e, p) => {
|
||||
@@ -501,6 +506,33 @@ export class Queue<T> extends Limiter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource
|
||||
* by disposing them once the queue is empty.
|
||||
*/
|
||||
export class ResourceQueue<T> {
|
||||
private queues: { [path: string]: Queue<void> };
|
||||
|
||||
constructor() {
|
||||
this.queues = Object.create(null);
|
||||
}
|
||||
|
||||
public queueFor(resource: URI): Queue<void> {
|
||||
const key = resource.toString();
|
||||
if (!this.queues[key]) {
|
||||
const queue = new Queue<void>();
|
||||
queue.onFinished(() => {
|
||||
queue.dispose();
|
||||
delete this.queues[key];
|
||||
});
|
||||
|
||||
this.queues[key] = queue;
|
||||
}
|
||||
|
||||
return this.queues[key];
|
||||
}
|
||||
}
|
||||
|
||||
export function setDisposableTimeout(handler: Function, timeout: number, ...args: any[]): IDisposable {
|
||||
const handle = setTimeout(handler, timeout, ...args);
|
||||
return { dispose() { clearTimeout(handle); } };
|
||||
|
||||
@@ -6,47 +6,21 @@
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
|
||||
export default class CallbackList {
|
||||
|
||||
private _callbacks: Function[];
|
||||
private _contexts: any[];
|
||||
private _callbacks: LinkedList<[Function, any]>;
|
||||
|
||||
public add(callback: Function, context: any = null, bucket?: IDisposable[]): void {
|
||||
public add(callback: Function, context: any = null, bucket?: IDisposable[]): () => void {
|
||||
if (!this._callbacks) {
|
||||
this._callbacks = [];
|
||||
this._contexts = [];
|
||||
this._callbacks = new LinkedList<[Function, any]>();
|
||||
}
|
||||
this._callbacks.push(callback);
|
||||
this._contexts.push(context);
|
||||
|
||||
const remove = this._callbacks.push([callback, context]);
|
||||
if (Array.isArray(bucket)) {
|
||||
bucket.push({ dispose: () => this.remove(callback, context) });
|
||||
}
|
||||
}
|
||||
|
||||
public remove(callback: Function, context: any = null): void {
|
||||
if (!this._callbacks) {
|
||||
return;
|
||||
}
|
||||
|
||||
let foundCallbackWithDifferentContext = false;
|
||||
for (let i = 0, len = this._callbacks.length; i < len; i++) {
|
||||
if (this._callbacks[i] === callback) {
|
||||
if (this._contexts[i] === context) {
|
||||
// callback & context match => remove it
|
||||
this._callbacks.splice(i, 1);
|
||||
this._contexts.splice(i, 1);
|
||||
return;
|
||||
} else {
|
||||
foundCallbackWithDifferentContext = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCallbackWithDifferentContext) {
|
||||
throw new Error('When adding a listener with a context, you should remove it with the same context');
|
||||
bucket.push({ dispose: remove });
|
||||
}
|
||||
return remove;
|
||||
}
|
||||
|
||||
public invoke(...args: any[]): any[] {
|
||||
@@ -54,13 +28,12 @@ export default class CallbackList {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ret: any[] = [],
|
||||
callbacks = this._callbacks.slice(0),
|
||||
contexts = this._contexts.slice(0);
|
||||
const ret: any[] = [];
|
||||
const elements = this._callbacks.toArray();
|
||||
|
||||
for (let i = 0, len = callbacks.length; i < len; i++) {
|
||||
for (const [callback, context] of elements) {
|
||||
try {
|
||||
ret.push(callbacks[i].apply(contexts[i], args));
|
||||
ret.push(callback.apply(context, args));
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
@@ -68,19 +41,20 @@ export default class CallbackList {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return !this._callbacks || this._callbacks.length === 0;
|
||||
}
|
||||
|
||||
public entries(): [Function, any][] {
|
||||
if (!this._callbacks) {
|
||||
return [];
|
||||
}
|
||||
return this._callbacks.map((fn, index) => <[Function, any]>[fn, this._contexts[index]]);
|
||||
return this._callbacks
|
||||
? this._callbacks.toArray()
|
||||
: [];
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return !this._callbacks || this._callbacks.isEmpty();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._callbacks = undefined;
|
||||
this._contexts = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface CancellationToken {
|
||||
readonly isCancellationRequested: boolean;
|
||||
@@ -16,10 +17,10 @@ export interface CancellationToken {
|
||||
readonly onCancellationRequested: Event<any>;
|
||||
}
|
||||
|
||||
const shortcutEvent: Event<any> = Object.freeze(function (callback, context?) {
|
||||
const shortcutEvent = Object.freeze(function (callback, context?): IDisposable {
|
||||
let handle = setTimeout(callback.bind(context), 0);
|
||||
return { dispose() { clearTimeout(handle); } };
|
||||
});
|
||||
} as Event<any>);
|
||||
|
||||
export namespace CancellationToken {
|
||||
|
||||
@@ -83,8 +84,10 @@ export class CancellationTokenSource {
|
||||
// cancelled token when cancellation happens
|
||||
// before someone asks for the token
|
||||
this._token = CancellationToken.Cancelled;
|
||||
} else {
|
||||
(<MutableToken>this._token).cancel();
|
||||
|
||||
} else if (this._token instanceof MutableToken) {
|
||||
// actually cancel
|
||||
this._token.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
*/
|
||||
export function values<T>(from: IStringDictionary<T> | INumberDictionary<T>): T[] {
|
||||
const result: T[] = [];
|
||||
for (var key in from) {
|
||||
for (let key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
result.push(from[key]);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export function values<T>(from: IStringDictionary<T> | INumberDictionary<T>): T[
|
||||
|
||||
export function size<T>(from: IStringDictionary<T> | INumberDictionary<T>): number {
|
||||
let count = 0;
|
||||
for (var key in from) {
|
||||
for (let key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import scorer = require('vs/base/common/scorer');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
|
||||
let intlFileNameCollator: Intl.Collator;
|
||||
@@ -37,14 +36,8 @@ export function compareFileNames(one: string, other: string): number {
|
||||
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
|
||||
|
||||
export function noIntlCompareFileNames(one: string, other: string): number {
|
||||
let oneMatch = FileNameMatch.exec(one.toLowerCase());
|
||||
let otherMatch = FileNameMatch.exec(other.toLowerCase());
|
||||
|
||||
let oneName = oneMatch[1] || '';
|
||||
let oneExtension = oneMatch[3] || '';
|
||||
|
||||
let otherName = otherMatch[1] || '';
|
||||
let otherExtension = otherMatch[3] || '';
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one, true);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other, true);
|
||||
|
||||
if (oneName !== otherName) {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
@@ -59,14 +52,8 @@ export function noIntlCompareFileNames(one: string, other: string): number {
|
||||
|
||||
export function compareFileExtensions(one: string, other: string): number {
|
||||
if (intlFileNameCollator) {
|
||||
const oneMatch = one ? FileNameMatch.exec(one) : [];
|
||||
const otherMatch = other ? FileNameMatch.exec(other) : [];
|
||||
|
||||
const oneName = oneMatch[1] || '';
|
||||
const oneExtension = oneMatch[3] || '';
|
||||
|
||||
const otherName = otherMatch[1] || '';
|
||||
const otherExtension = otherMatch[3] || '';
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other);
|
||||
|
||||
let result = intlFileNameCollator.compare(oneExtension, otherExtension);
|
||||
|
||||
@@ -92,14 +79,8 @@ export function compareFileExtensions(one: string, other: string): number {
|
||||
}
|
||||
|
||||
function noIntlCompareFileExtensions(one: string, other: string): number {
|
||||
const oneMatch = one ? FileNameMatch.exec(one.toLowerCase()) : [];
|
||||
const otherMatch = other ? FileNameMatch.exec(other.toLowerCase()) : [];
|
||||
|
||||
const oneName = oneMatch[1] || '';
|
||||
const oneExtension = oneMatch[3] || '';
|
||||
|
||||
const otherName = otherMatch[1] || '';
|
||||
const otherExtension = otherMatch[3] || '';
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one, true);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other, true);
|
||||
|
||||
if (oneExtension !== otherExtension) {
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
@@ -112,6 +93,12 @@ function noIntlCompareFileExtensions(one: string, other: string): number {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
}
|
||||
|
||||
function extractNameAndExtension(str?: string, lowercase?: boolean): [string, string] {
|
||||
const match = str ? FileNameMatch.exec(lowercase ? str.toLowerCase() : str) : [] as RegExpExecArray;
|
||||
|
||||
return [(match && match[1]) || '', (match && match[3]) || ''];
|
||||
}
|
||||
|
||||
export function comparePaths(one: string, other: string): number {
|
||||
const oneParts = one.split(paths.nativeSep);
|
||||
const otherParts = other.split(paths.nativeSep);
|
||||
@@ -186,56 +173,4 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export interface IScorableResourceAccessor<T> {
|
||||
getLabel(t: T): string;
|
||||
getResourcePath(t: T): string;
|
||||
}
|
||||
|
||||
export function compareByScore<T>(elementA: T, elementB: T, accessor: IScorableResourceAccessor<T>, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number {
|
||||
const labelA = accessor.getLabel(elementA);
|
||||
const labelB = accessor.getLabel(elementB);
|
||||
|
||||
// treat prefix matches highest in any case
|
||||
const prefixCompare = compareByPrefix(labelA, labelB, lookFor);
|
||||
if (prefixCompare) {
|
||||
return prefixCompare;
|
||||
}
|
||||
|
||||
// Give higher importance to label score
|
||||
const labelAScore = scorer.score(labelA, lookFor, scorerCache);
|
||||
const labelBScore = scorer.score(labelB, lookFor, scorerCache);
|
||||
|
||||
if (labelAScore !== labelBScore) {
|
||||
return labelAScore > labelBScore ? -1 : 1;
|
||||
}
|
||||
|
||||
// Score on full resource path comes next (if available)
|
||||
let resourcePathA = accessor.getResourcePath(elementA);
|
||||
let resourcePathB = accessor.getResourcePath(elementB);
|
||||
if (resourcePathA && resourcePathB) {
|
||||
const resourceAScore = scorer.score(resourcePathA, lookFor, scorerCache);
|
||||
const resourceBScore = scorer.score(resourcePathB, lookFor, scorerCache);
|
||||
|
||||
if (resourceAScore !== resourceBScore) {
|
||||
return resourceAScore > resourceBScore ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// At this place, the scores are identical so we check for string lengths and favor shorter ones
|
||||
if (labelA.length !== labelB.length) {
|
||||
return labelA.length < labelB.length ? -1 : 1;
|
||||
}
|
||||
|
||||
if (resourcePathA && resourcePathB && resourcePathA.length !== resourcePathB.length) {
|
||||
return resourcePathA.length < resourcePathB.length ? -1 : 1;
|
||||
}
|
||||
|
||||
// Finally compare by label or resource path
|
||||
if (labelA === labelB && resourcePathA && resourcePathB) {
|
||||
return compareAnything(resourcePathA, resourcePathB, lookForNormalizedLower);
|
||||
}
|
||||
|
||||
return compareAnything(labelA, labelB, lookForNormalizedLower);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
export function createDecorator(mapFn: (fn: Function) => Function): Function {
|
||||
export function createDecorator(mapFn: (fn: Function, key: string) => Function): Function {
|
||||
return (target: any, key: string, descriptor: any) => {
|
||||
let fnKey: string = null;
|
||||
let fn: Function = null;
|
||||
@@ -22,7 +22,7 @@ export function createDecorator(mapFn: (fn: Function) => Function): Function {
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
descriptor[fnKey] = mapFn(fn);
|
||||
descriptor[fnKey] = mapFn(fn, key);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,4 +56,15 @@ export function memoize(target: any, key: string, descriptor: any) {
|
||||
|
||||
return this[memoizeKey];
|
||||
};
|
||||
}
|
||||
|
||||
export function debounce(delay: number): Function {
|
||||
return createDecorator((fn, key) => {
|
||||
const timerKey = `$debounce$${key}`;
|
||||
|
||||
return function (...args: any[]) {
|
||||
clearTimeout(this[timerKey]);
|
||||
this[timerKey] = setTimeout(() => fn.apply(this, args), delay);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -11,15 +11,15 @@ import * as Platform from 'vs/base/common/platform';
|
||||
* Then trigger an action that will write to diagnostics to see all cached output from the past.
|
||||
*/
|
||||
|
||||
var globals = Platform.globals;
|
||||
const globals = Platform.globals;
|
||||
if (!globals.Monaco) {
|
||||
globals.Monaco = {};
|
||||
}
|
||||
globals.Monaco.Diagnostics = {};
|
||||
|
||||
var switches = globals.Monaco.Diagnostics;
|
||||
var map = new Map<string, Function[]>();
|
||||
var data: any[] = [];
|
||||
const switches = globals.Monaco.Diagnostics;
|
||||
const map = new Map<string, Function[]>();
|
||||
const data: any[] = [];
|
||||
|
||||
function fifo(array: any[], size: number) {
|
||||
while (array.length > size) {
|
||||
@@ -37,29 +37,29 @@ export function register(what: string, fn: Function): (...args: any[]) => void {
|
||||
}
|
||||
|
||||
// register switch
|
||||
var flag = switches[what] || false;
|
||||
const flag = switches[what] || false;
|
||||
switches[what] = flag;
|
||||
|
||||
// register function
|
||||
var tracers = map.get(what) || [];
|
||||
const tracers = map.get(what) || [];
|
||||
tracers.push(fn);
|
||||
map.set(what, tracers);
|
||||
|
||||
var result = function (...args: any[]) {
|
||||
const result = function (...args: any[]) {
|
||||
|
||||
var idx: number;
|
||||
let idx: number;
|
||||
|
||||
if (switches[what] === true) {
|
||||
// replay back-in-time functions
|
||||
var allArgs = [arguments];
|
||||
const allArgs = [arguments];
|
||||
idx = data.indexOf(fn);
|
||||
if (idx !== -1) {
|
||||
allArgs.unshift.apply(allArgs, data[idx + 1] || []);
|
||||
data[idx + 1] = [];
|
||||
}
|
||||
|
||||
var doIt: () => void = function () {
|
||||
var thisArguments = allArgs.shift();
|
||||
const doIt: () => void = function () {
|
||||
const thisArguments = allArgs.shift();
|
||||
fn.apply(fn, thisArguments);
|
||||
if (allArgs.length > 0) {
|
||||
Platform.setTimeout(doIt, 500);
|
||||
@@ -71,10 +71,10 @@ export function register(what: string, fn: Function): (...args: any[]) => void {
|
||||
// know where to store
|
||||
idx = data.indexOf(fn);
|
||||
idx = idx !== -1 ? idx : data.length;
|
||||
var dataIdx = idx + 1;
|
||||
const dataIdx = idx + 1;
|
||||
|
||||
// store arguments
|
||||
var allargs = data[dataIdx] || [];
|
||||
const allargs = data[dataIdx] || [];
|
||||
allargs.push(arguments);
|
||||
fifo(allargs, 50);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { DiffChange } from 'vs/base/common/diff/diffChange';
|
||||
export interface ISequence {
|
||||
getLength(): number;
|
||||
getElementHash(index: number): string;
|
||||
[index: number]: string;
|
||||
}
|
||||
|
||||
export interface IDiffChange {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable, combinedDisposable, empty as EmptyDisposable } from 'vs/base/common/lifecycle';
|
||||
import CallbackList from 'vs/base/common/callbackList';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -82,7 +82,7 @@ export class Emitter<T> {
|
||||
this._options.onFirstListenerAdd(this);
|
||||
}
|
||||
|
||||
this._callbacks.add(listener, thisArgs);
|
||||
const remove = this._callbacks.add(listener, thisArgs);
|
||||
|
||||
if (firstListener && this._options && this._options.onFirstListenerDidAdd) {
|
||||
this._options.onFirstListenerDidAdd(this);
|
||||
@@ -97,7 +97,7 @@ export class Emitter<T> {
|
||||
dispose: () => {
|
||||
result.dispose = Emitter._noop;
|
||||
if (!this._disposed) {
|
||||
this._callbacks.remove(listener, thisArgs);
|
||||
remove();
|
||||
if (this._options && this._options.onLastListenerRemove && this._callbacks.isEmpty()) {
|
||||
this._options.onLastListenerRemove(this);
|
||||
}
|
||||
@@ -304,7 +304,7 @@ export function once<T>(event: Event<T>): Event<T> {
|
||||
};
|
||||
}
|
||||
|
||||
export function any<T>(...events: Event<T>[]): Event<T> {
|
||||
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
|
||||
}
|
||||
|
||||
@@ -313,17 +313,17 @@ export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I)
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay: number = 100, leading = false): Event<O> {
|
||||
|
||||
let subscription: IDisposable;
|
||||
let output: O;
|
||||
let handle: number;
|
||||
let output: O = undefined;
|
||||
let handle: number = undefined;
|
||||
let numDebouncedCalls = 0;
|
||||
|
||||
const emitter = new Emitter<O>({
|
||||
onFirstListenerAdd() {
|
||||
subscription = event(cur => {
|
||||
numDebouncedCalls++;
|
||||
|
||||
output = merger(output, cur);
|
||||
if (!handle && leading) {
|
||||
|
||||
if (leading && !handle) {
|
||||
emitter.fire(output);
|
||||
}
|
||||
|
||||
@@ -331,11 +331,11 @@ export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I)
|
||||
handle = setTimeout(() => {
|
||||
let _output = output;
|
||||
output = undefined;
|
||||
handle = undefined;
|
||||
if (!leading || numDebouncedCalls > 1) {
|
||||
emitter.fire(_output);
|
||||
}
|
||||
|
||||
handle = null;
|
||||
numDebouncedCalls = 0;
|
||||
}, delay);
|
||||
});
|
||||
@@ -424,7 +424,7 @@ class ChainableEvent<T> implements IChainableEvent<T> {
|
||||
return new ChainableEvent(filterEvent(this._event, fn));
|
||||
}
|
||||
|
||||
on(listener, thisArgs, disposables: IDisposable[]) {
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return this._event(listener, thisArgs, disposables);
|
||||
}
|
||||
}
|
||||
@@ -514,10 +514,10 @@ export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Ev
|
||||
emitter.fire(e);
|
||||
});
|
||||
|
||||
const flush = (listener, thisArgs?) => buffer.forEach(e => listener.call(thisArgs, e));
|
||||
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onListenerDidAdd(emitter, listener, thisArgs?) {
|
||||
onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) {
|
||||
if (nextTick) {
|
||||
setTimeout(() => flush(listener, thisArgs));
|
||||
} else {
|
||||
@@ -528,3 +528,21 @@ export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Ev
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export class Relay<T> implements IDisposable {
|
||||
|
||||
private emitter = new Emitter<T>();
|
||||
readonly output: Event<T> = this.emitter.event;
|
||||
|
||||
private disposable: IDisposable = EmptyDisposable;
|
||||
|
||||
set input(event: Event<T>) {
|
||||
this.disposable.dispose();
|
||||
this.disposable = event(this.emitter.fire, this.emitter);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposable.dispose();
|
||||
this.emitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ function _matchesSubString(word: string, wordToMatchAgainst: string, i: number,
|
||||
if (result = _matchesSubString(word, wordToMatchAgainst, i + 1, j + 1)) {
|
||||
return join({ start: j, end: j + 1 }, result);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return _matchesSubString(word, wordToMatchAgainst, i, j + 1);
|
||||
@@ -121,7 +122,7 @@ function isLower(code: number): boolean {
|
||||
return CharCode.a <= code && code <= CharCode.z;
|
||||
}
|
||||
|
||||
function isUpper(code: number): boolean {
|
||||
export function isUpper(code: number): boolean {
|
||||
return CharCode.A <= code && code <= CharCode.Z;
|
||||
}
|
||||
|
||||
@@ -242,7 +243,13 @@ function isCamelCasePattern(word: string): boolean {
|
||||
}
|
||||
|
||||
export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] {
|
||||
if (!camelCaseWord || camelCaseWord.length === 0) {
|
||||
if (!camelCaseWord) {
|
||||
return null;
|
||||
}
|
||||
|
||||
camelCaseWord = camelCaseWord.trim();
|
||||
|
||||
if (camelCaseWord.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -414,20 +421,40 @@ function printTable(table: number[][], pattern: string, patternLen: number, word
|
||||
return ret;
|
||||
}
|
||||
|
||||
const _seps: { [ch: string]: boolean } = Object.create(null);
|
||||
_seps['_'] = true;
|
||||
_seps['-'] = true;
|
||||
_seps['.'] = true;
|
||||
_seps[' '] = true;
|
||||
_seps['/'] = true;
|
||||
_seps['\\'] = true;
|
||||
_seps['\''] = true;
|
||||
_seps['"'] = true;
|
||||
_seps[':'] = true;
|
||||
function isSeparatorAtPos(value: string, index: number): boolean {
|
||||
if (index < 0 || index >= value.length) {
|
||||
return false;
|
||||
}
|
||||
const code = value.charCodeAt(index);
|
||||
switch (code) {
|
||||
case CharCode.Underline:
|
||||
case CharCode.Dash:
|
||||
case CharCode.Period:
|
||||
case CharCode.Space:
|
||||
case CharCode.Slash:
|
||||
case CharCode.Backslash:
|
||||
case CharCode.SingleQuote:
|
||||
case CharCode.DoubleQuote:
|
||||
case CharCode.Colon:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const _ws: { [ch: string]: boolean } = Object.create(null);
|
||||
_ws[' '] = true;
|
||||
_ws['\t'] = true;
|
||||
function isWhitespaceAtPos(value: string, index: number): boolean {
|
||||
if (index < 0 || index >= value.length) {
|
||||
return false;
|
||||
}
|
||||
const code = value.charCodeAt(index);
|
||||
switch (code) {
|
||||
case CharCode.Space:
|
||||
case CharCode.Tab:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 }
|
||||
|
||||
@@ -445,7 +472,7 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
patternMaxWhitespaceIgnore = patternLen;
|
||||
}
|
||||
while (patternStartPos < patternMaxWhitespaceIgnore) {
|
||||
if (_ws[pattern[patternStartPos]]) {
|
||||
if (isWhitespaceAtPos(pattern, patternStartPos)) {
|
||||
patternStartPos += 1;
|
||||
} else {
|
||||
break;
|
||||
@@ -481,8 +508,6 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
// There will be a mach, fill in tables
|
||||
for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
|
||||
|
||||
let lastLowWordChar = '';
|
||||
|
||||
for (wordPos = 1; wordPos <= wordLen; wordPos++) {
|
||||
|
||||
let score = -1;
|
||||
@@ -502,7 +527,7 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
} else {
|
||||
score = 5;
|
||||
}
|
||||
} else if (_seps[lastLowWordChar]) {
|
||||
} else if (isSeparatorAtPos(lowWord, wordPos - 2)) {
|
||||
// post separator: `foo <-> bar_foo`
|
||||
score = 5;
|
||||
|
||||
@@ -542,8 +567,6 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
_arrows[patternPos][wordPos] = Arrow.Diag;
|
||||
}
|
||||
}
|
||||
|
||||
lastLowWordChar = lowWordChar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,25 +579,26 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
// _bucket is an array of [PrefixArray] we use to keep
|
||||
// track of scores and matches. After calling `_findAllMatches`
|
||||
// the best match (if available) is the first item in the array
|
||||
_bucket.length = 0;
|
||||
_matchesCount = 0;
|
||||
_topScore = -100;
|
||||
_patternStartPos = patternStartPos;
|
||||
_findAllMatches(patternLen, wordLen, 0, new LazyArray(), false);
|
||||
|
||||
if (_bucket.length === 0) {
|
||||
if (_matchesCount === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [_topScore, _bucket[0].toArray()];
|
||||
return [_topScore, _topMatch.toArray()];
|
||||
}
|
||||
|
||||
let _bucket: LazyArray[] = [];
|
||||
let _matchesCount: number = 0;
|
||||
let _topMatch: LazyArray;
|
||||
let _topScore: number = 0;
|
||||
let _patternStartPos: number = 0;
|
||||
|
||||
function _findAllMatches(patternPos: number, wordPos: number, total: number, matches: LazyArray, lastMatched: boolean): void {
|
||||
|
||||
if (_bucket.length >= 10 || total < -25) {
|
||||
if (_matchesCount >= 10 || total < -25) {
|
||||
// stop when having already 10 results, or
|
||||
// when a potential alignment as already 5 gaps
|
||||
return;
|
||||
@@ -645,11 +669,10 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat
|
||||
|
||||
// dynamically keep track of the current top score
|
||||
// and insert the current best score at head, the rest at tail
|
||||
_matchesCount += 1;
|
||||
if (total > _topScore) {
|
||||
_topScore = total;
|
||||
_bucket.unshift(matches);
|
||||
} else {
|
||||
_bucket.push(matches);
|
||||
_topMatch = matches;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ export interface IExpression {
|
||||
[pattern: string]: boolean | SiblingClause | any;
|
||||
}
|
||||
|
||||
export interface IRelativePattern {
|
||||
base: string;
|
||||
pattern: string;
|
||||
}
|
||||
|
||||
export function getEmptyExpression(): IExpression {
|
||||
return Object.create(null);
|
||||
}
|
||||
@@ -28,6 +33,8 @@ export interface SiblingClause {
|
||||
when: string;
|
||||
}
|
||||
|
||||
const GLOBSTAR = '**';
|
||||
const GLOB_SPLIT = '/';
|
||||
const PATH_REGEX = '[/\\\\]'; // any slash or backslash
|
||||
const NO_PATH_REGEX = '[^/\\\\]'; // any non-slash and non-backslash
|
||||
const ALL_FORWARD_SLASHES = /\//g;
|
||||
@@ -103,10 +110,10 @@ function parseRegExp(pattern: string): string {
|
||||
let regEx = '';
|
||||
|
||||
// Split up into segments for each slash found
|
||||
let segments = splitGlobAware(pattern, '/');
|
||||
let segments = splitGlobAware(pattern, GLOB_SPLIT);
|
||||
|
||||
// Special case where we only have globstars
|
||||
if (segments.every(s => s === '**')) {
|
||||
if (segments.every(s => s === GLOBSTAR)) {
|
||||
regEx = '.*';
|
||||
}
|
||||
|
||||
@@ -116,7 +123,7 @@ function parseRegExp(pattern: string): string {
|
||||
segments.forEach((segment, index) => {
|
||||
|
||||
// Globstar is special
|
||||
if (segment === '**') {
|
||||
if (segment === GLOBSTAR) {
|
||||
|
||||
// if we have more than one globstar after another, just ignore it
|
||||
if (!previousSegmentWasGlobStar) {
|
||||
@@ -207,7 +214,7 @@ function parseRegExp(pattern: string): string {
|
||||
}
|
||||
|
||||
// Tail: Add the slash we had split on if there is more to come and the next one is not a globstar
|
||||
if (index < segments.length - 1 && segments[index + 1] !== '**') {
|
||||
if (index < segments.length - 1 && segments[index + 1] !== GLOBSTAR) {
|
||||
regEx += PATH_REGEX;
|
||||
}
|
||||
|
||||
@@ -264,11 +271,19 @@ const NULL = function (): string {
|
||||
return null;
|
||||
};
|
||||
|
||||
function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPattern {
|
||||
if (!pattern) {
|
||||
function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): ParsedStringPattern {
|
||||
if (!arg1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Handle IRelativePattern
|
||||
let pattern: string;
|
||||
if (typeof arg1 !== 'string') {
|
||||
pattern = arg1.pattern;
|
||||
} else {
|
||||
pattern = arg1;
|
||||
}
|
||||
|
||||
// Whitespace trimming
|
||||
pattern = pattern.trim();
|
||||
|
||||
@@ -276,7 +291,7 @@ function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPatte
|
||||
const patternKey = `${pattern}_${!!options.trimForExclusions}`;
|
||||
let parsedPattern = CACHE.get(patternKey);
|
||||
if (parsedPattern) {
|
||||
return parsedPattern;
|
||||
return wrapRelativePattern(parsedPattern, arg1);
|
||||
}
|
||||
|
||||
// Check for Trivias
|
||||
@@ -304,7 +319,21 @@ function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPatte
|
||||
// Cache
|
||||
CACHE.set(patternKey, parsedPattern);
|
||||
|
||||
return parsedPattern;
|
||||
return wrapRelativePattern(parsedPattern, arg1);
|
||||
}
|
||||
|
||||
function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string | IRelativePattern): ParsedStringPattern {
|
||||
if (typeof arg2 === 'string') {
|
||||
return parsedPattern;
|
||||
}
|
||||
|
||||
return function (path, basename) {
|
||||
if (!paths.isEqualOrParent(path, arg2.base)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsedPattern(paths.relative(arg2.base, path), basename);
|
||||
};
|
||||
}
|
||||
|
||||
function trimForExclusions(pattern: string, options: IGlobOptions): string {
|
||||
@@ -395,9 +424,9 @@ function toRegExp(pattern: string): ParsedStringPattern {
|
||||
* - simple brace expansion ({js,ts} => js or ts)
|
||||
* - character ranges (using [...])
|
||||
*/
|
||||
export function match(pattern: string, path: string): boolean;
|
||||
export function match(pattern: string | IRelativePattern, path: string): boolean;
|
||||
export function match(expression: IExpression, path: string, siblingsFn?: () => string[]): string /* the matching pattern */;
|
||||
export function match(arg1: string | IExpression, path: string, siblingsFn?: () => string[]): any {
|
||||
export function match(arg1: string | IExpression | IRelativePattern, path: string, siblingsFn?: () => string[]): any {
|
||||
if (!arg1 || !path) {
|
||||
return false;
|
||||
}
|
||||
@@ -413,16 +442,16 @@ export function match(arg1: string | IExpression, path: string, siblingsFn?: ()
|
||||
* - simple brace expansion ({js,ts} => js or ts)
|
||||
* - character ranges (using [...])
|
||||
*/
|
||||
export function parse(pattern: string, options?: IGlobOptions): ParsedPattern;
|
||||
export function parse(pattern: string | IRelativePattern, options?: IGlobOptions): ParsedPattern;
|
||||
export function parse(expression: IExpression, options?: IGlobOptions): ParsedExpression;
|
||||
export function parse(arg1: string | IExpression, options: IGlobOptions = {}): any {
|
||||
export function parse(arg1: string | IExpression | IRelativePattern, options: IGlobOptions = {}): any {
|
||||
if (!arg1) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Glob with String
|
||||
if (typeof arg1 === 'string') {
|
||||
const parsedPattern = parsePattern(arg1, options);
|
||||
if (typeof arg1 === 'string' || isRelativePattern(arg1)) {
|
||||
const parsedPattern = parsePattern(arg1 as string | IRelativePattern, options);
|
||||
if (parsedPattern === NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
@@ -442,6 +471,12 @@ export function parse(arg1: string | IExpression, options: IGlobOptions = {}): a
|
||||
return parsedExpression(<IExpression>arg1, options);
|
||||
}
|
||||
|
||||
function isRelativePattern(obj: any): obj is IRelativePattern {
|
||||
const rp = obj as IRelativePattern;
|
||||
|
||||
return typeof rp.base === 'string' && typeof rp.pattern === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `parse`, but the ParsedExpression is guaranteed to return a Promise
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ export class Graph<T> {
|
||||
}
|
||||
|
||||
roots(): Node<T>[] {
|
||||
var ret: Node<T>[] = [];
|
||||
const ret: Node<T>[] = [];
|
||||
forEach(this._nodes, entry => {
|
||||
if (isEmptyObject(entry.value.outgoing)) {
|
||||
ret.push(entry.value);
|
||||
@@ -40,7 +40,7 @@ export class Graph<T> {
|
||||
}
|
||||
|
||||
traverse(start: T, inwards: boolean, callback: (data: T) => void): void {
|
||||
var startNode = this.lookup(start);
|
||||
const startNode = this.lookup(start);
|
||||
if (!startNode) {
|
||||
return;
|
||||
}
|
||||
@@ -48,18 +48,18 @@ export class Graph<T> {
|
||||
}
|
||||
|
||||
private _traverse(node: Node<T>, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void {
|
||||
var key = this._hashFn(node.data);
|
||||
const key = this._hashFn(node.data);
|
||||
if (seen[key]) {
|
||||
return;
|
||||
}
|
||||
seen[key] = true;
|
||||
callback(node.data);
|
||||
var nodes = inwards ? node.outgoing : node.incoming;
|
||||
const nodes = inwards ? node.outgoing : node.incoming;
|
||||
forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback));
|
||||
}
|
||||
|
||||
insertEdge(from: T, to: T): void {
|
||||
var fromNode = this.lookupOrInsertNode(from),
|
||||
const fromNode = this.lookupOrInsertNode(from),
|
||||
toNode = this.lookupOrInsertNode(to);
|
||||
|
||||
fromNode.outgoing[this._hashFn(to)] = toNode;
|
||||
@@ -67,7 +67,7 @@ export class Graph<T> {
|
||||
}
|
||||
|
||||
removeNode(data: T): void {
|
||||
var key = this._hashFn(data);
|
||||
const key = this._hashFn(data);
|
||||
delete this._nodes[key];
|
||||
forEach(this._nodes, (entry) => {
|
||||
delete entry.value.outgoing[key];
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
'use strict';
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { marked } from 'vs/base/common/marked/marked';
|
||||
|
||||
export interface IMarkdownString {
|
||||
value: string;
|
||||
@@ -56,7 +55,7 @@ export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownStri
|
||||
export function isMarkdownString(thing: any): thing is IMarkdownString {
|
||||
if (thing instanceof MarkdownString) {
|
||||
return true;
|
||||
} else if (typeof thing === 'object') {
|
||||
} else if (thing && typeof thing === 'object') {
|
||||
return typeof (<IMarkdownString>thing).value === 'string'
|
||||
&& (typeof (<IMarkdownString>thing).isTrusted === 'boolean' || (<IMarkdownString>thing).isTrusted === void 0);
|
||||
}
|
||||
@@ -93,16 +92,3 @@ export function removeMarkdownEscapes(text: string): string {
|
||||
}
|
||||
return text.replace(/\\([\\`*_{}[\]()#+\-.!])/g, '$1');
|
||||
}
|
||||
|
||||
export function containsCommandLink(value: string): boolean {
|
||||
let uses = false;
|
||||
const renderer = new marked.Renderer();
|
||||
renderer.link = (href, title, text): string => {
|
||||
if (href.match(/^command:/i)) {
|
||||
uses = true;
|
||||
}
|
||||
return 'link';
|
||||
};
|
||||
marked(value, { renderer });
|
||||
return uses;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
export interface IIterator<T> {
|
||||
export interface IIterator<E> {
|
||||
next(): { done: boolean, value: E };
|
||||
}
|
||||
|
||||
export interface INextIterator<T> {
|
||||
next(): T;
|
||||
}
|
||||
|
||||
export class ArrayIterator<T> implements IIterator<T> {
|
||||
export class ArrayIterator<T> implements INextIterator<T> {
|
||||
|
||||
private items: T[];
|
||||
protected start: number;
|
||||
@@ -73,16 +77,16 @@ export class ArrayNavigator<T> extends ArrayIterator<T> implements INavigator<T>
|
||||
|
||||
}
|
||||
|
||||
export class MappedIterator<T, R> implements IIterator<R> {
|
||||
export class MappedIterator<T, R> implements INextIterator<R> {
|
||||
|
||||
constructor(protected iterator: IIterator<T>, protected fn: (item: T) => R) {
|
||||
constructor(protected iterator: INextIterator<T>, protected fn: (item: T) => R) {
|
||||
// noop
|
||||
}
|
||||
|
||||
next() { return this.fn(this.iterator.next()); }
|
||||
}
|
||||
|
||||
export interface INavigator<T> extends IIterator<T> {
|
||||
export interface INavigator<T> extends INextIterator<T> {
|
||||
current(): T;
|
||||
previous(): T;
|
||||
parent(): T;
|
||||
|
||||
@@ -837,7 +837,7 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onArrayBegin: () => {
|
||||
let array = [];
|
||||
let array: any[] = [];
|
||||
onValue(array);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = array;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
export interface IJSONSchema {
|
||||
id?: string;
|
||||
$id?: string;
|
||||
$schema?: string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
@@ -17,19 +18,19 @@ export interface IJSONSchema {
|
||||
additionalProperties?: boolean | IJSONSchema;
|
||||
minProperties?: number;
|
||||
maxProperties?: number;
|
||||
dependencies?: IJSONSchemaMap | string[];
|
||||
dependencies?: IJSONSchemaMap | { [prop: string]: string[] };
|
||||
items?: IJSONSchema | IJSONSchema[];
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
additionalItems?: boolean;
|
||||
additionalItems?: boolean | IJSONSchema;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
exclusiveMinimum?: boolean;
|
||||
exclusiveMaximum?: boolean;
|
||||
exclusiveMinimum?: boolean | number;
|
||||
exclusiveMaximum?: boolean | number;
|
||||
multipleOf?: number;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
@@ -40,11 +41,20 @@ export interface IJSONSchema {
|
||||
enum?: any[];
|
||||
format?: string;
|
||||
|
||||
// schema draft 06
|
||||
const?: any;
|
||||
contains?: IJSONSchema;
|
||||
propertyNames?: IJSONSchema;
|
||||
|
||||
// VSCode extensions
|
||||
defaultSnippets?: IJSONSchemaSnippet[]; // VSCode extension
|
||||
errorMessage?: string; // VSCode extension
|
||||
patternErrorMessage?: string; // VSCode extension
|
||||
deprecationMessage?: string; // VSCode extension
|
||||
enumDescriptions?: string[]; // VSCode extension
|
||||
markdownEnumDescriptions?: string[]; // VSCode extension
|
||||
markdownDescription?: string; // VSCode extension
|
||||
doNotSuggest?: boolean; // VSCode extension
|
||||
}
|
||||
|
||||
export interface IJSONSchemaMap {
|
||||
|
||||
@@ -17,10 +17,10 @@ export interface ILabelProvider {
|
||||
getLabel(element: any): string;
|
||||
}
|
||||
|
||||
export interface IRootProvider {
|
||||
getRoot(resource: URI): URI;
|
||||
export interface IWorkspaceFolderProvider {
|
||||
getWorkspaceFolder(resource: URI): { uri: URI };
|
||||
getWorkspace(): {
|
||||
roots: URI[];
|
||||
folders: { uri: URI }[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface IUserHomeProvider {
|
||||
userHome: string;
|
||||
}
|
||||
|
||||
export function getPathLabel(resource: URI | string, rootProvider?: IRootProvider, userHomeProvider?: IUserHomeProvider): string {
|
||||
export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFolderProvider, userHomeProvider?: IUserHomeProvider): string {
|
||||
if (!resource) {
|
||||
return null;
|
||||
}
|
||||
@@ -37,20 +37,24 @@ export function getPathLabel(resource: URI | string, rootProvider?: IRootProvide
|
||||
resource = URI.file(resource);
|
||||
}
|
||||
|
||||
if (resource.scheme !== 'file' && resource.scheme !== 'untitled') {
|
||||
return resource.authority + resource.path;
|
||||
}
|
||||
|
||||
// return early if we can resolve a relative path label from the root
|
||||
const baseResource = rootProvider ? rootProvider.getRoot(resource) : null;
|
||||
const baseResource = rootProvider ? rootProvider.getWorkspaceFolder(resource) : null;
|
||||
if (baseResource) {
|
||||
const hasMultipleRoots = rootProvider.getWorkspace().roots.length > 1;
|
||||
const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1;
|
||||
|
||||
let pathLabel: string;
|
||||
if (isEqual(baseResource.fsPath, resource.fsPath, !platform.isLinux /* ignorecase */)) {
|
||||
if (isEqual(baseResource.uri.fsPath, resource.fsPath, !platform.isLinux /* ignorecase */)) {
|
||||
pathLabel = ''; // no label if pathes are identical
|
||||
} else {
|
||||
pathLabel = normalize(ltrim(resource.fsPath.substr(baseResource.fsPath.length), nativeSep), true);
|
||||
pathLabel = normalize(ltrim(resource.fsPath.substr(baseResource.uri.fsPath.length), nativeSep), true);
|
||||
}
|
||||
|
||||
if (hasMultipleRoots) {
|
||||
const rootName = basename(baseResource.fsPath);
|
||||
const rootName = basename(baseResource.uri.fsPath);
|
||||
pathLabel = pathLabel ? join(rootName, pathLabel) : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
|
||||
@@ -58,8 +62,8 @@ export function getPathLabel(resource: URI | string, rootProvider?: IRootProvide
|
||||
}
|
||||
|
||||
// convert c:\something => C:\something
|
||||
if (platform.isWindows && resource.fsPath && resource.fsPath[1] === ':') {
|
||||
return normalize(resource.fsPath.charAt(0).toUpperCase() + resource.fsPath.slice(1), true);
|
||||
if (hasDriveLetter(resource.fsPath)) {
|
||||
return normalize(normalizeDriveLetter(resource.fsPath), true);
|
||||
}
|
||||
|
||||
// normalize and tildify (macOS, Linux only)
|
||||
@@ -71,6 +75,18 @@ export function getPathLabel(resource: URI | string, rootProvider?: IRootProvide
|
||||
return res;
|
||||
}
|
||||
|
||||
function hasDriveLetter(path: string): boolean {
|
||||
return platform.isWindows && path && path[1] === ':';
|
||||
}
|
||||
|
||||
export function normalizeDriveLetter(path: string): string {
|
||||
if (hasDriveLetter(path)) {
|
||||
return path.charAt(0).toUpperCase() + path.slice(1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
export function tildify(path: string, userHome: string): string {
|
||||
if (path && (platform.isMacintosh || platform.isLinux) && isEqualOrParent(path, userHome, !platform.isLinux /* ignorecase */)) {
|
||||
path = `~${path.substr(userHome.length)}`;
|
||||
|
||||
@@ -15,6 +15,11 @@ export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export function isDisposable<E extends object>(thing: E): thing is E & IDisposable {
|
||||
return typeof (<IDisposable><any>thing).dispose === 'function'
|
||||
&& (<IDisposable><any>thing).dispose.length === 0;
|
||||
}
|
||||
|
||||
export function dispose<T extends IDisposable>(disposable: T): T;
|
||||
export function dispose<T extends IDisposable>(...disposables: T[]): T[];
|
||||
export function dispose<T extends IDisposable>(disposables: T[]): T[];
|
||||
@@ -68,22 +73,6 @@ export abstract class Disposable implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class OneDisposable implements IDisposable {
|
||||
|
||||
private _value: IDisposable;
|
||||
|
||||
set value(value: IDisposable) {
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IReference<T> extends IDisposable {
|
||||
readonly object: T;
|
||||
}
|
||||
|
||||
122
src/vs/base/common/linkedList.ts
Normal file
122
src/vs/base/common/linkedList.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IIterator } from 'vs/base/common/iterator';
|
||||
|
||||
class Node<E> {
|
||||
element: E;
|
||||
next: Node<E>;
|
||||
prev: Node<E>;
|
||||
|
||||
constructor(element: E) {
|
||||
this.element = element;
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkedList<E> {
|
||||
|
||||
private _first: Node<E>;
|
||||
private _last: Node<E>;
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this._first;
|
||||
}
|
||||
|
||||
unshift(element: E) {
|
||||
return this.insert(element, false);
|
||||
}
|
||||
|
||||
push(element: E) {
|
||||
return this.insert(element, true);
|
||||
}
|
||||
|
||||
private insert(element: E, atTheEnd: boolean) {
|
||||
const newNode = new Node(element);
|
||||
if (!this._first) {
|
||||
this._first = newNode;
|
||||
this._last = newNode;
|
||||
|
||||
} else if (atTheEnd) {
|
||||
// push
|
||||
const oldLast = this._last;
|
||||
this._last = newNode;
|
||||
newNode.prev = oldLast;
|
||||
oldLast.next = newNode;
|
||||
|
||||
} else {
|
||||
// unshift
|
||||
const oldFirst = this._first;
|
||||
this._first = newNode;
|
||||
newNode.next = oldFirst;
|
||||
oldFirst.prev = newNode;
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
||||
for (let candidate = this._first; candidate instanceof Node; candidate = candidate.next) {
|
||||
if (candidate !== newNode) {
|
||||
continue;
|
||||
}
|
||||
if (candidate.prev && candidate.next) {
|
||||
// middle
|
||||
let anchor = candidate.prev;
|
||||
anchor.next = candidate.next;
|
||||
candidate.next.prev = anchor;
|
||||
|
||||
} else if (!candidate.prev && !candidate.next) {
|
||||
// only node
|
||||
this._first = undefined;
|
||||
this._last = undefined;
|
||||
|
||||
} else if (!candidate.next) {
|
||||
// last
|
||||
this._last = this._last.prev;
|
||||
this._last.next = undefined;
|
||||
|
||||
} else if (!candidate.prev) {
|
||||
// first
|
||||
this._first = this._first.next;
|
||||
this._first.prev = undefined;
|
||||
}
|
||||
|
||||
// done
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
iterator(): IIterator<E> {
|
||||
let _done: boolean;
|
||||
let _value: E;
|
||||
let element = {
|
||||
get done() { return _done; },
|
||||
get value() { return _value; }
|
||||
};
|
||||
let node = this._first;
|
||||
return {
|
||||
next(): { done: boolean; value: E } {
|
||||
if (!node) {
|
||||
_done = true;
|
||||
_value = undefined;
|
||||
} else {
|
||||
_done = false;
|
||||
_value = node.element;
|
||||
node = node.next;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
toArray(): E[] {
|
||||
let result: E[] = [];
|
||||
for (let node = this._first; node instanceof Node; node = node.next) {
|
||||
result.push(node.element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -220,110 +220,315 @@ export class BoundedMap<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// --- trie'ish datastructure
|
||||
export interface IKeyIterator {
|
||||
reset(key: string): this;
|
||||
next(): this;
|
||||
join(parts: string[]): string;
|
||||
|
||||
class Node<E> {
|
||||
element?: E;
|
||||
readonly children = new Map<string, Node<E>>();
|
||||
hasNext(): boolean;
|
||||
cmp(a: string): number;
|
||||
value(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A trie map that allows for fast look up when keys are substrings
|
||||
* to the actual search keys (dir/subdir-problem).
|
||||
*/
|
||||
export class TrieMap<E> {
|
||||
export class StringIterator implements IKeyIterator {
|
||||
|
||||
static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s);
|
||||
private _value: string = '';
|
||||
private _pos: number = 0;
|
||||
|
||||
private readonly _splitter: (s: string) => string[];
|
||||
private _root = new Node<E>();
|
||||
|
||||
constructor(splitter: (s: string) => string[] = TrieMap.PathSplitter) {
|
||||
this._splitter = s => splitter(s).filter(s => Boolean(s));
|
||||
reset(key: string): this {
|
||||
this._value = key;
|
||||
this._pos = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
insert(path: string, element: E): void {
|
||||
const parts = this._splitter(path);
|
||||
let i = 0;
|
||||
next(): this {
|
||||
this._pos += 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// find insertion node
|
||||
let node = this._root;
|
||||
for (; i < parts.length; i++) {
|
||||
let child = node.children.get(parts[i]);
|
||||
if (child) {
|
||||
node = child;
|
||||
continue;
|
||||
join(parts: string[]): string {
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
hasNext(): boolean {
|
||||
return this._pos < this._value.length - 1;
|
||||
}
|
||||
|
||||
cmp(a: string): number {
|
||||
let aCode = a.charCodeAt(0);
|
||||
let thisCode = this._value.charCodeAt(this._pos);
|
||||
return aCode - thisCode;
|
||||
}
|
||||
|
||||
value(): string {
|
||||
return this._value[this._pos];
|
||||
}
|
||||
}
|
||||
|
||||
export class PathIterator implements IKeyIterator {
|
||||
|
||||
private static _fwd = '/'.charCodeAt(0);
|
||||
private static _bwd = '\\'.charCodeAt(0);
|
||||
|
||||
private _value: string;
|
||||
private _from: number;
|
||||
private _to: number;
|
||||
|
||||
reset(key: string): this {
|
||||
this._value = key.replace(/\\$|\/$/, '');
|
||||
this._from = 0;
|
||||
this._to = 0;
|
||||
return this.next();
|
||||
}
|
||||
|
||||
hasNext(): boolean {
|
||||
return this._to < this._value.length;
|
||||
}
|
||||
|
||||
join(parts: string[]): string {
|
||||
return parts.join('/');
|
||||
}
|
||||
|
||||
next(): this {
|
||||
// this._data = key.split(/[\\/]/).filter(s => !!s);
|
||||
this._from = this._to;
|
||||
let justSeps = true;
|
||||
for (; this._to < this._value.length; this._to++) {
|
||||
const ch = this._value.charCodeAt(this._to);
|
||||
if (ch === PathIterator._fwd || ch === PathIterator._bwd) {
|
||||
if (justSeps) {
|
||||
this._from++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
justSeps = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
cmp(a: string): number {
|
||||
|
||||
let aPos = 0;
|
||||
let aLen = a.length;
|
||||
let thisPos = this._from;
|
||||
|
||||
while (aPos < aLen && thisPos < this._to) {
|
||||
let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
|
||||
if (cmp !== 0) {
|
||||
return cmp;
|
||||
}
|
||||
aPos += 1;
|
||||
thisPos += 1;
|
||||
}
|
||||
|
||||
// create new nodes
|
||||
let newNode: Node<E>;
|
||||
for (; i < parts.length; i++) {
|
||||
newNode = new Node<E>();
|
||||
node.children.set(parts[i], newNode);
|
||||
node = newNode;
|
||||
if (aLen === this._to - this._from) {
|
||||
return 0;
|
||||
} else if (aPos < aLen) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
value(): string {
|
||||
return this._value.substring(this._from, this._to);
|
||||
}
|
||||
}
|
||||
|
||||
class TernarySearchTreeNode<E> {
|
||||
str: string;
|
||||
element: E;
|
||||
left: TernarySearchTreeNode<E>;
|
||||
mid: TernarySearchTreeNode<E>;
|
||||
right: TernarySearchTreeNode<E>;
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this.left && !this.mid && !this.right && !this.element;
|
||||
}
|
||||
}
|
||||
|
||||
export class TernarySearchTree<E> {
|
||||
|
||||
static forPaths<E>(): TernarySearchTree<E> {
|
||||
return new TernarySearchTree<E>(new PathIterator());
|
||||
}
|
||||
|
||||
static forStrings<E>(): TernarySearchTree<E> {
|
||||
return new TernarySearchTree<E>(new StringIterator());
|
||||
}
|
||||
|
||||
private _iter: IKeyIterator;
|
||||
private _root: TernarySearchTreeNode<E>;
|
||||
|
||||
constructor(segments: IKeyIterator) {
|
||||
this._iter = segments;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._root = undefined;
|
||||
}
|
||||
|
||||
set(key: string, element: E): void {
|
||||
let iter = this._iter.reset(key);
|
||||
let node: TernarySearchTreeNode<E>;
|
||||
|
||||
if (!this._root) {
|
||||
this._root = new TernarySearchTreeNode<E>();
|
||||
this._root.str = iter.value();
|
||||
}
|
||||
|
||||
node = this._root;
|
||||
while (true) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
if (!node.left) {
|
||||
node.left = new TernarySearchTreeNode<E>();
|
||||
node.left.str = iter.value();
|
||||
}
|
||||
node = node.left;
|
||||
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
if (!node.right) {
|
||||
node.right = new TernarySearchTreeNode<E>();
|
||||
node.right.str = iter.value();
|
||||
}
|
||||
node = node.right;
|
||||
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
if (!node.mid) {
|
||||
node.mid = new TernarySearchTreeNode<E>();
|
||||
node.mid.str = iter.value();
|
||||
}
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node.element = element;
|
||||
}
|
||||
|
||||
lookUp(path: string): E {
|
||||
const parts = this._splitter(path);
|
||||
|
||||
let { children } = this._root;
|
||||
let node: Node<E>;
|
||||
for (const part of parts) {
|
||||
node = children.get(part);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
children = node.children;
|
||||
}
|
||||
|
||||
return node.element;
|
||||
}
|
||||
|
||||
findSubstr(path: string): E {
|
||||
const parts = this._splitter(path);
|
||||
|
||||
let lastNode: Node<E>;
|
||||
let { children } = this._root;
|
||||
for (const part of parts) {
|
||||
const node = children.get(part);
|
||||
if (!node) {
|
||||
get(key: string): E {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (node.element) {
|
||||
lastNode = node;
|
||||
}
|
||||
children = node.children;
|
||||
}
|
||||
return node ? node.element : undefined;
|
||||
}
|
||||
|
||||
delete(key: string): void {
|
||||
this._delete(this._root, this._iter.reset(key));
|
||||
}
|
||||
|
||||
private _delete(node: TernarySearchTreeNode<E>, iter: IKeyIterator): TernarySearchTreeNode<E> {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
const cmp = iter.cmp(node.str);
|
||||
if (cmp > 0) {
|
||||
// left
|
||||
node.left = this._delete(node.left, iter);
|
||||
} else if (cmp < 0) {
|
||||
// right
|
||||
node.right = this._delete(node.right, iter);
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
node.mid = this._delete(node.mid, iter.next());
|
||||
} else {
|
||||
// remove element
|
||||
node.element = undefined;
|
||||
}
|
||||
|
||||
// return the last matching node
|
||||
// that had an element
|
||||
if (lastNode) {
|
||||
return lastNode.element;
|
||||
return node.isEmpty() ? undefined : node;
|
||||
}
|
||||
|
||||
findSubstr(key: string): E {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
let candidate: E;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
candidate = node.element || candidate;
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node && node.element || candidate;
|
||||
}
|
||||
|
||||
findSuperstr(key: string): TernarySearchTree<E> {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
} else {
|
||||
// collect
|
||||
if (!node.mid) {
|
||||
return undefined;
|
||||
}
|
||||
let ret = new TernarySearchTree<E>(this._iter);
|
||||
ret._root = node.mid;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
findSuperstr(path: string): TrieMap<E> {
|
||||
const parts = this._splitter(path);
|
||||
forEach(callback: (value: E, index: string) => any) {
|
||||
this._forEach(this._root, [], callback);
|
||||
}
|
||||
|
||||
let { children } = this._root;
|
||||
let node: Node<E>;
|
||||
for (const part of parts) {
|
||||
node = children.get(part);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
children = node.children;
|
||||
private _forEach(node: TernarySearchTreeNode<E>, parts: string[], callback: (value: E, index: string) => any) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = new TrieMap<E>(this._splitter);
|
||||
result._root = node;
|
||||
return result;
|
||||
this._forEach(node.left, parts, callback);
|
||||
this._forEach(node.right, parts, callback);
|
||||
let newParts = parts.slice();
|
||||
newParts.push(node.str);
|
||||
if (node.element) {
|
||||
callback(node.element, this._iter.join(newParts));
|
||||
}
|
||||
this._forEach(node.mid, newParts, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,5 +38,7 @@ export namespace Schemas {
|
||||
|
||||
export const file: string = 'file';
|
||||
|
||||
export const mailto: string = 'mailto';
|
||||
|
||||
export const untitled: string = 'untitled';
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ export type NumberCallback = (index: number) => void;
|
||||
export function count(to: number, callback: NumberCallback): void;
|
||||
export function count(from: number, to: number, callback: NumberCallback): void;
|
||||
export function count(fromOrTo: number, toOrCallback?: NumberCallback | number, callback?: NumberCallback): any {
|
||||
var from: number, to: number;
|
||||
let from: number;
|
||||
let to: number;
|
||||
|
||||
if (types.isNumber(toOrCallback)) {
|
||||
from = fromOrTo;
|
||||
@@ -23,10 +24,10 @@ export function count(fromOrTo: number, toOrCallback?: NumberCallback | number,
|
||||
callback = <NumberCallback>toOrCallback;
|
||||
}
|
||||
|
||||
var op = from <= to ? (i: number) => i + 1 : (i: number) => i - 1;
|
||||
var cmp = from <= to ? (a: number, b: number) => a < b : (a: number, b: number) => a > b;
|
||||
const op = from <= to ? (i: number) => i + 1 : (i: number) => i - 1;
|
||||
const cmp = from <= to ? (a: number, b: number) => a < b : (a: number, b: number) => a > b;
|
||||
|
||||
for (var i = from; cmp(i, to); i = op(i)) {
|
||||
for (let i = from; cmp(i, to); i = op(i)) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
@@ -34,8 +35,8 @@ export function count(fromOrTo: number, toOrCallback?: NumberCallback | number,
|
||||
export function countToArray(to: number): number[];
|
||||
export function countToArray(from: number, to: number): number[];
|
||||
export function countToArray(fromOrTo: number, to?: number): number[] {
|
||||
var result: number[] = [];
|
||||
var fn = (i: number) => result.push(i);
|
||||
const result: number[] = [];
|
||||
const fn = (i: number) => result.push(i);
|
||||
|
||||
if (types.isUndefined(to)) {
|
||||
count(fromOrTo, fn);
|
||||
@@ -45,3 +46,12 @@ export function countToArray(fromOrTo: number, to?: number): number[] {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export function clamp(value: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
export function rot(index: number, modulo: number): number {
|
||||
return (modulo + (index % modulo)) % modulo;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export function clone<T>(obj: T): T {
|
||||
return obj as any;
|
||||
}
|
||||
const result = (Array.isArray(obj)) ? <any>[] : <any>{};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (obj[key] && typeof obj[key] === 'object') {
|
||||
result[key] = clone(obj[key]);
|
||||
} else {
|
||||
@@ -31,7 +31,7 @@ export function deepClone<T>(obj: T): T {
|
||||
return obj;
|
||||
}
|
||||
const result = (Array.isArray(obj)) ? <any>[] : <any>{};
|
||||
Object.getOwnPropertyNames(obj).forEach((key) => {
|
||||
Object.getOwnPropertyNames(obj).forEach(key => {
|
||||
if (obj[key] && typeof obj[key] === 'object') {
|
||||
result[key] = deepClone(obj[key]);
|
||||
} else {
|
||||
@@ -93,7 +93,7 @@ export function mixin(destination: any, source: any, overwrite: boolean = true):
|
||||
}
|
||||
|
||||
if (isObject(source)) {
|
||||
Object.keys(source).forEach((key) => {
|
||||
Object.keys(source).forEach(key => {
|
||||
if (key in destination) {
|
||||
if (overwrite) {
|
||||
if (isObject(destination[key]) && isObject(source[key])) {
|
||||
@@ -111,7 +111,7 @@ export function mixin(destination: any, source: any, overwrite: boolean = true):
|
||||
}
|
||||
|
||||
export function assign(destination: any, ...sources: any[]): any {
|
||||
sources.forEach(source => Object.keys(source).forEach((key) => destination[key] = source[key]));
|
||||
sources.forEach(source => Object.keys(source).forEach(key => destination[key] = source[key]));
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
44
src/vs/base/common/resources.ts
Normal file
44
src/vs/base/common/resources.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 paths from 'vs/base/common/paths';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
export function basenameOrAuthority(resource: uri): string {
|
||||
return paths.basename(resource.fsPath) || resource.authority;
|
||||
}
|
||||
|
||||
export function isEqualOrParent(first: uri, second: uri, ignoreCase?: boolean): boolean {
|
||||
if (first.scheme === second.scheme && first.authority === second.authority) {
|
||||
return paths.isEqualOrParent(first.fsPath, second.fsPath, ignoreCase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean {
|
||||
const identityEquals = (first === second);
|
||||
if (identityEquals) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!first || !second) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
return equalsIgnoreCase(first.toString(), second.toString());
|
||||
}
|
||||
|
||||
return first.toString() === second.toString();
|
||||
}
|
||||
|
||||
export function dirname(resource: uri): uri {
|
||||
return resource.with({
|
||||
path: paths.dirname(resource.path)
|
||||
});
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Based on material from:
|
||||
/*!
|
||||
BEGIN THIRD PARTY
|
||||
*/
|
||||
/*!
|
||||
* string_score.js: String Scoring Algorithm 0.1.22
|
||||
*
|
||||
* http://joshaven.com/string_score
|
||||
* https://github.com/joshaven/string_score
|
||||
*
|
||||
* Copyright (C) 2009-2014 Joshaven Potter <yourtech@gmail.com>
|
||||
* Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
|
||||
* Source EULA: http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Date: Tue Mar 1 2011
|
||||
* Updated: Tue Mar 10 2015
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute a score for the given string and the given query.
|
||||
*
|
||||
* Rules:
|
||||
* Character score: 1
|
||||
* Same case bonus: 1
|
||||
* Upper case bonus: 1
|
||||
* Consecutive match bonus: 5
|
||||
* Start of word/path bonus: 7
|
||||
* Start of string bonus: 8
|
||||
*/
|
||||
const wordPathBoundary = ['-', '_', ' ', '/', '\\', '.'];
|
||||
export function score(target: string, query: string, cache?: { [id: string]: number }): number {
|
||||
if (!target || !query) {
|
||||
return 0; // return early if target or query are undefined
|
||||
}
|
||||
|
||||
const hash = target + query;
|
||||
const cached = cache && cache[hash];
|
||||
if (typeof cached === 'number') {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const queryLen = query.length;
|
||||
const targetLower = target.toLowerCase();
|
||||
const queryLower = query.toLowerCase();
|
||||
|
||||
let index = 0;
|
||||
let startAt = 0;
|
||||
let score = 0;
|
||||
while (index < queryLen) {
|
||||
let indexOf = targetLower.indexOf(queryLower[index], startAt);
|
||||
if (indexOf < 0) {
|
||||
score = 0; // This makes sure that the query is contained in the target
|
||||
break;
|
||||
}
|
||||
|
||||
// Character match bonus
|
||||
score += 1;
|
||||
|
||||
// Consecutive match bonus
|
||||
if (startAt === indexOf) {
|
||||
score += 5;
|
||||
}
|
||||
|
||||
// Same case bonus
|
||||
if (target[indexOf] === query[indexOf]) {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
// Start of word bonus
|
||||
if (indexOf === 0) {
|
||||
score += 8;
|
||||
}
|
||||
|
||||
// After separator bonus
|
||||
else if (wordPathBoundary.some(w => w === target[indexOf - 1])) {
|
||||
score += 7;
|
||||
}
|
||||
|
||||
// Inside word upper case bonus
|
||||
else if (isUpper(target.charCodeAt(indexOf))) {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
startAt = indexOf + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
cache[hash] = score;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
function isUpper(code: number): boolean {
|
||||
return 65 <= code && code <= 90;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fast method to check if a given string would produce a score > 0 for the given query.
|
||||
*/
|
||||
export function matches(target: string, queryLower: string): boolean {
|
||||
if (!target || !queryLower) {
|
||||
return false; // return early if target or query are undefined
|
||||
}
|
||||
|
||||
const queryLen = queryLower.length;
|
||||
const targetLower = target.toLowerCase();
|
||||
|
||||
let index = 0;
|
||||
let lastIndexOf = -1;
|
||||
while (index < queryLen) {
|
||||
let indexOf = targetLower.indexOf(queryLower[index], lastIndexOf + 1);
|
||||
if (indexOf < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lastIndexOf = indexOf;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/*!
|
||||
END THIRD PARTY
|
||||
*/
|
||||
@@ -16,12 +16,12 @@ enum Severity {
|
||||
|
||||
namespace Severity {
|
||||
|
||||
var _error = 'error',
|
||||
_warning = 'warning',
|
||||
_warn = 'warn',
|
||||
_info = 'info';
|
||||
const _error = 'error';
|
||||
const _warning = 'warning';
|
||||
const _warn = 'warn';
|
||||
const _info = 'info';
|
||||
|
||||
var _displayStrings: { [value: number]: string; } = Object.create(null);
|
||||
const _displayStrings: { [value: number]: string; } = Object.create(null);
|
||||
_displayStrings[Severity.Error] = nls.localize('sev.error', "Error");
|
||||
_displayStrings[Severity.Warning] = nls.localize('sev.warning', "Warning");
|
||||
_displayStrings[Severity.Info] = nls.localize('sev.info', "Info");
|
||||
|
||||
@@ -221,7 +221,7 @@ export function createRegExp(searchString: string, isRegex: boolean, options: Re
|
||||
export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
|
||||
// Exit early if it's one of these special cases which are meant to match
|
||||
// against an empty string
|
||||
if (regexp.source === '^' || regexp.source === '^$' || regexp.source === '$') {
|
||||
if (regexp.source === '^' || regexp.source === '^$' || regexp.source === '$' || regexp.source === '^\\s*$') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -231,6 +231,10 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
|
||||
return (match && <any>regexp.lastIndex === 0);
|
||||
}
|
||||
|
||||
export function regExpContainsBackreference(regexpValue: string): boolean {
|
||||
return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/);
|
||||
}
|
||||
|
||||
/**
|
||||
* The normalize() method returns the Unicode Normalization Form of a given string. The form will be
|
||||
* the Normalization Form Canonical Composition.
|
||||
@@ -238,9 +242,19 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize}
|
||||
*/
|
||||
export const canNormalize = typeof ((<any>'').normalize) === 'function';
|
||||
const nonAsciiCharactersPattern = /[^\u0000-\u0080]/;
|
||||
const normalizedCache = new BoundedMap<string>(10000); // bounded to 10000 elements
|
||||
|
||||
const nfcCache = new BoundedMap<string>(10000); // bounded to 10000 elements
|
||||
export function normalizeNFC(str: string): string {
|
||||
return normalize(str, 'NFC', nfcCache);
|
||||
}
|
||||
|
||||
const nfdCache = new BoundedMap<string>(10000); // bounded to 10000 elements
|
||||
export function normalizeNFD(str: string): string {
|
||||
return normalize(str, 'NFD', nfdCache);
|
||||
}
|
||||
|
||||
const nonAsciiCharactersPattern = /[^\u0000-\u0080]/;
|
||||
function normalize(str: string, form: string, normalizedCache: BoundedMap<string>): string {
|
||||
if (!canNormalize || !str) {
|
||||
return str;
|
||||
}
|
||||
@@ -252,7 +266,7 @@ export function normalizeNFC(str: string): string {
|
||||
|
||||
let res: string;
|
||||
if (nonAsciiCharactersPattern.test(str)) {
|
||||
res = (<any>str).normalize('NFC');
|
||||
res = (<any>str).normalize(form);
|
||||
} else {
|
||||
res = str;
|
||||
}
|
||||
@@ -705,6 +719,10 @@ export function startsWithUTF8BOM(str: string): boolean {
|
||||
return (str && str.length > 0 && str.charCodeAt(0) === CharCode.UTF8_BOM);
|
||||
}
|
||||
|
||||
export function stripUTF8BOM(str: string): string {
|
||||
return startsWithUTF8BOM(str) ? str.substr(1) : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends two strings. If the appended result is longer than maxLength,
|
||||
* trims the start of the result and replaces it with '...'.
|
||||
@@ -735,3 +753,35 @@ export function repeat(s: string, count: number): string {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the characters of the provided query string are included in the
|
||||
* target string. The characters do not have to be contiguous within the string.
|
||||
*/
|
||||
export function fuzzyContains(target: string, query: string): boolean {
|
||||
if (!target || !query) {
|
||||
return false; // return early if target or query are undefined
|
||||
}
|
||||
|
||||
if (target.length < query.length) {
|
||||
return false; // impossible for query to be contained in target
|
||||
}
|
||||
|
||||
const queryLen = query.length;
|
||||
const targetLower = target.toLowerCase();
|
||||
|
||||
let index = 0;
|
||||
let lastIndexOf = -1;
|
||||
while (index < queryLen) {
|
||||
let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1);
|
||||
if (indexOf < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lastIndexOf = indexOf;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -21,6 +21,42 @@ function encodeNoop(str: string): string {
|
||||
}
|
||||
|
||||
|
||||
const _schemePattern = /^\w[\w\d+.-]*$/;
|
||||
const _singleSlashStart = /^\//;
|
||||
const _doubleSlashStart = /^\/\//;
|
||||
|
||||
function _validateUri(ret: URI): void {
|
||||
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (ret.scheme && !_schemePattern.test(ret.scheme)) {
|
||||
throw new Error('[UriError]: Scheme contains illegal characters.');
|
||||
}
|
||||
|
||||
// path, http://tools.ietf.org/html/rfc3986#section-3.3
|
||||
// If a URI contains an authority component, then the path component
|
||||
// must either be empty or begin with a slash ("/") character. If a URI
|
||||
// does not contain an authority component, then the path cannot begin
|
||||
// with two slash characters ("//").
|
||||
if (ret.path) {
|
||||
if (ret.authority) {
|
||||
if (!_singleSlashStart.test(ret.path)) {
|
||||
throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character');
|
||||
}
|
||||
} else {
|
||||
if (_doubleSlashStart.test(ret.path)) {
|
||||
throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _empty = '';
|
||||
const _slash = '/';
|
||||
const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
||||
const _driveLetterPath = /^\/[a-zA-Z]:/;
|
||||
const _upperCaseDrive = /^(\/)?([A-Z]:)/;
|
||||
const _driveLetter = /^[a-zA-Z]:/;
|
||||
|
||||
/**
|
||||
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
|
||||
* This class is a simple parser which creates the basic component paths
|
||||
@@ -37,7 +73,7 @@ function encodeNoop(str: string): string {
|
||||
*
|
||||
*
|
||||
*/
|
||||
export default class URI {
|
||||
export default class URI implements UriComponents {
|
||||
|
||||
static isUri(thing: any): thing is URI {
|
||||
if (thing instanceof URI) {
|
||||
@@ -53,12 +89,6 @@ export default class URI {
|
||||
&& typeof (<URI>thing).scheme === 'string';
|
||||
}
|
||||
|
||||
private static _empty = '';
|
||||
private static _slash = '/';
|
||||
private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
||||
private static _driveLetterPath = /^\/[a-zA-Z]:/;
|
||||
private static _upperCaseDrive = /^(\/)?([A-Z]:)/;
|
||||
|
||||
/**
|
||||
* scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'.
|
||||
* The part before the first colon.
|
||||
@@ -86,21 +116,38 @@ export default class URI {
|
||||
*/
|
||||
readonly fragment: string;
|
||||
|
||||
private _formatted: string = null;
|
||||
private _fsPath: string = null;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(scheme: string, authority: string, path: string, query: string, fragment: string);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private constructor(scheme: string, authority: string, path: string, query: string, fragment: string) {
|
||||
protected constructor(components: UriComponents);
|
||||
|
||||
this.scheme = scheme || URI._empty;
|
||||
this.authority = authority || URI._empty;
|
||||
this.path = path || URI._empty;
|
||||
this.query = query || URI._empty;
|
||||
this.fragment = fragment || URI._empty;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) {
|
||||
|
||||
this._validate(this);
|
||||
if (typeof schemeOrData === 'object') {
|
||||
this.scheme = schemeOrData.scheme || _empty;
|
||||
this.authority = schemeOrData.authority || _empty;
|
||||
this.path = schemeOrData.path || _empty;
|
||||
this.query = schemeOrData.query || _empty;
|
||||
this.fragment = schemeOrData.fragment || _empty;
|
||||
// no validation because it's this URI
|
||||
// that creates uri components.
|
||||
// _validateUri(this);
|
||||
} else {
|
||||
this.scheme = schemeOrData || _empty;
|
||||
this.authority = authority || _empty;
|
||||
this.path = path || _empty;
|
||||
this.query = query || _empty;
|
||||
this.fragment = fragment || _empty;
|
||||
_validateUri(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- filesystem path -----------------------
|
||||
@@ -112,24 +159,7 @@ export default class URI {
|
||||
* invalid characters and semantics. Will *not* look at the scheme of this URI.
|
||||
*/
|
||||
get fsPath(): string {
|
||||
if (!this._fsPath) {
|
||||
let value: string;
|
||||
if (this.authority && this.path && this.scheme === 'file') {
|
||||
// unc path: file://shares/c$/far/boo
|
||||
value = `//${this.authority}${this.path}`;
|
||||
} else if (URI._driveLetterPath.test(this.path)) {
|
||||
// windows drive letter: file:///c:/far/boo
|
||||
value = this.path[1].toLowerCase() + this.path.substr(2);
|
||||
} else {
|
||||
// other path
|
||||
value = this.path;
|
||||
}
|
||||
if (platform.isWindows) {
|
||||
value = value.replace(/\//g, '\\');
|
||||
}
|
||||
this._fsPath = value;
|
||||
}
|
||||
return this._fsPath;
|
||||
return _makeFsPath(this);
|
||||
}
|
||||
|
||||
// ---- modify to new -------------------------
|
||||
@@ -176,60 +206,66 @@ export default class URI {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new URI(scheme, authority, path, query, fragment);
|
||||
return new _URI(scheme, authority, path, query, fragment);
|
||||
}
|
||||
|
||||
// ---- parse & validate ------------------------
|
||||
|
||||
public static parse(value: string): URI {
|
||||
const match = URI._regexp.exec(value);
|
||||
const match = _regexp.exec(value);
|
||||
if (!match) {
|
||||
return new URI(URI._empty, URI._empty, URI._empty, URI._empty, URI._empty);
|
||||
return new _URI(_empty, _empty, _empty, _empty, _empty);
|
||||
}
|
||||
return new URI(
|
||||
match[2] || URI._empty,
|
||||
decodeURIComponent(match[4] || URI._empty),
|
||||
decodeURIComponent(match[5] || URI._empty),
|
||||
decodeURIComponent(match[7] || URI._empty),
|
||||
decodeURIComponent(match[9] || URI._empty),
|
||||
return new _URI(
|
||||
match[2] || _empty,
|
||||
decodeURIComponent(match[4] || _empty),
|
||||
decodeURIComponent(match[5] || _empty),
|
||||
decodeURIComponent(match[7] || _empty),
|
||||
decodeURIComponent(match[9] || _empty),
|
||||
);
|
||||
}
|
||||
|
||||
public static file(path: string): URI {
|
||||
|
||||
let authority = URI._empty;
|
||||
let authority = _empty;
|
||||
|
||||
// normalize to fwd-slashes on windows,
|
||||
// on other systems bwd-slashes are valid
|
||||
// filename character, eg /f\oo/ba\r.txt
|
||||
if (platform.isWindows) {
|
||||
path = path.replace(/\\/g, URI._slash);
|
||||
path = path.replace(/\\/g, _slash);
|
||||
}
|
||||
|
||||
// check for authority as used in UNC shares
|
||||
// or use the path as given
|
||||
if (path[0] === URI._slash && path[0] === path[1]) {
|
||||
let idx = path.indexOf(URI._slash, 2);
|
||||
if (path[0] === _slash && path[1] === _slash) {
|
||||
let idx = path.indexOf(_slash, 2);
|
||||
if (idx === -1) {
|
||||
authority = path.substring(2);
|
||||
path = URI._empty;
|
||||
path = _slash;
|
||||
} else {
|
||||
authority = path.substring(2, idx);
|
||||
path = path.substring(idx);
|
||||
path = path.substring(idx) || _slash;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that path starts with a slash
|
||||
// or that it is at least a slash
|
||||
if (path[0] !== URI._slash) {
|
||||
path = URI._slash + path;
|
||||
if (_driveLetter.test(path)) {
|
||||
path = _slash + path;
|
||||
|
||||
} else if (path[0] !== _slash) {
|
||||
// tricky -> makes invalid paths
|
||||
// but otherwise we have to stop
|
||||
// allowing relative paths...
|
||||
path = _slash + path;
|
||||
}
|
||||
|
||||
return new URI('file', authority, path, URI._empty, URI._empty);
|
||||
return new _URI('file', authority, path, _empty, _empty);
|
||||
}
|
||||
|
||||
public static from(components: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
|
||||
return new URI(
|
||||
return new _URI(
|
||||
components.scheme,
|
||||
components.authority,
|
||||
components.path,
|
||||
@@ -238,35 +274,6 @@ export default class URI {
|
||||
);
|
||||
}
|
||||
|
||||
private static _schemePattern = /^\w[\w\d+.-]*$/;
|
||||
private static _singleSlashStart = /^\//;
|
||||
private static _doubleSlashStart = /^\/\//;
|
||||
|
||||
private _validate(ret: URI): void {
|
||||
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (ret.scheme && !URI._schemePattern.test(ret.scheme)) {
|
||||
throw new Error('[UriError]: Scheme contains illegal characters.');
|
||||
}
|
||||
|
||||
// path, http://tools.ietf.org/html/rfc3986#section-3.3
|
||||
// If a URI contains an authority component, then the path component
|
||||
// must either be empty or begin with a slash ("/") character. If a URI
|
||||
// does not contain an authority component, then the path cannot begin
|
||||
// with two slash characters ("//").
|
||||
if (ret.path) {
|
||||
if (ret.authority) {
|
||||
if (!URI._singleSlashStart.test(ret.path)) {
|
||||
throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character');
|
||||
}
|
||||
} else {
|
||||
if (URI._doubleSlashStart.test(ret.path)) {
|
||||
throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- printing/externalize ---------------------------
|
||||
|
||||
/**
|
||||
@@ -274,82 +281,14 @@ export default class URI {
|
||||
* @param skipEncoding Do not encode the result, default is `false`
|
||||
*/
|
||||
public toString(skipEncoding: boolean = false): string {
|
||||
if (!skipEncoding) {
|
||||
if (!this._formatted) {
|
||||
this._formatted = URI._asFormatted(this, false);
|
||||
}
|
||||
return this._formatted;
|
||||
} else {
|
||||
// we don't cache that
|
||||
return URI._asFormatted(this, true);
|
||||
}
|
||||
return _asFormatted(this, skipEncoding);
|
||||
}
|
||||
|
||||
private static _asFormatted(uri: URI, skipEncoding: boolean): string {
|
||||
|
||||
const encoder = !skipEncoding
|
||||
? encodeURIComponent2
|
||||
: encodeNoop;
|
||||
|
||||
const parts: string[] = [];
|
||||
|
||||
let { scheme, authority, path, query, fragment } = uri;
|
||||
if (scheme) {
|
||||
parts.push(scheme, ':');
|
||||
}
|
||||
if (authority || scheme === 'file') {
|
||||
parts.push('//');
|
||||
}
|
||||
if (authority) {
|
||||
authority = authority.toLowerCase();
|
||||
let idx = authority.indexOf(':');
|
||||
if (idx === -1) {
|
||||
parts.push(encoder(authority));
|
||||
} else {
|
||||
parts.push(encoder(authority.substr(0, idx)), authority.substr(idx));
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
// lower-case windows drive letters in /C:/fff or C:/fff
|
||||
const m = URI._upperCaseDrive.exec(path);
|
||||
if (m) {
|
||||
if (m[1]) {
|
||||
path = '/' + m[2].toLowerCase() + path.substr(3); // "/c:".length === 3
|
||||
} else {
|
||||
path = m[2].toLowerCase() + path.substr(2); // // "c:".length === 2
|
||||
}
|
||||
}
|
||||
|
||||
// encode every segement but not slashes
|
||||
// make sure that # and ? are always encoded
|
||||
// when occurring in paths - otherwise the result
|
||||
// cannot be parsed back again
|
||||
let lastIdx = 0;
|
||||
while (true) {
|
||||
let idx = path.indexOf(URI._slash, lastIdx);
|
||||
if (idx === -1) {
|
||||
parts.push(encoder(path.substring(lastIdx)));
|
||||
break;
|
||||
}
|
||||
parts.push(encoder(path.substring(lastIdx, idx)), URI._slash);
|
||||
lastIdx = idx + 1;
|
||||
};
|
||||
}
|
||||
if (query) {
|
||||
parts.push('?', encoder(query));
|
||||
}
|
||||
if (fragment) {
|
||||
parts.push('#', encoder(fragment));
|
||||
}
|
||||
|
||||
return parts.join(URI._empty);
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
public toJSON(): object {
|
||||
const res = <UriState>{
|
||||
$mid: 1,
|
||||
fsPath: this.fsPath,
|
||||
external: this.toString(),
|
||||
$mid: 1
|
||||
};
|
||||
|
||||
if (this.path) {
|
||||
@@ -375,21 +314,15 @@ export default class URI {
|
||||
return res;
|
||||
}
|
||||
|
||||
static revive(data: any): URI {
|
||||
let result = new URI(
|
||||
(<UriState>data).scheme,
|
||||
(<UriState>data).authority,
|
||||
(<UriState>data).path,
|
||||
(<UriState>data).query,
|
||||
(<UriState>data).fragment
|
||||
);
|
||||
static revive(data: UriComponents | any): URI {
|
||||
let result = new _URI(data);
|
||||
result._fsPath = (<UriState>data).fsPath;
|
||||
result._formatted = (<UriState>data).external;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
interface UriComponents {
|
||||
export interface UriComponents {
|
||||
scheme: string;
|
||||
authority: string;
|
||||
path: string;
|
||||
@@ -402,3 +335,129 @@ interface UriState extends UriComponents {
|
||||
fsPath: string;
|
||||
external: string;
|
||||
}
|
||||
|
||||
|
||||
// tslint:disable-next-line:class-name
|
||||
class _URI extends URI {
|
||||
|
||||
_formatted: string = null;
|
||||
_fsPath: string = null;
|
||||
|
||||
get fsPath(): string {
|
||||
if (!this._fsPath) {
|
||||
this._fsPath = _makeFsPath(this);
|
||||
}
|
||||
return this._fsPath;
|
||||
}
|
||||
|
||||
public toString(skipEncoding: boolean = false): string {
|
||||
if (!skipEncoding) {
|
||||
if (!this._formatted) {
|
||||
this._formatted = _asFormatted(this, false);
|
||||
}
|
||||
return this._formatted;
|
||||
} else {
|
||||
// we don't cache that
|
||||
return _asFormatted(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute `fsPath` for the given uri
|
||||
* @param uri
|
||||
*/
|
||||
function _makeFsPath(uri: URI): string {
|
||||
|
||||
let value: string;
|
||||
if (uri.authority && uri.path && uri.scheme === 'file') {
|
||||
// unc path: file://shares/c$/far/boo
|
||||
value = `//${uri.authority}${uri.path}`;
|
||||
} else if (_driveLetterPath.test(uri.path)) {
|
||||
// windows drive letter: file:///c:/far/boo
|
||||
value = uri.path[1].toLowerCase() + uri.path.substr(2);
|
||||
} else {
|
||||
// other path
|
||||
value = uri.path;
|
||||
}
|
||||
if (platform.isWindows) {
|
||||
value = value.replace(/\//g, '\\');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the external version of a uri
|
||||
*/
|
||||
function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
||||
|
||||
const encoder = !skipEncoding
|
||||
? encodeURIComponent2
|
||||
: encodeNoop;
|
||||
|
||||
const parts: string[] = [];
|
||||
|
||||
let { scheme, authority, path, query, fragment } = uri;
|
||||
if (scheme) {
|
||||
parts.push(scheme, ':');
|
||||
}
|
||||
if (authority || scheme === 'file') {
|
||||
parts.push('//');
|
||||
}
|
||||
if (authority) {
|
||||
let idx = authority.indexOf('@');
|
||||
if (idx !== -1) {
|
||||
const userinfo = authority.substr(0, idx);
|
||||
authority = authority.substr(idx + 1);
|
||||
idx = userinfo.indexOf(':');
|
||||
if (idx === -1) {
|
||||
parts.push(encoder(userinfo));
|
||||
} else {
|
||||
parts.push(encoder(userinfo.substr(0, idx)), ':', encoder(userinfo.substr(idx + 1)));
|
||||
}
|
||||
parts.push('@');
|
||||
}
|
||||
authority = authority.toLowerCase();
|
||||
idx = authority.indexOf(':');
|
||||
if (idx === -1) {
|
||||
parts.push(encoder(authority));
|
||||
} else {
|
||||
parts.push(encoder(authority.substr(0, idx)), authority.substr(idx));
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
// lower-case windows drive letters in /C:/fff or C:/fff
|
||||
const m = _upperCaseDrive.exec(path);
|
||||
if (m) {
|
||||
if (m[1]) {
|
||||
path = '/' + m[2].toLowerCase() + path.substr(3); // "/c:".length === 3
|
||||
} else {
|
||||
path = m[2].toLowerCase() + path.substr(2); // // "c:".length === 2
|
||||
}
|
||||
}
|
||||
|
||||
// encode every segement but not slashes
|
||||
// make sure that # and ? are always encoded
|
||||
// when occurring in paths - otherwise the result
|
||||
// cannot be parsed back again
|
||||
let lastIdx = 0;
|
||||
while (true) {
|
||||
let idx = path.indexOf(_slash, lastIdx);
|
||||
if (idx === -1) {
|
||||
parts.push(encoder(path.substring(lastIdx)));
|
||||
break;
|
||||
}
|
||||
parts.push(encoder(path.substring(lastIdx, idx)), _slash);
|
||||
lastIdx = idx + 1;
|
||||
};
|
||||
}
|
||||
if (query) {
|
||||
parts.push('?', encoder(query));
|
||||
}
|
||||
if (fragment) {
|
||||
parts.push('#', encoder(fragment));
|
||||
}
|
||||
|
||||
return parts.join(_empty);
|
||||
}
|
||||
|
||||
6
src/vs/base/common/winjs.base.d.ts
vendored
6
src/vs/base/common/winjs.base.d.ts
vendored
@@ -13,7 +13,7 @@ export declare class Promise<T = any, TProgress = any> {
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason: any) => void,
|
||||
progress: (progress: TProgress) => void) => void,
|
||||
oncancel?: () => void);
|
||||
oncancel?: () => void);
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
@@ -29,7 +29,7 @@ export declare class Promise<T = any, TProgress = any> {
|
||||
|
||||
public static as(value: null): Promise<null>;
|
||||
public static as(value: undefined): Promise<undefined>;
|
||||
public static as<T, TPromise extends PromiseLike<T>>(value: TPromise): TPromise;
|
||||
public static as<T, SomePromise extends PromiseLike<T>>(value: SomePromise): SomePromise;
|
||||
public static as<T>(value: T): Promise<T>;
|
||||
|
||||
public static is(value: any): value is PromiseLike<any>;
|
||||
@@ -49,7 +49,7 @@ export declare class Promise<T = any, TProgress = any> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void);
|
||||
public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void): void;
|
||||
}
|
||||
|
||||
export type TValueCallback<T = any> = (value: T | PromiseLike<T>) => void;
|
||||
|
||||
77
src/vs/base/common/winjs.polyfill.promise.ts
Normal file
77
src/vs/base/common/winjs.polyfill.promise.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Promise as WinJSPromise } from './winjs.base';
|
||||
|
||||
/**
|
||||
* A polyfill for the native promises. The implementation is based on
|
||||
* WinJS promises but tries to gap differences between winjs promises
|
||||
* and native promises.
|
||||
*/
|
||||
export class PolyfillPromise<T = any> implements Promise<T> {
|
||||
|
||||
static all(thenables: Thenable<any>[]): PolyfillPromise {
|
||||
return new PolyfillPromise(WinJSPromise.join(thenables).then(null, values => {
|
||||
// WinJSPromise returns a sparse array whereas
|
||||
// native promises return the *first* error
|
||||
for (var key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
return values[key];
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
static race(thenables: Thenable<any>[]): PolyfillPromise {
|
||||
// WinJSPromise returns `{ key: <index/key>, value: <promise> }`
|
||||
// from the `any` call and Promise.race just wants the value
|
||||
return new PolyfillPromise(WinJSPromise.any(thenables).then(entry => entry.value, err => err.value));
|
||||
}
|
||||
|
||||
static resolve(value): PolyfillPromise {
|
||||
return new PolyfillPromise(WinJSPromise.wrap(value));
|
||||
}
|
||||
|
||||
static reject(value): PolyfillPromise {
|
||||
return new PolyfillPromise(WinJSPromise.wrapError(value));
|
||||
}
|
||||
|
||||
private _winjsPromise: WinJSPromise;
|
||||
|
||||
constructor(winjsPromise: WinJSPromise);
|
||||
constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any);
|
||||
constructor(callback: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) {
|
||||
|
||||
if (WinJSPromise.is(callback)) {
|
||||
this._winjsPromise = callback;
|
||||
} else {
|
||||
this._winjsPromise = new WinJSPromise((resolve, reject) => {
|
||||
let initializing = true;
|
||||
callback(function (value) {
|
||||
if (!initializing) {
|
||||
resolve(value);
|
||||
} else {
|
||||
setImmediate(resolve, value);
|
||||
}
|
||||
}, function (err) {
|
||||
if (!initializing) {
|
||||
reject(err);
|
||||
} else {
|
||||
setImmediate(reject, err);
|
||||
}
|
||||
});
|
||||
initializing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
then(onFulfilled?: any, onRejected?: any): PolyfillPromise {
|
||||
return new PolyfillPromise(this._winjsPromise.then(onFulfilled, onRejected));
|
||||
}
|
||||
|
||||
catch(onRejected?: any): PolyfillPromise {
|
||||
return new PolyfillPromise(this._winjsPromise.then(null, onRejected));
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
loaderConfiguration
|
||||
]);
|
||||
this._onModuleLoaded.then((availableMethods: string[]) => {
|
||||
let proxy = <T><any>{};
|
||||
let proxy = <T>{};
|
||||
for (let i = 0; i < availableMethods.length; i++) {
|
||||
proxy[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user