Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -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
View 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;
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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.
*/

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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)
});
}

View File

@@ -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');

View File

@@ -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.

View File

@@ -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';

View File

@@ -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';

View 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;
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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');
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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';
}
}

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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];
}

View File

@@ -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);
}

View 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];
}
}

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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;
}
};

View File

@@ -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();
}

View File

@@ -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"
}]

View 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
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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()];
}
}

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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++;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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]);
}
};

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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];
}
}
}

View File

@@ -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 {

View File

@@ -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,

View 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;
}
}

View File

@@ -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;
}
}

View 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);
})();

View File

@@ -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;

View File

@@ -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> {

View File

@@ -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';

View File

@@ -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';

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;
}
};

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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);
});
});
}
}
));

View File

@@ -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);
});
}
}