mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from master
This commit is contained in:
@@ -2,10 +2,8 @@
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ITelemetryData {
|
||||
@@ -18,15 +16,15 @@ export interface IAction extends IDisposable {
|
||||
id: string;
|
||||
label: string;
|
||||
tooltip: string;
|
||||
class: string;
|
||||
class: string | undefined;
|
||||
enabled: boolean;
|
||||
checked: boolean;
|
||||
radio: boolean;
|
||||
run(event?: any): TPromise<any>;
|
||||
run(event?: any): Thenable<any>;
|
||||
}
|
||||
|
||||
export interface IActionRunner extends IDisposable {
|
||||
run(action: IAction, context?: any): TPromise<any>;
|
||||
run(action: IAction, context?: any): Thenable<any>;
|
||||
onDidRun: Event<IRunEvent>;
|
||||
onDidBeforeRun: Event<IRunEvent>;
|
||||
}
|
||||
@@ -53,17 +51,18 @@ export interface IActionChangeEvent {
|
||||
export class Action implements IAction {
|
||||
|
||||
protected _onDidChange = new Emitter<IActionChangeEvent>();
|
||||
readonly onDidChange: Event<IActionChangeEvent> = this._onDidChange.event;
|
||||
|
||||
protected _id: string;
|
||||
protected _label: string;
|
||||
protected _tooltip: string;
|
||||
protected _cssClass: string;
|
||||
protected _cssClass: string | undefined;
|
||||
protected _enabled: boolean;
|
||||
protected _checked: boolean;
|
||||
protected _radio: boolean;
|
||||
protected _order: number;
|
||||
protected _actionCallback: (event?: any) => TPromise<any>;
|
||||
protected _actionCallback?: (event?: any) => Thenable<any>;
|
||||
|
||||
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => TPromise<any>) {
|
||||
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Thenable<any>) {
|
||||
this._id = id;
|
||||
this._label = label;
|
||||
this._cssClass = cssClass;
|
||||
@@ -71,23 +70,15 @@ export class Action implements IAction {
|
||||
this._actionCallback = actionCallback;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
|
||||
public get onDidChange(): Event<IActionChangeEvent> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
public set label(value: string) {
|
||||
set label(value: string) {
|
||||
this._setLabel(value);
|
||||
}
|
||||
|
||||
@@ -98,11 +89,11 @@ export class Action implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
public get tooltip(): string {
|
||||
get tooltip(): string {
|
||||
return this._tooltip;
|
||||
}
|
||||
|
||||
public set tooltip(value: string) {
|
||||
set tooltip(value: string) {
|
||||
this._setTooltip(value);
|
||||
}
|
||||
|
||||
@@ -113,26 +104,26 @@ export class Action implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
public get class(): string {
|
||||
get class(): string | undefined {
|
||||
return this._cssClass;
|
||||
}
|
||||
|
||||
public set class(value: string) {
|
||||
set class(value: string | undefined) {
|
||||
this._setClass(value);
|
||||
}
|
||||
|
||||
protected _setClass(value: string): void {
|
||||
protected _setClass(value: string | undefined): void {
|
||||
if (this._cssClass !== value) {
|
||||
this._cssClass = value;
|
||||
this._onDidChange.fire({ class: value });
|
||||
}
|
||||
}
|
||||
|
||||
public get enabled(): boolean {
|
||||
get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
public set enabled(value: boolean) {
|
||||
set enabled(value: boolean) {
|
||||
this._setEnabled(value);
|
||||
}
|
||||
|
||||
@@ -143,19 +134,19 @@ export class Action implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
public get checked(): boolean {
|
||||
get checked(): boolean {
|
||||
return this._checked;
|
||||
}
|
||||
|
||||
public set checked(value: boolean) {
|
||||
set checked(value: boolean) {
|
||||
this._setChecked(value);
|
||||
}
|
||||
|
||||
public get radio(): boolean {
|
||||
get radio(): boolean {
|
||||
return this._radio;
|
||||
}
|
||||
|
||||
public set radio(value: boolean) {
|
||||
set radio(value: boolean) {
|
||||
this._setRadio(value);
|
||||
}
|
||||
|
||||
@@ -173,19 +164,16 @@ export class Action implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
public get order(): number {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
public set order(value: number) {
|
||||
this._order = value;
|
||||
}
|
||||
|
||||
public run(event?: any, data?: ITelemetryData): TPromise<any> {
|
||||
if (this._actionCallback !== void 0) {
|
||||
run(event?: any, _data?: ITelemetryData): Thenable<any> {
|
||||
if (this._actionCallback) {
|
||||
return this._actionCallback(event);
|
||||
}
|
||||
return TPromise.as(true);
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,22 +183,17 @@ export interface IRunEvent {
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export class ActionRunner implements IActionRunner {
|
||||
export class ActionRunner extends Disposable implements IActionRunner {
|
||||
|
||||
private _onDidBeforeRun = new Emitter<IRunEvent>();
|
||||
private _onDidRun = new Emitter<IRunEvent>();
|
||||
private _onDidBeforeRun = this._register(new Emitter<IRunEvent>());
|
||||
readonly onDidBeforeRun: Event<IRunEvent> = this._onDidBeforeRun.event;
|
||||
|
||||
public get onDidRun(): Event<IRunEvent> {
|
||||
return this._onDidRun.event;
|
||||
}
|
||||
private _onDidRun = this._register(new Emitter<IRunEvent>());
|
||||
readonly onDidRun: Event<IRunEvent> = this._onDidRun.event;
|
||||
|
||||
public get onDidBeforeRun(): Event<IRunEvent> {
|
||||
return this._onDidBeforeRun.event;
|
||||
}
|
||||
|
||||
public run(action: IAction, context?: any): TPromise<any> {
|
||||
run(action: IAction, context?: any): Thenable<any> {
|
||||
if (!action.enabled) {
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
this._onDidBeforeRun.fire({ action: action });
|
||||
@@ -222,28 +205,18 @@ export class ActionRunner implements IActionRunner {
|
||||
});
|
||||
}
|
||||
|
||||
protected runAction(action: IAction, context?: any): TPromise<any> {
|
||||
protected runAction(action: IAction, context?: any): Thenable<any> {
|
||||
const res = context ? action.run(context) : action.run();
|
||||
|
||||
if (TPromise.is(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return TPromise.wrap(res);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._onDidBeforeRun.dispose();
|
||||
this._onDidRun.dispose();
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
}
|
||||
|
||||
export class RadioGroup {
|
||||
|
||||
private _disposable: IDisposable;
|
||||
export class RadioGroup extends Disposable {
|
||||
|
||||
constructor(readonly actions: Action[]) {
|
||||
this._disposable = combinedDisposable(actions.map(action => {
|
||||
super();
|
||||
|
||||
this._register(combinedDisposable(actions.map(action => {
|
||||
return action.onDidChange(e => {
|
||||
if (e.checked && action.checked) {
|
||||
for (const candidate of actions) {
|
||||
@@ -253,10 +226,6 @@ export class RadioGroup {
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposable.dispose();
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
10
src/vs/base/common/amd.ts
Normal file
10
src/vs/base/common/amd.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string {
|
||||
return URI.parse(requirefn.toUrl(relativePath)).fsPath;
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
* 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 { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,15 @@ export function tail2<T>(arr: T[]): [T[], T] {
|
||||
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
|
||||
}
|
||||
|
||||
export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
|
||||
export function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
|
||||
if (one === other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!one || !other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (one.length !== other.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,7 +137,7 @@ function _sort<T>(a: T[], compare: Compare<T>, lo: number, hi: number, aux: T[])
|
||||
|
||||
export function groupBy<T>(data: T[], compare: (a: T, b: T) => number): T[][] {
|
||||
const result: T[][] = [];
|
||||
let currentGroup: T[];
|
||||
let currentGroup: T[] | undefined = undefined;
|
||||
for (const element of mergeSort(data.slice(0), compare)) {
|
||||
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
|
||||
currentGroup = [element];
|
||||
@@ -252,12 +260,12 @@ export function top<T>(array: T[], compare: (a: T, b: T) => number, n: number):
|
||||
* @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[]> {
|
||||
export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise<T[]> {
|
||||
if (n === 0) {
|
||||
return TPromise.as([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
let canceled = false;
|
||||
return new TPromise((resolve, reject) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
const o = array.length;
|
||||
const result = array.slice(0, n).sort(compare);
|
||||
@@ -265,16 +273,14 @@ export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: numb
|
||||
if (i > n) {
|
||||
await new Promise(resolve => setTimeout(resolve)); // nextTick() would starve I/O.
|
||||
}
|
||||
if (canceled) {
|
||||
throw new Error('canceled');
|
||||
if (token && token.isCancellationRequested) {
|
||||
throw canceled();
|
||||
}
|
||||
topStep(array, compare, result, i, m);
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
.then(resolve, reject);
|
||||
}, () => {
|
||||
canceled = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -290,29 +296,30 @@ function topStep<T>(array: T[], compare: (a: T, b: T) => number, result: T[], i:
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns a new array with all undefined or null values removed. The original array is not modified at all.
|
||||
* @returns a new array with all falsy values removed. The original array IS NOT modified.
|
||||
*/
|
||||
export function coalesce<T>(array: T[]): T[];
|
||||
export function coalesce<T>(array: T[], inplace: true): void;
|
||||
export function coalesce<T>(array: T[], inplace?: true): void | T[] {
|
||||
export function coalesce<T>(array: (T | undefined | null)[]): T[] {
|
||||
if (!array) {
|
||||
if (!inplace) {
|
||||
return array;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
if (!inplace) {
|
||||
return array.filter(e => !!e);
|
||||
return <T[]>array.filter(e => !!e);
|
||||
}
|
||||
|
||||
} else {
|
||||
let to = 0;
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!!array[i]) {
|
||||
array[to] = array[i];
|
||||
to += 1;
|
||||
}
|
||||
}
|
||||
array.length = to;
|
||||
/**
|
||||
* Remove all falsey values from `array`. The original array IS modified.
|
||||
*/
|
||||
export function coalesceInPlace<T>(array: (T | undefined | null)[]): void {
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
let to = 0;
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (!!array[i]) {
|
||||
array[to] = array[i];
|
||||
to += 1;
|
||||
}
|
||||
}
|
||||
array.length = to;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,7 +334,14 @@ export function move(array: any[], from: number, to: number): void {
|
||||
* and not empty.
|
||||
*/
|
||||
export function isFalsyOrEmpty(obj: any): boolean {
|
||||
return !Array.isArray(obj) || (<Array<any>>obj).length === 0;
|
||||
return !Array.isArray(obj) || obj.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{true}} if the provided object is an array and has at least one element.
|
||||
*/
|
||||
export function isNonEmptyArray<T>(obj: ReadonlyArray<T> | undefined | null): obj is Array<T> {
|
||||
return Array.isArray(obj) && obj.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +395,9 @@ export function firstIndex<T>(array: T[] | ReadonlyArray<T>, fn: (item: T) => bo
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function first<T>(array: T[] | ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T = null): T {
|
||||
export function first<T>(array: T[] | ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T): T;
|
||||
export function first<T>(array: T[] | ReadonlyArray<T>, fn: (item: T) => boolean): T | null;
|
||||
export function first<T>(array: T[] | ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null {
|
||||
const index = firstIndex(array, fn);
|
||||
return index < 0 ? notFoundValue : array[index];
|
||||
}
|
||||
@@ -397,7 +413,7 @@ export function commonPrefixLength<T>(one: T[], other: T[], equals: (a: T, b: T)
|
||||
}
|
||||
|
||||
export function flatten<T>(arr: T[][]): T[] {
|
||||
return [].concat(...arr);
|
||||
return (<T[]>[]).concat(...arr);
|
||||
}
|
||||
|
||||
export function range(to: number): number[];
|
||||
@@ -474,15 +490,20 @@ export function arrayInsert<T>(target: T[], insertIndex: number, insertArr: T[])
|
||||
* Uses Fisher-Yates shuffle to shuffle the given array
|
||||
* @param array
|
||||
*/
|
||||
export function shuffle<T>(array: T[], seed?: number): void {
|
||||
// Seeded random number generator in JS. Modified from:
|
||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
||||
const random = () => {
|
||||
var x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias
|
||||
return x - Math.floor(x);
|
||||
};
|
||||
export function shuffle<T>(array: T[], _seed?: number): void {
|
||||
let rand: () => number;
|
||||
|
||||
const rand = typeof seed === 'number' ? random : Math.random;
|
||||
if (typeof _seed === 'number') {
|
||||
let seed = _seed;
|
||||
// Seeded random number generator in JS. Modified from:
|
||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
||||
rand = () => {
|
||||
var x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias
|
||||
return x - Math.floor(x);
|
||||
};
|
||||
} else {
|
||||
rand = Math.random;
|
||||
}
|
||||
|
||||
for (let i = array.length - 1; i > 0; i -= 1) {
|
||||
let j = Math.floor(rand() * (i + 1));
|
||||
@@ -515,3 +536,20 @@ export function pushToEnd<T>(arr: T[], value: T): void {
|
||||
arr.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
export function find<T>(arr: ArrayLike<T>, predicate: (value: T, index: number, arr: ArrayLike<T>) => any): T | undefined {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const element = arr[i];
|
||||
if (predicate(element, i, arr)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {
|
||||
return Array.isArray(items) ?
|
||||
items.map(fn) :
|
||||
fn(items);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Throws an error with the provided message if the provided value does not evaluate to a true Javascript value.
|
||||
|
||||
@@ -3,35 +3,16 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { TPromise, ValueCallback, ErrorCallback, ProgressCallback } from 'vs/base/common/winjs.base';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function isThenable<T>(obj: any): obj is Thenable<T> {
|
||||
return obj && typeof (<Thenable<any>>obj).then === 'function';
|
||||
}
|
||||
|
||||
export function toThenable<T>(arg: T | Thenable<T>): Thenable<T> {
|
||||
if (isThenable(arg)) {
|
||||
return arg;
|
||||
} else {
|
||||
return TPromise.as(arg);
|
||||
}
|
||||
}
|
||||
|
||||
export function toWinJsPromise<T>(arg: Thenable<T> | TPromise<T>): TPromise<T> {
|
||||
if (arg instanceof TPromise) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
return new TPromise((resolve, reject) => arg.then(resolve, reject));
|
||||
}
|
||||
|
||||
export interface CancelablePromise<T> extends Promise<T> {
|
||||
cancel(): void;
|
||||
}
|
||||
@@ -66,78 +47,17 @@ export function createCancelablePromise<T>(callback: (token: CancellationToken)
|
||||
};
|
||||
}
|
||||
|
||||
export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | TPromise<T> | Thenable<T>): TPromise<T> {
|
||||
let source = new CancellationTokenSource();
|
||||
return new TPromise<T>((resolve, reject, progress) => {
|
||||
let item = callback(source.token);
|
||||
if (item instanceof TPromise) {
|
||||
item.then(result => {
|
||||
source.dispose();
|
||||
resolve(result);
|
||||
}, err => {
|
||||
source.dispose();
|
||||
reject(err);
|
||||
}, progress);
|
||||
} else if (isThenable<T>(item)) {
|
||||
item.then(result => {
|
||||
source.dispose();
|
||||
resolve(result);
|
||||
}, err => {
|
||||
source.dispose();
|
||||
reject(err);
|
||||
});
|
||||
export function asThenable<T>(callback: () => T | Thenable<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
let item = callback();
|
||||
if (isThenable<T>(item)) {
|
||||
item.then(resolve, reject);
|
||||
} else {
|
||||
source.dispose();
|
||||
resolve(item);
|
||||
}
|
||||
}, () => {
|
||||
source.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook a cancellation token to a WinJS Promise
|
||||
*/
|
||||
export function wireCancellationToken<T>(token: CancellationToken, promise: TPromise<T>, resolveAsUndefinedWhenCancelled?: boolean): Thenable<T> {
|
||||
const subscription = token.onCancellationRequested(() => promise.cancel());
|
||||
if (resolveAsUndefinedWhenCancelled) {
|
||||
promise = promise.then<T>(undefined, err => {
|
||||
if (!errors.isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return always(promise, () => subscription.dispose());
|
||||
}
|
||||
|
||||
export function asDisposablePromise<T>(input: Thenable<T>, cancelValue?: T, bucket?: IDisposable[]): { promise: Thenable<T> } & IDisposable {
|
||||
let dispose: () => void;
|
||||
let promise = new TPromise((resolve, reject) => {
|
||||
dispose = function () {
|
||||
resolve(cancelValue);
|
||||
if (isWinJSPromise(input)) {
|
||||
input.cancel();
|
||||
}
|
||||
};
|
||||
input.then(resolve, err => {
|
||||
if (errors.isPromiseCanceledError(err)) {
|
||||
resolve(cancelValue);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
let res = {
|
||||
promise,
|
||||
dispose
|
||||
};
|
||||
if (Array.isArray(bucket)) {
|
||||
bucket.push(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export interface ITask<T> {
|
||||
(): T;
|
||||
}
|
||||
@@ -170,9 +90,9 @@ export interface ITask<T> {
|
||||
*/
|
||||
export class Throttler {
|
||||
|
||||
private activePromise: TPromise;
|
||||
private queuedPromise: TPromise;
|
||||
private queuedPromiseFactory: ITask<TPromise>;
|
||||
private activePromise: Thenable<any> | null;
|
||||
private queuedPromise: Thenable<any> | null;
|
||||
private queuedPromiseFactory: ITask<Thenable<any>> | null;
|
||||
|
||||
constructor() {
|
||||
this.activePromise = null;
|
||||
@@ -180,7 +100,7 @@ export class Throttler {
|
||||
this.queuedPromiseFactory = null;
|
||||
}
|
||||
|
||||
queue<T>(promiseFactory: ITask<TPromise<T>>): TPromise<T> {
|
||||
queue<T>(promiseFactory: ITask<Thenable<T>>): Thenable<T> {
|
||||
if (this.activePromise) {
|
||||
this.queuedPromiseFactory = promiseFactory;
|
||||
|
||||
@@ -188,48 +108,41 @@ export class Throttler {
|
||||
const onComplete = () => {
|
||||
this.queuedPromise = null;
|
||||
|
||||
const result = this.queue(this.queuedPromiseFactory);
|
||||
const result = this.queue(this.queuedPromiseFactory!);
|
||||
this.queuedPromiseFactory = null;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
this.queuedPromise = new TPromise((c, e, p) => {
|
||||
this.activePromise.then(onComplete, onComplete, p).done(c);
|
||||
}, () => {
|
||||
this.activePromise.cancel();
|
||||
this.queuedPromise = new Promise(c => {
|
||||
this.activePromise!.then(onComplete, onComplete).then(c);
|
||||
});
|
||||
}
|
||||
|
||||
return new TPromise((c, e, p) => {
|
||||
this.queuedPromise.then(c, e, p);
|
||||
}, () => {
|
||||
// no-op
|
||||
return new Promise((c, e) => {
|
||||
this.queuedPromise!.then(c, e);
|
||||
});
|
||||
}
|
||||
|
||||
this.activePromise = promiseFactory();
|
||||
|
||||
return new TPromise((c, e, p) => {
|
||||
this.activePromise.done((result: any) => {
|
||||
return new Promise((c, e) => {
|
||||
this.activePromise!.then((result: any) => {
|
||||
this.activePromise = null;
|
||||
c(result);
|
||||
}, (err: any) => {
|
||||
this.activePromise = null;
|
||||
e(err);
|
||||
}, p);
|
||||
}, () => {
|
||||
this.activePromise.cancel();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@Joao: can the previous throttler be replaced with this?
|
||||
export class SimpleThrottler {
|
||||
export class Sequencer {
|
||||
|
||||
private current = TPromise.wrap<any>(null);
|
||||
private current: Promise<any> = Promise.resolve(null);
|
||||
|
||||
queue<T>(promiseTask: ITask<TPromise<T>>): TPromise<T> {
|
||||
queue<T>(promiseTask: ITask<Thenable<T>>): Thenable<T> {
|
||||
return this.current = this.current.then(() => promiseTask());
|
||||
}
|
||||
}
|
||||
@@ -257,33 +170,33 @@ export class SimpleThrottler {
|
||||
* delayer.trigger(() => { return makeTheTrip(); });
|
||||
* }
|
||||
*/
|
||||
export class Delayer<T> {
|
||||
export class Delayer<T> implements IDisposable {
|
||||
|
||||
private timeout: number;
|
||||
private completionPromise: TPromise;
|
||||
private onSuccess: ValueCallback;
|
||||
private task: ITask<T | TPromise<T>>;
|
||||
private timeout: any;
|
||||
private completionPromise: Thenable<any> | null;
|
||||
private doResolve: ((value?: any | Thenable<any>) => void) | null;
|
||||
private doReject: (err: any) => void;
|
||||
private task: ITask<T | Thenable<T>> | null;
|
||||
|
||||
constructor(public defaultDelay: number) {
|
||||
this.timeout = null;
|
||||
this.completionPromise = null;
|
||||
this.onSuccess = null;
|
||||
this.doResolve = null;
|
||||
this.task = null;
|
||||
}
|
||||
|
||||
trigger(task: ITask<T | TPromise<T>>, delay: number = this.defaultDelay): TPromise<T> {
|
||||
trigger(task: ITask<T | Thenable<T>>, delay: number = this.defaultDelay): Thenable<T> {
|
||||
this.task = task;
|
||||
this.cancelTimeout();
|
||||
|
||||
if (!this.completionPromise) {
|
||||
this.completionPromise = new TPromise((c) => {
|
||||
this.onSuccess = c;
|
||||
}, () => {
|
||||
// no-op
|
||||
this.completionPromise = new Promise((c, e) => {
|
||||
this.doResolve = c;
|
||||
this.doReject = e;
|
||||
}).then(() => {
|
||||
this.completionPromise = null;
|
||||
this.onSuccess = null;
|
||||
const task = this.task;
|
||||
this.doResolve = null;
|
||||
const task = this.task!;
|
||||
this.task = null;
|
||||
|
||||
return task();
|
||||
@@ -292,7 +205,7 @@ export class Delayer<T> {
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = null;
|
||||
this.onSuccess(null);
|
||||
this.doResolve!(null);
|
||||
}, delay);
|
||||
|
||||
return this.completionPromise;
|
||||
@@ -306,7 +219,7 @@ export class Delayer<T> {
|
||||
this.cancelTimeout();
|
||||
|
||||
if (this.completionPromise) {
|
||||
this.completionPromise.cancel();
|
||||
this.doReject(errors.canceled());
|
||||
this.completionPromise = null;
|
||||
}
|
||||
}
|
||||
@@ -317,27 +230,45 @@ export class Delayer<T> {
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.cancelTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to delay execution of a task that is being requested often, while
|
||||
* preventing accumulation of consecutive executions, while the task runs.
|
||||
*
|
||||
* Simply combine the two mail men's strategies from the Throttler and Delayer
|
||||
* helpers, for an analogy.
|
||||
* The mail man is clever and waits for a certain amount of time, before going
|
||||
* out to deliver letters. While the mail man is going out, more letters arrive
|
||||
* and can only be delivered once he is back. Once he is back the mail man will
|
||||
* do one more trip to deliver the letters that have accumulated while he was out.
|
||||
*/
|
||||
export class ThrottledDelayer<T> extends Delayer<TPromise<T>> {
|
||||
export class ThrottledDelayer<T> {
|
||||
|
||||
private delayer: Delayer<Thenable<T>>;
|
||||
private throttler: Throttler;
|
||||
|
||||
constructor(defaultDelay: number) {
|
||||
super(defaultDelay);
|
||||
|
||||
this.delayer = new Delayer(defaultDelay);
|
||||
this.throttler = new Throttler();
|
||||
}
|
||||
|
||||
trigger(promiseFactory: ITask<TPromise<T>>, delay?: number): TPromise {
|
||||
return super.trigger(() => this.throttler.queue(promiseFactory), delay);
|
||||
trigger(promiseFactory: ITask<Thenable<T>>, delay?: number): Thenable<T> {
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Thenable<T>;
|
||||
}
|
||||
|
||||
isTriggered(): boolean {
|
||||
return this.delayer.isTriggered();
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.delayer.cancel();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.delayer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,15 +278,13 @@ export class ThrottledDelayer<T> extends Delayer<TPromise<T>> {
|
||||
export class Barrier {
|
||||
|
||||
private _isOpen: boolean;
|
||||
private _promise: TPromise<boolean>;
|
||||
private _promise: Promise<boolean>;
|
||||
private _completePromise: (v: boolean) => void;
|
||||
|
||||
constructor() {
|
||||
this._isOpen = false;
|
||||
this._promise = new TPromise<boolean>((c, e) => {
|
||||
this._promise = new Promise<boolean>((c, e) => {
|
||||
this._completePromise = c;
|
||||
}, () => {
|
||||
console.warn('You should really not try to cancel this ready promise!');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -368,50 +297,34 @@ export class Barrier {
|
||||
this._completePromise(true);
|
||||
}
|
||||
|
||||
wait(): TPromise<boolean> {
|
||||
wait(): Promise<boolean> {
|
||||
return this._promise;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShallowCancelThenPromise<T> extends TPromise<T> {
|
||||
|
||||
constructor(outer: TPromise<T>) {
|
||||
|
||||
let completeCallback: ValueCallback,
|
||||
errorCallback: ErrorCallback,
|
||||
progressCallback: ProgressCallback;
|
||||
|
||||
super((c, e, p) => {
|
||||
completeCallback = c;
|
||||
errorCallback = e;
|
||||
progressCallback = p;
|
||||
}, () => {
|
||||
// cancel this promise but not the
|
||||
// outer promise
|
||||
errorCallback(errors.canceled());
|
||||
});
|
||||
|
||||
outer.then(completeCallback, errorCallback, progressCallback);
|
||||
export function timeout(millis: number): CancelablePromise<void>;
|
||||
export function timeout(millis: number, token: CancellationToken): Thenable<void>;
|
||||
export function timeout(millis: number, token?: CancellationToken): CancelablePromise<void> | Thenable<void> {
|
||||
if (!token) {
|
||||
return createCancelablePromise(token => timeout(millis, token));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement for `WinJS.TPromise.timeout`.
|
||||
*/
|
||||
export function timeout(n: number): CancelablePromise<void> {
|
||||
return createCancelablePromise(token => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const handle = setTimeout(resolve, n);
|
||||
token.onCancellationRequested(_ => {
|
||||
clearTimeout(handle);
|
||||
reject(errors.canceled());
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
const handle = setTimeout(resolve, millis);
|
||||
token.onCancellationRequested(() => {
|
||||
clearTimeout(handle);
|
||||
reject(errors.canceled());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isWinJSPromise(candidate: any): candidate is TPromise {
|
||||
return TPromise.is(candidate) && typeof (<TPromise>candidate).done === 'function';
|
||||
export function disposableTimeout(handler: Function, timeout = 0): IDisposable {
|
||||
const timer = setTimeout(handler, timeout);
|
||||
return {
|
||||
dispose() {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,39 +332,22 @@ function isWinJSPromise(candidate: any): candidate is TPromise {
|
||||
* the provided promise the provided function will always be called. This
|
||||
* method is comparable to a try-finally code block.
|
||||
* @param promise a promise
|
||||
* @param f a function that will be call in the success and error case.
|
||||
* @param callback a function that will be call in the success and error case.
|
||||
*/
|
||||
export function always<T>(thenable: TPromise<T>, f: Function): TPromise<T>;
|
||||
export function always<T>(promise: Thenable<T>, f: Function): Thenable<T>;
|
||||
export function always<T>(winjsPromiseOrThenable: Thenable<T> | TPromise<T>, f: Function): TPromise<T> | Thenable<T> {
|
||||
if (isWinJSPromise(winjsPromiseOrThenable)) {
|
||||
return new TPromise<T>((c, e, p) => {
|
||||
winjsPromiseOrThenable.done((result) => {
|
||||
try {
|
||||
f(result);
|
||||
} catch (e1) {
|
||||
errors.onUnexpectedError(e1);
|
||||
}
|
||||
c(result);
|
||||
}, (err) => {
|
||||
try {
|
||||
f(err);
|
||||
} catch (e1) {
|
||||
errors.onUnexpectedError(e1);
|
||||
}
|
||||
e(err);
|
||||
}, (progress) => {
|
||||
p(progress);
|
||||
});
|
||||
}, () => {
|
||||
winjsPromiseOrThenable.cancel();
|
||||
});
|
||||
|
||||
} else {
|
||||
// simple
|
||||
winjsPromiseOrThenable.then(_ => f(), _ => f());
|
||||
return winjsPromiseOrThenable;
|
||||
export function always<T>(promise: Thenable<T>, callback: () => void): Promise<T> {
|
||||
function safeCallback() {
|
||||
try {
|
||||
callback();
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
}
|
||||
promise.then(_ => safeCallback(), _ => safeCallback());
|
||||
return Promise.resolve(promise);
|
||||
}
|
||||
|
||||
export function ignoreErrors<T>(promise: Thenable<T>): Thenable<T | undefined> {
|
||||
return promise.then(undefined, _ => undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,7 +355,7 @@ export function always<T>(winjsPromiseOrThenable: Thenable<T> | TPromise<T>, f:
|
||||
* promise will complete to an array of results from each promise.
|
||||
*/
|
||||
|
||||
export function sequence<T>(promiseFactories: ITask<Thenable<T>>[]): TPromise<T[]> {
|
||||
export function sequence<T>(promiseFactories: ITask<Thenable<T>>[]): Promise<T[]> {
|
||||
const results: T[] = [];
|
||||
let index = 0;
|
||||
const len = promiseFactories.length;
|
||||
@@ -478,50 +374,28 @@ export function sequence<T>(promiseFactories: ITask<Thenable<T>>[]): TPromise<T[
|
||||
return n.then(thenHandler);
|
||||
}
|
||||
|
||||
return TPromise.as(results);
|
||||
return Promise.resolve(results);
|
||||
}
|
||||
|
||||
return TPromise.as(null).then(thenHandler);
|
||||
return Promise.resolve(null).then(thenHandler);
|
||||
}
|
||||
|
||||
export function first2<T>(promiseFactories: ITask<Promise<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T = null): Promise<T> {
|
||||
|
||||
export function first<T>(promiseFactories: ITask<Thenable<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise<T | null> {
|
||||
let index = 0;
|
||||
const len = promiseFactories.length;
|
||||
|
||||
const loop = () => {
|
||||
const loop: () => Promise<T | null> = () => {
|
||||
if (index >= len) {
|
||||
return Promise.resolve(defaultValue);
|
||||
}
|
||||
|
||||
const factory = promiseFactories[index++];
|
||||
const promise = factory();
|
||||
const promise = Promise.resolve(factory());
|
||||
|
||||
return promise.then(result => {
|
||||
if (shouldStop(result)) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
return loop();
|
||||
});
|
||||
};
|
||||
|
||||
return loop();
|
||||
}
|
||||
|
||||
export function first<T>(promiseFactories: ITask<TPromise<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T = null): TPromise<T> {
|
||||
let index = 0;
|
||||
const len = promiseFactories.length;
|
||||
|
||||
const loop: () => TPromise<T> = () => {
|
||||
if (index >= len) {
|
||||
return TPromise.as(defaultValue);
|
||||
}
|
||||
|
||||
const factory = promiseFactories[index++];
|
||||
const promise = factory();
|
||||
|
||||
return promise.then(result => {
|
||||
if (shouldStop(result)) {
|
||||
return TPromise.as(result);
|
||||
}
|
||||
|
||||
return loop();
|
||||
});
|
||||
@@ -530,11 +404,10 @@ export function first<T>(promiseFactories: ITask<TPromise<T>>[], shouldStop: (t:
|
||||
return loop();
|
||||
}
|
||||
|
||||
interface ILimitedTaskFactory {
|
||||
factory: ITask<TPromise>;
|
||||
c: ValueCallback;
|
||||
e: ErrorCallback;
|
||||
p: ProgressCallback;
|
||||
interface ILimitedTaskFactory<T> {
|
||||
factory: ITask<Thenable<T>>;
|
||||
c: (value?: T | Thenable<T>) => void;
|
||||
e: (error?: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -542,9 +415,11 @@ interface ILimitedTaskFactory {
|
||||
* ensures that at any time no more than M promises are running at the same time.
|
||||
*/
|
||||
export class Limiter<T> {
|
||||
|
||||
private _size = 0;
|
||||
private runningPromises: number;
|
||||
private maxDegreeOfParalellism: number;
|
||||
private outstandingPromises: ILimitedTaskFactory[];
|
||||
private outstandingPromises: ILimitedTaskFactory<T>[];
|
||||
private readonly _onFinished: Emitter<void>;
|
||||
|
||||
constructor(maxDegreeOfParalellism: number) {
|
||||
@@ -559,35 +434,32 @@ export class Limiter<T> {
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this.runningPromises + this.outstandingPromises.length;
|
||||
return this._size;
|
||||
// return this.runningPromises + this.outstandingPromises.length;
|
||||
}
|
||||
|
||||
queue(promiseFactory: ITask<TPromise>): TPromise;
|
||||
queue(promiseFactory: ITask<TPromise<T>>): TPromise<T> {
|
||||
return new TPromise<T>((c, e, p) => {
|
||||
this.outstandingPromises.push({
|
||||
factory: promiseFactory,
|
||||
c: c,
|
||||
e: e,
|
||||
p: p
|
||||
});
|
||||
queue(factory: ITask<Thenable<T>>): Thenable<T> {
|
||||
this._size++;
|
||||
|
||||
return new Promise<T>((c, e) => {
|
||||
this.outstandingPromises.push({ factory, c, e });
|
||||
this.consume();
|
||||
});
|
||||
}
|
||||
|
||||
private consume(): void {
|
||||
while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
|
||||
const iLimitedTask = this.outstandingPromises.shift();
|
||||
const iLimitedTask = this.outstandingPromises.shift()!;
|
||||
this.runningPromises++;
|
||||
|
||||
const promise = iLimitedTask.factory();
|
||||
promise.done(iLimitedTask.c, iLimitedTask.e, iLimitedTask.p);
|
||||
promise.done(() => this.consumed(), () => this.consumed());
|
||||
promise.then(iLimitedTask.c, iLimitedTask.e);
|
||||
promise.then(() => this.consumed(), () => this.consumed());
|
||||
}
|
||||
}
|
||||
|
||||
private consumed(): void {
|
||||
this._size--;
|
||||
this.runningPromises--;
|
||||
|
||||
if (this.outstandingPromises.length > 0) {
|
||||
@@ -639,17 +511,18 @@ export class ResourceQueue {
|
||||
}
|
||||
}
|
||||
|
||||
export function setDisposableTimeout(handler: Function, timeout: number, ...args: any[]): IDisposable {
|
||||
const handle = setTimeout(handler, timeout, ...args);
|
||||
return { dispose() { clearTimeout(handle); } };
|
||||
}
|
||||
|
||||
export class TimeoutTimer extends Disposable {
|
||||
private _token: number;
|
||||
private _token: any;
|
||||
|
||||
constructor() {
|
||||
constructor();
|
||||
constructor(runner: () => void, timeout: number);
|
||||
constructor(runner?: () => void, timeout?: number) {
|
||||
super();
|
||||
this._token = -1;
|
||||
|
||||
if (typeof runner === 'function' && typeof timeout === 'number') {
|
||||
this.setIfNotSet(runner, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -686,7 +559,7 @@ export class TimeoutTimer extends Disposable {
|
||||
|
||||
export class IntervalTimer extends Disposable {
|
||||
|
||||
private _token: number;
|
||||
private _token: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -715,9 +588,9 @@ export class IntervalTimer extends Disposable {
|
||||
|
||||
export class RunOnceScheduler {
|
||||
|
||||
protected runner: (...args: any[]) => void;
|
||||
protected runner: ((...args: any[]) => void) | null;
|
||||
|
||||
private timeoutToken: number;
|
||||
private timeoutToken: any;
|
||||
private timeout: number;
|
||||
private timeoutHandler: () => void;
|
||||
|
||||
@@ -769,7 +642,9 @@ export class RunOnceScheduler {
|
||||
}
|
||||
|
||||
protected doRun(): void {
|
||||
this.runner();
|
||||
if (this.runner) {
|
||||
this.runner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -792,7 +667,9 @@ export class RunOnceWorker<T> extends RunOnceScheduler {
|
||||
const units = this.units;
|
||||
this.units = [];
|
||||
|
||||
this.runner(units);
|
||||
if (this.runner) {
|
||||
this.runner(units);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -802,14 +679,109 @@ export class RunOnceWorker<T> extends RunOnceScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
export function nfcall(fn: Function, ...args: any[]): TPromise;
|
||||
export function nfcall<T>(fn: Function, ...args: any[]): TPromise<T>;
|
||||
export function nfcall(fn: Function, ...args: any[]): Promise<any>;
|
||||
export function nfcall<T>(fn: Function, ...args: any[]): Promise<T>;
|
||||
export function nfcall(fn: Function, ...args: any[]): any {
|
||||
return new TPromise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result)), () => null);
|
||||
return new Promise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result)));
|
||||
}
|
||||
|
||||
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise;
|
||||
export function ninvoke<T>(thisArg: any, fn: Function, ...args: any[]): TPromise<T>;
|
||||
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable<any>;
|
||||
export function ninvoke<T>(thisArg: any, fn: Function, ...args: any[]): Thenable<T>;
|
||||
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any {
|
||||
return new TPromise((c, e) => fn.call(thisArg, ...args, (err: any, result: any) => err ? e(err) : c(result)), () => null);
|
||||
return new Promise((resolve, reject) => fn.call(thisArg, ...args, (err: any, result: any) => err ? reject(err) : resolve(result)));
|
||||
}
|
||||
|
||||
|
||||
//#region -- run on idle tricks ------------
|
||||
|
||||
export interface IdleDeadline {
|
||||
readonly didTimeout: boolean;
|
||||
timeRemaining(): DOMHighResTimeStamp;
|
||||
}
|
||||
/**
|
||||
* Execute the callback the next time the browser is idle
|
||||
*/
|
||||
export let runWhenIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable;
|
||||
|
||||
declare function requestIdleCallback(callback: (args: IdleDeadline) => void, options?: { timeout: number }): number;
|
||||
declare function cancelIdleCallback(handle: number): void;
|
||||
|
||||
(function () {
|
||||
if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') {
|
||||
let dummyIdle: IdleDeadline = Object.freeze({
|
||||
didTimeout: true,
|
||||
timeRemaining() { return 15; }
|
||||
});
|
||||
runWhenIdle = (runner, timeout = 0) => {
|
||||
let handle = setTimeout(() => runner(dummyIdle), timeout);
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
clearTimeout(handle);
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
runWhenIdle = (runner, timeout?) => {
|
||||
let handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
cancelIdleCallback(handle);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* An implementation of the "idle-until-urgent"-strategy as introduced
|
||||
* here: https://philipwalton.com/articles/idle-until-urgent/
|
||||
*/
|
||||
export class IdleValue<T> {
|
||||
|
||||
private readonly _executor: () => void;
|
||||
private readonly _handle: IDisposable;
|
||||
|
||||
private _didRun: boolean;
|
||||
private _value: T;
|
||||
private _error: any;
|
||||
|
||||
constructor(executor: () => T) {
|
||||
this._executor = () => {
|
||||
try {
|
||||
this._value = executor();
|
||||
} catch (err) {
|
||||
this._error = err;
|
||||
} finally {
|
||||
this._didRun = true;
|
||||
}
|
||||
};
|
||||
this._handle = runWhenIdle(() => this._executor());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._handle.dispose();
|
||||
}
|
||||
|
||||
getValue(): T {
|
||||
if (!this._didRun) {
|
||||
this._handle.dispose();
|
||||
this._executor();
|
||||
}
|
||||
if (this._error) {
|
||||
throw this._error;
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -3,27 +3,37 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { always } from 'vs/base/common/async';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
export interface CacheResult<T> {
|
||||
promise: Promise<T>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export default class Cache<T> {
|
||||
export class Cache<T> {
|
||||
|
||||
private promise: TPromise<T> = null;
|
||||
constructor(private task: () => TPromise<T>) { }
|
||||
private result: CacheResult<T> | null = null;
|
||||
constructor(private task: (ct: CancellationToken) => Promise<T>) { }
|
||||
|
||||
get(): TPromise<T> {
|
||||
if (this.promise) {
|
||||
return this.promise;
|
||||
get(): CacheResult<T> {
|
||||
if (this.result) {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
const promise = this.task();
|
||||
const cts = new CancellationTokenSource();
|
||||
const promise = this.task(cts.token);
|
||||
always(promise, () => cts.dispose());
|
||||
|
||||
this.promise = new TPromise<T>((c, e) => promise.done(c, e), () => {
|
||||
this.promise = null;
|
||||
promise.cancel();
|
||||
});
|
||||
this.result = {
|
||||
promise,
|
||||
dispose: () => {
|
||||
this.result = null;
|
||||
cts.cancel();
|
||||
cts.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return this.promise;
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface CancellationToken {
|
||||
@@ -24,6 +22,21 @@ const shortcutEvent = Object.freeze(function (callback, context?): IDisposable {
|
||||
|
||||
export namespace CancellationToken {
|
||||
|
||||
export function isCancellationToken(thing: any): thing is CancellationToken {
|
||||
if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
|
||||
return true;
|
||||
}
|
||||
if (thing instanceof MutableToken) {
|
||||
return true;
|
||||
}
|
||||
if (!thing || typeof thing !== 'object') {
|
||||
return false;
|
||||
}
|
||||
return typeof (thing as CancellationToken).isCancellationRequested === 'boolean'
|
||||
&& typeof (thing as CancellationToken).onCancellationRequested === 'function';
|
||||
}
|
||||
|
||||
|
||||
export const None: CancellationToken = Object.freeze({
|
||||
isCancellationRequested: false,
|
||||
onCancellationRequested: Event.None
|
||||
@@ -38,7 +51,7 @@ export namespace CancellationToken {
|
||||
class MutableToken implements CancellationToken {
|
||||
|
||||
private _isCancelled: boolean = false;
|
||||
private _emitter: Emitter<any>;
|
||||
private _emitter: Emitter<any> | null = null;
|
||||
|
||||
public cancel() {
|
||||
if (!this._isCancelled) {
|
||||
@@ -67,7 +80,7 @@ class MutableToken implements CancellationToken {
|
||||
public dispose(): void {
|
||||
if (this._emitter) {
|
||||
this._emitter.dispose();
|
||||
this._emitter = undefined;
|
||||
this._emitter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
// Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/
|
||||
|
||||
@@ -12,6 +11,10 @@
|
||||
*/
|
||||
export const enum CharCode {
|
||||
Null = 0,
|
||||
/**
|
||||
* The `\b` character.
|
||||
*/
|
||||
Backspace = 8,
|
||||
/**
|
||||
* The `\t` character.
|
||||
*/
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* An interface for a JavaScript object that
|
||||
@@ -47,7 +45,7 @@ export function size<T>(from: IStringDictionary<T> | INumberDictionary<T>): numb
|
||||
return count;
|
||||
}
|
||||
|
||||
export function first<T>(from: IStringDictionary<T> | INumberDictionary<T>): T {
|
||||
export function first<T>(from: IStringDictionary<T> | INumberDictionary<T>): T | undefined {
|
||||
for (let key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
return from[key];
|
||||
@@ -73,18 +71,6 @@ export function forEach<T>(from: IStringDictionary<T> | INumberDictionary<T>, ca
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from the dictionary. Returns {{false}} if the property
|
||||
* does not exists.
|
||||
*/
|
||||
export function remove<T>(from: IStringDictionary<T> | INumberDictionary<T>, key: string): boolean {
|
||||
if (!hasOwnProperty.call(from, key)) {
|
||||
return false;
|
||||
}
|
||||
delete (from as any)[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the collection into a dictionary based on the provided
|
||||
* group function.
|
||||
@@ -101,3 +87,13 @@ export function groupBy<T>(data: T[], groupFn: (element: T) => string): IStringD
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function fromMap<T>(original: Map<string, T>): IStringDictionary<T> {
|
||||
const result: IStringDictionary<T> = Object.create(null);
|
||||
if (original) {
|
||||
original.forEach((value, key) => {
|
||||
result[key] = value;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -416,7 +416,7 @@ export class Color {
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return Color.Format.CSS.format(this);
|
||||
return '' + Color.Format.CSS.format(this);
|
||||
}
|
||||
|
||||
static getLighterColor(of: Color, relative: Color, factor?: number): Color {
|
||||
|
||||
@@ -2,28 +2,26 @@
|
||||
* 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 strings from 'vs/base/common/strings';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
|
||||
let intlFileNameCollator: Intl.Collator;
|
||||
let intlFileNameCollatorIsNumeric: boolean;
|
||||
let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>;
|
||||
|
||||
export function setFileNameComparer(collator: Intl.Collator): void {
|
||||
export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>): void {
|
||||
intlFileNameCollator = collator;
|
||||
intlFileNameCollatorIsNumeric = collator.resolvedOptions().numeric;
|
||||
}
|
||||
|
||||
export function compareFileNames(one: string, other: string, caseSensitive = false): number {
|
||||
if (intlFileNameCollator) {
|
||||
const a = one || '';
|
||||
const b = other || '';
|
||||
const result = intlFileNameCollator.compare(a, b);
|
||||
const result = intlFileNameCollator.getValue().collator.compare(a, b);
|
||||
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollatorIsNumeric && result === 0 && a !== b) {
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
@@ -60,19 +58,19 @@ export function compareFileExtensions(one: string, other: string): number {
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other);
|
||||
|
||||
let result = intlFileNameCollator.compare(oneExtension, otherExtension);
|
||||
let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
|
||||
|
||||
if (result === 0) {
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollatorIsNumeric && oneExtension !== otherExtension) {
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
}
|
||||
|
||||
// Extensions are equal, compare filenames
|
||||
result = intlFileNameCollator.compare(oneName, otherName);
|
||||
result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
|
||||
|
||||
if (intlFileNameCollatorIsNumeric && result === 0 && oneName !== otherName) {
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +97,7 @@ function noIntlCompareFileExtensions(one: string, other: string): number {
|
||||
}
|
||||
|
||||
function extractNameAndExtension(str?: string): [string, string] {
|
||||
const match = str ? FileNameMatch.exec(str) : [] as RegExpExecArray;
|
||||
const match = str ? FileNameMatch.exec(str) as Array<string> : ([] as Array<string>);
|
||||
|
||||
return [(match && match[1]) || '', (match && match[3]) || ''];
|
||||
}
|
||||
@@ -195,4 +193,4 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { pad } from './strings';
|
||||
|
||||
export function toLocalISOString(date: Date): string {
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
let fnKey: string | null = null;
|
||||
let fn: Function | null = null;
|
||||
|
||||
if (typeof descriptor.value === 'function') {
|
||||
fnKey = 'value';
|
||||
@@ -22,19 +20,19 @@ export function createDecorator(mapFn: (fn: Function, key: string) => Function):
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
descriptor[fnKey] = mapFn(fn, key);
|
||||
descriptor[fnKey!] = mapFn(fn, key);
|
||||
};
|
||||
}
|
||||
|
||||
export function memoize(target: any, key: string, descriptor: any) {
|
||||
let fnKey: string = null;
|
||||
let fn: Function = null;
|
||||
let fnKey: string | null = null;
|
||||
let fn: Function | null = null;
|
||||
|
||||
if (typeof descriptor.value === 'function') {
|
||||
fnKey = 'value';
|
||||
fn = descriptor.value;
|
||||
|
||||
if (fn.length !== 0) {
|
||||
if (fn!.length !== 0) {
|
||||
console.warn('Memoize should only be used in functions with zero parameters');
|
||||
}
|
||||
} else if (typeof descriptor.get === 'function') {
|
||||
@@ -48,13 +46,13 @@ export function memoize(target: any, key: string, descriptor: any) {
|
||||
|
||||
const memoizeKey = `$memoize$${key}`;
|
||||
|
||||
descriptor[fnKey] = function (...args: any[]) {
|
||||
descriptor[fnKey!] = function (...args: any[]) {
|
||||
if (!this.hasOwnProperty(memoizeKey)) {
|
||||
Object.defineProperty(this, memoizeKey, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: fn.apply(this, args)
|
||||
value: fn!.apply(this, args)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { DiffChange } from 'vs/base/common/diff/diffChange';
|
||||
|
||||
@@ -217,7 +216,7 @@ export class LcsDiff {
|
||||
|
||||
private OriginalSequence: ISequence;
|
||||
private ModifiedSequence: ISequence;
|
||||
private ContinueProcessingPredicate: IContinueProcessingPredicate;
|
||||
private ContinueProcessingPredicate: IContinueProcessingPredicate | null;
|
||||
|
||||
private m_forwardHistory: number[][];
|
||||
private m_reverseHistory: number[][];
|
||||
@@ -225,7 +224,7 @@ export class LcsDiff {
|
||||
/**
|
||||
* Constructs the DiffFinder
|
||||
*/
|
||||
constructor(originalSequence: ISequence, newSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate = null) {
|
||||
constructor(originalSequence: ISequence, newSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate | null = null) {
|
||||
this.OriginalSequence = originalSequence;
|
||||
this.ModifiedSequence = newSequence;
|
||||
this.ContinueProcessingPredicate = continueProcessingPredicate;
|
||||
@@ -263,7 +262,7 @@ export class LcsDiff {
|
||||
// We have to clean up the computed diff to be more intuitive
|
||||
// but it turns out this cannot be done correctly until the entire set
|
||||
// of diffs have been computed
|
||||
return this.ShiftChanges(changes);
|
||||
return this.PrettifyChanges(changes);
|
||||
}
|
||||
|
||||
return changes;
|
||||
@@ -363,7 +362,7 @@ export class LcsDiff {
|
||||
originalIndex: number, originalEnd: number, midOriginalArr: number[],
|
||||
modifiedIndex: number, modifiedEnd: number, midModifiedArr: number[],
|
||||
deltaIsEven: boolean, quitEarlyArr: boolean[]): DiffChange[] {
|
||||
let forwardChanges: DiffChange[] = null, reverseChanges: DiffChange[] = null;
|
||||
let forwardChanges: DiffChange[] | null = null, reverseChanges: DiffChange[] | null = null;
|
||||
|
||||
// First, walk backward through the forward diagonals history
|
||||
let changeHelper = new DiffChangeHelper();
|
||||
@@ -499,7 +498,7 @@ export class LcsDiff {
|
||||
* @returns The diff changes, if available, otherwise null
|
||||
*/
|
||||
private ComputeRecursionPoint(originalStart: number, originalEnd: number, modifiedStart: number, modifiedEnd: number, midOriginalArr: number[], midModifiedArr: number[], quitEarlyArr: boolean[]) {
|
||||
let originalIndex: number, modifiedIndex: number;
|
||||
let originalIndex = 0, modifiedIndex = 0;
|
||||
let diagonalForwardStart = 0, diagonalForwardEnd = 0;
|
||||
let diagonalReverseStart = 0, diagonalReverseEnd = 0;
|
||||
let numDifferences: number;
|
||||
@@ -746,45 +745,32 @@ export class LcsDiff {
|
||||
* @param changes The list of changes to shift
|
||||
* @returns The shifted changes
|
||||
*/
|
||||
private ShiftChanges(changes: DiffChange[]): DiffChange[] {
|
||||
let mergedDiffs: boolean;
|
||||
do {
|
||||
mergedDiffs = false;
|
||||
private PrettifyChanges(changes: DiffChange[]): DiffChange[] {
|
||||
|
||||
// Shift all the changes down first
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
const change = changes[i];
|
||||
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this.OriginalSequence.getLength();
|
||||
const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this.ModifiedSequence.getLength();
|
||||
const checkOriginal = change.originalLength > 0;
|
||||
const checkModified = change.modifiedLength > 0;
|
||||
// Shift all the changes down first
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
const change = changes[i];
|
||||
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this.OriginalSequence.getLength();
|
||||
const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this.ModifiedSequence.getLength();
|
||||
const checkOriginal = change.originalLength > 0;
|
||||
const checkModified = change.modifiedLength > 0;
|
||||
|
||||
while (change.originalStart + change.originalLength < originalStop &&
|
||||
change.modifiedStart + change.modifiedLength < modifiedStop &&
|
||||
(!checkOriginal || this.OriginalElementsAreEqual(change.originalStart, change.originalStart + change.originalLength)) &&
|
||||
(!checkModified || this.ModifiedElementsAreEqual(change.modifiedStart, change.modifiedStart + change.modifiedLength))) {
|
||||
change.originalStart++;
|
||||
change.modifiedStart++;
|
||||
}
|
||||
while (change.originalStart + change.originalLength < originalStop &&
|
||||
change.modifiedStart + change.modifiedLength < modifiedStop &&
|
||||
(!checkOriginal || this.OriginalElementsAreEqual(change.originalStart, change.originalStart + change.originalLength)) &&
|
||||
(!checkModified || this.ModifiedElementsAreEqual(change.modifiedStart, change.modifiedStart + change.modifiedLength))) {
|
||||
change.originalStart++;
|
||||
change.modifiedStart++;
|
||||
}
|
||||
|
||||
// Build up the new list (we have to build a new list because we
|
||||
// might have changes we can merge together now)
|
||||
let result = new Array<DiffChange>();
|
||||
let mergedChangeArr: DiffChange[] = [null];
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) {
|
||||
mergedDiffs = true;
|
||||
result.push(mergedChangeArr[0]);
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
result.push(changes[i]);
|
||||
}
|
||||
let mergedChangeArr: (DiffChange | null)[] = [null];
|
||||
if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) {
|
||||
changes[i] = mergedChangeArr[0]!;
|
||||
changes.splice(i + 1, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
changes = result;
|
||||
} while (mergedDiffs);
|
||||
}
|
||||
|
||||
// Shift changes back up until we hit empty or whitespace-only lines
|
||||
for (let i = changes.length - 1; i >= 0; i--) {
|
||||
@@ -896,7 +882,6 @@ export class LcsDiff {
|
||||
*/
|
||||
private ConcatenateChanges(left: DiffChange[], right: DiffChange[]): DiffChange[] {
|
||||
let mergedChangeArr: DiffChange[] = [];
|
||||
let result: DiffChange[] = null;
|
||||
|
||||
if (left.length === 0 || right.length === 0) {
|
||||
return (right.length > 0) ? right : left;
|
||||
@@ -905,14 +890,14 @@ export class LcsDiff {
|
||||
// might recurse in the middle of a change thereby splitting it into
|
||||
// two changes. Here in the combining stage, we detect and fuse those
|
||||
// changes back together
|
||||
result = new Array<DiffChange>(left.length + right.length - 1);
|
||||
let result = new Array<DiffChange>(left.length + right.length - 1);
|
||||
MyArray.Copy(left, 0, result, 0, left.length - 1);
|
||||
result[left.length - 1] = mergedChangeArr[0];
|
||||
MyArray.Copy(right, 1, result, left.length, right.length - 1);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
result = new Array<DiffChange>(left.length + right.length);
|
||||
let result = new Array<DiffChange>(left.length + right.length);
|
||||
MyArray.Copy(left, 0, result, 0, left.length);
|
||||
MyArray.Copy(right, 0, result, left.length, right.length);
|
||||
|
||||
@@ -928,7 +913,7 @@ export class LcsDiff {
|
||||
* @param mergedChange The merged change if the two overlap, null otherwise
|
||||
* @returns True if the two changes overlap
|
||||
*/
|
||||
private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: DiffChange[]): boolean {
|
||||
private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: (DiffChange | null)[]): boolean {
|
||||
Debug.Assert(left.originalStart <= right.originalStart, 'Left change is not less than or equal to right change');
|
||||
Debug.Assert(left.modifiedStart <= right.modifiedStart, 'Left change is not less than or equal to right change');
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents information about a specific difference between two sequences.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
* 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 { IAction } from 'vs/base/common/actions';
|
||||
import { TPromise, IPromiseError, IPromiseErrorDetail } from 'vs/base/common/winjs.base';
|
||||
|
||||
// ------ BEGIN Hook up error listeners to winjs promises
|
||||
@@ -234,28 +232,6 @@ export function disposed(what: string): Error {
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: IAction[];
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: IAction[];
|
||||
}
|
||||
|
||||
export function isErrorWithActions(obj: any): obj is IErrorWithActions {
|
||||
return obj instanceof Error && Array.isArray((obj as IErrorWithActions).actions);
|
||||
}
|
||||
|
||||
export function create(message: string, options: IErrorOptions = Object.create(null)): Error & IErrorWithActions {
|
||||
const result = new Error(message);
|
||||
|
||||
if (options.actions) {
|
||||
(<IErrorWithActions>result).actions = options.actions;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getErrorMessage(err: any): string {
|
||||
if (!err) {
|
||||
return 'Error';
|
||||
|
||||
28
src/vs/base/common/errorsWithActions.ts
Normal file
28
src/vs/base/common/errorsWithActions.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: IAction[];
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: IAction[];
|
||||
}
|
||||
|
||||
export function isErrorWithActions(obj: any): obj is IErrorWithActions {
|
||||
return obj instanceof Error && Array.isArray((obj as IErrorWithActions).actions);
|
||||
}
|
||||
|
||||
export function createErrorWithActions(message: string, options: IErrorOptions = Object.create(null)): Error & IErrorWithActions {
|
||||
const result = new Error(message);
|
||||
|
||||
if (options.actions) {
|
||||
(<IErrorWithActions>result).actions = options.actions;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -2,13 +2,11 @@
|
||||
* 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 { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { once as onceFn } from 'vs/base/common/functional';
|
||||
import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
/**
|
||||
* To an event a function with one or zero parameters
|
||||
@@ -30,6 +28,79 @@ export interface EmitterOptions {
|
||||
onFirstListenerDidAdd?: Function;
|
||||
onListenerDidAdd?: Function;
|
||||
onLastListenerRemove?: Function;
|
||||
leakWarningThreshold?: number;
|
||||
}
|
||||
|
||||
let _globalLeakWarningThreshold = -1;
|
||||
export function setGlobalLeakWarningThreshold(n: number): IDisposable {
|
||||
let oldValue = _globalLeakWarningThreshold;
|
||||
_globalLeakWarningThreshold = n;
|
||||
return {
|
||||
dispose() {
|
||||
_globalLeakWarningThreshold = oldValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class LeakageMonitor {
|
||||
|
||||
private _stacks: Map<string, number> | undefined;
|
||||
private _warnCountdown: number = 0;
|
||||
|
||||
constructor(
|
||||
readonly customThreshold?: number,
|
||||
readonly name: string = Math.random().toString(18).slice(2, 5),
|
||||
) { }
|
||||
|
||||
dispose(): void {
|
||||
if (this._stacks) {
|
||||
this._stacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
check(listenerCount: number): undefined | (() => void) {
|
||||
|
||||
let threshold = _globalLeakWarningThreshold;
|
||||
if (typeof this.customThreshold === 'number') {
|
||||
threshold = this.customThreshold;
|
||||
}
|
||||
|
||||
if (threshold <= 0 || listenerCount < threshold) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this._stacks) {
|
||||
this._stacks = new Map();
|
||||
}
|
||||
let stack = new Error().stack!.split('\n').slice(3).join('\n');
|
||||
let count = (this._stacks.get(stack) || 0);
|
||||
this._stacks.set(stack, count + 1);
|
||||
this._warnCountdown -= 1;
|
||||
|
||||
if (this._warnCountdown <= 0) {
|
||||
// only warn on first exceed and then every time the limit
|
||||
// is exceeded by 50% again
|
||||
this._warnCountdown = threshold * .5;
|
||||
|
||||
// find most frequent listener and print warning
|
||||
let topStack: string;
|
||||
let topCount: number = 0;
|
||||
this._stacks.forEach((count, stack) => {
|
||||
if (!topStack || topCount < count) {
|
||||
topStack = stack;
|
||||
topCount = count;
|
||||
}
|
||||
});
|
||||
|
||||
console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`);
|
||||
console.warn(topStack!);
|
||||
}
|
||||
|
||||
return () => {
|
||||
let count = (this._stacks!.get(stack) || 0);
|
||||
this._stacks!.set(stack, count - 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,13 +128,18 @@ export class Emitter<T> {
|
||||
|
||||
private static readonly _noop = function () { };
|
||||
|
||||
private _event: Event<T>;
|
||||
private _disposed: boolean;
|
||||
private _deliveryQueue: [Listener, T][];
|
||||
protected _listeners: LinkedList<Listener>;
|
||||
|
||||
constructor(private _options?: EmitterOptions) {
|
||||
private readonly _options: EmitterOptions | undefined;
|
||||
private readonly _leakageMon: LeakageMonitor | undefined;
|
||||
private _disposed: boolean = false;
|
||||
private _event: Event<T> | undefined;
|
||||
private _deliveryQueue: [Listener, (T | undefined)][] | undefined;
|
||||
protected _listeners: LinkedList<Listener> | undefined;
|
||||
|
||||
constructor(options?: EmitterOptions) {
|
||||
this._options = options;
|
||||
this._leakageMon = _globalLeakWarningThreshold > 0
|
||||
? new LeakageMonitor(this._options && this._options.leakWarningThreshold)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,14 +169,26 @@ export class Emitter<T> {
|
||||
this._options.onListenerDidAdd(this, listener, thisArgs);
|
||||
}
|
||||
|
||||
// check and record this emitter for potential leakage
|
||||
let removeMonitor: (() => void) | undefined;
|
||||
if (this._leakageMon) {
|
||||
removeMonitor = this._leakageMon.check(this._listeners.size);
|
||||
}
|
||||
|
||||
let result: IDisposable;
|
||||
result = {
|
||||
dispose: () => {
|
||||
if (removeMonitor) {
|
||||
removeMonitor();
|
||||
}
|
||||
result.dispose = Emitter._noop;
|
||||
if (!this._disposed) {
|
||||
remove();
|
||||
if (this._options && this._options.onLastListenerRemove && this._listeners.isEmpty()) {
|
||||
this._options.onLastListenerRemove(this);
|
||||
if (this._options && this._options.onLastListenerRemove) {
|
||||
const hasListeners = (this._listeners && !this._listeners.isEmpty());
|
||||
if (!hasListeners) {
|
||||
this._options.onLastListenerRemove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +222,7 @@ export class Emitter<T> {
|
||||
}
|
||||
|
||||
while (this._deliveryQueue.length > 0) {
|
||||
const [listener, event] = this._deliveryQueue.shift();
|
||||
const [listener, event] = this._deliveryQueue.shift()!;
|
||||
try {
|
||||
if (typeof listener === 'function') {
|
||||
listener.call(undefined, event);
|
||||
@@ -155,6 +243,9 @@ export class Emitter<T> {
|
||||
if (this._deliveryQueue) {
|
||||
this._deliveryQueue.length = 0;
|
||||
}
|
||||
if (this._leakageMon) {
|
||||
this._leakageMon.dispose();
|
||||
}
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -185,7 +276,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
}
|
||||
|
||||
while (this._asyncDeliveryQueue.length > 0) {
|
||||
const [listener, event, thenables] = this._asyncDeliveryQueue.shift();
|
||||
const [listener, event, thenables] = this._asyncDeliveryQueue.shift()!;
|
||||
try {
|
||||
if (typeof listener === 'function') {
|
||||
listener.call(undefined, event);
|
||||
@@ -209,7 +300,7 @@ export class EventMultiplexer<T> implements IDisposable {
|
||||
|
||||
private readonly emitter: Emitter<T>;
|
||||
private hasListeners = false;
|
||||
private events: { event: Event<T>; listener: IDisposable; }[] = [];
|
||||
private events: { event: Event<T>; listener: IDisposable | null; }[] = [];
|
||||
|
||||
constructor() {
|
||||
this.emitter = new Emitter<T>({
|
||||
@@ -252,12 +343,14 @@ export class EventMultiplexer<T> implements IDisposable {
|
||||
this.events.forEach(e => this.unhook(e));
|
||||
}
|
||||
|
||||
private hook(e: { event: Event<T>; listener: IDisposable; }): void {
|
||||
private hook(e: { event: Event<T>; listener: IDisposable | null; }): void {
|
||||
e.listener = e.event(r => this.emitter.fire(r));
|
||||
}
|
||||
|
||||
private unhook(e: { event: Event<T>; listener: IDisposable; }): void {
|
||||
e.listener.dispose();
|
||||
private unhook(e: { event: Event<T>; listener: IDisposable | null; }): void {
|
||||
if (e.listener) {
|
||||
e.listener.dispose();
|
||||
}
|
||||
e.listener = null;
|
||||
}
|
||||
|
||||
@@ -266,23 +359,12 @@ export class EventMultiplexer<T> implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export function fromCallback<T>(fn: (handler: (e: T) => void) => IDisposable): Event<T> {
|
||||
let listener: IDisposable;
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd: () => listener = fn(e => emitter.fire(e)),
|
||||
onLastListenerRemove: () => listener.dispose()
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export function fromPromise<T =any>(promise: TPromise<T>): Event<T> {
|
||||
export function fromPromise<T =any>(promise: Thenable<T>): Event<T> {
|
||||
const emitter = new Emitter<T>();
|
||||
let shouldEmit = false;
|
||||
|
||||
promise
|
||||
.then(null, () => null)
|
||||
.then(undefined, () => null)
|
||||
.then(() => {
|
||||
if (!shouldEmit) {
|
||||
setTimeout(() => emitter.fire(), 0);
|
||||
@@ -295,22 +377,31 @@ export function fromPromise<T =any>(promise: TPromise<T>): Event<T> {
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export function toPromise<T>(event: Event<T>): TPromise<T> {
|
||||
return new TPromise(complete => {
|
||||
const sub = event(e => {
|
||||
sub.dispose();
|
||||
complete(e);
|
||||
});
|
||||
});
|
||||
export function toPromise<T>(event: Event<T>): Thenable<T> {
|
||||
return new Promise(c => once(event)(c));
|
||||
}
|
||||
|
||||
export function once<T>(event: Event<T>): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => {
|
||||
// we need this, in case the event fires during the listener call
|
||||
let didFire = false;
|
||||
|
||||
const result = event(e => {
|
||||
result.dispose();
|
||||
if (didFire) {
|
||||
return;
|
||||
} else if (result) {
|
||||
result.dispose();
|
||||
} else {
|
||||
didFire = true;
|
||||
}
|
||||
|
||||
return listener.call(thisArgs, e);
|
||||
}, null, disposables);
|
||||
|
||||
if (didFire) {
|
||||
result.dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
@@ -319,16 +410,17 @@ 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)));
|
||||
}
|
||||
|
||||
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean): Event<T>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay?: number, leading?: boolean): Event<O>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay: number = 100, leading = false): Event<O> {
|
||||
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<T>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<O>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event<O> {
|
||||
|
||||
let subscription: IDisposable;
|
||||
let output: O = undefined;
|
||||
let output: O | undefined = undefined;
|
||||
let handle: any = undefined;
|
||||
let numDebouncedCalls = 0;
|
||||
|
||||
const emitter = new Emitter<O>({
|
||||
leakWarningThreshold,
|
||||
onFirstListenerAdd() {
|
||||
subscription = event(cur => {
|
||||
numDebouncedCalls++;
|
||||
@@ -360,7 +452,7 @@ export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I)
|
||||
}
|
||||
|
||||
/**
|
||||
* The EventDelayer is useful in situations in which you want
|
||||
* The EventBufferer is useful in situations in which you want
|
||||
* to delay firing your events during some code.
|
||||
* You can wrap that code and be sure that the event will not
|
||||
* be fired during that wrap.
|
||||
@@ -397,12 +489,13 @@ export class EventBufferer {
|
||||
};
|
||||
}
|
||||
|
||||
bufferEvents(fn: () => void): void {
|
||||
bufferEvents<R = void>(fn: () => R): R {
|
||||
const buffer: Function[] = [];
|
||||
this.buffers.push(buffer);
|
||||
fn();
|
||||
const r = fn();
|
||||
this.buffers.pop();
|
||||
buffer.forEach(flush => flush());
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +523,10 @@ export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Even
|
||||
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
|
||||
}
|
||||
|
||||
export function signalEvent<T>(event: Event<T>): Event<void> {
|
||||
return event as Event<any> as Event<void>;
|
||||
}
|
||||
|
||||
class ChainableEvent<T> implements IChainableEvent<T> {
|
||||
|
||||
get event(): Event<T> { return this._event; }
|
||||
@@ -492,10 +589,10 @@ export function stopwatch<T>(event: Event<T>): Event<number> {
|
||||
* // 4
|
||||
* ```
|
||||
*/
|
||||
export function buffer<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Event<T> {
|
||||
buffer = buffer.slice();
|
||||
export function buffer<T>(event: Event<T>, nextTick = false, _buffer: T[] = []): Event<T> {
|
||||
let buffer: T[] | null = _buffer.slice();
|
||||
|
||||
let listener = event(e => {
|
||||
let listener: IDisposable | null = event(e => {
|
||||
if (buffer) {
|
||||
buffer.push(e);
|
||||
} else {
|
||||
@@ -504,7 +601,9 @@ export function buffer<T>(event: Event<T>, nextTick = false, buffer: T[] = []):
|
||||
});
|
||||
|
||||
const flush = () => {
|
||||
buffer.forEach(e => emitter.fire(e));
|
||||
if (buffer) {
|
||||
buffer.forEach(e => emitter.fire(e));
|
||||
}
|
||||
buffer = null;
|
||||
};
|
||||
|
||||
@@ -526,7 +625,9 @@ export function buffer<T>(event: Event<T>, nextTick = false, buffer: T[] = []):
|
||||
},
|
||||
|
||||
onLastListenerRemove() {
|
||||
listener.dispose();
|
||||
if (listener) {
|
||||
listener.dispose();
|
||||
}
|
||||
listener = null;
|
||||
}
|
||||
});
|
||||
@@ -563,18 +664,34 @@ export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Ev
|
||||
|
||||
export class Relay<T> implements IDisposable {
|
||||
|
||||
private emitter = new Emitter<T>();
|
||||
private listening = false;
|
||||
private inputEvent: Event<T> = Event.None;
|
||||
private inputEventListener: IDisposable = Disposable.None;
|
||||
|
||||
private emitter = new Emitter<T>({
|
||||
onFirstListenerDidAdd: () => {
|
||||
this.listening = true;
|
||||
this.inputEventListener = this.inputEvent(this.emitter.fire, this.emitter);
|
||||
},
|
||||
onLastListenerRemove: () => {
|
||||
this.listening = false;
|
||||
this.inputEventListener.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
readonly event: Event<T> = this.emitter.event;
|
||||
|
||||
private disposable: IDisposable = Disposable.None;
|
||||
|
||||
set input(event: Event<T>) {
|
||||
this.disposable.dispose();
|
||||
this.disposable = event(this.emitter.fire, this.emitter);
|
||||
this.inputEvent = event;
|
||||
|
||||
if (this.listening) {
|
||||
this.inputEventListener.dispose();
|
||||
this.inputEventListener = event(this.emitter.fire, this.emitter);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposable.dispose();
|
||||
this.inputEventListener.dispose();
|
||||
this.emitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
* 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 strings from 'vs/base/common/strings';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
export interface IFilter {
|
||||
// Returns null if word doesn't match.
|
||||
(word: string, wordToMatchAgainst: string): IMatch[];
|
||||
(word: string, wordToMatchAgainst: string): IMatch[] | null;
|
||||
}
|
||||
|
||||
export interface IMatch {
|
||||
@@ -27,7 +26,7 @@ export interface IMatch {
|
||||
* filter.
|
||||
*/
|
||||
export function or(...filter: IFilter[]): IFilter {
|
||||
return function (word: string, wordToMatchAgainst: string): IMatch[] {
|
||||
return function (word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
for (let i = 0, len = filter.length; i < len; i++) {
|
||||
let match = filter[i](word, wordToMatchAgainst);
|
||||
if (match) {
|
||||
@@ -43,7 +42,7 @@ export function or(...filter: IFilter[]): IFilter {
|
||||
export const matchesStrictPrefix: IFilter = _matchesPrefix.bind(undefined, false);
|
||||
export const matchesPrefix: IFilter = _matchesPrefix.bind(undefined, true);
|
||||
|
||||
function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: string): IMatch[] {
|
||||
function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
if (!wordToMatchAgainst || wordToMatchAgainst.length < word.length) {
|
||||
return null;
|
||||
}
|
||||
@@ -64,7 +63,7 @@ function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: s
|
||||
|
||||
// Contiguous Substring
|
||||
|
||||
export function matchesContiguousSubString(word: string, wordToMatchAgainst: string): IMatch[] {
|
||||
export function matchesContiguousSubString(word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
let index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
|
||||
if (index === -1) {
|
||||
return null;
|
||||
@@ -75,18 +74,18 @@ export function matchesContiguousSubString(word: string, wordToMatchAgainst: str
|
||||
|
||||
// Substring
|
||||
|
||||
export function matchesSubString(word: string, wordToMatchAgainst: string): IMatch[] {
|
||||
export function matchesSubString(word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
return _matchesSubString(word.toLowerCase(), wordToMatchAgainst.toLowerCase(), 0, 0);
|
||||
}
|
||||
|
||||
function _matchesSubString(word: string, wordToMatchAgainst: string, i: number, j: number): IMatch[] {
|
||||
function _matchesSubString(word: string, wordToMatchAgainst: string, i: number, j: number): IMatch[] | null {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
} else if (j === wordToMatchAgainst.length) {
|
||||
return null;
|
||||
} else {
|
||||
if (word[i] === wordToMatchAgainst[j]) {
|
||||
let result: IMatch[] = null;
|
||||
let result: IMatch[] | null = null;
|
||||
if (result = _matchesSubString(word, wordToMatchAgainst, i + 1, j + 1)) {
|
||||
return join({ start: j, end: j + 1 }, result);
|
||||
}
|
||||
@@ -145,7 +144,7 @@ function nextAnchor(camelCaseWord: string, start: number): number {
|
||||
return camelCaseWord.length;
|
||||
}
|
||||
|
||||
function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: number): IMatch[] {
|
||||
function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: number): IMatch[] | null {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
} else if (j === camelCaseWord.length) {
|
||||
@@ -153,7 +152,7 @@ function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: nu
|
||||
} else if (word[i] !== camelCaseWord[j].toLowerCase()) {
|
||||
return null;
|
||||
} else {
|
||||
let result: IMatch[] = null;
|
||||
let result: IMatch[] | null = null;
|
||||
let nextUpperIndex = j + 1;
|
||||
result = _matchesCamelCase(word, camelCaseWord, i + 1, j + 1);
|
||||
while (!result && (nextUpperIndex = nextAnchor(camelCaseWord, nextUpperIndex)) < camelCaseWord.length) {
|
||||
@@ -223,7 +222,7 @@ function isCamelCasePattern(word: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] {
|
||||
export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] | null {
|
||||
if (!camelCaseWord) {
|
||||
return null;
|
||||
}
|
||||
@@ -252,7 +251,7 @@ export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[]
|
||||
camelCaseWord = camelCaseWord.toLowerCase();
|
||||
}
|
||||
|
||||
let result: IMatch[] = null;
|
||||
let result: IMatch[] | null = null;
|
||||
let i = 0;
|
||||
|
||||
word = word.toLowerCase();
|
||||
@@ -268,12 +267,12 @@ export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[]
|
||||
// Otherwise also matches sub string of the word with beginnings of the words in the target. E.g. "gp" or "g p" will match "Git: Pull"
|
||||
// Useful in cases where the target is words (e.g. command labels)
|
||||
|
||||
export function matchesWords(word: string, target: string, contiguous: boolean = false): IMatch[] {
|
||||
export function matchesWords(word: string, target: string, contiguous: boolean = false): IMatch[] | null {
|
||||
if (!target || target.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: IMatch[] = null;
|
||||
let result: IMatch[] | null = null;
|
||||
let i = 0;
|
||||
|
||||
word = word.toLowerCase();
|
||||
@@ -285,7 +284,7 @@ export function matchesWords(word: string, target: string, contiguous: boolean =
|
||||
return result;
|
||||
}
|
||||
|
||||
function _matchesWords(word: string, target: string, i: number, j: number, contiguous: boolean): IMatch[] {
|
||||
function _matchesWords(word: string, target: string, i: number, j: number, contiguous: boolean): IMatch[] | null {
|
||||
if (i === word.length) {
|
||||
return [];
|
||||
} else if (j === target.length) {
|
||||
@@ -293,7 +292,7 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti
|
||||
} else if (word[i] !== target[j]) {
|
||||
return null;
|
||||
} else {
|
||||
let result: IMatch[] = null;
|
||||
let result: IMatch[] | null = null;
|
||||
let nextWordIndex = j + 1;
|
||||
result = _matchesWords(word, target, i + 1, j + 1, contiguous);
|
||||
if (!contiguous) {
|
||||
@@ -322,7 +321,7 @@ export const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matches
|
||||
const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString);
|
||||
const fuzzyRegExpCache = new LRUCache<string, RegExp>(10000); // bounded to 10000 elements
|
||||
|
||||
export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
|
||||
export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] | null {
|
||||
if (typeof word !== 'string' || typeof wordToMatchAgainst !== 'string') {
|
||||
return null; // return early for invalid input
|
||||
}
|
||||
@@ -335,7 +334,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
|
||||
}
|
||||
|
||||
// RegExp Filter
|
||||
let match: RegExpExecArray = regexp.exec(wordToMatchAgainst);
|
||||
let match = regexp.exec(wordToMatchAgainst);
|
||||
if (match) {
|
||||
return [{ start: match.index, end: match.index + match[0].length }];
|
||||
}
|
||||
@@ -362,13 +361,19 @@ export function anyScore(pattern: string, word: string, patternMaxWhitespaceIgno
|
||||
|
||||
//#region --- fuzzyScore ---
|
||||
|
||||
export function createMatches(position: number[]): IMatch[] {
|
||||
export function createMatches(offsetOrScore: number[] | FuzzyScore): IMatch[] {
|
||||
let ret: IMatch[] = [];
|
||||
if (!position) {
|
||||
if (!offsetOrScore) {
|
||||
return ret;
|
||||
}
|
||||
let last: IMatch;
|
||||
for (const pos of position) {
|
||||
let offsets: number[];
|
||||
if (Array.isArray(offsetOrScore[1])) {
|
||||
offsets = (offsetOrScore as FuzzyScore)[1];
|
||||
} else {
|
||||
offsets = offsetOrScore as number[];
|
||||
}
|
||||
let last: IMatch | undefined;
|
||||
for (const pos of offsets) {
|
||||
if (last && last.end === pos) {
|
||||
last.end += 1;
|
||||
} else {
|
||||
@@ -457,43 +462,24 @@ const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 }
|
||||
|
||||
export type FuzzyScore = [number, number[]];
|
||||
|
||||
export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIgnore?: number, firstMatchCanBeWeak?: boolean): FuzzyScore {
|
||||
export interface FuzzyScorer {
|
||||
(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined;
|
||||
}
|
||||
|
||||
export function fuzzyScore(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
|
||||
|
||||
const patternLen = pattern.length > 100 ? 100 : pattern.length;
|
||||
const wordLen = word.length > 100 ? 100 : word.length;
|
||||
|
||||
// Check for leading whitespace in the pattern and
|
||||
// start matching just after that position. This is
|
||||
// like `pattern = pattern.rtrim()` but doesn't create
|
||||
// a new string
|
||||
let patternStartPos = 0;
|
||||
if (patternMaxWhitespaceIgnore === undefined) {
|
||||
patternMaxWhitespaceIgnore = patternLen;
|
||||
}
|
||||
while (patternStartPos < patternMaxWhitespaceIgnore) {
|
||||
if (isWhitespaceAtPos(pattern, patternStartPos)) {
|
||||
patternStartPos += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (patternStartPos === patternLen) {
|
||||
return [-100, []];
|
||||
}
|
||||
|
||||
if (patternLen > wordLen) {
|
||||
if (patternPos >= patternLen || wordPos >= wordLen || patternLen > wordLen) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const lowPattern = pattern.toLowerCase();
|
||||
const lowWord = word.toLowerCase();
|
||||
|
||||
let patternPos = patternStartPos;
|
||||
let wordPos = 0;
|
||||
|
||||
// Run a simple check if the characters of pattern occur
|
||||
// (in order) at all in word. If that isn't the case we
|
||||
// stop because no match will be possible
|
||||
const patternStartPos = patternPos;
|
||||
const wordStartPos = wordPos;
|
||||
while (patternPos < patternLen && wordPos < wordLen) {
|
||||
if (lowPattern[patternPos] === lowWord[wordPos]) {
|
||||
patternPos += 1;
|
||||
@@ -504,6 +490,9 @@ export function fuzzyScore(pattern: string, word: string, patternMaxWhitespaceIg
|
||||
return undefined;
|
||||
}
|
||||
|
||||
patternPos = patternStartPos;
|
||||
wordPos = wordStartPos;
|
||||
|
||||
// There will be a mach, fill in tables
|
||||
for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
|
||||
|
||||
@@ -722,16 +711,16 @@ class LazyArray {
|
||||
|
||||
//#region --- graceful ---
|
||||
|
||||
export function fuzzyScoreGracefulAggressive(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): FuzzyScore {
|
||||
return fuzzyScoreWithPermutations(pattern, word, true, patternMaxWhitespaceIgnore);
|
||||
export function fuzzyScoreGracefulAggressive(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
|
||||
return fuzzyScoreWithPermutations(pattern, lowPattern, patternPos, word, lowWord, wordPos, true, firstMatchCanBeWeak);
|
||||
}
|
||||
|
||||
export function fuzzyScoreGraceful(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): FuzzyScore {
|
||||
return fuzzyScoreWithPermutations(pattern, word, false, patternMaxWhitespaceIgnore);
|
||||
export function fuzzyScoreGraceful(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
|
||||
return fuzzyScoreWithPermutations(pattern, lowPattern, patternPos, word, lowWord, wordPos, false, firstMatchCanBeWeak);
|
||||
}
|
||||
|
||||
function fuzzyScoreWithPermutations(pattern: string, word: string, aggressive?: boolean, patternMaxWhitespaceIgnore?: number): FuzzyScore {
|
||||
let top: [number, number[]] = fuzzyScore(pattern, word, patternMaxWhitespaceIgnore);
|
||||
function fuzzyScoreWithPermutations(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, aggressive: boolean, firstMatchCanBeWeak: boolean): FuzzyScore | undefined {
|
||||
let top = fuzzyScore(pattern, lowPattern, patternPos, word, lowWord, wordPos, firstMatchCanBeWeak);
|
||||
|
||||
if (top && !aggressive) {
|
||||
// when using the original pattern yield a result we`
|
||||
@@ -746,10 +735,10 @@ function fuzzyScoreWithPermutations(pattern: string, word: string, aggressive?:
|
||||
// permutations only swap neighbouring characters, e.g
|
||||
// `cnoso` becomes `conso`, `cnsoo`, `cnoos`.
|
||||
let tries = Math.min(7, pattern.length - 1);
|
||||
for (let patternPos = 1; patternPos < tries; patternPos++) {
|
||||
let newPattern = nextTypoPermutation(pattern, patternPos);
|
||||
for (let movingPatternPos = patternPos + 1; movingPatternPos < tries; movingPatternPos++) {
|
||||
let newPattern = nextTypoPermutation(pattern, movingPatternPos);
|
||||
if (newPattern) {
|
||||
let candidate = fuzzyScore(newPattern, word, patternMaxWhitespaceIgnore);
|
||||
let candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak);
|
||||
if (candidate) {
|
||||
candidate[0] -= 3; // permutation penalty
|
||||
if (!top || candidate[0] > top[0]) {
|
||||
@@ -763,7 +752,7 @@ function fuzzyScoreWithPermutations(pattern: string, word: string, aggressive?:
|
||||
return top;
|
||||
}
|
||||
|
||||
function nextTypoPermutation(pattern: string, patternPos: number): string {
|
||||
function nextTypoPermutation(pattern: string, patternPos: number): string | undefined {
|
||||
|
||||
if (patternPos + 1 >= pattern.length) {
|
||||
return undefined;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export function once<T extends Function>(this: any, fn: T): T {
|
||||
const _this = this;
|
||||
let didCall = false;
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
* 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 arrays from 'vs/base/common/arrays';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isThenable } from 'vs/base/common/async';
|
||||
|
||||
export interface IExpression {
|
||||
[pattern: string]: boolean | SiblingClause | any;
|
||||
@@ -248,7 +247,7 @@ const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else
|
||||
export type ParsedPattern = (path: string, basename?: string) => boolean;
|
||||
|
||||
// The ParsedExpression returns a Promise iff hasSibling returns a Promise.
|
||||
export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>) => string | TPromise<string> /* the matching pattern */;
|
||||
export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) => string | null | Promise<string | null> /* the matching pattern */;
|
||||
|
||||
export interface IGlobOptions {
|
||||
/**
|
||||
@@ -258,14 +257,14 @@ export interface IGlobOptions {
|
||||
}
|
||||
|
||||
interface ParsedStringPattern {
|
||||
(path: string, basename: string): string | TPromise<string> /* the matching pattern */;
|
||||
(path: string, basename: string): string | null | Promise<string | null> /* the matching pattern */;
|
||||
basenames?: string[];
|
||||
patterns?: string[];
|
||||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
}
|
||||
interface ParsedExpressionPattern {
|
||||
(path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise<boolean>): string | TPromise<string> /* the matching pattern */;
|
||||
(path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | null | Promise<string | null> /* the matching pattern */;
|
||||
requiresSiblings?: boolean;
|
||||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
@@ -277,7 +276,7 @@ const FALSE = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
const NULL = function (): string {
|
||||
const NULL = function (): string | null {
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -305,7 +304,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P
|
||||
}
|
||||
|
||||
// Check for Trivias
|
||||
let match: RegExpExecArray;
|
||||
let match: RegExpExecArray | null;
|
||||
if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check
|
||||
const base = pattern.substr(4); // '**/*'.length === 4
|
||||
parsedPattern = function (path, basename) {
|
||||
@@ -342,7 +341,7 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsedPattern(paths.normalize(arg2.pathToRelative(arg2.base, path)), basename);
|
||||
return parsedPattern(arg2.pathToRelative(arg2.base, path), basename);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -481,15 +480,15 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG
|
||||
return parsedExpression(<IExpression>arg1, options);
|
||||
}
|
||||
|
||||
export function hasSiblingPromiseFn(siblingsFn?: () => TPromise<string[]>) {
|
||||
export function hasSiblingPromiseFn(siblingsFn?: () => Promise<string[]>) {
|
||||
if (!siblingsFn) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let siblings: TPromise<Record<string, true>>;
|
||||
let siblings: Promise<Record<string, true>>;
|
||||
return (name: string) => {
|
||||
if (!siblings) {
|
||||
siblings = (siblingsFn() || TPromise.as([]))
|
||||
siblings = (siblingsFn() || Promise.resolve([]))
|
||||
.then(list => list ? listToMap(list) : {});
|
||||
}
|
||||
return siblings.then(map => !!map[name]);
|
||||
@@ -530,9 +529,9 @@ export function isRelativePattern(obj: any): obj is IRelativePattern {
|
||||
*/
|
||||
export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression {
|
||||
const parsedExpression = parse(expression, options);
|
||||
return (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>): string | TPromise<string> => {
|
||||
return (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | null | Promise<string | null> => {
|
||||
const result = parsedExpression(path, basename, hasSibling);
|
||||
return result instanceof TPromise ? result : TPromise.as(result);
|
||||
return isThenable(result) ? result : Promise.resolve(result);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -554,7 +553,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!parsedPatterns.some(parsedPattern => (<ParsedExpressionPattern>parsedPattern).requiresSiblings)) {
|
||||
if (!parsedPatterns.some(parsedPattern => !!(<ParsedExpressionPattern>parsedPattern).requiresSiblings)) {
|
||||
if (n === 1) {
|
||||
return <ParsedStringPattern>parsedPatterns[0];
|
||||
}
|
||||
@@ -584,8 +583,8 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
||||
return resultExpression;
|
||||
}
|
||||
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | TPromise<boolean>) {
|
||||
let name: string;
|
||||
const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise<boolean>) {
|
||||
let name: string | undefined = undefined;
|
||||
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
// Pattern matches path
|
||||
@@ -639,14 +638,14 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio
|
||||
if (value) {
|
||||
const when = (<SiblingClause>value).when;
|
||||
if (typeof when === 'string') {
|
||||
const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise<boolean>) => {
|
||||
const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise<boolean>) => {
|
||||
if (!hasSibling || !parsedPattern(path, basename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const clausePattern = when.replace('$(basename)', name);
|
||||
const matched = hasSibling(clausePattern);
|
||||
return TPromise.is(matched) ?
|
||||
return isThenable(matched) ?
|
||||
matched.then(m => m ? pattern : null) :
|
||||
matched ? pattern : null;
|
||||
};
|
||||
@@ -665,7 +664,10 @@ function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedE
|
||||
return parsedPatterns;
|
||||
}
|
||||
|
||||
const basenames = basenamePatterns.reduce<string[]>((all, current) => all.concat((<ParsedStringPattern>current).basenames), []);
|
||||
const basenames = basenamePatterns.reduce<string[]>((all, current) => {
|
||||
const basenames = (<ParsedStringPattern>current).basenames;
|
||||
return basenames ? all.concat(basenames) : all;
|
||||
}, <string[]>[]);
|
||||
let patterns: string[];
|
||||
if (result) {
|
||||
patterns = [];
|
||||
@@ -673,7 +675,10 @@ function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedE
|
||||
patterns.push(result);
|
||||
}
|
||||
} else {
|
||||
patterns = basenamePatterns.reduce((all, current) => all.concat((<ParsedStringPattern>current).patterns), []);
|
||||
patterns = basenamePatterns.reduce((all, current) => {
|
||||
const patterns = (<ParsedStringPattern>current).patterns;
|
||||
return patterns ? all.concat(patterns) : all;
|
||||
}, <string[]>[]);
|
||||
}
|
||||
const aggregate: ParsedStringPattern = function (path, basename) {
|
||||
if (!path) {
|
||||
@@ -699,4 +704,4 @@ function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedE
|
||||
const aggregatedPatterns = parsedPatterns.filter(parsedPattern => !(<ParsedStringPattern>parsedPattern).basenames);
|
||||
aggregatedPatterns.push(aggregate);
|
||||
return aggregatedPatterns;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +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';
|
||||
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
|
||||
export interface Node<T> {
|
||||
data: T;
|
||||
incoming: { [key: string]: Node<T> };
|
||||
outgoing: { [key: string]: Node<T> };
|
||||
}
|
||||
|
||||
function newNode<T>(data: T): Node<T> {
|
||||
return {
|
||||
data: data,
|
||||
incoming: Object.create(null),
|
||||
outgoing: Object.create(null)
|
||||
};
|
||||
}
|
||||
|
||||
export class Graph<T> {
|
||||
|
||||
private _nodes: { [key: string]: Node<T> } = Object.create(null);
|
||||
|
||||
constructor(private _hashFn: (element: T) => string) {
|
||||
// empty
|
||||
}
|
||||
|
||||
roots(): Node<T>[] {
|
||||
const ret: Node<T>[] = [];
|
||||
forEach(this._nodes, entry => {
|
||||
if (isEmptyObject(entry.value.outgoing)) {
|
||||
ret.push(entry.value);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
traverse(start: T, inwards: boolean, callback: (data: T) => void): void {
|
||||
const startNode = this.lookup(start);
|
||||
if (!startNode) {
|
||||
return;
|
||||
}
|
||||
this._traverse(startNode, inwards, Object.create(null), callback);
|
||||
}
|
||||
|
||||
private _traverse(node: Node<T>, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void {
|
||||
const key = this._hashFn(node.data);
|
||||
if (seen[key]) {
|
||||
return;
|
||||
}
|
||||
seen[key] = true;
|
||||
callback(node.data);
|
||||
const nodes = inwards ? node.outgoing : node.incoming;
|
||||
forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback));
|
||||
}
|
||||
|
||||
insertEdge(from: T, to: T): void {
|
||||
const fromNode = this.lookupOrInsertNode(from),
|
||||
toNode = this.lookupOrInsertNode(to);
|
||||
|
||||
fromNode.outgoing[this._hashFn(to)] = toNode;
|
||||
toNode.incoming[this._hashFn(from)] = fromNode;
|
||||
}
|
||||
|
||||
removeNode(data: T): void {
|
||||
const key = this._hashFn(data);
|
||||
delete this._nodes[key];
|
||||
forEach(this._nodes, (entry) => {
|
||||
delete entry.value.outgoing[key];
|
||||
delete entry.value.incoming[key];
|
||||
});
|
||||
}
|
||||
|
||||
lookupOrInsertNode(data: T): Node<T> {
|
||||
const key = this._hashFn(data);
|
||||
let node = this._nodes[key];
|
||||
|
||||
if (!node) {
|
||||
node = newNode(data);
|
||||
this._nodes[key] = node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
lookup(data: T): Node<T> {
|
||||
return this._nodes[this._hashFn(data)];
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return Object.keys(this._nodes).length;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let data: string[] = [];
|
||||
forEach(this._nodes, entry => {
|
||||
data.push(`${entry.key}, (incoming)[${Object.keys(entry.value.incoming).join(', ')}], (outgoing)[${Object.keys(entry.value.outgoing).join(',')}]`);
|
||||
});
|
||||
return data.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Return a hash value for an object.
|
||||
@@ -23,9 +22,9 @@ export function hash(obj: any, hashVal = 0): number {
|
||||
case 'number':
|
||||
return numberHash(obj, hashVal);
|
||||
case 'undefined':
|
||||
return numberHash(obj, 937);
|
||||
return numberHash(0, 937);
|
||||
default:
|
||||
return numberHash(obj, 617);
|
||||
return numberHash(0, 617);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,3 +56,17 @@ function objectHash(obj: any, initialHashVal: number): number {
|
||||
return hash(obj[key], hashVal);
|
||||
}, initialHashVal);
|
||||
}
|
||||
|
||||
export class Hasher {
|
||||
|
||||
private _value = 0;
|
||||
|
||||
get value(): number {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
hash(obj: any): number {
|
||||
this._value = hash(obj, this._value);
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
@@ -27,27 +27,27 @@ export class HistoryNavigator<T> implements INavigator<T> {
|
||||
this._onChange();
|
||||
}
|
||||
|
||||
public next(): T {
|
||||
public next(): T | null {
|
||||
return this._navigator.next();
|
||||
}
|
||||
|
||||
public previous(): T {
|
||||
public previous(): T | null {
|
||||
return this._navigator.previous();
|
||||
}
|
||||
|
||||
public current(): T {
|
||||
public current(): T | null {
|
||||
return this._navigator.current();
|
||||
}
|
||||
|
||||
public parent(): T {
|
||||
public parent(): null {
|
||||
return null;
|
||||
}
|
||||
|
||||
public first(): T {
|
||||
public first(): T | null {
|
||||
return this._navigator.first();
|
||||
}
|
||||
|
||||
public last(): T {
|
||||
public last(): T | null {
|
||||
return this._navigator.last();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
export interface IMarkdownString {
|
||||
value: string;
|
||||
isTrusted?: boolean;
|
||||
uris?: { [href: string]: UriComponents };
|
||||
}
|
||||
|
||||
export class MarkdownString implements IMarkdownString {
|
||||
|
||||
value: string;
|
||||
isTrusted?: boolean;
|
||||
sanitize: boolean = true;
|
||||
|
||||
constructor(value: string = '') {
|
||||
this.value = value;
|
||||
@@ -42,7 +43,7 @@ export class MarkdownString implements IMarkdownString {
|
||||
}
|
||||
}
|
||||
|
||||
export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[]): boolean {
|
||||
export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[] | null | undefined): boolean {
|
||||
if (isMarkdownString(oneOrMany)) {
|
||||
return !oneOrMany.value;
|
||||
} else if (Array.isArray(oneOrMany)) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export class IdGenerator {
|
||||
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export interface IteratorResult<T> {
|
||||
readonly done: boolean;
|
||||
readonly value: T | undefined;
|
||||
export interface IteratorDefinedResult<T> {
|
||||
readonly done: false;
|
||||
readonly value: T;
|
||||
}
|
||||
export interface IteratorUndefinedResult {
|
||||
readonly done: true;
|
||||
readonly value: undefined;
|
||||
}
|
||||
export const FIN: IteratorUndefinedResult = { done: true, value: undefined };
|
||||
export type IteratorResult<T> = IteratorDefinedResult<T> | IteratorUndefinedResult;
|
||||
|
||||
export interface Iterator<T> {
|
||||
next(): IteratorResult<T>;
|
||||
@@ -17,7 +21,7 @@ export interface Iterator<T> {
|
||||
export module Iterator {
|
||||
const _empty: Iterator<any> = {
|
||||
next() {
|
||||
return { done: true, value: undefined };
|
||||
return FIN;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,11 +29,11 @@ export module Iterator {
|
||||
return _empty;
|
||||
}
|
||||
|
||||
export function iterate<T>(array: T[], index = 0, length = array.length): Iterator<T> {
|
||||
export function fromArray<T>(array: T[], index = 0, length = array.length): Iterator<T> {
|
||||
return {
|
||||
next(): IteratorResult<T> {
|
||||
if (index >= length) {
|
||||
return { done: true, value: undefined };
|
||||
return FIN;
|
||||
}
|
||||
|
||||
return { done: false, value: array[index++] };
|
||||
@@ -37,11 +41,25 @@ export module Iterator {
|
||||
};
|
||||
}
|
||||
|
||||
export function from<T>(elements: Iterator<T> | T[] | undefined): Iterator<T> {
|
||||
if (!elements) {
|
||||
return Iterator.empty();
|
||||
} else if (Array.isArray(elements)) {
|
||||
return Iterator.fromArray(elements);
|
||||
} else {
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
export function map<T, R>(iterator: Iterator<T>, fn: (t: T) => R): Iterator<R> {
|
||||
return {
|
||||
next() {
|
||||
const { done, value } = iterator.next();
|
||||
return { done, value: done ? undefined : fn(value) };
|
||||
const element = iterator.next();
|
||||
if (element.done) {
|
||||
return FIN;
|
||||
} else {
|
||||
return { done: false, value: fn(element.value) };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -50,14 +68,12 @@ export module Iterator {
|
||||
return {
|
||||
next() {
|
||||
while (true) {
|
||||
const { done, value } = iterator.next();
|
||||
|
||||
if (done) {
|
||||
return { done, value: undefined };
|
||||
const element = iterator.next();
|
||||
if (element.done) {
|
||||
return FIN;
|
||||
}
|
||||
|
||||
if (fn(value)) {
|
||||
return { done, value };
|
||||
if (fn(element.value)) {
|
||||
return { done: false, value: element.value };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,14 +97,14 @@ export type ISequence<T> = Iterator<T> | T[];
|
||||
|
||||
export function getSequenceIterator<T>(arg: Iterator<T> | T[]): Iterator<T> {
|
||||
if (Array.isArray(arg)) {
|
||||
return Iterator.iterate(arg);
|
||||
return Iterator.fromArray(arg);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
export interface INextIterator<T> {
|
||||
next(): T;
|
||||
next(): T | null;
|
||||
}
|
||||
|
||||
export class ArrayIterator<T> implements INextIterator<T> {
|
||||
@@ -105,17 +121,17 @@ export class ArrayIterator<T> implements INextIterator<T> {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public first(): T {
|
||||
public first(): T | null {
|
||||
this.index = this.start;
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public next(): T {
|
||||
public next(): T | null {
|
||||
this.index = Math.min(this.index + 1, this.end);
|
||||
return this.current();
|
||||
}
|
||||
|
||||
protected current(): T {
|
||||
protected current(): T | null {
|
||||
if (this.index === this.start - 1 || this.index === this.end) {
|
||||
return null;
|
||||
}
|
||||
@@ -130,34 +146,33 @@ export class ArrayNavigator<T> extends ArrayIterator<T> implements INavigator<T>
|
||||
super(items, start, end, index);
|
||||
}
|
||||
|
||||
public current(): T {
|
||||
public current(): T | null {
|
||||
return super.current();
|
||||
}
|
||||
|
||||
public previous(): T {
|
||||
public previous(): T | null {
|
||||
this.index = Math.max(this.index - 1, this.start - 1);
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public first(): T {
|
||||
public first(): T | null {
|
||||
this.index = this.start;
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public last(): T {
|
||||
public last(): T | null {
|
||||
this.index = this.end - 1;
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public parent(): T {
|
||||
public parent(): T | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class MappedIterator<T, R> implements INextIterator<R> {
|
||||
|
||||
constructor(protected iterator: INextIterator<T>, protected fn: (item: T) => R) {
|
||||
constructor(protected iterator: INextIterator<T>, protected fn: (item: T | null) => R) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -165,12 +180,12 @@ export class MappedIterator<T, R> implements INextIterator<R> {
|
||||
}
|
||||
|
||||
export interface INavigator<T> extends INextIterator<T> {
|
||||
current(): T;
|
||||
previous(): T;
|
||||
parent(): T;
|
||||
first(): T;
|
||||
last(): T;
|
||||
next(): T;
|
||||
current(): T | null;
|
||||
previous(): T | null;
|
||||
parent(): T | null;
|
||||
first(): T | null;
|
||||
last(): T | null;
|
||||
next(): T | null;
|
||||
}
|
||||
|
||||
export class MappedNavigator<T, R> extends MappedIterator<T, R> implements INavigator<R> {
|
||||
|
||||
@@ -2,36 +2,35 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export enum ScanError {
|
||||
None,
|
||||
UnexpectedEndOfComment,
|
||||
UnexpectedEndOfString,
|
||||
UnexpectedEndOfNumber,
|
||||
InvalidUnicode,
|
||||
InvalidEscapeCharacter,
|
||||
InvalidCharacter
|
||||
export const enum ScanError {
|
||||
None = 0,
|
||||
UnexpectedEndOfComment = 1,
|
||||
UnexpectedEndOfString = 2,
|
||||
UnexpectedEndOfNumber = 3,
|
||||
InvalidUnicode = 4,
|
||||
InvalidEscapeCharacter = 5,
|
||||
InvalidCharacter = 6
|
||||
}
|
||||
|
||||
export enum SyntaxKind {
|
||||
Unknown = 0,
|
||||
OpenBraceToken,
|
||||
CloseBraceToken,
|
||||
OpenBracketToken,
|
||||
CloseBracketToken,
|
||||
CommaToken,
|
||||
ColonToken,
|
||||
NullKeyword,
|
||||
TrueKeyword,
|
||||
FalseKeyword,
|
||||
StringLiteral,
|
||||
NumericLiteral,
|
||||
LineCommentTrivia,
|
||||
BlockCommentTrivia,
|
||||
LineBreakTrivia,
|
||||
Trivia,
|
||||
EOF
|
||||
export const enum SyntaxKind {
|
||||
OpenBraceToken = 1,
|
||||
CloseBraceToken = 2,
|
||||
OpenBracketToken = 3,
|
||||
CloseBracketToken = 4,
|
||||
CommaToken = 5,
|
||||
ColonToken = 6,
|
||||
NullKeyword = 7,
|
||||
TrueKeyword = 8,
|
||||
FalseKeyword = 9,
|
||||
StringLiteral = 10,
|
||||
NumericLiteral = 11,
|
||||
LineCommentTrivia = 12,
|
||||
BlockCommentTrivia = 13,
|
||||
LineBreakTrivia = 14,
|
||||
Trivia = 15,
|
||||
Unknown = 16,
|
||||
EOF = 17
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ export interface JSONScanner {
|
||||
*/
|
||||
setPosition(pos: number): void;
|
||||
/**
|
||||
* Read the next token. Returns the tolen code.
|
||||
* Read the next token. Returns the token code.
|
||||
*/
|
||||
scan(): SyntaxKind;
|
||||
/**
|
||||
@@ -71,6 +70,128 @@ export interface JSONScanner {
|
||||
*/
|
||||
getTokenError(): ScanError;
|
||||
}
|
||||
|
||||
|
||||
export interface ParseError {
|
||||
error: ParseErrorCode;
|
||||
offset: number;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export const enum ParseErrorCode {
|
||||
InvalidSymbol = 1,
|
||||
InvalidNumberFormat = 2,
|
||||
PropertyNameExpected = 3,
|
||||
ValueExpected = 4,
|
||||
ColonExpected = 5,
|
||||
CommaExpected = 6,
|
||||
CloseBraceExpected = 7,
|
||||
CloseBracketExpected = 8,
|
||||
EndOfFileExpected = 9,
|
||||
InvalidCommentToken = 10,
|
||||
UnexpectedEndOfComment = 11,
|
||||
UnexpectedEndOfString = 12,
|
||||
UnexpectedEndOfNumber = 13,
|
||||
InvalidUnicode = 14,
|
||||
InvalidEscapeCharacter = 15,
|
||||
InvalidCharacter = 16
|
||||
}
|
||||
|
||||
export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
|
||||
|
||||
export interface Node {
|
||||
readonly type: NodeType;
|
||||
readonly value?: any;
|
||||
readonly offset: number;
|
||||
readonly length: number;
|
||||
readonly colonOffset?: number;
|
||||
readonly parent?: Node;
|
||||
readonly children?: Node[];
|
||||
}
|
||||
|
||||
export type Segment = string | number;
|
||||
export type JSONPath = Segment[];
|
||||
|
||||
export interface Location {
|
||||
/**
|
||||
* The previous property key or literal value (string, number, boolean or null) or undefined.
|
||||
*/
|
||||
previousNode?: Node;
|
||||
/**
|
||||
* The path describing the location in the JSON document. The path consists of a sequence strings
|
||||
* representing an object property or numbers for array indices.
|
||||
*/
|
||||
path: JSONPath;
|
||||
/**
|
||||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
|
||||
* '*' will match a single segment, of any property name or index.
|
||||
* '**' will match a sequece of segments or no segment, of any property name or index.
|
||||
*/
|
||||
matches: (patterns: JSONPath) => boolean;
|
||||
/**
|
||||
* If set, the location's offset is at a property key.
|
||||
*/
|
||||
isAtPropertyKey: boolean;
|
||||
}
|
||||
|
||||
export interface ParseOptions {
|
||||
disallowComments?: boolean;
|
||||
allowTrailingComma?: boolean;
|
||||
}
|
||||
|
||||
export namespace ParseOptions {
|
||||
export const DEFAULT = {
|
||||
allowTrailingComma: true
|
||||
};
|
||||
}
|
||||
|
||||
export interface JSONVisitor {
|
||||
/**
|
||||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
|
||||
*/
|
||||
onObjectBegin?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a property is encountered. The offset and length represent the location of the property name.
|
||||
*/
|
||||
onObjectProperty?: (property: string, offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
|
||||
*/
|
||||
onObjectEnd?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
|
||||
*/
|
||||
onArrayBegin?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
|
||||
*/
|
||||
onArrayEnd?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
|
||||
*/
|
||||
onLiteralValue?: (value: any, offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
|
||||
*/
|
||||
onSeparator?: (character: string, offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
|
||||
*/
|
||||
onComment?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked on an error.
|
||||
*/
|
||||
onError?: (error: ParseErrorCode, offset: number, length: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON scanner on the given text.
|
||||
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
||||
@@ -585,59 +706,142 @@ const enum CharacterCodes {
|
||||
verticalTab = 0x0B, // \v
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ParseError {
|
||||
error: ParseErrorCode;
|
||||
}
|
||||
|
||||
export enum ParseErrorCode {
|
||||
InvalidSymbol,
|
||||
InvalidNumberFormat,
|
||||
PropertyNameExpected,
|
||||
ValueExpected,
|
||||
ColonExpected,
|
||||
CommaExpected,
|
||||
CloseBraceExpected,
|
||||
CloseBracketExpected,
|
||||
EndOfFileExpected
|
||||
}
|
||||
|
||||
export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
|
||||
|
||||
function getLiteralNodeType(value: any): NodeType {
|
||||
switch (typeof value) {
|
||||
case 'boolean': return 'boolean';
|
||||
case 'number': return 'number';
|
||||
case 'string': return 'string';
|
||||
default: return 'null';
|
||||
}
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
interface NodeImpl extends Node {
|
||||
type: NodeType;
|
||||
value?: any;
|
||||
offset: number;
|
||||
length: number;
|
||||
columnOffset?: number;
|
||||
parent?: Node;
|
||||
children?: Node[];
|
||||
colonOffset?: number;
|
||||
parent?: NodeImpl;
|
||||
children?: NodeImpl[];
|
||||
}
|
||||
|
||||
export type Segment = string | number;
|
||||
export type JSONPath = Segment[];
|
||||
/**
|
||||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
|
||||
*/
|
||||
export function getLocation(text: string, position: number): Location {
|
||||
let segments: Segment[] = []; // strings or numbers
|
||||
let earlyReturnException = new Object();
|
||||
let previousNode: NodeImpl | undefined = void 0;
|
||||
const previousNodeInst: NodeImpl = {
|
||||
value: {},
|
||||
offset: 0,
|
||||
length: 0,
|
||||
type: 'object',
|
||||
parent: void 0
|
||||
};
|
||||
let isAtPropertyKey = false;
|
||||
function setPreviousNode(value: string, offset: number, length: number, type: NodeType) {
|
||||
previousNodeInst.value = value;
|
||||
previousNodeInst.offset = offset;
|
||||
previousNodeInst.length = length;
|
||||
previousNodeInst.type = type;
|
||||
previousNodeInst.colonOffset = void 0;
|
||||
previousNode = previousNodeInst;
|
||||
}
|
||||
try {
|
||||
|
||||
export interface ParseOptions {
|
||||
disallowComments?: boolean;
|
||||
disallowTrailingComma?: boolean;
|
||||
visit(text, {
|
||||
onObjectBegin: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
isAtPropertyKey = position > offset;
|
||||
segments.push(''); // push a placeholder (will be replaced)
|
||||
},
|
||||
onObjectProperty: (name: string, offset: number, length: number) => {
|
||||
if (position < offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
setPreviousNode(name, offset, length, 'property');
|
||||
segments[segments.length - 1] = name;
|
||||
if (position <= offset + length) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
},
|
||||
onObjectEnd: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.pop();
|
||||
},
|
||||
onArrayBegin: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.push(0);
|
||||
},
|
||||
onArrayEnd: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.pop();
|
||||
},
|
||||
onLiteralValue: (value: any, offset: number, length: number) => {
|
||||
if (position < offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
setPreviousNode(value, offset, length, getLiteralNodeType(value));
|
||||
|
||||
if (position <= offset + length) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
},
|
||||
onSeparator: (sep: string, offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
if (sep === ':' && previousNode && previousNode.type === 'property') {
|
||||
previousNode.colonOffset = offset;
|
||||
isAtPropertyKey = false;
|
||||
previousNode = void 0;
|
||||
} else if (sep === ',') {
|
||||
let last = segments[segments.length - 1];
|
||||
if (typeof last === 'number') {
|
||||
segments[segments.length - 1] = last + 1;
|
||||
} else {
|
||||
isAtPropertyKey = true;
|
||||
segments[segments.length - 1] = '';
|
||||
}
|
||||
previousNode = void 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e !== earlyReturnException) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
path: segments,
|
||||
previousNode,
|
||||
isAtPropertyKey,
|
||||
matches: (pattern: Segment[]) => {
|
||||
let k = 0;
|
||||
for (let i = 0; k < pattern.length && i < segments.length; i++) {
|
||||
if (pattern[k] === segments[i] || pattern[k] === '*') {
|
||||
k++;
|
||||
} else if (pattern[k] !== '**') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return k === pattern.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
* Therefore always check the errors list to find out if the input was valid.
|
||||
*/
|
||||
export function parse(text: string, errors: ParseError[] = [], options?: ParseOptions): any {
|
||||
let currentProperty: string = null;
|
||||
export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any {
|
||||
let currentProperty: string | null = null;
|
||||
let currentParent: any = [];
|
||||
let previousParents: any[] = [];
|
||||
|
||||
@@ -674,8 +878,8 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onLiteralValue: onValue,
|
||||
onError: (error: ParseErrorCode) => {
|
||||
errors.push({ error: error });
|
||||
onError: (error: ParseErrorCode, offset: number, length: number) => {
|
||||
errors.push({ error, offset, length });
|
||||
}
|
||||
};
|
||||
visit(text, visitor, options);
|
||||
@@ -686,18 +890,18 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp
|
||||
/**
|
||||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
*/
|
||||
export function parseTree(text: string, errors: ParseError[] = [], options?: ParseOptions): Node {
|
||||
let currentParent: Node = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root
|
||||
export function parseTree(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): Node {
|
||||
let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: void 0 }; // artificial root
|
||||
|
||||
function ensurePropertyComplete(endOffset: number) {
|
||||
if (currentParent.type === 'property') {
|
||||
currentParent.length = endOffset - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
currentParent = currentParent.parent!;
|
||||
}
|
||||
}
|
||||
|
||||
function onValue(valueNode: Node): Node {
|
||||
currentParent.children.push(valueNode);
|
||||
currentParent.children!.push(valueNode);
|
||||
return valueNode;
|
||||
}
|
||||
|
||||
@@ -707,11 +911,11 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par
|
||||
},
|
||||
onObjectProperty: (name: string, offset: number, length: number) => {
|
||||
currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });
|
||||
currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent });
|
||||
currentParent.children!.push({ type: 'string', value: name, offset, length, parent: currentParent });
|
||||
},
|
||||
onObjectEnd: (offset: number, length: number) => {
|
||||
currentParent.length = offset + length - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
currentParent = currentParent.parent!;
|
||||
ensurePropertyComplete(offset + length);
|
||||
},
|
||||
onArrayBegin: (offset: number, length: number) => {
|
||||
@@ -719,7 +923,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par
|
||||
},
|
||||
onArrayEnd: (offset: number, length: number) => {
|
||||
currentParent.length = offset + length - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
currentParent = currentParent.parent!;
|
||||
ensurePropertyComplete(offset + length);
|
||||
},
|
||||
onLiteralValue: (value: any, offset: number, length: number) => {
|
||||
@@ -729,38 +933,41 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par
|
||||
onSeparator: (sep: string, offset: number, length: number) => {
|
||||
if (currentParent.type === 'property') {
|
||||
if (sep === ':') {
|
||||
currentParent.columnOffset = offset;
|
||||
currentParent.colonOffset = offset;
|
||||
} else if (sep === ',') {
|
||||
ensurePropertyComplete(offset);
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: (error: ParseErrorCode) => {
|
||||
errors.push({ error: error });
|
||||
onError: (error: ParseErrorCode, offset: number, length: number) => {
|
||||
errors.push({ error, offset, length });
|
||||
}
|
||||
};
|
||||
visit(text, visitor, options);
|
||||
|
||||
let result = currentParent.children[0];
|
||||
let result = currentParent.children![0];
|
||||
if (result) {
|
||||
delete result.parent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findNodeAtLocation(root: Node, path: JSONPath): Node {
|
||||
/**
|
||||
* Finds the node at the given path in a JSON DOM.
|
||||
*/
|
||||
export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined {
|
||||
if (!root) {
|
||||
return void 0;
|
||||
}
|
||||
let node = root;
|
||||
for (let segment of path) {
|
||||
if (typeof segment === 'string') {
|
||||
if (node.type !== 'object') {
|
||||
if (node.type !== 'object' || !Array.isArray(node.children)) {
|
||||
return void 0;
|
||||
}
|
||||
let found = false;
|
||||
for (let propertyNode of node.children) {
|
||||
if (propertyNode.children[0].value === segment) {
|
||||
for (const propertyNode of node.children) {
|
||||
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {
|
||||
node = propertyNode.children[1];
|
||||
found = true;
|
||||
break;
|
||||
@@ -771,7 +978,7 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node {
|
||||
}
|
||||
} else {
|
||||
let index = <number>segment;
|
||||
if (node.type !== 'array' || index < 0 || index >= node.children.length) {
|
||||
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
|
||||
return void 0;
|
||||
}
|
||||
node = node.children[index];
|
||||
@@ -780,31 +987,89 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getNodeValue(node: Node): any {
|
||||
if (node.type === 'array') {
|
||||
return node.children.map(getNodeValue);
|
||||
} else if (node.type === 'object') {
|
||||
let obj = {};
|
||||
for (let prop of node.children) {
|
||||
obj[prop.children[0].value] = getNodeValue(prop.children[1]);
|
||||
}
|
||||
return obj;
|
||||
/**
|
||||
* Gets the JSON path of the given JSON DOM node
|
||||
*/
|
||||
export function getNodePath(node: Node): JSONPath {
|
||||
if (!node.parent || !node.parent.children) {
|
||||
return [];
|
||||
}
|
||||
return node.value;
|
||||
let path = getNodePath(node.parent);
|
||||
if (node.parent.type === 'property') {
|
||||
let key = node.parent.children[0].value;
|
||||
path.push(key);
|
||||
} else if (node.parent.type === 'array') {
|
||||
let index = node.parent.children.indexOf(node);
|
||||
if (index !== -1) {
|
||||
path.push(index);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the JavaScript object of the given JSON DOM node
|
||||
*/
|
||||
export function getNodeValue(node: Node): any {
|
||||
switch (node.type) {
|
||||
case 'array':
|
||||
return node.children!.map(getNodeValue);
|
||||
case 'object':
|
||||
let obj = Object.create(null);
|
||||
for (let prop of node.children!) {
|
||||
let valueNode = prop.children![1];
|
||||
if (valueNode) {
|
||||
obj[prop.children![0].value] = getNodeValue(valueNode);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
case 'null':
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
return node.value;
|
||||
default:
|
||||
return void 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function contains(node: Node, offset: number, includeRightBound = false): boolean {
|
||||
return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
|
||||
*/
|
||||
export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined {
|
||||
if (contains(node, offset, includeRightBound)) {
|
||||
let children = node.children;
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length && children[i].offset <= offset; i++) {
|
||||
let item = findNodeAtOffset(children[i], offset, includeRightBound);
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
|
||||
*/
|
||||
export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any {
|
||||
export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any {
|
||||
|
||||
let _scanner = createScanner(text, false);
|
||||
|
||||
function toNoArgVisit(visitFunction: (offset: number, length: number) => void): () => void {
|
||||
function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void {
|
||||
return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
}
|
||||
function toOneArgVisit<T>(visitFunction: (arg: T, offset: number, length: number) => void): (arg: T) => void {
|
||||
function toOneArgVisit<T>(visitFunction?: (arg: T, offset: number, length: number) => void): (arg: T) => void {
|
||||
return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
}
|
||||
|
||||
@@ -815,18 +1080,43 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
|
||||
onArrayEnd = toNoArgVisit(visitor.onArrayEnd),
|
||||
onLiteralValue = toOneArgVisit(visitor.onLiteralValue),
|
||||
onSeparator = toOneArgVisit(visitor.onSeparator),
|
||||
onComment = toNoArgVisit(visitor.onComment),
|
||||
onError = toOneArgVisit(visitor.onError);
|
||||
|
||||
let disallowComments = options && options.disallowComments;
|
||||
let disallowTrailingComma = options && options.disallowTrailingComma;
|
||||
let allowTrailingComma = options && options.allowTrailingComma;
|
||||
function scanNext(): SyntaxKind {
|
||||
while (true) {
|
||||
let token = _scanner.scan();
|
||||
switch (_scanner.getTokenError()) {
|
||||
case ScanError.InvalidUnicode:
|
||||
handleError(ParseErrorCode.InvalidUnicode);
|
||||
break;
|
||||
case ScanError.InvalidEscapeCharacter:
|
||||
handleError(ParseErrorCode.InvalidEscapeCharacter);
|
||||
break;
|
||||
case ScanError.UnexpectedEndOfNumber:
|
||||
handleError(ParseErrorCode.UnexpectedEndOfNumber);
|
||||
break;
|
||||
case ScanError.UnexpectedEndOfComment:
|
||||
if (!disallowComments) {
|
||||
handleError(ParseErrorCode.UnexpectedEndOfComment);
|
||||
}
|
||||
break;
|
||||
case ScanError.UnexpectedEndOfString:
|
||||
handleError(ParseErrorCode.UnexpectedEndOfString);
|
||||
break;
|
||||
case ScanError.InvalidCharacter:
|
||||
handleError(ParseErrorCode.InvalidCharacter);
|
||||
break;
|
||||
}
|
||||
switch (token) {
|
||||
case SyntaxKind.LineCommentTrivia:
|
||||
case SyntaxKind.BlockCommentTrivia:
|
||||
if (disallowComments) {
|
||||
handleError(ParseErrorCode.InvalidSymbol);
|
||||
handleError(ParseErrorCode.InvalidCommentToken);
|
||||
} else {
|
||||
onComment();
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Unknown:
|
||||
@@ -930,7 +1220,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
|
||||
}
|
||||
onSeparator(',');
|
||||
scanNext(); // consume comma
|
||||
if (_scanner.getToken() === SyntaxKind.CloseBraceToken && !disallowTrailingComma) {
|
||||
if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) {
|
||||
break;
|
||||
}
|
||||
} else if (needsComma) {
|
||||
@@ -962,7 +1252,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
|
||||
}
|
||||
onSeparator(',');
|
||||
scanNext(); // consume comma
|
||||
if (_scanner.getToken() === SyntaxKind.CloseBracketToken && !disallowTrailingComma) {
|
||||
if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) {
|
||||
break;
|
||||
}
|
||||
} else if (needsComma) {
|
||||
@@ -1009,44 +1299,45 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface JSONVisitor {
|
||||
/**
|
||||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
|
||||
*/
|
||||
onObjectBegin?: (offset: number, length: number) => void;
|
||||
/**
|
||||
* Takes JSON with JavaScript-style comments and remove
|
||||
* them. Optionally replaces every none-newline character
|
||||
* of comments with a replaceCharacter
|
||||
*/
|
||||
export function stripComments(text: string, replaceCh?: string): string {
|
||||
|
||||
/**
|
||||
* Invoked when a property is encountered. The offset and length represent the location of the property name.
|
||||
*/
|
||||
onObjectProperty?: (property: string, offset: number, length: number) => void;
|
||||
let _scanner = createScanner(text),
|
||||
parts: string[] = [],
|
||||
kind: SyntaxKind,
|
||||
offset = 0,
|
||||
pos: number;
|
||||
|
||||
/**
|
||||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
|
||||
*/
|
||||
onObjectEnd?: (offset: number, length: number) => void;
|
||||
do {
|
||||
pos = _scanner.getPosition();
|
||||
kind = _scanner.scan();
|
||||
switch (kind) {
|
||||
case SyntaxKind.LineCommentTrivia:
|
||||
case SyntaxKind.BlockCommentTrivia:
|
||||
case SyntaxKind.EOF:
|
||||
if (offset !== pos) {
|
||||
parts.push(text.substring(offset, pos));
|
||||
}
|
||||
if (replaceCh !== void 0) {
|
||||
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
|
||||
}
|
||||
offset = _scanner.getPosition();
|
||||
break;
|
||||
}
|
||||
} while (kind !== SyntaxKind.EOF);
|
||||
|
||||
/**
|
||||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
|
||||
*/
|
||||
onArrayBegin?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
|
||||
*/
|
||||
onArrayEnd?: (offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
|
||||
*/
|
||||
onLiteralValue?: (value: any, offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
|
||||
*/
|
||||
onSeparator?: (charcter: string, offset: number, length: number) => void;
|
||||
|
||||
/**
|
||||
* Invoked on an error.
|
||||
*/
|
||||
onError?: (error: ParseErrorCode, offset: number, length: number) => void;
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function getLiteralNodeType(value: any): NodeType {
|
||||
switch (typeof value) {
|
||||
case 'boolean': return 'boolean';
|
||||
case 'number': return 'number';
|
||||
case 'string': return 'string';
|
||||
default: return 'null';
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,22 @@
|
||||
* 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 { ParseError, Node, parseTree, findNodeAtLocation, JSONPath, Segment } from 'vs/base/common/json';
|
||||
import { Edit, FormattingOptions, format, applyEdit } from 'vs/base/common/jsonFormatter';
|
||||
import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json';
|
||||
import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter';
|
||||
|
||||
|
||||
export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] {
|
||||
return setProperty(text, path, void 0, formattingOptions);
|
||||
}
|
||||
|
||||
export function setProperty(text: string, path: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] {
|
||||
export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] {
|
||||
let path = originalPath.slice();
|
||||
let errors: ParseError[] = [];
|
||||
let root = parseTree(text, errors);
|
||||
let parent: Node = void 0;
|
||||
let parent: Node | undefined = void 0;
|
||||
|
||||
let lastSegment: Segment = void 0;
|
||||
let lastSegment: Segment | undefined = void 0;
|
||||
while (path.length > 0) {
|
||||
lastSegment = path.pop();
|
||||
parent = findNodeAtLocation(root, path);
|
||||
@@ -37,10 +38,13 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting
|
||||
throw new Error('Can not delete in empty document');
|
||||
}
|
||||
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions);
|
||||
} else if (parent.type === 'object' && typeof lastSegment === 'string') {
|
||||
} else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
|
||||
let existing = findNodeAtLocation(parent, [lastSegment]);
|
||||
if (existing !== void 0) {
|
||||
if (value === void 0) { // delete
|
||||
if (!existing.parent) {
|
||||
throw new Error('Malformed AST');
|
||||
}
|
||||
let propertyIndex = parent.children.indexOf(existing.parent);
|
||||
let removeBegin: number;
|
||||
let removeEnd = existing.parent.offset + existing.parent.length;
|
||||
@@ -66,7 +70,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting
|
||||
return []; // property does not exist, nothing to do
|
||||
}
|
||||
let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
|
||||
let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children[0].value)) : parent.children.length;
|
||||
let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length;
|
||||
let edit: Edit;
|
||||
if (index > 0) {
|
||||
let previous = parent.children[index - 1];
|
||||
@@ -78,7 +82,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting
|
||||
}
|
||||
return withFormatting(text, edit, formattingOptions);
|
||||
}
|
||||
} else if (parent.type === 'array' && typeof lastSegment === 'number') {
|
||||
} else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
|
||||
let insertIndex = lastSegment;
|
||||
if (insertIndex === -1) {
|
||||
// Insert
|
||||
@@ -126,6 +130,15 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO
|
||||
// format the new text
|
||||
let begin = edit.offset;
|
||||
let end = edit.offset + edit.content.length;
|
||||
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
|
||||
while (begin > 0 && !isEOL(newText, begin - 1)) {
|
||||
begin--;
|
||||
}
|
||||
while (end < newText.length && !isEOL(newText, end)) {
|
||||
end++;
|
||||
}
|
||||
}
|
||||
|
||||
let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions);
|
||||
|
||||
// apply the formatting edits and track the begin and end offsets of the changes
|
||||
@@ -139,4 +152,12 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO
|
||||
// create a single edit with all changes
|
||||
let editLength = text.length - (newText.length - end) - begin;
|
||||
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
|
||||
}
|
||||
|
||||
export function applyEdit(text: string, edit: Edit): string {
|
||||
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
|
||||
}
|
||||
|
||||
export function isWS(text: string, offset: number) {
|
||||
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Extracted from json.ts to keep json nls free.
|
||||
|
||||
@@ -2,65 +2,83 @@
|
||||
* 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 Json from './json';
|
||||
import { createScanner, SyntaxKind, ScanError } from './json';
|
||||
|
||||
export interface FormattingOptions {
|
||||
/**
|
||||
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent?
|
||||
*/
|
||||
tabSize: number;
|
||||
tabSize?: number;
|
||||
/**
|
||||
* Is indentation based on spaces?
|
||||
*/
|
||||
insertSpaces: boolean;
|
||||
insertSpaces?: boolean;
|
||||
/**
|
||||
* The default end of line line character
|
||||
* The default 'end of line' character. If not set, '\n' is used as default.
|
||||
*/
|
||||
eol: string;
|
||||
eol?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a text modification
|
||||
*/
|
||||
export interface Edit {
|
||||
/**
|
||||
* The start offset of the modification.
|
||||
*/
|
||||
offset: number;
|
||||
/**
|
||||
* The length of the modification. Must not be negative. Empty length represents an *insert*.
|
||||
*/
|
||||
length: number;
|
||||
/**
|
||||
* The new content. Empty content represents a *remove*.
|
||||
*/
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function applyEdit(text: string, edit: Edit): string {
|
||||
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
|
||||
/**
|
||||
* A text range in the document
|
||||
*/
|
||||
export interface Range {
|
||||
/**
|
||||
* The start offset of the range.
|
||||
*/
|
||||
offset: number;
|
||||
/**
|
||||
* The length of the range. Must not be negative.
|
||||
*/
|
||||
length: number;
|
||||
}
|
||||
|
||||
export function applyEdits(text: string, edits: Edit[]): string {
|
||||
for (let i = edits.length - 1; i >= 0; i--) {
|
||||
text = applyEdit(text, edits[i]);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
export function format(documentText: string, range: { offset: number, length: number }, options: FormattingOptions): Edit[] {
|
||||
export function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[] {
|
||||
let initialIndentLevel: number;
|
||||
let value: string;
|
||||
let formatText: string;
|
||||
let formatTextStart: number;
|
||||
let rangeStart: number;
|
||||
let rangeEnd: number;
|
||||
if (range) {
|
||||
rangeStart = range.offset;
|
||||
rangeEnd = rangeStart + range.length;
|
||||
while (rangeStart > 0 && !isEOL(documentText, rangeStart - 1)) {
|
||||
rangeStart--;
|
||||
}
|
||||
let scanner = Json.createScanner(documentText, true);
|
||||
scanner.setPosition(rangeEnd);
|
||||
scanner.scan();
|
||||
rangeEnd = scanner.getPosition();
|
||||
|
||||
value = documentText.substring(rangeStart, rangeEnd);
|
||||
initialIndentLevel = computeIndentLevel(value, 0, options);
|
||||
formatTextStart = rangeStart;
|
||||
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
|
||||
formatTextStart--;
|
||||
}
|
||||
let endOffset = rangeEnd;
|
||||
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
|
||||
endOffset++;
|
||||
}
|
||||
formatText = documentText.substring(formatTextStart, endOffset);
|
||||
initialIndentLevel = computeIndentLevel(formatText, options);
|
||||
} else {
|
||||
value = documentText;
|
||||
formatText = documentText;
|
||||
initialIndentLevel = 0;
|
||||
formatTextStart = 0;
|
||||
rangeStart = 0;
|
||||
rangeEnd = documentText.length;
|
||||
initialIndentLevel = 0;
|
||||
}
|
||||
let eol = getEOL(options, documentText);
|
||||
|
||||
@@ -68,75 +86,78 @@ export function format(documentText: string, range: { offset: number, length: nu
|
||||
let indentLevel = 0;
|
||||
let indentValue: string;
|
||||
if (options.insertSpaces) {
|
||||
indentValue = repeat(' ', options.tabSize);
|
||||
indentValue = repeat(' ', options.tabSize || 4);
|
||||
} else {
|
||||
indentValue = '\t';
|
||||
}
|
||||
|
||||
let scanner = Json.createScanner(value, false);
|
||||
let scanner = createScanner(formatText, false);
|
||||
let hasError = false;
|
||||
|
||||
function newLineAndIndent(): string {
|
||||
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
|
||||
}
|
||||
function scanNext(): Json.SyntaxKind {
|
||||
function scanNext(): SyntaxKind {
|
||||
let token = scanner.scan();
|
||||
lineBreak = false;
|
||||
while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) {
|
||||
lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia);
|
||||
while (token === SyntaxKind.Trivia || token === SyntaxKind.LineBreakTrivia) {
|
||||
lineBreak = lineBreak || (token === SyntaxKind.LineBreakTrivia);
|
||||
token = scanner.scan();
|
||||
}
|
||||
hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None;
|
||||
return token;
|
||||
}
|
||||
let editOperations: Edit[] = [];
|
||||
function addEdit(text: string, startOffset: number, endOffset: number) {
|
||||
if (documentText.substring(startOffset, endOffset) !== text) {
|
||||
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) {
|
||||
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
|
||||
}
|
||||
}
|
||||
|
||||
let firstToken = scanNext();
|
||||
if (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
|
||||
if (firstToken !== SyntaxKind.EOF) {
|
||||
let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
let initialIndent = repeat(indentValue, initialIndentLevel);
|
||||
addEdit(initialIndent, rangeStart, firstTokenStart);
|
||||
addEdit(initialIndent, formatTextStart, firstTokenStart);
|
||||
}
|
||||
|
||||
while (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart;
|
||||
while (firstToken !== SyntaxKind.EOF) {
|
||||
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
|
||||
let secondToken = scanNext();
|
||||
|
||||
let replaceContent = '';
|
||||
while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
|
||||
// comments on the same line: keep them on the same line, but ignore them otherwise
|
||||
let commentTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
addEdit(' ', firstTokenEnd, commentTokenStart);
|
||||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart;
|
||||
replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
|
||||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
|
||||
replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
|
||||
secondToken = scanNext();
|
||||
}
|
||||
|
||||
if (secondToken === Json.SyntaxKind.CloseBraceToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBraceToken) {
|
||||
if (secondToken === SyntaxKind.CloseBraceToken) {
|
||||
if (firstToken !== SyntaxKind.OpenBraceToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else if (secondToken === Json.SyntaxKind.CloseBracketToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBracketToken) {
|
||||
} else if (secondToken === SyntaxKind.CloseBracketToken) {
|
||||
if (firstToken !== SyntaxKind.OpenBracketToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else if (secondToken !== Json.SyntaxKind.EOF) {
|
||||
} else {
|
||||
switch (firstToken) {
|
||||
case Json.SyntaxKind.OpenBracketToken:
|
||||
case Json.SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
indentLevel++;
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.CommaToken:
|
||||
case Json.SyntaxKind.LineCommentTrivia:
|
||||
case SyntaxKind.CommaToken:
|
||||
case SyntaxKind.LineCommentTrivia:
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.BlockCommentTrivia:
|
||||
case SyntaxKind.BlockCommentTrivia:
|
||||
if (lineBreak) {
|
||||
replaceContent = newLineAndIndent();
|
||||
} else {
|
||||
@@ -144,24 +165,37 @@ export function format(documentText: string, range: { offset: number, length: nu
|
||||
replaceContent = ' ';
|
||||
}
|
||||
break;
|
||||
case Json.SyntaxKind.ColonToken:
|
||||
case SyntaxKind.ColonToken:
|
||||
replaceContent = ' ';
|
||||
break;
|
||||
case Json.SyntaxKind.NullKeyword:
|
||||
case Json.SyntaxKind.TrueKeyword:
|
||||
case Json.SyntaxKind.FalseKeyword:
|
||||
case Json.SyntaxKind.NumericLiteral:
|
||||
if (secondToken === Json.SyntaxKind.NullKeyword || secondToken === Json.SyntaxKind.FalseKeyword || secondToken === Json.SyntaxKind.NumericLiteral) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
if (secondToken === SyntaxKind.ColonToken) {
|
||||
replaceContent = '';
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) {
|
||||
replaceContent = ' ';
|
||||
} else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) {
|
||||
hasError = true;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Unknown:
|
||||
hasError = true;
|
||||
break;
|
||||
}
|
||||
if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
if (lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) {
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
|
||||
}
|
||||
let secondTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
let secondTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
|
||||
firstToken = secondToken;
|
||||
}
|
||||
@@ -176,7 +210,7 @@ function repeat(s: string, count: number): string {
|
||||
return result;
|
||||
}
|
||||
|
||||
function computeIndentLevel(content: string, offset: number, options: FormattingOptions): number {
|
||||
function computeIndentLevel(content: string, options: FormattingOptions): number {
|
||||
let i = 0;
|
||||
let nChars = 0;
|
||||
let tabSize = options.tabSize || 4;
|
||||
@@ -209,6 +243,6 @@ function getEOL(options: FormattingOptions, text: string): string {
|
||||
return (options && options.eol) || '\n';
|
||||
}
|
||||
|
||||
function isEOL(text: string, offset: number) {
|
||||
export function isEOL(text: string, offset: number) {
|
||||
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export interface IJSONSchema {
|
||||
id?: string;
|
||||
@@ -46,6 +45,12 @@ export interface IJSONSchema {
|
||||
contains?: IJSONSchema;
|
||||
propertyNames?: IJSONSchema;
|
||||
|
||||
// schema draft 07
|
||||
$comment?: string;
|
||||
if?: IJSONSchema;
|
||||
then?: IJSONSchema;
|
||||
else?: IJSONSchema;
|
||||
|
||||
// VSCode extensions
|
||||
defaultSnippets?: IJSONSchemaSnippet[]; // VSCode extension
|
||||
errorMessage?: string; // VSCode extension
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
|
||||
/**
|
||||
@@ -412,7 +410,7 @@ export function KeyChord(firstPart: number, secondPart: number): number {
|
||||
return (firstPart | chordPart) >>> 0;
|
||||
}
|
||||
|
||||
export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybinding {
|
||||
export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybinding | null {
|
||||
if (keybinding === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -531,10 +529,10 @@ export class ResolvedKeybindingPart {
|
||||
readonly altKey: boolean;
|
||||
readonly metaKey: boolean;
|
||||
|
||||
readonly keyLabel: string;
|
||||
readonly keyAriaLabel: string;
|
||||
readonly keyLabel: string | null;
|
||||
readonly keyAriaLabel: string | null;
|
||||
|
||||
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, kbLabel: string, kbAriaLabel: string) {
|
||||
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, kbLabel: string | null, kbAriaLabel: string | null) {
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.altKey = altKey;
|
||||
@@ -551,20 +549,20 @@ export abstract class ResolvedKeybinding {
|
||||
/**
|
||||
* This prints the binding in a format suitable for displaying in the UI.
|
||||
*/
|
||||
public abstract getLabel(): string;
|
||||
public abstract getLabel(): string | null;
|
||||
/**
|
||||
* This prints the binding in a format suitable for ARIA.
|
||||
*/
|
||||
public abstract getAriaLabel(): string;
|
||||
public abstract getAriaLabel(): string | null;
|
||||
/**
|
||||
* This prints the binding in a format suitable for electron's accelerators.
|
||||
* See https://github.com/electron/electron/blob/master/docs/api/accelerator.md
|
||||
*/
|
||||
public abstract getElectronAccelerator(): string;
|
||||
public abstract getElectronAccelerator(): string | null;
|
||||
/**
|
||||
* This prints the binding in a format suitable for user settings.
|
||||
*/
|
||||
public abstract getUserSettingsLabel(): string;
|
||||
public abstract getUserSettingsLabel(): string | null;
|
||||
/**
|
||||
* Is the user settings label reflecting the label?
|
||||
*/
|
||||
@@ -578,10 +576,10 @@ export abstract class ResolvedKeybinding {
|
||||
/**
|
||||
* Returns the firstPart, chordPart that should be used for dispatching.
|
||||
*/
|
||||
public abstract getDispatchParts(): [string, string];
|
||||
public abstract getDispatchParts(): [string | null, string | null];
|
||||
/**
|
||||
* Returns the firstPart, chordPart of the keybinding.
|
||||
* For simple keybindings, the second element will be null.
|
||||
*/
|
||||
public abstract getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart];
|
||||
public abstract getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart | null];
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
|
||||
@@ -28,14 +26,14 @@ export class ModifierLabelProvider {
|
||||
public readonly modifierLabels: ModifierLabels[];
|
||||
|
||||
constructor(mac: ModifierLabels, windows: ModifierLabels, linux: ModifierLabels = windows) {
|
||||
this.modifierLabels = [null];
|
||||
this.modifierLabels = [null!]; // index 0 will never me accessed.
|
||||
this.modifierLabels[OperatingSystem.Macintosh] = mac;
|
||||
this.modifierLabels[OperatingSystem.Windows] = windows;
|
||||
this.modifierLabels[OperatingSystem.Linux] = linux;
|
||||
}
|
||||
|
||||
public toLabel(firstPartMod: Modifiers, firstPartKey: string, chordPartMod: Modifiers, chordPartKey: string, OS: OperatingSystem): string {
|
||||
if (firstPartKey === null && chordPartKey === null) {
|
||||
public toLabel(firstPartMod: Modifiers | null, firstPartKey: string | null, chordPartMod: Modifiers | null, chordPartKey: string | null, OS: OperatingSystem): string | null {
|
||||
if (firstPartMod === null || firstPartKey === null) {
|
||||
return null;
|
||||
}
|
||||
return _asString(firstPartMod, firstPartKey, chordPartMod, chordPartKey, this.modifierLabels[OS]);
|
||||
@@ -174,10 +172,10 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe
|
||||
return result.join(labels.separator);
|
||||
}
|
||||
|
||||
function _asString(firstPartMod: Modifiers, firstPartKey: string, chordPartMod: Modifiers, chordPartKey: string, labels: ModifierLabels): string {
|
||||
function _asString(firstPartMod: Modifiers, firstPartKey: string, chordPartMod: Modifiers | null, chordPartKey: string | null, labels: ModifierLabels): string {
|
||||
let result = _simpleAsString(firstPartMod, firstPartKey, labels);
|
||||
|
||||
if (chordPartKey !== null) {
|
||||
if (chordPartMod !== null && chordPartKey !== null) {
|
||||
result += ' ';
|
||||
result += _simpleAsString(chordPartMod, chordPartKey, labels);
|
||||
}
|
||||
|
||||
124
src/vs/base/common/keybindingParser.ts
Normal file
124
src/vs/base/common/keybindingParser.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChordKeybinding, KeyCodeUtils, Keybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode';
|
||||
|
||||
export class KeybindingParser {
|
||||
|
||||
private static _readModifiers(input: string) {
|
||||
input = input.toLowerCase().trim();
|
||||
|
||||
let ctrl = false;
|
||||
let shift = false;
|
||||
let alt = false;
|
||||
let meta = false;
|
||||
|
||||
let matchedModifier: boolean;
|
||||
|
||||
do {
|
||||
matchedModifier = false;
|
||||
if (/^ctrl(\+|\-)/.test(input)) {
|
||||
ctrl = true;
|
||||
input = input.substr('ctrl-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^shift(\+|\-)/.test(input)) {
|
||||
shift = true;
|
||||
input = input.substr('shift-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^alt(\+|\-)/.test(input)) {
|
||||
alt = true;
|
||||
input = input.substr('alt-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^meta(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('meta-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^win(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('win-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
if (/^cmd(\+|\-)/.test(input)) {
|
||||
meta = true;
|
||||
input = input.substr('cmd-'.length);
|
||||
matchedModifier = true;
|
||||
}
|
||||
} while (matchedModifier);
|
||||
|
||||
let key: string;
|
||||
|
||||
const firstSpaceIdx = input.indexOf(' ');
|
||||
if (firstSpaceIdx > 0) {
|
||||
key = input.substring(0, firstSpaceIdx);
|
||||
input = input.substring(firstSpaceIdx);
|
||||
} else {
|
||||
key = input;
|
||||
input = '';
|
||||
}
|
||||
|
||||
return {
|
||||
remains: input,
|
||||
ctrl,
|
||||
shift,
|
||||
alt,
|
||||
meta,
|
||||
key
|
||||
};
|
||||
}
|
||||
|
||||
private static parseSimpleKeybinding(input: string): [SimpleKeybinding, string] {
|
||||
const mods = this._readModifiers(input);
|
||||
const keyCode = KeyCodeUtils.fromUserSettings(mods.key);
|
||||
return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
|
||||
}
|
||||
|
||||
public static parseKeybinding(input: string, OS: OperatingSystem): Keybinding | null {
|
||||
if (!input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this.parseSimpleKeybinding(input);
|
||||
let chordPart: SimpleKeybinding | null = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this.parseSimpleKeybinding(remains);
|
||||
}
|
||||
|
||||
if (chordPart) {
|
||||
return new ChordKeybinding(firstPart, chordPart);
|
||||
}
|
||||
return firstPart;
|
||||
}
|
||||
|
||||
private static parseSimpleUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, string] {
|
||||
const mods = this._readModifiers(input);
|
||||
const scanCodeMatch = mods.key.match(/^\[([^\]]+)\]$/);
|
||||
if (scanCodeMatch) {
|
||||
const strScanCode = scanCodeMatch[1];
|
||||
const scanCode = ScanCodeUtils.lowerCaseToEnum(strScanCode);
|
||||
return [new ScanCodeBinding(mods.ctrl, mods.shift, mods.alt, mods.meta, scanCode), mods.remains];
|
||||
}
|
||||
const keyCode = KeyCodeUtils.fromUserSettings(mods.key);
|
||||
return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
|
||||
}
|
||||
|
||||
static parseUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding | null, SimpleKeybinding | ScanCodeBinding | null] {
|
||||
if (!input) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this.parseSimpleUserBinding(input);
|
||||
let chordPart: SimpleKeybinding | ScanCodeBinding | null = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this.parseSimpleUserBinding(remains);
|
||||
}
|
||||
return [firstPart, chordPart];
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
* 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 URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { nativeSep, normalize, basename as pathsBasename, sep } from 'vs/base/common/paths';
|
||||
import { endsWith, ltrim, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -12,7 +11,7 @@ import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
export interface IWorkspaceFolderProvider {
|
||||
getWorkspaceFolder(resource: URI): { uri: URI, name?: string };
|
||||
getWorkspaceFolder(resource: URI): { uri: URI, name?: string } | null;
|
||||
getWorkspace(): {
|
||||
folders: { uri: URI, name?: string }[];
|
||||
};
|
||||
@@ -23,35 +22,33 @@ export interface IUserHomeProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use UriLabelService instead
|
||||
* @deprecated use LabelService instead
|
||||
*/
|
||||
export function getPathLabel(resource: URI | string, userHomeProvider: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string {
|
||||
if (!resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string {
|
||||
if (typeof resource === 'string') {
|
||||
resource = URI.file(resource);
|
||||
}
|
||||
|
||||
// return early if we can resolve a relative path label from the root
|
||||
const baseResource = rootProvider ? rootProvider.getWorkspaceFolder(resource) : null;
|
||||
if (baseResource) {
|
||||
const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1;
|
||||
if (rootProvider) {
|
||||
const baseResource = rootProvider.getWorkspaceFolder(resource);
|
||||
if (baseResource) {
|
||||
const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1;
|
||||
|
||||
let pathLabel: string;
|
||||
if (isEqual(baseResource.uri, resource, !isLinux)) {
|
||||
pathLabel = ''; // no label if paths are identical
|
||||
} else {
|
||||
pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), sep), true);
|
||||
let pathLabel: string;
|
||||
if (isEqual(baseResource.uri, resource, !isLinux)) {
|
||||
pathLabel = ''; // no label if paths are identical
|
||||
} else {
|
||||
pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), sep)!, true);
|
||||
}
|
||||
|
||||
if (hasMultipleRoots) {
|
||||
const rootName = (baseResource && baseResource.name) ? baseResource.name : pathsBasename(baseResource.uri.fsPath);
|
||||
pathLabel = pathLabel ? (rootName + ' • ' + pathLabel) : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
|
||||
return pathLabel;
|
||||
}
|
||||
|
||||
if (hasMultipleRoots) {
|
||||
const rootName = (baseResource && baseResource.name) ? baseResource.name : pathsBasename(baseResource.uri.fsPath);
|
||||
pathLabel = pathLabel ? (rootName + ' • ' + pathLabel) : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
|
||||
return pathLabel;
|
||||
}
|
||||
|
||||
// return if the resource is neither file:// nor untitled:// and no baseResource was provided
|
||||
@@ -73,9 +70,9 @@ export function getPathLabel(resource: URI | string, userHomeProvider: IUserHome
|
||||
return res;
|
||||
}
|
||||
|
||||
export function getBaseLabel(resource: URI | string): string {
|
||||
export function getBaseLabel(resource: URI | string): string | undefined {
|
||||
if (!resource) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof resource === 'string') {
|
||||
@@ -93,7 +90,7 @@ export function getBaseLabel(resource: URI | string): string {
|
||||
}
|
||||
|
||||
function hasDriveLetter(path: string): boolean {
|
||||
return isWindows && path && path[1] === ':';
|
||||
return !!(isWindows && path && path[1] === ':');
|
||||
}
|
||||
|
||||
export function normalizeDriveLetter(path: string): string {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
export interface IDisposable {
|
||||
@@ -17,9 +15,9 @@ export function isDisposable<E extends object>(thing: E): thing is E & IDisposab
|
||||
}
|
||||
|
||||
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 | undefined)[]): T[];
|
||||
export function dispose<T extends IDisposable>(disposables: T[]): T[];
|
||||
export function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] {
|
||||
export function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] | undefined {
|
||||
if (Array.isArray(first)) {
|
||||
first.forEach(d => d && d.dispose());
|
||||
return [];
|
||||
@@ -82,7 +80,7 @@ export abstract class ReferenceCollection<T> {
|
||||
const { object } = reference;
|
||||
const dispose = once(() => {
|
||||
if (--reference.counter === 0) {
|
||||
this.destroyReferencedObject(reference.object);
|
||||
this.destroyReferencedObject(key, reference.object);
|
||||
delete this.references[key];
|
||||
}
|
||||
});
|
||||
@@ -93,7 +91,7 @@ export abstract class ReferenceCollection<T> {
|
||||
}
|
||||
|
||||
protected abstract createReferencedObject(key: string): T;
|
||||
protected abstract destroyReferencedObject(object: T): void;
|
||||
protected abstract destroyReferencedObject(key: string, object: T): void;
|
||||
}
|
||||
|
||||
export class ImmortalReference<T> implements IReference<T> {
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Iterator } from 'vs/base/common/iterator';
|
||||
import { Iterator, IteratorResult, FIN } from 'vs/base/common/iterator';
|
||||
|
||||
class Node<E> {
|
||||
element: E;
|
||||
next: Node<E>;
|
||||
prev: Node<E>;
|
||||
next: Node<E> | undefined;
|
||||
prev: Node<E> | undefined;
|
||||
|
||||
constructor(element: E) {
|
||||
this.element = element;
|
||||
@@ -19,8 +17,13 @@ class Node<E> {
|
||||
|
||||
export class LinkedList<E> {
|
||||
|
||||
private _first: Node<E>;
|
||||
private _last: Node<E>;
|
||||
private _first: Node<E> | undefined;
|
||||
private _last: Node<E> | undefined;
|
||||
private _size: number = 0;
|
||||
|
||||
get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this._first;
|
||||
@@ -47,7 +50,7 @@ export class LinkedList<E> {
|
||||
|
||||
} else if (atTheEnd) {
|
||||
// push
|
||||
const oldLast = this._last;
|
||||
const oldLast = this._last!;
|
||||
this._last = newNode;
|
||||
newNode.prev = oldLast;
|
||||
oldLast.next = newNode;
|
||||
@@ -59,11 +62,13 @@ export class LinkedList<E> {
|
||||
newNode.next = oldFirst;
|
||||
oldFirst.prev = newNode;
|
||||
}
|
||||
this._size += 1;
|
||||
|
||||
return () => {
|
||||
|
||||
for (let candidate = this._first; candidate instanceof Node; candidate = candidate.next) {
|
||||
let candidate: Node<E> | undefined = this._first;
|
||||
while (candidate instanceof Node) {
|
||||
if (candidate !== newNode) {
|
||||
candidate = candidate.next;
|
||||
continue;
|
||||
}
|
||||
if (candidate.prev && candidate.next) {
|
||||
@@ -79,37 +84,37 @@ export class LinkedList<E> {
|
||||
|
||||
} else if (!candidate.next) {
|
||||
// last
|
||||
this._last = this._last.prev;
|
||||
this._last = this._last!.prev!;
|
||||
this._last.next = undefined;
|
||||
|
||||
} else if (!candidate.prev) {
|
||||
// first
|
||||
this._first = this._first.next;
|
||||
this._first = this._first!.next!;
|
||||
this._first.prev = undefined;
|
||||
}
|
||||
|
||||
// done
|
||||
this._size -= 1;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
iterator(): Iterator<E> {
|
||||
let element = {
|
||||
done: undefined,
|
||||
value: undefined,
|
||||
};
|
||||
let element: { done: false; value: E; };
|
||||
let node = this._first;
|
||||
return {
|
||||
next(): { done: boolean; value: E } {
|
||||
next(): IteratorResult<E> {
|
||||
if (!node) {
|
||||
element.done = true;
|
||||
element.value = undefined;
|
||||
} else {
|
||||
element.done = false;
|
||||
element.value = node.element;
|
||||
node = node.next;
|
||||
return FIN;
|
||||
}
|
||||
|
||||
if (!element) {
|
||||
element = { done: false, value: node.element };
|
||||
} else {
|
||||
element.value = node.element;
|
||||
}
|
||||
node = node.next;
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { Iterator } from './iterator';
|
||||
import { Iterator, IteratorResult, FIN } from './iterator';
|
||||
|
||||
export function values<V = any>(set: Set<V>): V[];
|
||||
export function values<K = any, V = any>(map: Map<K, V>): V[];
|
||||
@@ -34,6 +32,44 @@ export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapToString<K, V>(map: Map<K, V>): string {
|
||||
const entries: string[] = [];
|
||||
map.forEach((value, key) => {
|
||||
entries.push(`${key} => ${value}`);
|
||||
});
|
||||
|
||||
return `Map(${map.size}) {${entries.join(', ')}}`;
|
||||
}
|
||||
|
||||
export function setToString<K>(set: Set<K>): string {
|
||||
const entries: K[] = [];
|
||||
set.forEach(value => {
|
||||
entries.push(value);
|
||||
});
|
||||
|
||||
return `Set(${set.size}) {${entries.join(', ')}}`;
|
||||
}
|
||||
|
||||
export function mapToSerializable(map: Map<string, string>): [string, string][] {
|
||||
const serializable: [string, string][] = [];
|
||||
|
||||
map.forEach((value, key) => {
|
||||
serializable.push([key, value]);
|
||||
});
|
||||
|
||||
return serializable;
|
||||
}
|
||||
|
||||
export function serializableToMap(serializable: [string, string][]): Map<string, string> {
|
||||
const items = new Map<string, string>();
|
||||
|
||||
for (const [key, value] of serializable) {
|
||||
items.set(key, value);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export interface IKeyIterator {
|
||||
reset(key: string): this;
|
||||
next(): this;
|
||||
@@ -141,11 +177,11 @@ export class PathIterator implements IKeyIterator {
|
||||
|
||||
class TernarySearchTreeNode<E> {
|
||||
segment: string;
|
||||
value: E;
|
||||
value: E | undefined;
|
||||
key: string;
|
||||
left: TernarySearchTreeNode<E>;
|
||||
mid: TernarySearchTreeNode<E>;
|
||||
right: TernarySearchTreeNode<E>;
|
||||
left: TernarySearchTreeNode<E> | undefined;
|
||||
mid: TernarySearchTreeNode<E> | undefined;
|
||||
right: TernarySearchTreeNode<E> | undefined;
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this.left && !this.mid && !this.right && !this.value;
|
||||
@@ -163,7 +199,7 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
private _iter: IKeyIterator;
|
||||
private _root: TernarySearchTreeNode<E>;
|
||||
private _root: TernarySearchTreeNode<E> | undefined;
|
||||
|
||||
constructor(segments: IKeyIterator) {
|
||||
this._iter = segments;
|
||||
@@ -173,7 +209,7 @@ export class TernarySearchTree<E> {
|
||||
this._root = undefined;
|
||||
}
|
||||
|
||||
set(key: string, element: E): E {
|
||||
set(key: string, element: E): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
let node: TernarySearchTreeNode<E>;
|
||||
|
||||
@@ -219,7 +255,7 @@ export class TernarySearchTree<E> {
|
||||
return oldElement;
|
||||
}
|
||||
|
||||
get(key: string): E {
|
||||
get(key: string): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
@@ -269,7 +305,7 @@ export class TernarySearchTree<E> {
|
||||
|
||||
// clean up empty nodes
|
||||
while (stack.length > 0 && node.isEmpty()) {
|
||||
let [dir, parent] = stack.pop();
|
||||
let [dir, parent] = stack.pop()!;
|
||||
switch (dir) {
|
||||
case 1: parent.left = undefined; break;
|
||||
case 0: parent.mid = undefined; break;
|
||||
@@ -282,10 +318,10 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
}
|
||||
|
||||
findSubstr(key: string): E {
|
||||
findSubstr(key: string): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
let candidate: E;
|
||||
let candidate: E | undefined = undefined;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
@@ -306,7 +342,7 @@ export class TernarySearchTree<E> {
|
||||
return node && node.value || candidate;
|
||||
}
|
||||
|
||||
findSuperstr(key: string): Iterator<E> {
|
||||
findSuperstr(key: string): Iterator<E> | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
@@ -334,13 +370,10 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
private _nodeIterator(node: TernarySearchTreeNode<E>): Iterator<E> {
|
||||
let res = {
|
||||
done: false,
|
||||
value: undefined
|
||||
};
|
||||
let res: { done: false; value: E; };
|
||||
let idx: number;
|
||||
let data: E[];
|
||||
let next = () => {
|
||||
let next = (): IteratorResult<E> => {
|
||||
if (!data) {
|
||||
// lazy till first invocation
|
||||
data = [];
|
||||
@@ -348,10 +381,12 @@ export class TernarySearchTree<E> {
|
||||
this._forEach(node, value => data.push(value));
|
||||
}
|
||||
if (idx >= data.length) {
|
||||
res.done = true;
|
||||
res.value = undefined;
|
||||
return FIN;
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
res = { done: false, value: data[idx++] };
|
||||
} else {
|
||||
res.done = false;
|
||||
res.value = data[idx++];
|
||||
}
|
||||
return res;
|
||||
@@ -363,7 +398,7 @@ export class TernarySearchTree<E> {
|
||||
this._forEach(this._root, callback);
|
||||
}
|
||||
|
||||
private _forEach(node: TernarySearchTreeNode<E>, callback: (value: E, index: string) => any) {
|
||||
private _forEach(node: TernarySearchTreeNode<E> | undefined, callback: (value: E, index: string) => any) {
|
||||
if (node) {
|
||||
// left
|
||||
this._forEach(node.left, callback);
|
||||
@@ -392,35 +427,35 @@ export class ResourceMap<T> {
|
||||
this.ignoreCase = false; // in the future this should be an uri-comparator
|
||||
}
|
||||
|
||||
public set(resource: URI, value: T): void {
|
||||
set(resource: URI, value: T): void {
|
||||
this.map.set(this.toKey(resource), value);
|
||||
}
|
||||
|
||||
public get(resource: URI): T {
|
||||
get(resource: URI): T {
|
||||
return this.map.get(this.toKey(resource));
|
||||
}
|
||||
|
||||
public has(resource: URI): boolean {
|
||||
has(resource: URI): boolean {
|
||||
return this.map.has(this.toKey(resource));
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
get size(): number {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
clear(): void {
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
public delete(resource: URI): boolean {
|
||||
delete(resource: URI): boolean {
|
||||
return this.map.delete(this.toKey(resource));
|
||||
}
|
||||
|
||||
public forEach(clb: (value: T) => void): void {
|
||||
forEach(clb: (value: T) => void): void {
|
||||
this.map.forEach(clb);
|
||||
}
|
||||
|
||||
public values(): T[] {
|
||||
values(): T[] {
|
||||
return values(this.map);
|
||||
}
|
||||
|
||||
@@ -433,11 +468,11 @@ export class ResourceMap<T> {
|
||||
return key;
|
||||
}
|
||||
|
||||
public keys(): URI[] {
|
||||
return keys(this.map).map(URI.parse);
|
||||
keys(): URI[] {
|
||||
return keys(this.map).map(k => URI.parse(k));
|
||||
}
|
||||
|
||||
public clone(): ResourceMap<T> {
|
||||
clone(): ResourceMap<T> {
|
||||
const resourceMap = new ResourceMap<T>();
|
||||
|
||||
this.map.forEach((value, key) => resourceMap.map.set(key, value));
|
||||
@@ -455,7 +490,7 @@ interface Item<K, V> {
|
||||
value: V;
|
||||
}
|
||||
|
||||
export enum Touch {
|
||||
export const enum Touch {
|
||||
None = 0,
|
||||
AsOld = 1,
|
||||
AsNew = 2
|
||||
@@ -475,26 +510,26 @@ export class LinkedMap<K, V> {
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
clear(): void {
|
||||
this._map.clear();
|
||||
this._head = undefined;
|
||||
this._tail = undefined;
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
isEmpty(): boolean {
|
||||
return !this._head && !this._tail;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public has(key: K): boolean {
|
||||
has(key: K): boolean {
|
||||
return this._map.has(key);
|
||||
}
|
||||
|
||||
public get(key: K, touch: Touch = Touch.None): V | undefined {
|
||||
get(key: K, touch: Touch = Touch.None): V | undefined {
|
||||
const item = this._map.get(key);
|
||||
if (!item) {
|
||||
return undefined;
|
||||
@@ -505,7 +540,7 @@ export class LinkedMap<K, V> {
|
||||
return item.value;
|
||||
}
|
||||
|
||||
public set(key: K, value: V, touch: Touch = Touch.None): void {
|
||||
set(key: K, value: V, touch: Touch = Touch.None): void {
|
||||
let item = this._map.get(key);
|
||||
if (item) {
|
||||
item.value = value;
|
||||
@@ -533,11 +568,11 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
public delete(key: K): boolean {
|
||||
delete(key: K): boolean {
|
||||
return !!this.remove(key);
|
||||
}
|
||||
|
||||
public remove(key: K): V | undefined {
|
||||
remove(key: K): V | undefined {
|
||||
const item = this._map.get(key);
|
||||
if (!item) {
|
||||
return undefined;
|
||||
@@ -548,7 +583,7 @@ export class LinkedMap<K, V> {
|
||||
return item.value;
|
||||
}
|
||||
|
||||
public shift(): V | undefined {
|
||||
shift(): V | undefined {
|
||||
if (!this._head && !this._tail) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -562,7 +597,7 @@ export class LinkedMap<K, V> {
|
||||
return item.value;
|
||||
}
|
||||
|
||||
public forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: any): void {
|
||||
forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: any): void {
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
if (thisArg) {
|
||||
@@ -574,7 +609,7 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
public values(): V[] {
|
||||
values(): V[] {
|
||||
let result: V[] = [];
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
@@ -584,7 +619,7 @@ export class LinkedMap<K, V> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public keys(): K[] {
|
||||
keys(): K[] {
|
||||
let result: K[] = [];
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
@@ -595,7 +630,7 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
|
||||
/* VS Code / Monaco editor runs on es5 which has no Symbol.iterator
|
||||
public keys(): IterableIterator<K> {
|
||||
keys(): IterableIterator<K> {
|
||||
let current = this._head;
|
||||
let iterator: IterableIterator<K> = {
|
||||
[Symbol.iterator]() {
|
||||
@@ -614,7 +649,7 @@ export class LinkedMap<K, V> {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
public values(): IterableIterator<V> {
|
||||
values(): IterableIterator<V> {
|
||||
let current = this._head;
|
||||
let iterator: IterableIterator<V> = {
|
||||
[Symbol.iterator]() {
|
||||
@@ -651,7 +686,9 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
this._head = current;
|
||||
this._size = currentSize;
|
||||
current.previous = void 0;
|
||||
if (current) {
|
||||
current.previous = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
private addItemFirst(item: Item<K, V>): void {
|
||||
@@ -762,7 +799,7 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
public toJSON(): [K, V][] {
|
||||
toJSON(): [K, V][] {
|
||||
const data: [K, V][] = [];
|
||||
|
||||
this.forEach((value, key) => {
|
||||
@@ -772,7 +809,7 @@ export class LinkedMap<K, V> {
|
||||
return data;
|
||||
}
|
||||
|
||||
public fromJSON(data: [K, V][]): void {
|
||||
fromJSON(data: [K, V][]): void {
|
||||
this.clear();
|
||||
|
||||
for (const [key, value] of data) {
|
||||
@@ -792,33 +829,33 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
|
||||
this._ratio = Math.min(Math.max(0, ratio), 1);
|
||||
}
|
||||
|
||||
public get limit(): number {
|
||||
get limit(): number {
|
||||
return this._limit;
|
||||
}
|
||||
|
||||
public set limit(limit: number) {
|
||||
set limit(limit: number) {
|
||||
this._limit = limit;
|
||||
this.checkTrim();
|
||||
}
|
||||
|
||||
public get ratio(): number {
|
||||
get ratio(): number {
|
||||
return this._ratio;
|
||||
}
|
||||
|
||||
public set ratio(ratio: number) {
|
||||
set ratio(ratio: number) {
|
||||
this._ratio = Math.min(Math.max(0, ratio), 1);
|
||||
this.checkTrim();
|
||||
}
|
||||
|
||||
public get(key: K): V | undefined {
|
||||
get(key: K): V | undefined {
|
||||
return super.get(key, Touch.AsNew);
|
||||
}
|
||||
|
||||
public peek(key: K): V | undefined {
|
||||
peek(key: K): V | undefined {
|
||||
return super.get(key, Touch.None);
|
||||
}
|
||||
|
||||
public set(key: K, value: V): void {
|
||||
set(key: K, value: V): void {
|
||||
super.set(key, value, Touch.AsNew);
|
||||
this.checkTrim();
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
|
||||
|
||||
[{
|
||||
"name": "chjj-marked",
|
||||
"repositoryURL": "https://github.com/npmcomponent/chjj-marked",
|
||||
"version": "0.3.18",
|
||||
"license": "MIT"
|
||||
}]
|
||||
17
src/vs/base/common/marked/cgmanifest.json
Normal file
17
src/vs/base/common/marked/cgmanifest.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"registrations": [
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"name": "marked",
|
||||
"repositoryUrl": "https://github.com/markedjs/marked",
|
||||
"commitHash": "78c977bc3a47f9e2fb146477d1ca3dad0cb134e6"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"version": "0.5.0"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
391
src/vs/base/common/marked/marked.d.ts
vendored
391
src/vs/base/common/marked/marked.d.ts
vendored
@@ -3,12 +3,42 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Type definitions for Marked
|
||||
// Project:https://github.com/chjj/marked
|
||||
// Definitions by:William Orr <https://github.com/worr>
|
||||
// Definitions:https://github.com/borisyankov/DefinitelyTyped
|
||||
// Type definitions for Marked 0.4
|
||||
// Project: https://github.com/markedjs/marked
|
||||
// Definitions by: William Orr <https://github.com/worr>
|
||||
// BendingBender <https://github.com/BendingBender>
|
||||
// CrossR <https://github.com/CrossR>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
export as namespace marked;
|
||||
|
||||
export = marked;
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
declare function marked(src: string, callback: (error: any | undefined, parseResult: string) => void): string;
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
declare function marked(src: string, options?: marked.MarkedOptions, callback?: (error: any | undefined, parseResult: string) => void): string;
|
||||
|
||||
declare namespace marked {
|
||||
/**
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
*/
|
||||
function lexer(src: string, options?: MarkedOptions): TokensList;
|
||||
|
||||
export interface MarkedStatic {
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
*
|
||||
@@ -16,7 +46,7 @@ export interface MarkedStatic {
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
(src: string, callback: Function): string;
|
||||
function parse(src: string, callback: (error: any | undefined, parseResult: string) => void): string;
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
@@ -26,137 +56,242 @@ export interface MarkedStatic {
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
(src: string, options?: MarkedOptions, callback?: Function): string;
|
||||
function parse(src: string, options?: MarkedOptions, callback?: (error: any | undefined, parseResult: string) => void): string;
|
||||
|
||||
/**
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param src Tokenized source as array of tokens
|
||||
* @param options Hash of options
|
||||
*/
|
||||
lexer(src: string, options?: MarkedOptions): any[];
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
parse(src: string, callback: Function): string;
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
parse(src: string, options?: MarkedOptions, callback?: Function): string;
|
||||
|
||||
/**
|
||||
* @param options Hash of options
|
||||
*/
|
||||
parser(src: any[], options?: MarkedOptions): string;
|
||||
function parser(src: TokensList, options?: MarkedOptions): string;
|
||||
|
||||
/**
|
||||
* Sets the default options.
|
||||
*
|
||||
* @param options Hash of options
|
||||
*/
|
||||
setOptions(options: MarkedOptions): void;
|
||||
function setOptions(options: MarkedOptions): typeof marked;
|
||||
|
||||
/**
|
||||
* Custom renderer for marked.
|
||||
*/
|
||||
Renderer: Renderer;
|
||||
class Renderer {
|
||||
constructor(options?: MarkedOptions);
|
||||
code(code: string, language: string, isEscaped: boolean): string;
|
||||
blockquote(quote: string): string;
|
||||
html(html: string): string;
|
||||
heading(text: string, level: number, raw: string): string;
|
||||
hr(): string;
|
||||
list(body: string, ordered: boolean): string;
|
||||
listitem(text: string): string;
|
||||
paragraph(text: string): string;
|
||||
table(header: string, body: string): string;
|
||||
tablerow(content: string): string;
|
||||
tablecell(content: string, flags: {
|
||||
header: boolean;
|
||||
align: 'center' | 'left' | 'right' | null;
|
||||
}): string;
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(code: string): string;
|
||||
br(): string;
|
||||
del(text: string): string;
|
||||
link(href: string, title: string, text: string): string;
|
||||
image(href: string, title: string, text: string): string;
|
||||
text(text: string): string;
|
||||
}
|
||||
|
||||
class Lexer {
|
||||
rules: Rules;
|
||||
tokens: TokensList;
|
||||
constructor(options?: MarkedOptions);
|
||||
lex(src: string): TokensList;
|
||||
}
|
||||
|
||||
interface Rules {
|
||||
[ruleName: string]: RegExp | Rules;
|
||||
}
|
||||
|
||||
type TokensList = Token[] & {
|
||||
links: {
|
||||
[key: string]: { href: string; title: string; }
|
||||
}
|
||||
};
|
||||
|
||||
type Token =
|
||||
Tokens.Space
|
||||
| Tokens.Code
|
||||
| Tokens.Heading
|
||||
| Tokens.Table
|
||||
| Tokens.Hr
|
||||
| Tokens.BlockquoteStart
|
||||
| Tokens.BlockquoteEnd
|
||||
| Tokens.ListStart
|
||||
| Tokens.LooseItemStart
|
||||
| Tokens.ListItemStart
|
||||
| Tokens.ListItemEnd
|
||||
| Tokens.ListEnd
|
||||
| Tokens.Paragraph
|
||||
| Tokens.HTML
|
||||
| Tokens.Text;
|
||||
|
||||
namespace Tokens {
|
||||
interface Space {
|
||||
type: 'space';
|
||||
}
|
||||
|
||||
interface Code {
|
||||
type: 'code';
|
||||
lang?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface Heading {
|
||||
type: 'heading';
|
||||
depth: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface Table {
|
||||
type: 'table';
|
||||
header: string[];
|
||||
align: Array<'center' | 'left' | 'right' | null>;
|
||||
cells: string[][];
|
||||
}
|
||||
|
||||
interface Hr {
|
||||
type: 'hr';
|
||||
}
|
||||
|
||||
interface BlockquoteStart {
|
||||
type: 'blockquote_start';
|
||||
}
|
||||
|
||||
interface BlockquoteEnd {
|
||||
type: 'blockquote_end';
|
||||
}
|
||||
|
||||
interface ListStart {
|
||||
type: 'list_start';
|
||||
ordered: boolean;
|
||||
}
|
||||
|
||||
interface LooseItemStart {
|
||||
type: 'loose_item_start';
|
||||
}
|
||||
|
||||
interface ListItemStart {
|
||||
type: 'list_item_start';
|
||||
}
|
||||
|
||||
interface ListItemEnd {
|
||||
type: 'list_item_end';
|
||||
}
|
||||
|
||||
interface ListEnd {
|
||||
type: 'list_end';
|
||||
}
|
||||
|
||||
interface Paragraph {
|
||||
type: 'paragraph';
|
||||
pre?: boolean;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface HTML {
|
||||
type: 'html';
|
||||
pre: boolean;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface Text {
|
||||
type: 'text';
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
interface MarkedOptions {
|
||||
/**
|
||||
* A prefix URL for any relative link.
|
||||
*/
|
||||
baseUrl?: string;
|
||||
|
||||
/**
|
||||
* Enable GFM line breaks. This option requires the gfm option to be true.
|
||||
*/
|
||||
breaks?: boolean;
|
||||
|
||||
/**
|
||||
* Enable GitHub flavored markdown.
|
||||
*/
|
||||
gfm?: boolean;
|
||||
|
||||
/**
|
||||
* Include an id attribute when emitting headings.
|
||||
*/
|
||||
headerIds?: boolean;
|
||||
|
||||
/**
|
||||
* Set the prefix for header tag ids.
|
||||
*/
|
||||
headerPrefix?: string;
|
||||
|
||||
/**
|
||||
* A function to highlight code blocks. The function takes three arguments: code, lang, and callback.
|
||||
*/
|
||||
highlight?(code: string, lang: string, callback?: (error: any | undefined, code: string) => void): string;
|
||||
|
||||
/**
|
||||
* Set the prefix for code block classes.
|
||||
*/
|
||||
langPrefix?: string;
|
||||
|
||||
/**
|
||||
* Mangle autolinks (<email@domain.com>).
|
||||
*/
|
||||
mangle?: boolean;
|
||||
|
||||
/**
|
||||
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
|
||||
*/
|
||||
pedantic?: boolean;
|
||||
|
||||
/**
|
||||
* Type: object Default: new Renderer()
|
||||
*
|
||||
* An object containing functions to render tokens to HTML.
|
||||
*/
|
||||
renderer?: Renderer;
|
||||
|
||||
/**
|
||||
* Sanitize the output. Ignore any HTML that has been input.
|
||||
*/
|
||||
sanitize?: boolean;
|
||||
|
||||
/**
|
||||
* Optionally sanitize found HTML with a sanitizer function.
|
||||
*/
|
||||
sanitizer?(html: string): string;
|
||||
|
||||
/**
|
||||
* Shows an HTML error message when rendering fails.
|
||||
*/
|
||||
silent?: boolean;
|
||||
|
||||
/**
|
||||
* Use smarter list behavior than the original markdown. May eventually be default with the old behavior moved into pedantic.
|
||||
*/
|
||||
smartLists?: boolean;
|
||||
|
||||
/**
|
||||
* Use "smart" typograhic punctuation for things like quotes and dashes.
|
||||
*/
|
||||
smartypants?: boolean;
|
||||
|
||||
/**
|
||||
* Enable GFM tables. This option requires the gfm option to be true.
|
||||
*/
|
||||
tables?: boolean;
|
||||
|
||||
/**
|
||||
* Generate closing slash for self-closing tags (<br/> instead of <br>)
|
||||
*/
|
||||
xhtml?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Renderer {
|
||||
prototype: MarkedRenderer;
|
||||
new(): MarkedRenderer;
|
||||
}
|
||||
|
||||
export interface MarkedRenderer {
|
||||
image(href: string, title: string, text: string): string;
|
||||
code(code: string, language: string): string;
|
||||
blockquote(quote: string): string;
|
||||
html(html: string): string;
|
||||
heading(text: string, level: number): string;
|
||||
hr(): string;
|
||||
list(body: string, ordered: boolean): string;
|
||||
listitem(text: string): string;
|
||||
paragraph(text: string): string;
|
||||
table(header: string, body: string): string;
|
||||
tablerow(content: string): string;
|
||||
tablecell(content: string, flags: ITableFlags): string;
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(code: string): string;
|
||||
br(): string;
|
||||
del(text: string): string;
|
||||
link(href: string, title: string, text: string): string;
|
||||
}
|
||||
|
||||
export interface ITableFlags {
|
||||
header: boolean;
|
||||
align: string; // 'center' || 'left' || 'right'
|
||||
}
|
||||
|
||||
export interface MarkedOptions {
|
||||
/**
|
||||
* Enable GitHub flavored markdown.
|
||||
*/
|
||||
gfm?: boolean;
|
||||
|
||||
/**
|
||||
* Enable GFM tables. This option requires the gfm option to be true.
|
||||
*/
|
||||
tables?: boolean;
|
||||
|
||||
/**
|
||||
* Enable GFM line breaks. This option requires the gfm option to be true.
|
||||
*/
|
||||
breaks?: boolean;
|
||||
|
||||
/**
|
||||
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
|
||||
*/
|
||||
pedantic?: boolean;
|
||||
|
||||
/**
|
||||
* Sanitize the output. Ignore any HTML that has been input.
|
||||
*/
|
||||
sanitize?: boolean;
|
||||
|
||||
/**
|
||||
* Use smarter list behavior than the original markdown. May eventually be default with the old behavior moved into pedantic.
|
||||
*/
|
||||
smartLists?: boolean;
|
||||
|
||||
/**
|
||||
* Shows an HTML error message when rendering fails.
|
||||
*/
|
||||
silent?: boolean;
|
||||
|
||||
/**
|
||||
* A function to highlight code blocks. The function takes three arguments:code, lang, and callback.
|
||||
*/
|
||||
highlight?(code: string, lang: string, callback?: Function): void;
|
||||
|
||||
/**
|
||||
* Set the prefix for code block classes.
|
||||
*/
|
||||
langPrefix?: string;
|
||||
|
||||
/**
|
||||
* Use "smart" typograhic punctuation for things like quotes and dashes.
|
||||
*/
|
||||
smartypants?: boolean;
|
||||
|
||||
/**
|
||||
* The renderer to use with marked rendering.
|
||||
*/
|
||||
renderer?: any;
|
||||
}
|
||||
|
||||
export declare var marked: MarkedStatic;
|
||||
@@ -4,9 +4,11 @@
|
||||
* https://github.com/markedjs/marked
|
||||
*/
|
||||
|
||||
// BEGIN MONACOCHANGE
|
||||
var __marked_exports;
|
||||
// END MONACOCHANGE
|
||||
|
||||
; (function (root) {
|
||||
;(function(root) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
@@ -137,7 +139,7 @@ block.pedantic = merge({}, block.normal, {
|
||||
|
||||
function Lexer(options) {
|
||||
this.tokens = [];
|
||||
this.tokens.links = {};
|
||||
this.tokens.links = Object.create(null);
|
||||
this.options = options || marked.defaults;
|
||||
this.rules = block.normal;
|
||||
|
||||
@@ -193,6 +195,9 @@ Lexer.prototype.token = function(src, top) {
|
||||
bull,
|
||||
b,
|
||||
item,
|
||||
listStart,
|
||||
listItems,
|
||||
t,
|
||||
space,
|
||||
i,
|
||||
tag,
|
||||
@@ -318,15 +323,19 @@ Lexer.prototype.token = function(src, top) {
|
||||
bull = cap[2];
|
||||
isordered = bull.length > 1;
|
||||
|
||||
this.tokens.push({
|
||||
listStart = {
|
||||
type: 'list_start',
|
||||
ordered: isordered,
|
||||
start: isordered ? +bull : ''
|
||||
});
|
||||
start: isordered ? +bull : '',
|
||||
loose: false
|
||||
};
|
||||
|
||||
this.tokens.push(listStart);
|
||||
|
||||
// Get each top-level item.
|
||||
cap = cap[0].match(this.rules.item);
|
||||
|
||||
listItems = [];
|
||||
next = false;
|
||||
l = cap.length;
|
||||
i = 0;
|
||||
@@ -367,6 +376,10 @@ Lexer.prototype.token = function(src, top) {
|
||||
if (!loose) loose = next;
|
||||
}
|
||||
|
||||
if (loose) {
|
||||
listStart.loose = true;
|
||||
}
|
||||
|
||||
// Check for task list items
|
||||
istask = /^\[[ xX]\] /.test(item);
|
||||
ischecked = undefined;
|
||||
@@ -375,13 +388,15 @@ Lexer.prototype.token = function(src, top) {
|
||||
item = item.replace(/^\[[ xX]\] +/, '');
|
||||
}
|
||||
|
||||
this.tokens.push({
|
||||
type: loose
|
||||
? 'loose_item_start'
|
||||
: 'list_item_start',
|
||||
t = {
|
||||
type: 'list_item_start',
|
||||
task: istask,
|
||||
checked: ischecked
|
||||
});
|
||||
checked: ischecked,
|
||||
loose: loose
|
||||
};
|
||||
|
||||
listItems.push(t);
|
||||
this.tokens.push(t);
|
||||
|
||||
// Recurse.
|
||||
this.token(item, false);
|
||||
@@ -391,6 +406,14 @@ Lexer.prototype.token = function(src, top) {
|
||||
});
|
||||
}
|
||||
|
||||
if (listStart.loose) {
|
||||
l = listItems.length;
|
||||
i = 0;
|
||||
for (; i < l; i++) {
|
||||
listItems[i].loose = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.tokens.push({
|
||||
type: 'list_end'
|
||||
});
|
||||
@@ -521,10 +544,10 @@ var inline = {
|
||||
link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
|
||||
reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
|
||||
nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
|
||||
strong: /^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,
|
||||
em: /^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,
|
||||
strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
|
||||
em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
|
||||
code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
|
||||
br: /^ {2,}\n(?!\s*$)/,
|
||||
br: /^( {2,}|\\)\n(?!\s*$)/,
|
||||
del: noop,
|
||||
text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
|
||||
};
|
||||
@@ -546,7 +569,7 @@ inline.tag = edit(inline.tag)
|
||||
.getRegex();
|
||||
|
||||
inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
|
||||
inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/;
|
||||
inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
|
||||
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
|
||||
|
||||
inline.link = edit(inline.link)
|
||||
@@ -590,7 +613,7 @@ inline.gfm = merge({}, inline.normal, {
|
||||
.replace('email', inline._email)
|
||||
.getRegex(),
|
||||
_backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
|
||||
del: /^~~(?=\S)([\s\S]*?\S)~~/,
|
||||
del: /^~+(?=\S)([\s\S]*?\S)~+/,
|
||||
text: edit(inline.text)
|
||||
.replace(']|', '~]|')
|
||||
.replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
|
||||
@@ -657,7 +680,8 @@ InlineLexer.prototype.output = function(src) {
|
||||
text,
|
||||
href,
|
||||
title,
|
||||
cap;
|
||||
cap,
|
||||
prevCapZero;
|
||||
|
||||
while (src) {
|
||||
// escape
|
||||
@@ -683,7 +707,10 @@ InlineLexer.prototype.output = function(src) {
|
||||
|
||||
// url (gfm)
|
||||
if (!this.inLink && (cap = this.rules.url.exec(src))) {
|
||||
cap[0] = this.rules._backpedal.exec(cap[0])[0];
|
||||
do {
|
||||
prevCapZero = cap[0];
|
||||
cap[0] = this.rules._backpedal.exec(cap[0])[0];
|
||||
} while (prevCapZero !== cap[0]);
|
||||
src = src.substring(cap[0].length);
|
||||
if (cap[2] === '@') {
|
||||
text = escape(cap[0]);
|
||||
@@ -1219,28 +1246,20 @@ Parser.prototype.tok = function() {
|
||||
}
|
||||
case 'list_item_start': {
|
||||
body = '';
|
||||
var loose = this.token.loose;
|
||||
|
||||
if (this.token.task) {
|
||||
body += this.renderer.checkbox(this.token.checked);
|
||||
}
|
||||
|
||||
while (this.next().type !== 'list_item_end') {
|
||||
body += this.token.type === 'text'
|
||||
body += !loose && this.token.type === 'text'
|
||||
? this.parseText()
|
||||
: this.tok();
|
||||
}
|
||||
|
||||
return this.renderer.listitem(body);
|
||||
}
|
||||
case 'loose_item_start': {
|
||||
body = '';
|
||||
|
||||
while (this.next().type !== 'list_item_end') {
|
||||
body += this.tok();
|
||||
}
|
||||
|
||||
return this.renderer.listitem(body);
|
||||
}
|
||||
case 'html': {
|
||||
// TODO parse inline content if parameter markdown=1
|
||||
return this.renderer.html(this.token.text);
|
||||
@@ -1547,18 +1566,23 @@ marked.InlineLexer = InlineLexer;
|
||||
marked.inlineLexer = InlineLexer.output;
|
||||
|
||||
marked.parse = marked;
|
||||
__marked_exports = marked;
|
||||
|
||||
// BEGIN MONACOCHANGE
|
||||
// if (typeof module !== 'undefined' && typeof exports === 'object') {
|
||||
// module.exports = marked;
|
||||
// } else if (typeof define === 'function' && define.amd) {
|
||||
// define(function() { return marked; });
|
||||
// } else {
|
||||
// root.marked = marked;
|
||||
// }
|
||||
// })(this || (typeof window !== 'undefined' ? window : global));
|
||||
__marked_exports = marked;
|
||||
}).call(this);
|
||||
|
||||
// ESM-comment-begin
|
||||
define([], function() {
|
||||
return {
|
||||
marked: __marked_exports
|
||||
};
|
||||
});
|
||||
define(function() { return __marked_exports; });
|
||||
// ESM-comment-end
|
||||
|
||||
|
||||
// ESM-uncomment-begin
|
||||
// export var marked = __marked_exports;
|
||||
// export var Parser = __marked_exports.Parser;
|
||||
@@ -1571,3 +1595,4 @@ return {
|
||||
// export var inlineLexer = __marked_exports.inlineLexer;
|
||||
// export var parse = __marked_exports.parse;
|
||||
// ESM-uncomment-end
|
||||
// END MONACOCHANGE
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
* 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 URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function stringify(obj: any): string {
|
||||
return JSON.stringify(obj, replacer);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
* 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 * as strings from 'vs/base/common/strings';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
|
||||
export const MIME_TEXT = 'text/plain';
|
||||
@@ -106,7 +106,7 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void {
|
||||
/**
|
||||
* Given a file, return the best matching mime type for it
|
||||
*/
|
||||
export function guessMimeTypes(path: string, firstLine?: string): string[] {
|
||||
export function guessMimeTypes(path: string, firstLine?: string, skipUserAssociations: boolean = false): string[] {
|
||||
if (!path) {
|
||||
return [MIME_UNKNOWN];
|
||||
}
|
||||
@@ -114,10 +114,12 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] {
|
||||
path = path.toLowerCase();
|
||||
const filename = paths.basename(path);
|
||||
|
||||
// 1.) User configured mappings have highest priority
|
||||
const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations);
|
||||
if (configuredMime) {
|
||||
return [configuredMime, MIME_TEXT];
|
||||
if (!skipUserAssociations) {
|
||||
// 1.) User configured mappings have highest priority
|
||||
const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations);
|
||||
if (configuredMime) {
|
||||
return [configuredMime, MIME_TEXT];
|
||||
}
|
||||
}
|
||||
|
||||
// 2.) Registered mappings have middle priority
|
||||
@@ -137,10 +139,10 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] {
|
||||
return [MIME_UNKNOWN];
|
||||
}
|
||||
|
||||
function guessMimeTypeByPath(path: string, filename: string, associations: ITextMimeAssociationItem[]): string {
|
||||
let filenameMatch: ITextMimeAssociationItem;
|
||||
let patternMatch: ITextMimeAssociationItem;
|
||||
let extensionMatch: ITextMimeAssociationItem;
|
||||
function guessMimeTypeByPath(path: string, filename: string, associations: ITextMimeAssociationItem[]): string | null {
|
||||
let filenameMatch: ITextMimeAssociationItem | null = null;
|
||||
let patternMatch: ITextMimeAssociationItem | null = null;
|
||||
let extensionMatch: ITextMimeAssociationItem | null = null;
|
||||
|
||||
// We want to prioritize associations based on the order they are registered so that the last registered
|
||||
// association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074
|
||||
@@ -155,9 +157,9 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
|
||||
|
||||
// Longest pattern match
|
||||
if (association.filepattern) {
|
||||
if (!patternMatch || association.filepattern.length > patternMatch.filepattern.length) {
|
||||
if (!patternMatch || association.filepattern.length > patternMatch.filepattern!.length) {
|
||||
const target = association.filepatternOnPath ? path : filename; // match on full path if pattern contains path separator
|
||||
if (match(association.filepatternLowercase, target)) {
|
||||
if (match(association.filepatternLowercase!, target)) {
|
||||
patternMatch = association;
|
||||
}
|
||||
}
|
||||
@@ -165,8 +167,8 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
|
||||
|
||||
// Longest extension match
|
||||
if (association.extension) {
|
||||
if (!extensionMatch || association.extension.length > extensionMatch.extension.length) {
|
||||
if (strings.endsWith(filename, association.extensionLowercase)) {
|
||||
if (!extensionMatch || association.extension.length > extensionMatch.extension!.length) {
|
||||
if (strings.endsWith(filename, association.extensionLowercase!)) {
|
||||
extensionMatch = association;
|
||||
}
|
||||
}
|
||||
@@ -191,7 +193,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
|
||||
return null;
|
||||
}
|
||||
|
||||
function guessMimeTypeByFirstline(firstLine: string): string {
|
||||
function guessMimeTypeByFirstline(firstLine: string): string | null {
|
||||
if (strings.startsWithUTF8BOM(firstLine)) {
|
||||
firstLine = firstLine.substr(1);
|
||||
}
|
||||
@@ -225,19 +227,24 @@ export function isUnspecific(mime: string[] | string): boolean {
|
||||
return mime.length === 1 && isUnspecific(mime[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a suggestion for the filename by the following logic:
|
||||
* 1. If a relevant extension exists and is an actual filename extension (starting with a dot), suggest the prefix appended by the first one.
|
||||
* 2. Otherwise, if there are other extensions, suggest the first one.
|
||||
* 3. Otherwise, suggest the prefix.
|
||||
*/
|
||||
export function suggestFilename(langId: string, prefix: string): string {
|
||||
for (let i = 0; i < registeredAssociations.length; i++) {
|
||||
const association = registeredAssociations[i];
|
||||
if (association.userConfigured) {
|
||||
continue; // only support registered ones
|
||||
}
|
||||
const extensions = registeredAssociations
|
||||
.filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === langId)
|
||||
.map(assoc => assoc.extension);
|
||||
const extensionsWithDotFirst = arrays.coalesce(extensions)
|
||||
.filter(assoc => strings.startsWith(assoc, '.'));
|
||||
|
||||
if (association.id === langId && association.extension) {
|
||||
return prefix + association.extension;
|
||||
}
|
||||
if (extensionsWithDotFirst.length > 0) {
|
||||
return prefix + extensionsWithDotFirst[0];
|
||||
}
|
||||
|
||||
return prefix; // without any known extension, just return the prefix
|
||||
return extensions[0] || prefix;
|
||||
}
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
@@ -297,4 +304,4 @@ const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
export function getMediaMime(path: string): string | undefined {
|
||||
const ext = paths.extname(path);
|
||||
return mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export namespace Schemas {
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { LRUCache } from 'vs/base/common/map';
|
||||
|
||||
|
||||
@@ -57,3 +57,12 @@ export function countToArray(fromOrTo: number, to?: number): number[] {
|
||||
|
||||
return result;
|
||||
}
|
||||
// {{END SQL CARBON EDIT}}
|
||||
|
||||
export class Counter {
|
||||
private _next = 0;
|
||||
|
||||
getNext(): number {
|
||||
return this._next++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { isObject, isUndefinedOrNull, isArray } from 'vs/base/common/types';
|
||||
|
||||
export function deepClone<T>(obj: T): T {
|
||||
@@ -49,10 +47,10 @@ export function deepFreeze<T>(obj: T): T {
|
||||
const _hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
export function cloneAndChange(obj: any, changer: (orig: any) => any): any {
|
||||
return _cloneAndChange(obj, changer, []);
|
||||
return _cloneAndChange(obj, changer, new Set());
|
||||
}
|
||||
|
||||
function _cloneAndChange(obj: any, changer: (orig: any) => any, encounteredObjects: any[]): any {
|
||||
function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set<any>): any {
|
||||
if (isUndefinedOrNull(obj)) {
|
||||
return obj;
|
||||
}
|
||||
@@ -65,23 +63,23 @@ function _cloneAndChange(obj: any, changer: (orig: any) => any, encounteredObjec
|
||||
if (isArray(obj)) {
|
||||
const r1: any[] = [];
|
||||
for (let i1 = 0; i1 < obj.length; i1++) {
|
||||
r1.push(_cloneAndChange(obj[i1], changer, encounteredObjects));
|
||||
r1.push(_cloneAndChange(obj[i1], changer, seen));
|
||||
}
|
||||
return r1;
|
||||
}
|
||||
|
||||
if (isObject(obj)) {
|
||||
if (encounteredObjects.indexOf(obj) >= 0) {
|
||||
if (seen.has(obj)) {
|
||||
throw new Error('Cannot clone recursive data-structure');
|
||||
}
|
||||
encounteredObjects.push(obj);
|
||||
seen.add(obj);
|
||||
const r2 = {};
|
||||
for (let i2 in obj) {
|
||||
if (_hasOwnProperty.call(obj, i2)) {
|
||||
(r2 as any)[i2] = _cloneAndChange(obj[i2], changer, encounteredObjects);
|
||||
(r2 as any)[i2] = _cloneAndChange(obj[i2], changer, seen);
|
||||
}
|
||||
}
|
||||
encounteredObjects.pop();
|
||||
seen.delete(obj);
|
||||
return r2;
|
||||
}
|
||||
|
||||
@@ -115,6 +113,10 @@ export function mixin(destination: any, source: any, overwrite: boolean = true):
|
||||
return destination;
|
||||
}
|
||||
|
||||
export function assign<T>(destination: T): T;
|
||||
export function assign<T, U>(destination: T, u: U): T & U;
|
||||
export function assign<T, U, V>(destination: T, u: U, v: V): T & U & V;
|
||||
export function assign<T, U, V, W>(destination: T, u: U, v: V, w: W): T & U & V & W;
|
||||
export function assign(destination: any, ...sources: any[]): any {
|
||||
sources.forEach(source => Object.keys(source).forEach(key => destination[key] = source[key]));
|
||||
return destination;
|
||||
@@ -173,7 +175,7 @@ export function equals(one: any, other: any): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function arrayToHash(array: any[]) {
|
||||
function arrayToHash(array: string[]): { [name: string]: true } {
|
||||
const result: any = {};
|
||||
for (let i = 0; i < array.length; ++i) {
|
||||
result[array[i]] = true;
|
||||
@@ -220,7 +222,7 @@ export function safeStringify(obj: any): string {
|
||||
});
|
||||
}
|
||||
|
||||
export function getOrDefault<T, R>(obj: T, fn: (obj: T) => R, defaultValue: R = null): R {
|
||||
export function getOrDefault<T, R>(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R {
|
||||
const result = fn(obj);
|
||||
return typeof result === 'undefined' ? defaultValue : result;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
@@ -99,7 +97,7 @@ function doParseOcticons(text: string, firstOcticonIndex: number): IParsedOctico
|
||||
return { text: textWithoutOcticons, octiconOffsets };
|
||||
}
|
||||
|
||||
export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, enableSeparateSubstringMatching = false): IMatch[] {
|
||||
export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, enableSeparateSubstringMatching = false): IMatch[] | null {
|
||||
const { text, octiconOffsets } = target;
|
||||
|
||||
// Return early if there are no octicon markers in the word to match against
|
||||
@@ -118,7 +116,7 @@ export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons,
|
||||
// Map matches back to offsets with octicons and trimming
|
||||
if (matches) {
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
const octiconOffset = octiconOffsets[matches[i].start] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
||||
const octiconOffset = octiconOffsets[matches[i].start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
||||
matches[i].start += octiconOffset;
|
||||
matches[i].end += octiconOffset;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isArray } from 'vs/base/common/types';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
|
||||
/**
|
||||
* A Pager is a stateless abstraction over a paged collection.
|
||||
@@ -15,16 +15,27 @@ export interface IPager<T> {
|
||||
firstPage: T[];
|
||||
total: number;
|
||||
pageSize: number;
|
||||
getPage(pageIndex: number): TPromise<T[]>;
|
||||
getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable<T[]>;
|
||||
}
|
||||
|
||||
interface IPage<T> {
|
||||
isResolved: boolean;
|
||||
promise: TPromise<any>;
|
||||
promise: Thenable<void> | null;
|
||||
cts: CancellationTokenSource | null;
|
||||
promiseIndexes: Set<number>;
|
||||
elements: T[];
|
||||
}
|
||||
|
||||
function createPage<T>(elements?: T[]): IPage<T> {
|
||||
return {
|
||||
isResolved: !!elements,
|
||||
promise: null,
|
||||
cts: null,
|
||||
promiseIndexes: new Set<number>(),
|
||||
elements: elements || []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A PagedModel is a stateful model over an abstracted paged collection.
|
||||
*/
|
||||
@@ -32,7 +43,7 @@ export interface IPagedModel<T> {
|
||||
length: number;
|
||||
isResolved(index: number): boolean;
|
||||
get(index: number): T;
|
||||
resolve(index: number): TPromise<T>;
|
||||
resolve(index: number, cancellationToken: CancellationToken): Thenable<T>;
|
||||
}
|
||||
|
||||
export function singlePagePager<T>(elements: T[]): IPager<T> {
|
||||
@@ -40,7 +51,9 @@ export function singlePagePager<T>(elements: T[]): IPager<T> {
|
||||
firstPage: elements,
|
||||
total: elements.length,
|
||||
pageSize: elements.length,
|
||||
getPage: null
|
||||
getPage: (pageIndex: number, cancellationToken: CancellationToken): Thenable<T[]> => {
|
||||
return Promise.resolve(elements);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,21 +64,21 @@ export class PagedModel<T> implements IPagedModel<T> {
|
||||
|
||||
get length(): number { return this.pager.total; }
|
||||
|
||||
constructor(arg: IPager<T> | T[], private pageTimeout: number = 500) {
|
||||
constructor(arg: IPager<T> | T[]) {
|
||||
this.pager = isArray(arg) ? singlePagePager<T>(arg) : arg;
|
||||
|
||||
this.pages = [{ isResolved: true, promise: null, promiseIndexes: new Set<number>(), elements: this.pager.firstPage.slice() }];
|
||||
|
||||
const totalPages = Math.ceil(this.pager.total / this.pager.pageSize);
|
||||
|
||||
for (let i = 0, len = totalPages - 1; i < len; i++) {
|
||||
this.pages.push({ isResolved: false, promise: null, promiseIndexes: new Set<number>(), elements: [] });
|
||||
}
|
||||
this.pages = [
|
||||
createPage(this.pager.firstPage.slice()),
|
||||
...range(totalPages - 1).map(() => createPage<T>())
|
||||
];
|
||||
}
|
||||
|
||||
isResolved(index: number): boolean {
|
||||
const pageIndex = Math.floor(index / this.pager.pageSize);
|
||||
const page = this.pages[pageIndex];
|
||||
|
||||
return !!page.isResolved;
|
||||
}
|
||||
|
||||
@@ -77,43 +90,88 @@ export class PagedModel<T> implements IPagedModel<T> {
|
||||
return page.elements[indexInPage];
|
||||
}
|
||||
|
||||
resolve(index: number): TPromise<T> {
|
||||
resolve(index: number, cancellationToken: CancellationToken): Thenable<T> {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
return Promise.reject(canceled());
|
||||
}
|
||||
|
||||
const pageIndex = Math.floor(index / this.pager.pageSize);
|
||||
const indexInPage = index % this.pager.pageSize;
|
||||
const page = this.pages[pageIndex];
|
||||
|
||||
if (page.isResolved) {
|
||||
return TPromise.as(page.elements[indexInPage]);
|
||||
return Promise.resolve(page.elements[indexInPage]);
|
||||
}
|
||||
|
||||
if (!page.promise) {
|
||||
page.promise = TPromise.timeout(this.pageTimeout)
|
||||
.then(() => this.pager.getPage(pageIndex))
|
||||
page.cts = new CancellationTokenSource();
|
||||
page.promise = this.pager.getPage(pageIndex, page.cts.token)
|
||||
.then(elements => {
|
||||
page.elements = elements;
|
||||
page.isResolved = true;
|
||||
page.promise = null;
|
||||
page.cts = null;
|
||||
}, err => {
|
||||
page.isResolved = false;
|
||||
page.promise = null;
|
||||
return TPromise.wrapError(err);
|
||||
page.cts = null;
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
return new TPromise<T>((c, e) => {
|
||||
page.promiseIndexes.add(index);
|
||||
page.promise.done(() => c(page.elements[indexInPage]));
|
||||
}, () => {
|
||||
if (!page.promise) {
|
||||
cancellationToken.onCancellationRequested(() => {
|
||||
if (!page.cts) {
|
||||
return;
|
||||
}
|
||||
|
||||
page.promiseIndexes.delete(index);
|
||||
|
||||
if (page.promiseIndexes.size === 0) {
|
||||
page.promise.cancel();
|
||||
page.cts.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
page.promiseIndexes.add(index);
|
||||
|
||||
return page.promise.then(() => page.elements[indexInPage]);
|
||||
}
|
||||
}
|
||||
|
||||
export class DelayedPagedModel<T> implements IPagedModel<T> {
|
||||
|
||||
get length(): number { return this.model.length; }
|
||||
|
||||
constructor(private model: IPagedModel<T>, private timeout: number = 500) { }
|
||||
|
||||
isResolved(index: number): boolean {
|
||||
return this.model.isResolved(index);
|
||||
}
|
||||
|
||||
get(index: number): T {
|
||||
return this.model.get(index);
|
||||
}
|
||||
|
||||
resolve(index: number, cancellationToken: CancellationToken): Thenable<T> {
|
||||
return new Promise((c, e) => {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
return e(canceled());
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
return e(canceled());
|
||||
}
|
||||
|
||||
timeoutCancellation.dispose();
|
||||
this.model.resolve(index, cancellationToken).then(c, e);
|
||||
}, this.timeout);
|
||||
|
||||
const timeoutCancellation = cancellationToken.onCancellationRequested(() => {
|
||||
clearTimeout(timer);
|
||||
timeoutCancellation.dispose();
|
||||
e(canceled());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +184,7 @@ export function mapPager<T, R>(pager: IPager<T>, fn: (t: T) => R): IPager<R> {
|
||||
firstPage: pager.firstPage.map(fn),
|
||||
total: pager.total,
|
||||
pageSize: pager.pageSize,
|
||||
getPage: pageIndex => pager.getPage(pageIndex).then(r => r.map(fn))
|
||||
getPage: (pageIndex, token) => pager.getPage(pageIndex, token).then(r => r.map(fn))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,8 +196,8 @@ export function mergePagers<T>(one: IPager<T>, other: IPager<T>): IPager<T> {
|
||||
firstPage: [...one.firstPage, ...other.firstPage],
|
||||
total: one.total + other.total,
|
||||
pageSize: one.pageSize + other.pageSize,
|
||||
getPage(pageIndex: number): TPromise<T[]> {
|
||||
return TPromise.join([one.getPage(pageIndex), other.getPage(pageIndex)])
|
||||
getPage(pageIndex: number, token): Thenable<T[]> {
|
||||
return Promise.all([one.getPage(pageIndex, token), other.getPage(pageIndex, token)])
|
||||
.then(([onePage, otherPage]) => [...onePage, ...otherPage]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
* 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 Types from 'vs/base/common/types';
|
||||
|
||||
export enum ValidationState {
|
||||
export const enum ValidationState {
|
||||
OK = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { isWindows } from 'vs/base/common/platform';
|
||||
import { startsWithIgnoreCase, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
@@ -19,9 +18,12 @@ export const sep = '/';
|
||||
export const nativeSep = isWindows ? '\\' : '/';
|
||||
|
||||
/**
|
||||
* @param path the path to get the dirname from
|
||||
* @param separator the separator to use
|
||||
* @returns the directory name of a path.
|
||||
*
|
||||
*/
|
||||
export function dirname(path: string): string {
|
||||
export function dirname(path: string, separator = nativeSep): string {
|
||||
const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
|
||||
if (idx === 0) {
|
||||
return '.';
|
||||
@@ -32,7 +34,7 @@ export function dirname(path: string): string {
|
||||
} else {
|
||||
let res = path.substring(0, ~idx);
|
||||
if (isWindows && res[res.length - 1] === ':') {
|
||||
res += nativeSep; // make sure drive letters end with backslash
|
||||
res += separator; // make sure drive letters end with backslash
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -70,7 +72,10 @@ function _isNormal(path: string, win: boolean): boolean {
|
||||
: !_posixBadPath.test(path);
|
||||
}
|
||||
|
||||
export function normalize(path: string, toOSPath?: boolean): string {
|
||||
export function normalize(path: undefined, toOSPath?: boolean): undefined;
|
||||
export function normalize(path: null, toOSPath?: boolean): null;
|
||||
export function normalize(path: string, toOSPath?: boolean): string;
|
||||
export function normalize(path: string | null | undefined, toOSPath?: boolean): string | null | undefined {
|
||||
|
||||
if (path === null || path === void 0) {
|
||||
return path;
|
||||
@@ -81,7 +86,7 @@ export function normalize(path: string, toOSPath?: boolean): string {
|
||||
return '.';
|
||||
}
|
||||
|
||||
const wantsBackslash = isWindows && toOSPath;
|
||||
const wantsBackslash = !!(isWindows && toOSPath);
|
||||
if (_isNormal(path, wantsBackslash)) {
|
||||
return path;
|
||||
}
|
||||
@@ -286,7 +291,7 @@ export function isUNC(path: string): boolean {
|
||||
// Reference: https://en.wikipedia.org/wiki/Filename
|
||||
const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g;
|
||||
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
|
||||
export function isValidBasename(name: string): boolean {
|
||||
export function isValidBasename(name: string | null | undefined): boolean {
|
||||
if (!name || name.length === 0 || /^\s+$/.test(name)) {
|
||||
return false; // require a name that is not just whitespace
|
||||
}
|
||||
@@ -396,5 +401,5 @@ export function isAbsolute_win32(path: string): boolean {
|
||||
}
|
||||
|
||||
export function isAbsolute_posix(path: string): boolean {
|
||||
return path && path.charCodeAt(0) === CharCode.Slash;
|
||||
return !!(path && path.charCodeAt(0) === CharCode.Slash);
|
||||
}
|
||||
|
||||
@@ -76,17 +76,16 @@ define([], function () {
|
||||
|
||||
function getDuration(from, to) {
|
||||
const entries = global._performanceEntries;
|
||||
let name = from;
|
||||
let startTime = 0;
|
||||
for (let i = 0; i < entries.length; i += 5) {
|
||||
if (entries[i + 1] === name) {
|
||||
if (name === from) {
|
||||
// found `from` (start of interval)
|
||||
name = to;
|
||||
startTime = entries[i + 2];
|
||||
let target = to;
|
||||
let endTime = 0;
|
||||
for (let i = entries.length - 1; i >= 0; i -= 5) {
|
||||
if (entries[i - 3] === target) {
|
||||
if (target === to) {
|
||||
// found `to` (end of interval)
|
||||
endTime = entries[i - 2];
|
||||
target = from;
|
||||
} else {
|
||||
// from `to` (end of interval)
|
||||
return entries[i + 2] - startTime;
|
||||
return endTime - entries[i - 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
let _isWindows = false;
|
||||
let _isMacintosh = false;
|
||||
let _isLinux = false;
|
||||
let _isNative = false;
|
||||
let _isWeb = false;
|
||||
let _locale: string = undefined;
|
||||
let _language: string = undefined;
|
||||
let _translationsConfigFile: string = undefined;
|
||||
let _locale: string | undefined = undefined;
|
||||
let _language: string | undefined = undefined;
|
||||
let _translationsConfigFile: string | undefined = undefined;
|
||||
|
||||
interface NLSConfig {
|
||||
locale: string;
|
||||
@@ -28,6 +27,10 @@ interface INodeProcess {
|
||||
env: IProcessEnvironment;
|
||||
getuid(): number;
|
||||
nextTick: Function;
|
||||
versions?: {
|
||||
electron?: string;
|
||||
};
|
||||
type?: string;
|
||||
}
|
||||
declare let process: INodeProcess;
|
||||
declare let global: any;
|
||||
@@ -41,8 +44,18 @@ declare let self: any;
|
||||
|
||||
export const LANGUAGE_DEFAULT = 'en';
|
||||
|
||||
const isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer');
|
||||
|
||||
// OS detection
|
||||
if (typeof process === 'object' && typeof process.nextTick === 'function' && typeof process.platform === 'string') {
|
||||
if (typeof navigator === 'object' && !isElectronRenderer) {
|
||||
const userAgent = navigator.userAgent;
|
||||
_isWindows = userAgent.indexOf('Windows') >= 0;
|
||||
_isMacintosh = userAgent.indexOf('Macintosh') >= 0;
|
||||
_isLinux = userAgent.indexOf('Linux') >= 0;
|
||||
_isWeb = true;
|
||||
_locale = navigator.language;
|
||||
_language = _locale;
|
||||
} else if (typeof process === 'object') {
|
||||
_isWindows = (process.platform === 'win32');
|
||||
_isMacintosh = (process.platform === 'darwin');
|
||||
_isLinux = (process.platform === 'linux');
|
||||
@@ -61,22 +74,22 @@ if (typeof process === 'object' && typeof process.nextTick === 'function' && typ
|
||||
}
|
||||
}
|
||||
_isNative = true;
|
||||
} else if (typeof navigator === 'object') {
|
||||
const userAgent = navigator.userAgent;
|
||||
_isWindows = userAgent.indexOf('Windows') >= 0;
|
||||
_isMacintosh = userAgent.indexOf('Macintosh') >= 0;
|
||||
_isLinux = userAgent.indexOf('Linux') >= 0;
|
||||
_isWeb = true;
|
||||
_locale = navigator.language;
|
||||
_language = _locale;
|
||||
}
|
||||
|
||||
export enum Platform {
|
||||
export const enum Platform {
|
||||
Web,
|
||||
Mac,
|
||||
Linux,
|
||||
Windows
|
||||
}
|
||||
export function PlatformToString(platform: Platform) {
|
||||
switch (platform) {
|
||||
case Platform.Web: return 'Web';
|
||||
case Platform.Mac: return 'Mac';
|
||||
case Platform.Linux: return 'Linux';
|
||||
case Platform.Windows: return 'Windows';
|
||||
}
|
||||
}
|
||||
|
||||
let _platform: Platform = Platform.Web;
|
||||
if (_isNative) {
|
||||
@@ -122,7 +135,7 @@ export const translationsConfigFile = _translationsConfigFile;
|
||||
const _globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {} as any);
|
||||
export const globals: any = _globals;
|
||||
|
||||
let _setImmediate: (callback: (...args: any[]) => void) => number = null;
|
||||
let _setImmediate: ((callback: (...args: any[]) => void) => number) | null = null;
|
||||
export function setImmediate(callback: (...args: any[]) => void): number {
|
||||
if (_setImmediate === null) {
|
||||
if (globals.setImmediate) {
|
||||
@@ -133,7 +146,7 @@ export function setImmediate(callback: (...args: any[]) => void): number {
|
||||
_setImmediate = globals.setTimeout.bind(globals);
|
||||
}
|
||||
}
|
||||
return _setImmediate(callback);
|
||||
return _setImmediate!(callback);
|
||||
}
|
||||
|
||||
export const enum OperatingSystem {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Options to be passed to the external program or shell.
|
||||
@@ -49,7 +48,7 @@ export interface ForkOptions extends CommandOptions {
|
||||
execArgv?: string[];
|
||||
}
|
||||
|
||||
export enum Source {
|
||||
export const enum Source {
|
||||
stdout,
|
||||
stderr
|
||||
}
|
||||
@@ -79,7 +78,7 @@ export interface TerminateResponse {
|
||||
error?: any;
|
||||
}
|
||||
|
||||
export enum TerminateResponseCode {
|
||||
export const enum TerminateResponseCode {
|
||||
Success = 0,
|
||||
Unknown = 1,
|
||||
AccessDenied = 2,
|
||||
|
||||
60
src/vs/base/common/range.ts
Normal file
60
src/vs/base/common/range.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface IRange {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export interface IRangedGroup {
|
||||
range: IRange;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export namespace Range {
|
||||
|
||||
/**
|
||||
* Returns the intersection between two ranges as a range itself.
|
||||
* Returns `{ start: 0, end: 0 }` if the intersection is empty.
|
||||
*/
|
||||
export function intersect(one: IRange, other: IRange): IRange {
|
||||
if (one.start >= other.end || other.start >= one.end) {
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
|
||||
const start = Math.max(one.start, other.start);
|
||||
const end = Math.min(one.end, other.end);
|
||||
|
||||
if (end - start <= 0) {
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
export function isEmpty(range: IRange): boolean {
|
||||
return range.end - range.start <= 0;
|
||||
}
|
||||
|
||||
export function intersects(one: IRange, other: IRange): boolean {
|
||||
return !isEmpty(intersect(one, other));
|
||||
}
|
||||
|
||||
export function relativeComplement(one: IRange, other: IRange): IRange[] {
|
||||
const result: IRange[] = [];
|
||||
const first = { start: one.start, end: Math.min(other.start, one.end) };
|
||||
const second = { start: Math.max(other.end, one.start), end: one.end };
|
||||
|
||||
if (!isEmpty(first)) {
|
||||
result.push(first);
|
||||
}
|
||||
|
||||
if (!isEmpty(second)) {
|
||||
result.push(second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -2,41 +2,50 @@
|
||||
* 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 { URI } from 'vs/base/common/uri';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export function getComparisonKey(resource: uri): string {
|
||||
export function getComparisonKey(resource: URI): string {
|
||||
return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString();
|
||||
}
|
||||
|
||||
export function hasToIgnoreCase(resource: uri): boolean {
|
||||
export function hasToIgnoreCase(resource: URI | undefined): boolean {
|
||||
// A file scheme resource is in the same platform as code, so ignore case for non linux platforms
|
||||
// Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
|
||||
return resource && resource.scheme === Schemas.file ? !isLinux : true;
|
||||
}
|
||||
|
||||
export function basenameOrAuthority(resource: uri): string {
|
||||
return paths.basename(resource.path) || resource.authority;
|
||||
export function basenameOrAuthority(resource: URI): string {
|
||||
return basename(resource) || resource.authority;
|
||||
}
|
||||
|
||||
export function isEqualOrParent(resource: uri, candidate: uri, ignoreCase?: boolean): boolean {
|
||||
if (resource.scheme === candidate.scheme && resource.authority === candidate.authority) {
|
||||
if (resource.scheme === 'file') {
|
||||
return paths.isEqualOrParent(resource.fsPath, candidate.fsPath, ignoreCase);
|
||||
/**
|
||||
* Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
|
||||
* @param base A uri which is "longer"
|
||||
* @param parentCandidate A uri which is "shorter" then `base`
|
||||
*/
|
||||
export function isEqualOrParent(base: URI, parentCandidate: URI, ignoreCase = hasToIgnoreCase(base)): boolean {
|
||||
if (base.scheme === parentCandidate.scheme) {
|
||||
if (base.scheme === Schemas.file) {
|
||||
return paths.isEqualOrParent(fsPath(base), fsPath(parentCandidate), ignoreCase);
|
||||
}
|
||||
if (isEqualAuthority(base.authority, parentCandidate.authority, ignoreCase)) {
|
||||
return paths.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/');
|
||||
}
|
||||
|
||||
return paths.isEqualOrParent(resource.path, candidate.path, ignoreCase, '/');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean {
|
||||
function isEqualAuthority(a1: string, a2: string, ignoreCase?: boolean) {
|
||||
return a1 === a2 || ignoreCase && a1 && a2 && equalsIgnoreCase(a1, a2);
|
||||
}
|
||||
|
||||
export function isEqual(first: URI | undefined, second: URI | undefined, ignoreCase = hasToIgnoreCase(first)): boolean {
|
||||
const identityEquals = (first === second);
|
||||
if (identityEquals) {
|
||||
return true;
|
||||
@@ -53,25 +62,100 @@ export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean
|
||||
return first.toString() === second.toString();
|
||||
}
|
||||
|
||||
export function dirname(resource: uri): uri {
|
||||
const dirname = paths.dirname(resource.path);
|
||||
if (resource.authority && dirname && !paths.isAbsolute(dirname)) {
|
||||
return null; // If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character
|
||||
}
|
||||
export function basename(resource: URI): string {
|
||||
return paths.basename(resource.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a URI representing the directory of a URI path.
|
||||
*
|
||||
* @param resource The input URI.
|
||||
* @returns The URI representing the directory of the input URI.
|
||||
*/
|
||||
export function dirname(resource: URI): URI | null {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
return URI.file(paths.dirname(fsPath(resource)));
|
||||
}
|
||||
let dirname = paths.dirname(resource.path, '/');
|
||||
if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) {
|
||||
return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
|
||||
}
|
||||
return resource.with({
|
||||
path: dirname
|
||||
});
|
||||
}
|
||||
|
||||
export function joinPath(resource: uri, pathFragment: string): uri {
|
||||
const joinedPath = paths.join(resource.path || '/', pathFragment);
|
||||
/**
|
||||
* Join a URI path with a path fragment and normalizes the resulting path.
|
||||
*
|
||||
* @param resource The input URI.
|
||||
* @param pathFragment The path fragment to add to the URI path.
|
||||
* @returns The resulting URI.
|
||||
*/
|
||||
export function joinPath(resource: URI, pathFragment: string): URI {
|
||||
let joinedPath: string;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
joinedPath = URI.file(paths.join(fsPath(resource), pathFragment)).path;
|
||||
} else {
|
||||
joinedPath = paths.join(resource.path, pathFragment);
|
||||
}
|
||||
return resource.with({
|
||||
path: joinedPath
|
||||
});
|
||||
}
|
||||
|
||||
export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => uri): T[] {
|
||||
/**
|
||||
* Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
|
||||
*
|
||||
* @param resource The URI to normalize the path.
|
||||
* @returns The URI with the normalized path.
|
||||
*/
|
||||
export function normalizePath(resource: URI): URI {
|
||||
let normalizedPath: string;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
normalizedPath = URI.file(paths.normalize(fsPath(resource))).path;
|
||||
} else {
|
||||
normalizedPath = paths.normalize(resource.path);
|
||||
}
|
||||
return resource.with({
|
||||
path: normalizedPath
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fsPath of an URI where the drive letter is not normalized.
|
||||
* See #56403.
|
||||
*/
|
||||
export function fsPath(uri: URI): string {
|
||||
let value: string;
|
||||
if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') {
|
||||
// unc path: file://shares/c$/far/boo
|
||||
value = `//${uri.authority}${uri.path}`;
|
||||
} else if (
|
||||
isWindows
|
||||
&& uri.path.charCodeAt(0) === CharCode.Slash
|
||||
&& (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)
|
||||
&& uri.path.charCodeAt(2) === CharCode.Colon
|
||||
) {
|
||||
value = uri.path.substr(1);
|
||||
} else {
|
||||
// other path
|
||||
value = uri.path;
|
||||
}
|
||||
if (isWindows) {
|
||||
value = value.replace(/\//g, '\\');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the URI path is absolute.
|
||||
*/
|
||||
export function isAbsolutePath(resource: URI): boolean {
|
||||
return paths.isAbsolute(resource.path);
|
||||
}
|
||||
|
||||
export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] {
|
||||
const distinctParents: T[] = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const candidateResource = resourceAccessor(items[i]);
|
||||
@@ -90,3 +174,52 @@ export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => ur
|
||||
|
||||
return distinctParents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the given URL is a file URI created by `URI.parse` instead of `URI.file`.
|
||||
* Such URI have no scheme or scheme that consist of a single letter (windows drive letter)
|
||||
* @param candidate The URI to test
|
||||
* @returns A corrected, real file URI if the input seems to be malformed.
|
||||
* Undefined is returned if the input URI looks fine.
|
||||
*/
|
||||
export function isMalformedFileUri(candidate: URI): URI | undefined {
|
||||
if (!candidate.scheme || isWindows && candidate.scheme.match(/^[a-zA-Z]$/)) {
|
||||
return URI.file((candidate.scheme ? candidate.scheme + ':' : '') + candidate.path);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data URI related helpers.
|
||||
*/
|
||||
export namespace DataUri {
|
||||
|
||||
export const META_DATA_LABEL = 'label';
|
||||
export const META_DATA_DESCRIPTION = 'description';
|
||||
export const META_DATA_SIZE = 'size';
|
||||
export const META_DATA_MIME = 'mime';
|
||||
|
||||
export function parseMetaData(dataUri: URI): Map<string, string> {
|
||||
const metadata = new Map<string, string>();
|
||||
|
||||
// Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
|
||||
// the metadata is: size:2313;label:SomeLabel;description:SomeDescription
|
||||
const meta = dataUri.path.substring(dataUri.path.indexOf(';') + 1, dataUri.path.lastIndexOf(';'));
|
||||
meta.split(';').forEach(property => {
|
||||
const [key, value] = property.split(':');
|
||||
if (key && value) {
|
||||
metadata.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
// Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
|
||||
// the mime is: image/png
|
||||
const mime = dataUri.path.substring(0, dataUri.path.indexOf(';'));
|
||||
if (mime) {
|
||||
metadata.set(META_DATA_MIME, mime);
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
689
src/vs/base/common/scanCode.ts
Normal file
689
src/vs/base/common/scanCode.ts
Normal file
@@ -0,0 +1,689 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
/**
|
||||
* keyboardEvent.code
|
||||
*/
|
||||
export const enum ScanCode {
|
||||
None,
|
||||
|
||||
Hyper,
|
||||
Super,
|
||||
Fn,
|
||||
FnLock,
|
||||
Suspend,
|
||||
Resume,
|
||||
Turbo,
|
||||
Sleep,
|
||||
WakeUp,
|
||||
KeyA,
|
||||
KeyB,
|
||||
KeyC,
|
||||
KeyD,
|
||||
KeyE,
|
||||
KeyF,
|
||||
KeyG,
|
||||
KeyH,
|
||||
KeyI,
|
||||
KeyJ,
|
||||
KeyK,
|
||||
KeyL,
|
||||
KeyM,
|
||||
KeyN,
|
||||
KeyO,
|
||||
KeyP,
|
||||
KeyQ,
|
||||
KeyR,
|
||||
KeyS,
|
||||
KeyT,
|
||||
KeyU,
|
||||
KeyV,
|
||||
KeyW,
|
||||
KeyX,
|
||||
KeyY,
|
||||
KeyZ,
|
||||
Digit1,
|
||||
Digit2,
|
||||
Digit3,
|
||||
Digit4,
|
||||
Digit5,
|
||||
Digit6,
|
||||
Digit7,
|
||||
Digit8,
|
||||
Digit9,
|
||||
Digit0,
|
||||
Enter,
|
||||
Escape,
|
||||
Backspace,
|
||||
Tab,
|
||||
Space,
|
||||
Minus,
|
||||
Equal,
|
||||
BracketLeft,
|
||||
BracketRight,
|
||||
Backslash,
|
||||
IntlHash,
|
||||
Semicolon,
|
||||
Quote,
|
||||
Backquote,
|
||||
Comma,
|
||||
Period,
|
||||
Slash,
|
||||
CapsLock,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
PrintScreen,
|
||||
ScrollLock,
|
||||
Pause,
|
||||
Insert,
|
||||
Home,
|
||||
PageUp,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
ArrowRight,
|
||||
ArrowLeft,
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
NumLock,
|
||||
NumpadDivide,
|
||||
NumpadMultiply,
|
||||
NumpadSubtract,
|
||||
NumpadAdd,
|
||||
NumpadEnter,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
Numpad0,
|
||||
NumpadDecimal,
|
||||
IntlBackslash,
|
||||
ContextMenu,
|
||||
Power,
|
||||
NumpadEqual,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
Open,
|
||||
Help,
|
||||
Select,
|
||||
Again,
|
||||
Undo,
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Find,
|
||||
AudioVolumeMute,
|
||||
AudioVolumeUp,
|
||||
AudioVolumeDown,
|
||||
NumpadComma,
|
||||
IntlRo,
|
||||
KanaMode,
|
||||
IntlYen,
|
||||
Convert,
|
||||
NonConvert,
|
||||
Lang1,
|
||||
Lang2,
|
||||
Lang3,
|
||||
Lang4,
|
||||
Lang5,
|
||||
Abort,
|
||||
Props,
|
||||
NumpadParenLeft,
|
||||
NumpadParenRight,
|
||||
NumpadBackspace,
|
||||
NumpadMemoryStore,
|
||||
NumpadMemoryRecall,
|
||||
NumpadMemoryClear,
|
||||
NumpadMemoryAdd,
|
||||
NumpadMemorySubtract,
|
||||
NumpadClear,
|
||||
NumpadClearEntry,
|
||||
ControlLeft,
|
||||
ShiftLeft,
|
||||
AltLeft,
|
||||
MetaLeft,
|
||||
ControlRight,
|
||||
ShiftRight,
|
||||
AltRight,
|
||||
MetaRight,
|
||||
BrightnessUp,
|
||||
BrightnessDown,
|
||||
MediaPlay,
|
||||
MediaRecord,
|
||||
MediaFastForward,
|
||||
MediaRewind,
|
||||
MediaTrackNext,
|
||||
MediaTrackPrevious,
|
||||
MediaStop,
|
||||
Eject,
|
||||
MediaPlayPause,
|
||||
MediaSelect,
|
||||
LaunchMail,
|
||||
LaunchApp2,
|
||||
LaunchApp1,
|
||||
SelectTask,
|
||||
LaunchScreenSaver,
|
||||
BrowserSearch,
|
||||
BrowserHome,
|
||||
BrowserBack,
|
||||
BrowserForward,
|
||||
BrowserStop,
|
||||
BrowserRefresh,
|
||||
BrowserFavorites,
|
||||
ZoomToggle,
|
||||
MailReply,
|
||||
MailForward,
|
||||
MailSend,
|
||||
|
||||
MAX_VALUE
|
||||
}
|
||||
|
||||
const scanCodeIntToStr: string[] = [];
|
||||
const scanCodeStrToInt: { [code: string]: number; } = Object.create(null);
|
||||
const scanCodeLowerCaseStrToInt: { [code: string]: number; } = Object.create(null);
|
||||
|
||||
export const ScanCodeUtils = {
|
||||
lowerCaseToEnum: (scanCode: string) => scanCodeLowerCaseStrToInt[scanCode] || ScanCode.None,
|
||||
toEnum: (scanCode: string) => scanCodeStrToInt[scanCode] || ScanCode.None,
|
||||
toString: (scanCode: ScanCode) => scanCodeIntToStr[scanCode] || 'None'
|
||||
};
|
||||
|
||||
/**
|
||||
* -1 if a ScanCode => KeyCode mapping depends on kb layout.
|
||||
*/
|
||||
export const IMMUTABLE_CODE_TO_KEY_CODE: KeyCode[] = [];
|
||||
|
||||
/**
|
||||
* -1 if a KeyCode => ScanCode mapping depends on kb layout.
|
||||
*/
|
||||
export const IMMUTABLE_KEY_CODE_TO_CODE: ScanCode[] = [];
|
||||
|
||||
export class ScanCodeBinding {
|
||||
public readonly ctrlKey: boolean;
|
||||
public readonly shiftKey: boolean;
|
||||
public readonly altKey: boolean;
|
||||
public readonly metaKey: boolean;
|
||||
public readonly scanCode: ScanCode;
|
||||
|
||||
constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, scanCode: ScanCode) {
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.altKey = altKey;
|
||||
this.metaKey = metaKey;
|
||||
this.scanCode = scanCode;
|
||||
}
|
||||
|
||||
public equals(other: ScanCodeBinding): boolean {
|
||||
return (
|
||||
this.ctrlKey === other.ctrlKey
|
||||
&& this.shiftKey === other.shiftKey
|
||||
&& this.altKey === other.altKey
|
||||
&& this.metaKey === other.metaKey
|
||||
&& this.scanCode === other.scanCode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this keybinding refer to the key code of a modifier and it also has the modifier flag?
|
||||
*/
|
||||
public isDuplicateModifierCase(): boolean {
|
||||
return (
|
||||
(this.ctrlKey && (this.scanCode === ScanCode.ControlLeft || this.scanCode === ScanCode.ControlRight))
|
||||
|| (this.shiftKey && (this.scanCode === ScanCode.ShiftLeft || this.scanCode === ScanCode.ShiftRight))
|
||||
|| (this.altKey && (this.scanCode === ScanCode.AltLeft || this.scanCode === ScanCode.AltRight))
|
||||
|| (this.metaKey && (this.scanCode === ScanCode.MetaLeft || this.scanCode === ScanCode.MetaRight))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
function d(intScanCode: ScanCode, strScanCode: string): void {
|
||||
scanCodeIntToStr[intScanCode] = strScanCode;
|
||||
scanCodeStrToInt[strScanCode] = intScanCode;
|
||||
scanCodeLowerCaseStrToInt[strScanCode.toLowerCase()] = intScanCode;
|
||||
}
|
||||
d(ScanCode.None, 'None');
|
||||
d(ScanCode.Hyper, 'Hyper');
|
||||
d(ScanCode.Super, 'Super');
|
||||
d(ScanCode.Fn, 'Fn');
|
||||
d(ScanCode.FnLock, 'FnLock');
|
||||
d(ScanCode.Suspend, 'Suspend');
|
||||
d(ScanCode.Resume, 'Resume');
|
||||
d(ScanCode.Turbo, 'Turbo');
|
||||
d(ScanCode.Sleep, 'Sleep');
|
||||
d(ScanCode.WakeUp, 'WakeUp');
|
||||
d(ScanCode.KeyA, 'KeyA');
|
||||
d(ScanCode.KeyB, 'KeyB');
|
||||
d(ScanCode.KeyC, 'KeyC');
|
||||
d(ScanCode.KeyD, 'KeyD');
|
||||
d(ScanCode.KeyE, 'KeyE');
|
||||
d(ScanCode.KeyF, 'KeyF');
|
||||
d(ScanCode.KeyG, 'KeyG');
|
||||
d(ScanCode.KeyH, 'KeyH');
|
||||
d(ScanCode.KeyI, 'KeyI');
|
||||
d(ScanCode.KeyJ, 'KeyJ');
|
||||
d(ScanCode.KeyK, 'KeyK');
|
||||
d(ScanCode.KeyL, 'KeyL');
|
||||
d(ScanCode.KeyM, 'KeyM');
|
||||
d(ScanCode.KeyN, 'KeyN');
|
||||
d(ScanCode.KeyO, 'KeyO');
|
||||
d(ScanCode.KeyP, 'KeyP');
|
||||
d(ScanCode.KeyQ, 'KeyQ');
|
||||
d(ScanCode.KeyR, 'KeyR');
|
||||
d(ScanCode.KeyS, 'KeyS');
|
||||
d(ScanCode.KeyT, 'KeyT');
|
||||
d(ScanCode.KeyU, 'KeyU');
|
||||
d(ScanCode.KeyV, 'KeyV');
|
||||
d(ScanCode.KeyW, 'KeyW');
|
||||
d(ScanCode.KeyX, 'KeyX');
|
||||
d(ScanCode.KeyY, 'KeyY');
|
||||
d(ScanCode.KeyZ, 'KeyZ');
|
||||
d(ScanCode.Digit1, 'Digit1');
|
||||
d(ScanCode.Digit2, 'Digit2');
|
||||
d(ScanCode.Digit3, 'Digit3');
|
||||
d(ScanCode.Digit4, 'Digit4');
|
||||
d(ScanCode.Digit5, 'Digit5');
|
||||
d(ScanCode.Digit6, 'Digit6');
|
||||
d(ScanCode.Digit7, 'Digit7');
|
||||
d(ScanCode.Digit8, 'Digit8');
|
||||
d(ScanCode.Digit9, 'Digit9');
|
||||
d(ScanCode.Digit0, 'Digit0');
|
||||
d(ScanCode.Enter, 'Enter');
|
||||
d(ScanCode.Escape, 'Escape');
|
||||
d(ScanCode.Backspace, 'Backspace');
|
||||
d(ScanCode.Tab, 'Tab');
|
||||
d(ScanCode.Space, 'Space');
|
||||
d(ScanCode.Minus, 'Minus');
|
||||
d(ScanCode.Equal, 'Equal');
|
||||
d(ScanCode.BracketLeft, 'BracketLeft');
|
||||
d(ScanCode.BracketRight, 'BracketRight');
|
||||
d(ScanCode.Backslash, 'Backslash');
|
||||
d(ScanCode.IntlHash, 'IntlHash');
|
||||
d(ScanCode.Semicolon, 'Semicolon');
|
||||
d(ScanCode.Quote, 'Quote');
|
||||
d(ScanCode.Backquote, 'Backquote');
|
||||
d(ScanCode.Comma, 'Comma');
|
||||
d(ScanCode.Period, 'Period');
|
||||
d(ScanCode.Slash, 'Slash');
|
||||
d(ScanCode.CapsLock, 'CapsLock');
|
||||
d(ScanCode.F1, 'F1');
|
||||
d(ScanCode.F2, 'F2');
|
||||
d(ScanCode.F3, 'F3');
|
||||
d(ScanCode.F4, 'F4');
|
||||
d(ScanCode.F5, 'F5');
|
||||
d(ScanCode.F6, 'F6');
|
||||
d(ScanCode.F7, 'F7');
|
||||
d(ScanCode.F8, 'F8');
|
||||
d(ScanCode.F9, 'F9');
|
||||
d(ScanCode.F10, 'F10');
|
||||
d(ScanCode.F11, 'F11');
|
||||
d(ScanCode.F12, 'F12');
|
||||
d(ScanCode.PrintScreen, 'PrintScreen');
|
||||
d(ScanCode.ScrollLock, 'ScrollLock');
|
||||
d(ScanCode.Pause, 'Pause');
|
||||
d(ScanCode.Insert, 'Insert');
|
||||
d(ScanCode.Home, 'Home');
|
||||
d(ScanCode.PageUp, 'PageUp');
|
||||
d(ScanCode.Delete, 'Delete');
|
||||
d(ScanCode.End, 'End');
|
||||
d(ScanCode.PageDown, 'PageDown');
|
||||
d(ScanCode.ArrowRight, 'ArrowRight');
|
||||
d(ScanCode.ArrowLeft, 'ArrowLeft');
|
||||
d(ScanCode.ArrowDown, 'ArrowDown');
|
||||
d(ScanCode.ArrowUp, 'ArrowUp');
|
||||
d(ScanCode.NumLock, 'NumLock');
|
||||
d(ScanCode.NumpadDivide, 'NumpadDivide');
|
||||
d(ScanCode.NumpadMultiply, 'NumpadMultiply');
|
||||
d(ScanCode.NumpadSubtract, 'NumpadSubtract');
|
||||
d(ScanCode.NumpadAdd, 'NumpadAdd');
|
||||
d(ScanCode.NumpadEnter, 'NumpadEnter');
|
||||
d(ScanCode.Numpad1, 'Numpad1');
|
||||
d(ScanCode.Numpad2, 'Numpad2');
|
||||
d(ScanCode.Numpad3, 'Numpad3');
|
||||
d(ScanCode.Numpad4, 'Numpad4');
|
||||
d(ScanCode.Numpad5, 'Numpad5');
|
||||
d(ScanCode.Numpad6, 'Numpad6');
|
||||
d(ScanCode.Numpad7, 'Numpad7');
|
||||
d(ScanCode.Numpad8, 'Numpad8');
|
||||
d(ScanCode.Numpad9, 'Numpad9');
|
||||
d(ScanCode.Numpad0, 'Numpad0');
|
||||
d(ScanCode.NumpadDecimal, 'NumpadDecimal');
|
||||
d(ScanCode.IntlBackslash, 'IntlBackslash');
|
||||
d(ScanCode.ContextMenu, 'ContextMenu');
|
||||
d(ScanCode.Power, 'Power');
|
||||
d(ScanCode.NumpadEqual, 'NumpadEqual');
|
||||
d(ScanCode.F13, 'F13');
|
||||
d(ScanCode.F14, 'F14');
|
||||
d(ScanCode.F15, 'F15');
|
||||
d(ScanCode.F16, 'F16');
|
||||
d(ScanCode.F17, 'F17');
|
||||
d(ScanCode.F18, 'F18');
|
||||
d(ScanCode.F19, 'F19');
|
||||
d(ScanCode.F20, 'F20');
|
||||
d(ScanCode.F21, 'F21');
|
||||
d(ScanCode.F22, 'F22');
|
||||
d(ScanCode.F23, 'F23');
|
||||
d(ScanCode.F24, 'F24');
|
||||
d(ScanCode.Open, 'Open');
|
||||
d(ScanCode.Help, 'Help');
|
||||
d(ScanCode.Select, 'Select');
|
||||
d(ScanCode.Again, 'Again');
|
||||
d(ScanCode.Undo, 'Undo');
|
||||
d(ScanCode.Cut, 'Cut');
|
||||
d(ScanCode.Copy, 'Copy');
|
||||
d(ScanCode.Paste, 'Paste');
|
||||
d(ScanCode.Find, 'Find');
|
||||
d(ScanCode.AudioVolumeMute, 'AudioVolumeMute');
|
||||
d(ScanCode.AudioVolumeUp, 'AudioVolumeUp');
|
||||
d(ScanCode.AudioVolumeDown, 'AudioVolumeDown');
|
||||
d(ScanCode.NumpadComma, 'NumpadComma');
|
||||
d(ScanCode.IntlRo, 'IntlRo');
|
||||
d(ScanCode.KanaMode, 'KanaMode');
|
||||
d(ScanCode.IntlYen, 'IntlYen');
|
||||
d(ScanCode.Convert, 'Convert');
|
||||
d(ScanCode.NonConvert, 'NonConvert');
|
||||
d(ScanCode.Lang1, 'Lang1');
|
||||
d(ScanCode.Lang2, 'Lang2');
|
||||
d(ScanCode.Lang3, 'Lang3');
|
||||
d(ScanCode.Lang4, 'Lang4');
|
||||
d(ScanCode.Lang5, 'Lang5');
|
||||
d(ScanCode.Abort, 'Abort');
|
||||
d(ScanCode.Props, 'Props');
|
||||
d(ScanCode.NumpadParenLeft, 'NumpadParenLeft');
|
||||
d(ScanCode.NumpadParenRight, 'NumpadParenRight');
|
||||
d(ScanCode.NumpadBackspace, 'NumpadBackspace');
|
||||
d(ScanCode.NumpadMemoryStore, 'NumpadMemoryStore');
|
||||
d(ScanCode.NumpadMemoryRecall, 'NumpadMemoryRecall');
|
||||
d(ScanCode.NumpadMemoryClear, 'NumpadMemoryClear');
|
||||
d(ScanCode.NumpadMemoryAdd, 'NumpadMemoryAdd');
|
||||
d(ScanCode.NumpadMemorySubtract, 'NumpadMemorySubtract');
|
||||
d(ScanCode.NumpadClear, 'NumpadClear');
|
||||
d(ScanCode.NumpadClearEntry, 'NumpadClearEntry');
|
||||
d(ScanCode.ControlLeft, 'ControlLeft');
|
||||
d(ScanCode.ShiftLeft, 'ShiftLeft');
|
||||
d(ScanCode.AltLeft, 'AltLeft');
|
||||
d(ScanCode.MetaLeft, 'MetaLeft');
|
||||
d(ScanCode.ControlRight, 'ControlRight');
|
||||
d(ScanCode.ShiftRight, 'ShiftRight');
|
||||
d(ScanCode.AltRight, 'AltRight');
|
||||
d(ScanCode.MetaRight, 'MetaRight');
|
||||
d(ScanCode.BrightnessUp, 'BrightnessUp');
|
||||
d(ScanCode.BrightnessDown, 'BrightnessDown');
|
||||
d(ScanCode.MediaPlay, 'MediaPlay');
|
||||
d(ScanCode.MediaRecord, 'MediaRecord');
|
||||
d(ScanCode.MediaFastForward, 'MediaFastForward');
|
||||
d(ScanCode.MediaRewind, 'MediaRewind');
|
||||
d(ScanCode.MediaTrackNext, 'MediaTrackNext');
|
||||
d(ScanCode.MediaTrackPrevious, 'MediaTrackPrevious');
|
||||
d(ScanCode.MediaStop, 'MediaStop');
|
||||
d(ScanCode.Eject, 'Eject');
|
||||
d(ScanCode.MediaPlayPause, 'MediaPlayPause');
|
||||
d(ScanCode.MediaSelect, 'MediaSelect');
|
||||
d(ScanCode.LaunchMail, 'LaunchMail');
|
||||
d(ScanCode.LaunchApp2, 'LaunchApp2');
|
||||
d(ScanCode.LaunchApp1, 'LaunchApp1');
|
||||
d(ScanCode.SelectTask, 'SelectTask');
|
||||
d(ScanCode.LaunchScreenSaver, 'LaunchScreenSaver');
|
||||
d(ScanCode.BrowserSearch, 'BrowserSearch');
|
||||
d(ScanCode.BrowserHome, 'BrowserHome');
|
||||
d(ScanCode.BrowserBack, 'BrowserBack');
|
||||
d(ScanCode.BrowserForward, 'BrowserForward');
|
||||
d(ScanCode.BrowserStop, 'BrowserStop');
|
||||
d(ScanCode.BrowserRefresh, 'BrowserRefresh');
|
||||
d(ScanCode.BrowserFavorites, 'BrowserFavorites');
|
||||
d(ScanCode.ZoomToggle, 'ZoomToggle');
|
||||
d(ScanCode.MailReply, 'MailReply');
|
||||
d(ScanCode.MailForward, 'MailForward');
|
||||
d(ScanCode.MailSend, 'MailSend');
|
||||
})();
|
||||
|
||||
(function () {
|
||||
for (let i = 0; i <= ScanCode.MAX_VALUE; i++) {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[i] = -1;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= KeyCode.MAX_VALUE; i++) {
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[i] = -1;
|
||||
}
|
||||
|
||||
function define(code: ScanCode, keyCode: KeyCode): void {
|
||||
IMMUTABLE_CODE_TO_KEY_CODE[code] = keyCode;
|
||||
|
||||
if (
|
||||
(keyCode !== KeyCode.Unknown)
|
||||
&& (keyCode !== KeyCode.Enter)
|
||||
&& (keyCode !== KeyCode.Ctrl)
|
||||
&& (keyCode !== KeyCode.Shift)
|
||||
&& (keyCode !== KeyCode.Alt)
|
||||
&& (keyCode !== KeyCode.Meta)
|
||||
) {
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[keyCode] = code;
|
||||
}
|
||||
}
|
||||
|
||||
// Manually added due to the exclusion above (due to duplication with NumpadEnter)
|
||||
IMMUTABLE_KEY_CODE_TO_CODE[KeyCode.Enter] = ScanCode.Enter;
|
||||
|
||||
define(ScanCode.None, KeyCode.Unknown);
|
||||
define(ScanCode.Hyper, KeyCode.Unknown);
|
||||
define(ScanCode.Super, KeyCode.Unknown);
|
||||
define(ScanCode.Fn, KeyCode.Unknown);
|
||||
define(ScanCode.FnLock, KeyCode.Unknown);
|
||||
define(ScanCode.Suspend, KeyCode.Unknown);
|
||||
define(ScanCode.Resume, KeyCode.Unknown);
|
||||
define(ScanCode.Turbo, KeyCode.Unknown);
|
||||
define(ScanCode.Sleep, KeyCode.Unknown);
|
||||
define(ScanCode.WakeUp, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyA, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyB, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyC, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyD, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyE, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyF, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyG, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyH, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyI, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyJ, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyK, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyL, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyM, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyN, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyO, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyP, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyQ, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyR, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyS, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyT, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyU, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyV, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyW, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyX, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyY, KeyCode.Unknown);
|
||||
// define(ScanCode.KeyZ, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit1, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit2, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit3, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit4, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit5, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit6, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit7, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit8, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit9, KeyCode.Unknown);
|
||||
// define(ScanCode.Digit0, KeyCode.Unknown);
|
||||
define(ScanCode.Enter, KeyCode.Enter);
|
||||
define(ScanCode.Escape, KeyCode.Escape);
|
||||
define(ScanCode.Backspace, KeyCode.Backspace);
|
||||
define(ScanCode.Tab, KeyCode.Tab);
|
||||
define(ScanCode.Space, KeyCode.Space);
|
||||
// define(ScanCode.Minus, KeyCode.Unknown);
|
||||
// define(ScanCode.Equal, KeyCode.Unknown);
|
||||
// define(ScanCode.BracketLeft, KeyCode.Unknown);
|
||||
// define(ScanCode.BracketRight, KeyCode.Unknown);
|
||||
// define(ScanCode.Backslash, KeyCode.Unknown);
|
||||
// define(ScanCode.IntlHash, KeyCode.Unknown);
|
||||
// define(ScanCode.Semicolon, KeyCode.Unknown);
|
||||
// define(ScanCode.Quote, KeyCode.Unknown);
|
||||
// define(ScanCode.Backquote, KeyCode.Unknown);
|
||||
// define(ScanCode.Comma, KeyCode.Unknown);
|
||||
// define(ScanCode.Period, KeyCode.Unknown);
|
||||
// define(ScanCode.Slash, KeyCode.Unknown);
|
||||
define(ScanCode.CapsLock, KeyCode.CapsLock);
|
||||
define(ScanCode.F1, KeyCode.F1);
|
||||
define(ScanCode.F2, KeyCode.F2);
|
||||
define(ScanCode.F3, KeyCode.F3);
|
||||
define(ScanCode.F4, KeyCode.F4);
|
||||
define(ScanCode.F5, KeyCode.F5);
|
||||
define(ScanCode.F6, KeyCode.F6);
|
||||
define(ScanCode.F7, KeyCode.F7);
|
||||
define(ScanCode.F8, KeyCode.F8);
|
||||
define(ScanCode.F9, KeyCode.F9);
|
||||
define(ScanCode.F10, KeyCode.F10);
|
||||
define(ScanCode.F11, KeyCode.F11);
|
||||
define(ScanCode.F12, KeyCode.F12);
|
||||
define(ScanCode.PrintScreen, KeyCode.Unknown);
|
||||
define(ScanCode.ScrollLock, KeyCode.ScrollLock);
|
||||
define(ScanCode.Pause, KeyCode.PauseBreak);
|
||||
define(ScanCode.Insert, KeyCode.Insert);
|
||||
define(ScanCode.Home, KeyCode.Home);
|
||||
define(ScanCode.PageUp, KeyCode.PageUp);
|
||||
define(ScanCode.Delete, KeyCode.Delete);
|
||||
define(ScanCode.End, KeyCode.End);
|
||||
define(ScanCode.PageDown, KeyCode.PageDown);
|
||||
define(ScanCode.ArrowRight, KeyCode.RightArrow);
|
||||
define(ScanCode.ArrowLeft, KeyCode.LeftArrow);
|
||||
define(ScanCode.ArrowDown, KeyCode.DownArrow);
|
||||
define(ScanCode.ArrowUp, KeyCode.UpArrow);
|
||||
define(ScanCode.NumLock, KeyCode.NumLock);
|
||||
define(ScanCode.NumpadDivide, KeyCode.NUMPAD_DIVIDE);
|
||||
define(ScanCode.NumpadMultiply, KeyCode.NUMPAD_MULTIPLY);
|
||||
define(ScanCode.NumpadSubtract, KeyCode.NUMPAD_SUBTRACT);
|
||||
define(ScanCode.NumpadAdd, KeyCode.NUMPAD_ADD);
|
||||
define(ScanCode.NumpadEnter, KeyCode.Enter); // Duplicate
|
||||
define(ScanCode.Numpad1, KeyCode.NUMPAD_1);
|
||||
define(ScanCode.Numpad2, KeyCode.NUMPAD_2);
|
||||
define(ScanCode.Numpad3, KeyCode.NUMPAD_3);
|
||||
define(ScanCode.Numpad4, KeyCode.NUMPAD_4);
|
||||
define(ScanCode.Numpad5, KeyCode.NUMPAD_5);
|
||||
define(ScanCode.Numpad6, KeyCode.NUMPAD_6);
|
||||
define(ScanCode.Numpad7, KeyCode.NUMPAD_7);
|
||||
define(ScanCode.Numpad8, KeyCode.NUMPAD_8);
|
||||
define(ScanCode.Numpad9, KeyCode.NUMPAD_9);
|
||||
define(ScanCode.Numpad0, KeyCode.NUMPAD_0);
|
||||
define(ScanCode.NumpadDecimal, KeyCode.NUMPAD_DECIMAL);
|
||||
// define(ScanCode.IntlBackslash, KeyCode.Unknown);
|
||||
define(ScanCode.ContextMenu, KeyCode.ContextMenu);
|
||||
define(ScanCode.Power, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadEqual, KeyCode.Unknown);
|
||||
define(ScanCode.F13, KeyCode.F13);
|
||||
define(ScanCode.F14, KeyCode.F14);
|
||||
define(ScanCode.F15, KeyCode.F15);
|
||||
define(ScanCode.F16, KeyCode.F16);
|
||||
define(ScanCode.F17, KeyCode.F17);
|
||||
define(ScanCode.F18, KeyCode.F18);
|
||||
define(ScanCode.F19, KeyCode.F19);
|
||||
define(ScanCode.F20, KeyCode.Unknown);
|
||||
define(ScanCode.F21, KeyCode.Unknown);
|
||||
define(ScanCode.F22, KeyCode.Unknown);
|
||||
define(ScanCode.F23, KeyCode.Unknown);
|
||||
define(ScanCode.F24, KeyCode.Unknown);
|
||||
define(ScanCode.Open, KeyCode.Unknown);
|
||||
define(ScanCode.Help, KeyCode.Unknown);
|
||||
define(ScanCode.Select, KeyCode.Unknown);
|
||||
define(ScanCode.Again, KeyCode.Unknown);
|
||||
define(ScanCode.Undo, KeyCode.Unknown);
|
||||
define(ScanCode.Cut, KeyCode.Unknown);
|
||||
define(ScanCode.Copy, KeyCode.Unknown);
|
||||
define(ScanCode.Paste, KeyCode.Unknown);
|
||||
define(ScanCode.Find, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeMute, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeUp, KeyCode.Unknown);
|
||||
define(ScanCode.AudioVolumeDown, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadComma, KeyCode.NUMPAD_SEPARATOR);
|
||||
// define(ScanCode.IntlRo, KeyCode.Unknown);
|
||||
define(ScanCode.KanaMode, KeyCode.Unknown);
|
||||
// define(ScanCode.IntlYen, KeyCode.Unknown);
|
||||
define(ScanCode.Convert, KeyCode.Unknown);
|
||||
define(ScanCode.NonConvert, KeyCode.Unknown);
|
||||
define(ScanCode.Lang1, KeyCode.Unknown);
|
||||
define(ScanCode.Lang2, KeyCode.Unknown);
|
||||
define(ScanCode.Lang3, KeyCode.Unknown);
|
||||
define(ScanCode.Lang4, KeyCode.Unknown);
|
||||
define(ScanCode.Lang5, KeyCode.Unknown);
|
||||
define(ScanCode.Abort, KeyCode.Unknown);
|
||||
define(ScanCode.Props, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadParenLeft, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadParenRight, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadBackspace, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryStore, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryRecall, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryClear, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemoryAdd, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadMemorySubtract, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadClear, KeyCode.Unknown);
|
||||
define(ScanCode.NumpadClearEntry, KeyCode.Unknown);
|
||||
define(ScanCode.ControlLeft, KeyCode.Ctrl); // Duplicate
|
||||
define(ScanCode.ShiftLeft, KeyCode.Shift); // Duplicate
|
||||
define(ScanCode.AltLeft, KeyCode.Alt); // Duplicate
|
||||
define(ScanCode.MetaLeft, KeyCode.Meta); // Duplicate
|
||||
define(ScanCode.ControlRight, KeyCode.Ctrl); // Duplicate
|
||||
define(ScanCode.ShiftRight, KeyCode.Shift); // Duplicate
|
||||
define(ScanCode.AltRight, KeyCode.Alt); // Duplicate
|
||||
define(ScanCode.MetaRight, KeyCode.Meta); // Duplicate
|
||||
define(ScanCode.BrightnessUp, KeyCode.Unknown);
|
||||
define(ScanCode.BrightnessDown, KeyCode.Unknown);
|
||||
define(ScanCode.MediaPlay, KeyCode.Unknown);
|
||||
define(ScanCode.MediaRecord, KeyCode.Unknown);
|
||||
define(ScanCode.MediaFastForward, KeyCode.Unknown);
|
||||
define(ScanCode.MediaRewind, KeyCode.Unknown);
|
||||
define(ScanCode.MediaTrackNext, KeyCode.Unknown);
|
||||
define(ScanCode.MediaTrackPrevious, KeyCode.Unknown);
|
||||
define(ScanCode.MediaStop, KeyCode.Unknown);
|
||||
define(ScanCode.Eject, KeyCode.Unknown);
|
||||
define(ScanCode.MediaPlayPause, KeyCode.Unknown);
|
||||
define(ScanCode.MediaSelect, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchMail, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchApp2, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchApp1, KeyCode.Unknown);
|
||||
define(ScanCode.SelectTask, KeyCode.Unknown);
|
||||
define(ScanCode.LaunchScreenSaver, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserSearch, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserHome, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserBack, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserForward, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserStop, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserRefresh, KeyCode.Unknown);
|
||||
define(ScanCode.BrowserFavorites, KeyCode.Unknown);
|
||||
define(ScanCode.ZoomToggle, KeyCode.Unknown);
|
||||
define(ScanCode.MailReply, KeyCode.Unknown);
|
||||
define(ScanCode.MailForward, KeyCode.Unknown);
|
||||
define(ScanCode.MailSend, KeyCode.Unknown);
|
||||
})();
|
||||
@@ -2,12 +2,11 @@
|
||||
* 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 { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export enum ScrollbarVisibility {
|
||||
export const enum ScrollbarVisibility {
|
||||
Auto = 1,
|
||||
Hidden = 2,
|
||||
Visible = 3
|
||||
@@ -183,7 +182,7 @@ export class Scrollable extends Disposable {
|
||||
private _smoothScrollDuration: number;
|
||||
private readonly _scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable;
|
||||
private _state: ScrollState;
|
||||
private _smoothScrolling: SmoothScrollingOperation;
|
||||
private _smoothScrolling: SmoothScrollingOperation | null;
|
||||
|
||||
private _onScroll = this._register(new Emitter<ScrollEvent>());
|
||||
public readonly onScroll: Event<ScrollEvent> = this._onScroll.event;
|
||||
@@ -301,6 +300,9 @@ export class Scrollable extends Disposable {
|
||||
}
|
||||
|
||||
private _performSmoothScrolling(): void {
|
||||
if (!this._smoothScrolling) {
|
||||
return;
|
||||
}
|
||||
const update = this._smoothScrolling.tick();
|
||||
const newState = this._state.withScrollPosition(update);
|
||||
|
||||
@@ -373,7 +375,7 @@ export class SmoothScrollingOperation {
|
||||
public to: ISmoothScrollPosition;
|
||||
public readonly duration: number;
|
||||
private readonly _startTime: number;
|
||||
public animationFrameDisposable: IDisposable;
|
||||
public animationFrameDisposable: IDisposable | null;
|
||||
|
||||
private scrollLeft: IAnimation;
|
||||
private scrollTop: IAnimation;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ISplice<T> {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 nls from 'vs/nls';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { globals } from 'vs/base/common/platform';
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
@@ -11,7 +10,7 @@ import { CharCode } from 'vs/base/common/charCode';
|
||||
*/
|
||||
export const empty = '';
|
||||
|
||||
export function isFalsyOrWhitespace(str: string): boolean {
|
||||
export function isFalsyOrWhitespace(str: string | undefined): boolean {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return true;
|
||||
}
|
||||
@@ -89,7 +88,7 @@ export function trim(haystack: string, needle: string = ' '): string {
|
||||
* @param haystack string to trim
|
||||
* @param needle the thing to trim
|
||||
*/
|
||||
export function ltrim(haystack?: string, needle?: string): string {
|
||||
export function ltrim(haystack: string, needle: string): string {
|
||||
if (!haystack || !needle) {
|
||||
return haystack;
|
||||
}
|
||||
@@ -99,10 +98,9 @@ export function ltrim(haystack?: string, needle?: string): string {
|
||||
return haystack;
|
||||
}
|
||||
|
||||
let offset = 0,
|
||||
idx = -1;
|
||||
let offset = 0;
|
||||
|
||||
while ((idx = haystack.indexOf(needle, offset)) === offset) {
|
||||
while (haystack.indexOf(needle, offset) === offset) {
|
||||
offset = offset + needleLen;
|
||||
}
|
||||
return haystack.substring(offset);
|
||||
@@ -113,7 +111,7 @@ export function ltrim(haystack?: string, needle?: string): string {
|
||||
* @param haystack string to trim
|
||||
* @param needle the thing to trim
|
||||
*/
|
||||
export function rtrim(haystack?: string, needle?: string): string {
|
||||
export function rtrim(haystack: string, needle: string): string {
|
||||
if (!haystack || !needle) {
|
||||
return haystack;
|
||||
}
|
||||
@@ -231,7 +229,7 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
|
||||
// We check against an empty string. If the regular expression doesn't advance
|
||||
// (e.g. ends in an endless loop) it will match an empty string.
|
||||
let match = regexp.exec('');
|
||||
return (match && <any>regexp.lastIndex === 0);
|
||||
return !!(match && <any>regexp.lastIndex === 0);
|
||||
}
|
||||
|
||||
export function regExpContainsBackreference(regexpValue: string): boolean {
|
||||
@@ -623,7 +621,7 @@ export function removeAnsiEscapeCodes(str: string): string {
|
||||
export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM);
|
||||
|
||||
export function startsWithUTF8BOM(str: string): boolean {
|
||||
return (str && str.length > 0 && str.charCodeAt(0) === CharCode.UTF8_BOM);
|
||||
return !!(str && str.length > 0 && str.charCodeAt(0) === CharCode.UTF8_BOM);
|
||||
}
|
||||
|
||||
export function stripUTF8BOM(str: string): string {
|
||||
@@ -685,3 +683,23 @@ export function containsUppercaseCharacter(target: string, ignoreEscapedChars =
|
||||
|
||||
return target.toLowerCase() !== target;
|
||||
}
|
||||
|
||||
export function uppercaseFirstLetter(str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
export function getNLines(str: string, n = 1): string {
|
||||
if (n === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let idx = -1;
|
||||
do {
|
||||
idx = str.indexOf('\n', idx + 1);
|
||||
n--;
|
||||
} while (n > 0 && idx >= 0);
|
||||
|
||||
return idx >= 0 ?
|
||||
str.substr(0, idx) :
|
||||
str;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
const _typeof = {
|
||||
number: 'number',
|
||||
@@ -83,14 +82,14 @@ export function isBoolean(obj: any): obj is boolean {
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): boolean {
|
||||
export function isUndefined(obj: any): obj is undefined {
|
||||
return typeof (obj) === _typeof.undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
export function isUndefinedOrNull(obj: any): boolean {
|
||||
export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
return isUndefined(obj) || obj === null;
|
||||
}
|
||||
|
||||
@@ -130,14 +129,14 @@ export function areFunctions(...objects: any[]): boolean {
|
||||
|
||||
export type TypeConstraint = string | Function;
|
||||
|
||||
export function validateConstraints(args: any[], constraints: TypeConstraint[]): void {
|
||||
export function validateConstraints(args: any[], constraints: (TypeConstraint | undefined)[]): void {
|
||||
const len = Math.min(args.length, constraints.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
validateConstraint(args[i], constraints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateConstraint(arg: any, constraint: TypeConstraint): void {
|
||||
export function validateConstraint(arg: any, constraint: TypeConstraint | undefined): void {
|
||||
|
||||
if (isString(constraint)) {
|
||||
if (typeof arg !== constraint) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 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 { isWindows } from 'vs/base/common/platform';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
@@ -11,7 +10,28 @@ const _schemePattern = /^\w[\w\d+.-]*$/;
|
||||
const _singleSlashStart = /^\//;
|
||||
const _doubleSlashStart = /^\/\//;
|
||||
|
||||
function _validateUri(ret: URI): void {
|
||||
let _throwOnMissingSchema: boolean = true;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function setUriThrowOnMissingScheme(value: boolean): boolean {
|
||||
const old = _throwOnMissingSchema;
|
||||
_throwOnMissingSchema = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
function _validateUri(ret: URI, _strict?: boolean): void {
|
||||
|
||||
// scheme, must be set
|
||||
if (!ret.scheme) {
|
||||
if (_strict || _throwOnMissingSchema) {
|
||||
throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
|
||||
} else {
|
||||
console.warn(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
|
||||
}
|
||||
}
|
||||
|
||||
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (ret.scheme && !_schemePattern.test(ret.scheme)) {
|
||||
@@ -63,7 +83,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
||||
|
||||
/**
|
||||
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
|
||||
* This class is a simple parser which creates the basic component paths
|
||||
* This class is a simple parser which creates the basic component parts
|
||||
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
||||
* and encoding.
|
||||
*
|
||||
@@ -74,10 +94,8 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
||||
* | _____________________|__
|
||||
* / \ / \
|
||||
* urn:example:animal:ferret:nose
|
||||
*
|
||||
*
|
||||
*/
|
||||
export default class URI implements UriComponents {
|
||||
export class URI implements UriComponents {
|
||||
|
||||
static isUri(thing: any): thing is URI {
|
||||
if (thing instanceof URI) {
|
||||
@@ -123,7 +141,7 @@ export default class URI implements UriComponents {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(scheme: string, authority: string, path: string, query: string, fragment: string);
|
||||
protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -133,7 +151,7 @@ export default class URI implements UriComponents {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) {
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean) {
|
||||
|
||||
if (typeof schemeOrData === 'object') {
|
||||
this.scheme = schemeOrData.scheme || _empty;
|
||||
@@ -151,7 +169,7 @@ export default class URI implements UriComponents {
|
||||
this.query = query || _empty;
|
||||
this.fragment = fragment || _empty;
|
||||
|
||||
_validateUri(this);
|
||||
_validateUri(this, _strict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,17 +177,38 @@ export default class URI implements UriComponents {
|
||||
|
||||
/**
|
||||
* Returns a string representing the corresponding file system path of this URI.
|
||||
* Will handle UNC paths and normalize windows drive letters to lower-case. Also
|
||||
* uses the platform specific path separator. Will *not* validate the path for
|
||||
* invalid characters and semantics. Will *not* look at the scheme of this URI.
|
||||
* Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
|
||||
* platform specific path separator.
|
||||
*
|
||||
* * Will *not* validate the path for invalid characters and semantics.
|
||||
* * Will *not* look at the scheme of this URI.
|
||||
* * The result shall *not* be used for display purposes but for accessing a file on disk.
|
||||
*
|
||||
*
|
||||
* The *difference* to `URI#path` is the use of the platform specific separator and the handling
|
||||
* of UNC paths. See the below sample of a file-uri with an authority (UNC path).
|
||||
*
|
||||
* ```ts
|
||||
const u = URI.parse('file://server/c$/folder/file.txt')
|
||||
u.authority === 'server'
|
||||
u.path === '/shares/c$/file.txt'
|
||||
u.fsPath === '\\server\c$\folder\file.txt'
|
||||
```
|
||||
*
|
||||
* Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
|
||||
* namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
|
||||
* with URIs that represent files on disk (`file` scheme).
|
||||
*/
|
||||
get fsPath(): string {
|
||||
// if (this.scheme !== 'file') {
|
||||
// console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
|
||||
// }
|
||||
return _makeFsPath(this);
|
||||
}
|
||||
|
||||
// ---- modify to new -------------------------
|
||||
|
||||
public with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
|
||||
public with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
|
||||
|
||||
if (!change) {
|
||||
return this;
|
||||
@@ -216,7 +255,13 @@ export default class URI implements UriComponents {
|
||||
|
||||
// ---- parse & validate ------------------------
|
||||
|
||||
public static parse(value: string): URI {
|
||||
/**
|
||||
* Creates a new URI from a string, e.g. `http://www.msft.com/some/path`,
|
||||
* `file:///usr/home`, or `scheme:with/path`.
|
||||
*
|
||||
* @param value A string which represents an URI (see `URI#toString`).
|
||||
*/
|
||||
public static parse(value: string, _strict: boolean = false): URI {
|
||||
const match = _regexp.exec(value);
|
||||
if (!match) {
|
||||
return new _URI(_empty, _empty, _empty, _empty, _empty);
|
||||
@@ -227,9 +272,31 @@ export default class URI implements UriComponents {
|
||||
decodeURIComponent(match[5] || _empty),
|
||||
decodeURIComponent(match[7] || _empty),
|
||||
decodeURIComponent(match[9] || _empty),
|
||||
_strict
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new URI from a file system path, e.g. `c:\my\files`,
|
||||
* `/usr/home`, or `\\server\share\some\path`.
|
||||
*
|
||||
* The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
|
||||
* as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
|
||||
* `URI.parse('file://' + path)` because the path might contain characters that are
|
||||
* interpreted (# and ?). See the following sample:
|
||||
* ```ts
|
||||
const good = URI.file('/coding/c#/project1');
|
||||
good.scheme === 'file';
|
||||
good.path === '/coding/c#/project1';
|
||||
good.fragment === '';
|
||||
const bad = URI.parse('file://' + '/coding/c#/project1');
|
||||
bad.scheme === 'file';
|
||||
bad.path === '/coding/c'; // path is now broken
|
||||
bad.fragment === '/project1';
|
||||
```
|
||||
*
|
||||
* @param path A file system path (see `URI#fsPath`)
|
||||
*/
|
||||
public static file(path: string): URI {
|
||||
|
||||
let authority = _empty;
|
||||
@@ -257,7 +324,7 @@ export default class URI implements UriComponents {
|
||||
return new _URI('file', authority, path, _empty, _empty);
|
||||
}
|
||||
|
||||
public static from(components: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
|
||||
public static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
|
||||
return new _URI(
|
||||
components.scheme,
|
||||
components.authority,
|
||||
@@ -270,6 +337,13 @@ export default class URI implements UriComponents {
|
||||
// ---- printing/externalize ---------------------------
|
||||
|
||||
/**
|
||||
* Creates a string representation for this URI. It's guaranteed that calling
|
||||
* `URI.parse` with the result of this function creates an URI which is equal
|
||||
* to this URI.
|
||||
*
|
||||
* * The result shall *not* be used for display purposes but for externalization or transport.
|
||||
* * The result will be encoded using the percentage encoding and encoding happens mostly
|
||||
* ignore the scheme-specific encoding rules.
|
||||
*
|
||||
* @param skipEncoding Do not encode the result, default is `false`
|
||||
*/
|
||||
@@ -313,8 +387,8 @@ interface UriState extends UriComponents {
|
||||
// tslint:disable-next-line:class-name
|
||||
class _URI extends URI {
|
||||
|
||||
_formatted: string = null;
|
||||
_fsPath: string = null;
|
||||
_formatted: string | null = null;
|
||||
_fsPath: string | null = null;
|
||||
|
||||
get fsPath(): string {
|
||||
if (!this._fsPath) {
|
||||
@@ -392,7 +466,7 @@ const encodeTable: { [ch: number]: string } = {
|
||||
};
|
||||
|
||||
function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): string {
|
||||
let res: string = undefined;
|
||||
let res: string | undefined = undefined;
|
||||
let nativeEncodePos = -1;
|
||||
|
||||
for (let pos = 0; pos < uriComponent.length; pos++) {
|
||||
@@ -453,7 +527,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
||||
}
|
||||
|
||||
function encodeURIComponentMinimal(path: string): string {
|
||||
let res: string = undefined;
|
||||
let res: string | undefined = undefined;
|
||||
for (let pos = 0; pos < path.length; pos++) {
|
||||
let code = path.charCodeAt(pos);
|
||||
if (code === CharCode.Hash || code === CharCode.QuestionMark) {
|
||||
|
||||
@@ -3,16 +3,22 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
export interface IURITransformer {
|
||||
transformIncoming(uri: UriComponents): UriComponents;
|
||||
transformOutgoing(uri: URI): URI;
|
||||
transformOutgoing(uri: UriComponents): UriComponents;
|
||||
}
|
||||
|
||||
export const DefaultURITransformer: IURITransformer = {
|
||||
transformIncoming: (uri: UriComponents) => uri,
|
||||
transformOutgoing: (uri: URI) => uri,
|
||||
export const DefaultURITransformer: IURITransformer = new class {
|
||||
transformIncoming(uri: UriComponents) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
transformOutgoing(uri: URI): URI;
|
||||
transformOutgoing(uri: UriComponents): UriComponents;
|
||||
transformOutgoing(uri: URI | UriComponents): URI | UriComponents {
|
||||
return uri;
|
||||
}
|
||||
};
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents a UUID as defined by rfc4122.
|
||||
|
||||
29
src/vs/base/common/winjs.base.d.ts
vendored
29
src/vs/base/common/winjs.base.d.ts
vendored
@@ -5,27 +5,14 @@
|
||||
/// Interfaces for WinJS
|
||||
|
||||
export type ErrorCallback = (error: any) => void;
|
||||
export type ProgressCallback<TProgress = any> = (progress: TProgress) => void;
|
||||
|
||||
export declare class Promise<T = any, TProgress = any> {
|
||||
constructor(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason: any) => void,
|
||||
progress: (progress: TProgress) => void) => void,
|
||||
oncancel?: () => void);
|
||||
export class Promise<T = any> {
|
||||
constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason: any) => void) => void);
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
|
||||
onprogress?: (progress: TProgress) => void): Promise<TResult1 | TResult2, TProgress>;
|
||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
||||
|
||||
public done(
|
||||
onfulfilled?: (value: T) => void,
|
||||
onrejected?: (reason: any) => void,
|
||||
onprogress?: (progress: TProgress) => void): void;
|
||||
|
||||
public cancel(): void;
|
||||
|
||||
public static as(value: null): Promise<null>;
|
||||
public static as(value: undefined): Promise<undefined>;
|
||||
@@ -33,15 +20,9 @@ export declare class Promise<T = any, TProgress = any> {
|
||||
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>;
|
||||
|
||||
public static timeout(delay: number): Promise<void>;
|
||||
|
||||
public static join<T1, T2>(promises: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
|
||||
public static join<T>(promises: (T | PromiseLike<T>)[]): Promise<T[]>;
|
||||
|
||||
public static any<T>(promises: (T | PromiseLike<T>)[]): Promise<{ key: string; value: Promise<T>; }>;
|
||||
|
||||
public static wrap<T>(value: T | PromiseLike<T>): Promise<T>;
|
||||
|
||||
public static wrapError<T = never>(error: Error): Promise<T>;
|
||||
@@ -56,9 +37,7 @@ export type TValueCallback<T = any> = (value: T | PromiseLike<T>) => void;
|
||||
|
||||
export {
|
||||
Promise as TPromise,
|
||||
Promise as PPromise,
|
||||
TValueCallback as ValueCallback,
|
||||
ProgressCallback as TProgressCallback
|
||||
TValueCallback as ValueCallback
|
||||
};
|
||||
|
||||
export interface IPromiseErrorDetail {
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
|
||||
import { Promise as WinJSPromise } from './winjs.base';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { isThenable } from 'vs/base/common/async';
|
||||
|
||||
function isWinJSPromise(candidate: any): candidate is WinJSPromise {
|
||||
return isThenable(candidate) && typeof (candidate as any).done === 'function';
|
||||
}
|
||||
|
||||
declare class WinJSPromiseRemovals {
|
||||
any<T=any>(promises: (T | PromiseLike<T>)[]): WinJSPromise<{ key: string; value: WinJSPromise<T>; }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A polyfill for the native promises. The implementation is based on
|
||||
@@ -28,7 +37,7 @@ export class PolyfillPromise<T = any> implements Promise<T> {
|
||||
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));
|
||||
return new PolyfillPromise((WinJSPromise as any as WinJSPromiseRemovals).any(thenables).then(entry => entry.value, err => err.value));
|
||||
}
|
||||
|
||||
static resolve(value): PolyfillPromise {
|
||||
@@ -45,7 +54,7 @@ export class PolyfillPromise<T = any> implements Promise<T> {
|
||||
constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any);
|
||||
constructor(initOrPromise: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) {
|
||||
|
||||
if (WinJSPromise.is(initOrPromise)) {
|
||||
if (isWinJSPromise(initOrPromise)) {
|
||||
this._winjsPromise = initOrPromise;
|
||||
} else {
|
||||
this._winjsPromise = new WinJSPromise((resolve, reject) => {
|
||||
@@ -70,19 +79,47 @@ export class PolyfillPromise<T = any> implements Promise<T> {
|
||||
|
||||
then(onFulfilled?: any, onRejected?: any): PolyfillPromise {
|
||||
let sync = true;
|
||||
// To support chaining, we need to return the value of the
|
||||
// onFulfilled and onRejected callback.
|
||||
// WinJSPromise supports a flat-map style #then, ie. the callbacks
|
||||
// passed to WinJSPromise#then can return a Promise.
|
||||
let promise = new PolyfillPromise(this._winjsPromise.then(
|
||||
onFulfilled && function (value) {
|
||||
if (!sync) {
|
||||
onFulfilled(value);
|
||||
return onFulfilled(value);
|
||||
} else {
|
||||
platform.setImmediate(() => onFulfilled(value));
|
||||
return new WinJSPromise((resolve, reject) => {
|
||||
platform.setImmediate(() => {
|
||||
let result;
|
||||
try {
|
||||
result = onFulfilled(value);
|
||||
}
|
||||
catch (err2) {
|
||||
reject(err2);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
onRejected && function (err) {
|
||||
if (!sync) {
|
||||
onRejected(err);
|
||||
return onRejected(err);
|
||||
} else {
|
||||
platform.setImmediate(() => onRejected(err));
|
||||
return new WinJSPromise((resolve, reject) => {
|
||||
platform.setImmediate(() => {
|
||||
let result;
|
||||
try {
|
||||
result = onRejected(err);
|
||||
}
|
||||
catch (err2) {
|
||||
reject(err2);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
* 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 { transformErrorForSerialization } from 'vs/base/common/errors';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ErrorCallback, TPromise, ValueCallback } from 'vs/base/common/winjs.base';
|
||||
import { ShallowCancelThenPromise } from 'vs/base/common/async';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise';
|
||||
|
||||
var global: any = self;
|
||||
|
||||
// When missing, polyfill the native promise
|
||||
// with our winjs-based polyfill
|
||||
if (typeof global.Promise === 'undefined') {
|
||||
global.Promise = PolyfillPromise;
|
||||
}
|
||||
|
||||
const INITIALIZE = '$initialize';
|
||||
|
||||
@@ -58,13 +64,13 @@ interface IReplyMessage extends IMessage {
|
||||
}
|
||||
|
||||
interface IMessageReply {
|
||||
c: ValueCallback;
|
||||
e: ErrorCallback;
|
||||
resolve: (value?: any) => void;
|
||||
reject: (error?: any) => void;
|
||||
}
|
||||
|
||||
interface IMessageHandler {
|
||||
sendMessage(msg: string): void;
|
||||
handleMessage(method: string, args: any[]): TPromise<any>;
|
||||
handleMessage(method: string, args: any[]): Promise<any>;
|
||||
}
|
||||
|
||||
class SimpleWorkerProtocol {
|
||||
@@ -85,28 +91,20 @@ class SimpleWorkerProtocol {
|
||||
this._workerId = workerId;
|
||||
}
|
||||
|
||||
public sendMessage(method: string, args: any[]): TPromise<any> {
|
||||
public sendMessage(method: string, args: any[]): Promise<any> {
|
||||
let req = String(++this._lastSentReq);
|
||||
let reply: IMessageReply = {
|
||||
c: null,
|
||||
e: null
|
||||
};
|
||||
let result = new TPromise<any>((c, e) => {
|
||||
reply.c = c;
|
||||
reply.e = e;
|
||||
}, () => {
|
||||
// Cancel not supported
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
this._pendingReplies[req] = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
this._send({
|
||||
vsWorker: this._workerId,
|
||||
req: req,
|
||||
method: method,
|
||||
args: args
|
||||
});
|
||||
});
|
||||
this._pendingReplies[req] = reply;
|
||||
|
||||
this._send({
|
||||
vsWorker: this._workerId,
|
||||
req: req,
|
||||
method: method,
|
||||
args: args
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public handleMessage(serializedMessage: string): void {
|
||||
@@ -115,6 +113,7 @@ class SimpleWorkerProtocol {
|
||||
message = JSON.parse(serializedMessage);
|
||||
} catch (e) {
|
||||
// nothing
|
||||
return;
|
||||
}
|
||||
if (!message || !message.vsWorker) {
|
||||
return;
|
||||
@@ -144,11 +143,11 @@ class SimpleWorkerProtocol {
|
||||
err.message = replyMessage.err.message;
|
||||
err.stack = replyMessage.err.stack;
|
||||
}
|
||||
reply.e(err);
|
||||
reply.reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
reply.c(replyMessage.res);
|
||||
reply.resolve(replyMessage.res);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,15 +188,14 @@ class SimpleWorkerProtocol {
|
||||
export class SimpleWorkerClient<T> extends Disposable {
|
||||
|
||||
private _worker: IWorker;
|
||||
private _onModuleLoaded: TPromise<string[]>;
|
||||
private _onModuleLoaded: Promise<string[]>;
|
||||
private _protocol: SimpleWorkerProtocol;
|
||||
private _lazyProxy: TPromise<T>;
|
||||
private _lazyProxy: Promise<T>;
|
||||
|
||||
constructor(workerFactory: IWorkerFactory, moduleId: string) {
|
||||
super();
|
||||
|
||||
let lazyProxyFulfill: (v: T) => void = null;
|
||||
let lazyProxyReject: (err: any) => void = null;
|
||||
let lazyProxyReject: ((err: any) => void) | null = null;
|
||||
|
||||
this._worker = this._register(workerFactory.create(
|
||||
'vs/base/common/worker/simpleWorker',
|
||||
@@ -207,7 +205,9 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
(err: any) => {
|
||||
// in Firefox, web workers fail lazily :(
|
||||
// we will reject the proxy
|
||||
lazyProxyReject(err);
|
||||
if (lazyProxyReject) {
|
||||
lazyProxyReject(err);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -215,9 +215,9 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
sendMessage: (msg: string): void => {
|
||||
this._worker.postMessage(msg);
|
||||
},
|
||||
handleMessage: (method: string, args: any[]): TPromise<any> => {
|
||||
handleMessage: (method: string, args: any[]): Promise<any> => {
|
||||
// Intentionally not supporting worker -> main requests
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
this._protocol.setWorkerId(this._worker.getId());
|
||||
@@ -232,34 +232,33 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
loaderConfiguration = (<any>self).requirejs.s.contexts._.config;
|
||||
}
|
||||
|
||||
this._lazyProxy = new TPromise<T>((c, e) => {
|
||||
lazyProxyFulfill = c;
|
||||
lazyProxyReject = e;
|
||||
}, () => { /* no cancel */ });
|
||||
|
||||
// Send initialize message
|
||||
this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [
|
||||
this._worker.getId(),
|
||||
moduleId,
|
||||
loaderConfiguration
|
||||
]);
|
||||
this._onModuleLoaded.then((availableMethods: string[]) => {
|
||||
let proxy = <T>{};
|
||||
for (let i = 0; i < availableMethods.length; i++) {
|
||||
(proxy as any)[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest);
|
||||
}
|
||||
lazyProxyFulfill(proxy);
|
||||
}, (e) => {
|
||||
lazyProxyReject(e);
|
||||
this._onError('Worker failed to load ' + moduleId, e);
|
||||
|
||||
this._lazyProxy = new Promise<T>((resolve, reject) => {
|
||||
lazyProxyReject = reject;
|
||||
this._onModuleLoaded.then((availableMethods: string[]) => {
|
||||
let proxy = <T>{};
|
||||
for (let i = 0; i < availableMethods.length; i++) {
|
||||
(proxy as any)[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest);
|
||||
}
|
||||
resolve(proxy);
|
||||
}, (e) => {
|
||||
reject(e);
|
||||
this._onError('Worker failed to load ' + moduleId, e);
|
||||
});
|
||||
});
|
||||
|
||||
// Create proxy to loaded code
|
||||
let proxyMethodRequest = (method: string, args: any[]): TPromise<any> => {
|
||||
let proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
|
||||
return this._request(method, args);
|
||||
};
|
||||
|
||||
let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => TPromise<any>): Function => {
|
||||
let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise<any>): Function => {
|
||||
return function () {
|
||||
let args = Array.prototype.slice.call(arguments, 0);
|
||||
return proxyMethodRequest(method, args);
|
||||
@@ -267,18 +266,15 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
public getProxyObject(): TPromise<T> {
|
||||
// Do not allow chaining promises to cancel the proxy creation
|
||||
return new ShallowCancelThenPromise(this._lazyProxy);
|
||||
public getProxyObject(): Promise<T> {
|
||||
return this._lazyProxy;
|
||||
}
|
||||
|
||||
private _request(method: string, args: any[]): TPromise<any> {
|
||||
return new TPromise<any>((c, e) => {
|
||||
private _request(method: string, args: any[]): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
this._onModuleLoaded.then(() => {
|
||||
this._protocol.sendMessage(method, args).then(c, e);
|
||||
}, e);
|
||||
}, () => {
|
||||
// Cancel intentionally not supported
|
||||
this._protocol.sendMessage(method, args).then(resolve, reject);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -298,16 +294,16 @@ export interface IRequestHandler {
|
||||
*/
|
||||
export class SimpleWorkerServer {
|
||||
|
||||
private _requestHandler: IRequestHandler;
|
||||
private _requestHandler: IRequestHandler | null;
|
||||
private _protocol: SimpleWorkerProtocol;
|
||||
|
||||
constructor(postSerializedMessage: (msg: string) => void, requestHandler: IRequestHandler) {
|
||||
constructor(postSerializedMessage: (msg: string) => void, requestHandler: IRequestHandler | null) {
|
||||
this._requestHandler = requestHandler;
|
||||
this._protocol = new SimpleWorkerProtocol({
|
||||
sendMessage: (msg: string): void => {
|
||||
postSerializedMessage(msg);
|
||||
},
|
||||
handleMessage: (method: string, args: any[]): TPromise<any> => this._handleMessage(method, args)
|
||||
handleMessage: (method: string, args: any[]): Promise<any> => this._handleMessage(method, args)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -315,23 +311,23 @@ export class SimpleWorkerServer {
|
||||
this._protocol.handleMessage(msg);
|
||||
}
|
||||
|
||||
private _handleMessage(method: string, args: any[]): TPromise<any> {
|
||||
private _handleMessage(method: string, args: any[]): Promise<any> {
|
||||
if (method === INITIALIZE) {
|
||||
return this.initialize(<number>args[0], <string>args[1], <any>args[2]);
|
||||
}
|
||||
|
||||
if (!this._requestHandler || typeof this._requestHandler[method] !== 'function') {
|
||||
return TPromise.wrapError(new Error('Missing requestHandler or method: ' + method));
|
||||
return Promise.reject(new Error('Missing requestHandler or method: ' + method));
|
||||
}
|
||||
|
||||
try {
|
||||
return TPromise.as(this._requestHandler[method].apply(this._requestHandler, args));
|
||||
return Promise.resolve(this._requestHandler[method].apply(this._requestHandler, args));
|
||||
} catch (e) {
|
||||
return TPromise.wrapError(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
private initialize(workerId: number, moduleId: string, loaderConfig: any): TPromise<any> {
|
||||
private initialize(workerId: number, moduleId: string, loaderConfig: any): Promise<string[]> {
|
||||
this._protocol.setWorkerId(workerId);
|
||||
|
||||
if (this._requestHandler) {
|
||||
@@ -342,7 +338,7 @@ export class SimpleWorkerServer {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
return TPromise.as(methods);
|
||||
return Promise.resolve(methods);
|
||||
}
|
||||
|
||||
if (loaderConfig) {
|
||||
@@ -361,29 +357,27 @@ export class SimpleWorkerServer {
|
||||
(<any>self).require.config(loaderConfig);
|
||||
}
|
||||
|
||||
let cc: ValueCallback;
|
||||
let ee: ErrorCallback;
|
||||
let r = new TPromise<any>((c, e) => {
|
||||
cc = c;
|
||||
ee = e;
|
||||
});
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
// Use the global require to be sure to get the global config
|
||||
(<any>self).require([moduleId], (...result: any[]) => {
|
||||
let handlerModule = result[0];
|
||||
this._requestHandler = handlerModule.create();
|
||||
|
||||
// Use the global require to be sure to get the global config
|
||||
(<any>self).require([moduleId], (...result: any[]) => {
|
||||
let handlerModule = result[0];
|
||||
this._requestHandler = handlerModule.create();
|
||||
|
||||
let methods: string[] = [];
|
||||
for (let prop in this._requestHandler) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
if (!this._requestHandler) {
|
||||
reject(new Error(`No RequestHandler!`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cc(methods);
|
||||
}, ee);
|
||||
let methods: string[] = [];
|
||||
for (let prop in this._requestHandler) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
resolve(methods);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user