mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -51,8 +51,8 @@ export function binarySearch<T>(array: ReadonlyArray<T>, key: T, comparator: (op
|
||||
high = array.length - 1;
|
||||
|
||||
while (low <= high) {
|
||||
let mid = ((low + high) / 2) | 0;
|
||||
let comp = comparator(array[mid], key);
|
||||
const mid = ((low + high) / 2) | 0;
|
||||
const comp = comparator(array[mid], key);
|
||||
if (comp < 0) {
|
||||
low = mid + 1;
|
||||
} else if (comp > 0) {
|
||||
@@ -75,7 +75,7 @@ export function findFirstInSorted<T>(array: ReadonlyArray<T>, p: (x: T) => boole
|
||||
return 0; // no children
|
||||
}
|
||||
while (low < high) {
|
||||
let mid = Math.floor((low + high) / 2);
|
||||
const mid = Math.floor((low + high) / 2);
|
||||
if (p(array[mid])) {
|
||||
high = mid;
|
||||
} else {
|
||||
@@ -122,7 +122,7 @@ function _sort<T>(a: T[], compare: Compare<T>, lo: number, hi: number, aux: T[])
|
||||
if (hi <= lo) {
|
||||
return;
|
||||
}
|
||||
let mid = lo + ((hi - lo) / 2) | 0;
|
||||
const mid = lo + ((hi - lo) / 2) | 0;
|
||||
_sort(a, compare, lo, mid, aux);
|
||||
_sort(a, compare, mid + 1, hi, aux);
|
||||
if (compare(a[mid], a[mid + 1]) <= 0) {
|
||||
@@ -393,6 +393,7 @@ export function firstIndex<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean)
|
||||
|
||||
export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T): T;
|
||||
export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean): T | null;
|
||||
export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T | null): T | null;
|
||||
export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null {
|
||||
const index = firstIndex(array, fn);
|
||||
return index < 0 ? notFoundValue : array[index];
|
||||
@@ -501,8 +502,8 @@ export function shuffle<T>(array: T[], _seed?: number): void {
|
||||
}
|
||||
|
||||
for (let i = array.length - 1; i > 0; i -= 1) {
|
||||
let j = Math.floor(rand() * (i + 1));
|
||||
let temp = array[i];
|
||||
const j = Math.floor(rand() * (i + 1));
|
||||
const temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
|
||||
@@ -45,14 +45,14 @@ export function createCancelablePromise<T>(callback: (token: CancellationToken)
|
||||
return this.then(undefined, reject);
|
||||
}
|
||||
finally(onfinally?: (() => void) | undefined | null): Promise<T> {
|
||||
return always(promise, onfinally || (() => { }));
|
||||
return promise.finally(onfinally);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function asPromise<T>(callback: () => T | Thenable<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
let item = callback();
|
||||
const item = callback();
|
||||
if (isThenable<T>(item)) {
|
||||
item.then(resolve, reject);
|
||||
} else {
|
||||
@@ -326,25 +326,6 @@ export function disposableTimeout(handler: () => void, timeout = 0): IDisposable
|
||||
return toDisposable(() => clearTimeout(timer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new promise that joins the provided promise. Upon completion of
|
||||
* 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 callback a function that will be call in the success and error case.
|
||||
*/
|
||||
export function always<T>(promise: Promise<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: Promise<T>): Promise<T | undefined> {
|
||||
return promise.then(undefined, _ => undefined);
|
||||
}
|
||||
@@ -707,12 +688,12 @@ declare function cancelIdleCallback(handle: number): void;
|
||||
|
||||
(function () {
|
||||
if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') {
|
||||
let dummyIdle: IdleDeadline = Object.freeze({
|
||||
const dummyIdle: IdleDeadline = Object.freeze({
|
||||
didTimeout: true,
|
||||
timeRemaining() { return 15; }
|
||||
});
|
||||
runWhenIdle = (runner) => {
|
||||
let handle = setTimeout(() => runner(dummyIdle));
|
||||
const handle = setTimeout(() => runner(dummyIdle));
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
@@ -726,7 +707,7 @@ declare function cancelIdleCallback(handle: number): void;
|
||||
};
|
||||
} else {
|
||||
runWhenIdle = (runner, timeout?) => {
|
||||
let handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
|
||||
const handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { always } from 'vs/base/common/async';
|
||||
|
||||
export interface CacheResult<T> {
|
||||
promise: Promise<T>;
|
||||
@@ -23,7 +22,7 @@ export class Cache<T> {
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
const promise = this.task(cts.token);
|
||||
always(promise, () => cts.dispose());
|
||||
promise.finally(() => cts.dispose());
|
||||
|
||||
this.result = {
|
||||
promise,
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface CancellationToken {
|
||||
}
|
||||
|
||||
const shortcutEvent = Object.freeze(function (callback, context?): IDisposable {
|
||||
let handle = setTimeout(callback.bind(context), 0);
|
||||
const handle = setTimeout(callback.bind(context), 0);
|
||||
return { dispose() { clearTimeout(handle); } };
|
||||
} as Event<any>);
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ export class Color {
|
||||
const thisA = this.rgba.a;
|
||||
const colorA = rgba.a;
|
||||
|
||||
let a = thisA + colorA * (1 - thisA);
|
||||
const a = thisA + colorA * (1 - thisA);
|
||||
if (a < 1e-6) {
|
||||
return Color.transparent;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
|
||||
let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>;
|
||||
@@ -116,8 +116,8 @@ function comparePathComponents(one: string, other: string, caseSensitive = false
|
||||
}
|
||||
|
||||
export function comparePaths(one: string, other: string, caseSensitive = false): number {
|
||||
const oneParts = one.split(paths.nativeSep);
|
||||
const otherParts = other.split(paths.nativeSep);
|
||||
const oneParts = one.split(sep);
|
||||
const otherParts = other.split(sep);
|
||||
|
||||
const lastOne = oneParts.length - 1;
|
||||
const lastOther = otherParts.length - 1;
|
||||
@@ -144,8 +144,8 @@ export function comparePaths(one: string, other: string, caseSensitive = false):
|
||||
}
|
||||
|
||||
export function compareAnything(one: string, other: string, lookFor: string): number {
|
||||
let elementAName = one.toLowerCase();
|
||||
let elementBName = other.toLowerCase();
|
||||
const elementAName = one.toLowerCase();
|
||||
const elementBName = other.toLowerCase();
|
||||
|
||||
// Sort prefix matches over non prefix matches
|
||||
const prefixCompare = compareByPrefix(one, other, lookFor);
|
||||
@@ -154,14 +154,14 @@ export function compareAnything(one: string, other: string, lookFor: string): nu
|
||||
}
|
||||
|
||||
// Sort suffix matches over non suffix matches
|
||||
let elementASuffixMatch = strings.endsWith(elementAName, lookFor);
|
||||
let elementBSuffixMatch = strings.endsWith(elementBName, lookFor);
|
||||
const elementASuffixMatch = strings.endsWith(elementAName, lookFor);
|
||||
const elementBSuffixMatch = strings.endsWith(elementBName, lookFor);
|
||||
if (elementASuffixMatch !== elementBSuffixMatch) {
|
||||
return elementASuffixMatch ? -1 : 1;
|
||||
}
|
||||
|
||||
// Understand file names
|
||||
let r = compareFileNames(elementAName, elementBName);
|
||||
const r = compareFileNames(elementAName, elementBName);
|
||||
if (r !== 0) {
|
||||
return r;
|
||||
}
|
||||
@@ -171,12 +171,12 @@ export function compareAnything(one: string, other: string, lookFor: string): nu
|
||||
}
|
||||
|
||||
export function compareByPrefix(one: string, other: string, lookFor: string): number {
|
||||
let elementAName = one.toLowerCase();
|
||||
let elementBName = other.toLowerCase();
|
||||
const elementAName = one.toLowerCase();
|
||||
const elementBName = other.toLowerCase();
|
||||
|
||||
// Sort prefix matches over non prefix matches
|
||||
let elementAPrefixMatch = strings.startsWith(elementAName, lookFor);
|
||||
let elementBPrefixMatch = strings.startsWith(elementBName, lookFor);
|
||||
const elementAPrefixMatch = strings.startsWith(elementAName, lookFor);
|
||||
const elementBPrefixMatch = strings.startsWith(elementBName, lookFor);
|
||||
if (elementAPrefixMatch !== elementBPrefixMatch) {
|
||||
return elementAPrefixMatch ? -1 : 1;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export function transformErrorForSerialization(error: any): any;
|
||||
export function transformErrorForSerialization(error: any): any {
|
||||
if (error instanceof Error) {
|
||||
let { name, message } = error;
|
||||
let stack: string = (<any>error).stacktrace || (<any>error).stack;
|
||||
const stack: string = (<any>error).stacktrace || (<any>error).stack;
|
||||
return {
|
||||
$isError: true,
|
||||
name,
|
||||
@@ -146,7 +146,7 @@ export function isPromiseCanceledError(error: any): boolean {
|
||||
* Returns an error that signals cancellation.
|
||||
*/
|
||||
export function canceled(): Error {
|
||||
let error = new Error(canceledName);
|
||||
const error = new Error(canceledName);
|
||||
error.name = error.message;
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ export namespace Event {
|
||||
return (listener, thisArgs = null, disposables?) => {
|
||||
// we need this, in case the event fires during the listener call
|
||||
let didFire = false;
|
||||
|
||||
const result = event(e => {
|
||||
let result: IDisposable;
|
||||
result = event(e => {
|
||||
if (didFire) {
|
||||
return;
|
||||
} else if (result) {
|
||||
@@ -53,7 +53,7 @@ export namespace Event {
|
||||
* throught the mapping function.
|
||||
*/
|
||||
export function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
|
||||
return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +61,7 @@ export namespace Event {
|
||||
* the `each` function per each element.
|
||||
*/
|
||||
export function forEach<I>(event: Event<I>, each: (i: I) => void): Event<I> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables);
|
||||
return snapshot((listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ export namespace Event {
|
||||
export function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T>;
|
||||
export function filter<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R): Event<R>;
|
||||
export function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
|
||||
return snapshot((listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,6 +102,25 @@ export namespace Event {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a chain of event processing functions (filter, map, etc), each
|
||||
* function will be invoked per event & per listener. Snapshotting an event
|
||||
* chain allows each function to be invoked just once per event.
|
||||
*/
|
||||
export function snapshot<T>(event: Event<T>): Event<T> {
|
||||
let listener: IDisposable;
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd() {
|
||||
listener = event(emitter.fire, emitter);
|
||||
},
|
||||
onLastListenerRemove() {
|
||||
listener.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debounces the provided event, given a `merge` function.
|
||||
*
|
||||
@@ -133,7 +152,7 @@ export namespace Event {
|
||||
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => {
|
||||
let _output = output;
|
||||
const _output = output;
|
||||
output = undefined;
|
||||
handle = undefined;
|
||||
if (!leading || numDebouncedCalls > 1) {
|
||||
@@ -171,7 +190,7 @@ export namespace Event {
|
||||
let cache: T;
|
||||
|
||||
return filter(event, value => {
|
||||
let shouldEmit = firstCall || value !== cache;
|
||||
const shouldEmit = firstCall || value !== cache;
|
||||
firstCall = false;
|
||||
cache = value;
|
||||
return shouldEmit;
|
||||
@@ -261,7 +280,7 @@ export namespace Event {
|
||||
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) {
|
||||
onListenerDidAdd(emitter: Emitter<T>, listener: (e: T) => any, thisArgs?: any) {
|
||||
if (nextTick) {
|
||||
setTimeout(() => flush(listener, thisArgs));
|
||||
} else {
|
||||
@@ -286,36 +305,34 @@ export namespace Event {
|
||||
|
||||
class ChainableEvent<T> implements IChainableEvent<T> {
|
||||
|
||||
get event(): Event<T> { return this._event; }
|
||||
|
||||
constructor(private _event: Event<T>) { }
|
||||
constructor(readonly event: Event<T>) { }
|
||||
|
||||
map<O>(fn: (i: T) => O): IChainableEvent<O> {
|
||||
return new ChainableEvent(map(this._event, fn));
|
||||
return new ChainableEvent(map(this.event, fn));
|
||||
}
|
||||
|
||||
forEach(fn: (i: T) => void): IChainableEvent<T> {
|
||||
return new ChainableEvent(forEach(this._event, fn));
|
||||
return new ChainableEvent(forEach(this.event, fn));
|
||||
}
|
||||
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T> {
|
||||
return new ChainableEvent(filter(this._event, fn));
|
||||
return new ChainableEvent(filter(this.event, fn));
|
||||
}
|
||||
|
||||
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R> {
|
||||
return new ChainableEvent(reduce(this._event, merge, initial));
|
||||
return new ChainableEvent(reduce(this.event, merge, initial));
|
||||
}
|
||||
|
||||
latch(): IChainableEvent<T> {
|
||||
return new ChainableEvent(latch(this._event));
|
||||
return new ChainableEvent(latch(this.event));
|
||||
}
|
||||
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return this._event(listener, thisArgs, disposables);
|
||||
return this.event(listener, thisArgs, disposables);
|
||||
}
|
||||
|
||||
once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return once(this._event)(listener, thisArgs, disposables);
|
||||
return once(this.event)(listener, thisArgs, disposables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +389,7 @@ export interface EmitterOptions {
|
||||
|
||||
let _globalLeakWarningThreshold = -1;
|
||||
export function setGlobalLeakWarningThreshold(n: number): IDisposable {
|
||||
let oldValue = _globalLeakWarningThreshold;
|
||||
const oldValue = _globalLeakWarningThreshold;
|
||||
_globalLeakWarningThreshold = n;
|
||||
return {
|
||||
dispose() {
|
||||
@@ -411,8 +428,8 @@ class LeakageMonitor {
|
||||
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);
|
||||
const stack = new Error().stack!.split('\n').slice(3).join('\n');
|
||||
const count = (this._stacks.get(stack) || 0);
|
||||
this._stacks.set(stack, count + 1);
|
||||
this._warnCountdown -= 1;
|
||||
|
||||
@@ -436,7 +453,7 @@ class LeakageMonitor {
|
||||
}
|
||||
|
||||
return () => {
|
||||
let count = (this._stacks!.get(stack) || 0);
|
||||
const count = (this._stacks!.get(stack) || 0);
|
||||
this._stacks!.set(stack, count - 1);
|
||||
};
|
||||
}
|
||||
@@ -610,7 +627,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
}
|
||||
|
||||
for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
|
||||
let thenables: Promise<void>[] = [];
|
||||
const thenables: Promise<void>[] = [];
|
||||
this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,152 +6,19 @@
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { startsWithIgnoreCase, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
/**
|
||||
* The forward slash path separator.
|
||||
*/
|
||||
export const sep = '/';
|
||||
|
||||
/**
|
||||
* The native path separator depending on the OS.
|
||||
*/
|
||||
export const nativeSep = isWindows ? '\\' : '/';
|
||||
|
||||
import { sep, posix } from 'vs/base/common/path';
|
||||
|
||||
function isPathSeparator(code: number) {
|
||||
return code === CharCode.Slash || code === CharCode.Backslash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path the path to get the dirname from
|
||||
* @param separator the separator to use
|
||||
* @returns the directory name of a path.
|
||||
* '.' is returned for empty paths or single segment relative paths (as done by NodeJS)
|
||||
* For paths consisting only of a root, the input path is returned
|
||||
* Takes a Windows OS path and changes backward slashes to forward slashes.
|
||||
* This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
|
||||
* Using it on a Linux or MaxOS path might change it.
|
||||
*/
|
||||
export function dirname(path: string, separator = nativeSep): string {
|
||||
const len = path.length;
|
||||
if (len === 0) {
|
||||
return '.';
|
||||
} else if (len === 1) {
|
||||
return isPathSeparator(path.charCodeAt(0)) ? path : '.';
|
||||
}
|
||||
const root = getRoot(path, separator);
|
||||
let rootLength = root.length;
|
||||
if (rootLength >= len) {
|
||||
return path; // matched the root
|
||||
}
|
||||
if (rootLength === 0 && isPathSeparator(path.charCodeAt(0))) {
|
||||
rootLength = 1; // absolute paths stay absolute paths.
|
||||
}
|
||||
|
||||
let i = len - 1;
|
||||
if (i > rootLength) {
|
||||
i--; // no need to look at the last character. If it's a trailing slash, we ignore it.
|
||||
while (i > rootLength && !isPathSeparator(path.charCodeAt(i))) {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (i === 0) {
|
||||
return '.'; // it was a relative path with a single segment, no root. Nodejs returns '.' here.
|
||||
}
|
||||
return path.substr(0, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the base name of a path.
|
||||
*/
|
||||
export function basename(path: string): string {
|
||||
const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
|
||||
if (idx === 0) {
|
||||
return path;
|
||||
} else if (~idx === path.length - 1) {
|
||||
return basename(path.substring(0, path.length - 1));
|
||||
} else {
|
||||
return path.substr(~idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `.far` from `boo.far` or the empty string.
|
||||
*/
|
||||
export function extname(path: string): string {
|
||||
path = basename(path);
|
||||
const idx = ~path.lastIndexOf('.');
|
||||
return idx ? path.substring(~idx) : '';
|
||||
}
|
||||
|
||||
const _posixBadPath = /(\/\.\.?\/)|(\/\.\.?)$|^(\.\.?\/)|(\/\/+)|(\\)/;
|
||||
const _winBadPath = /(\\\.\.?\\)|(\\\.\.?)$|^(\.\.?\\)|(\\\\+)|(\/)/;
|
||||
|
||||
function _isNormal(path: string, win: boolean): boolean {
|
||||
return win
|
||||
? !_winBadPath.test(path)
|
||||
: !_posixBadPath.test(path);
|
||||
}
|
||||
|
||||
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 === undefined) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const len = path.length;
|
||||
if (len === 0) {
|
||||
return '.';
|
||||
}
|
||||
|
||||
const wantsBackslash = !!(isWindows && toOSPath);
|
||||
if (_isNormal(path, wantsBackslash)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const sep = wantsBackslash ? '\\' : '/';
|
||||
const root = getRoot(path, sep);
|
||||
|
||||
// skip the root-portion of the path
|
||||
let start = root.length;
|
||||
let skip = false;
|
||||
let res = '';
|
||||
|
||||
for (let end = root.length; end <= len; end++) {
|
||||
|
||||
// either at the end or at a path-separator character
|
||||
if (end === len || isPathSeparator(path.charCodeAt(end))) {
|
||||
|
||||
if (streql(path, start, end, '..')) {
|
||||
// skip current and remove parent (if there is already something)
|
||||
let prev_start = res.lastIndexOf(sep);
|
||||
let prev_part = res.slice(prev_start + 1);
|
||||
if ((root || prev_part.length > 0) && prev_part !== '..') {
|
||||
res = prev_start === -1 ? '' : res.slice(0, prev_start);
|
||||
skip = true;
|
||||
}
|
||||
} else if (streql(path, start, end, '.') && (root || res || end < len - 1)) {
|
||||
// skip current (if there is already something or if there is more to come)
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
let part = path.slice(start, end);
|
||||
if (res !== '' && res[res.length - 1] !== sep) {
|
||||
res += sep;
|
||||
}
|
||||
res += part;
|
||||
}
|
||||
start = end + 1;
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
return root + res;
|
||||
}
|
||||
|
||||
function streql(value: string, start: number, end: number, other: string): boolean {
|
||||
return start + other.length === end && value.indexOf(other, start) === start;
|
||||
export function toSlashes(osPath: string) {
|
||||
return osPath.replace(/[\\/]/g, posix.sep);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,13 +26,13 @@ function streql(value: string, start: number, end: number, other: string): boole
|
||||
* `getRoot('files:///files/path') === files:///`,
|
||||
* or `getRoot('\\server\shares\path') === \\server\shares\`
|
||||
*/
|
||||
export function getRoot(path: string, sep: string = '/'): string {
|
||||
export function getRoot(path: string, sep: string = posix.sep): string {
|
||||
|
||||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let len = path.length;
|
||||
const len = path.length;
|
||||
const firstLetter = path.charCodeAt(0);
|
||||
if (isPathSeparator(firstLetter)) {
|
||||
if (isPathSeparator(path.charCodeAt(1))) {
|
||||
@@ -173,7 +40,7 @@ export function getRoot(path: string, sep: string = '/'): string {
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
if (!isPathSeparator(path.charCodeAt(2))) {
|
||||
let pos = 3;
|
||||
let start = pos;
|
||||
const start = pos;
|
||||
for (; pos < len; pos++) {
|
||||
if (isPathSeparator(path.charCodeAt(pos))) {
|
||||
break;
|
||||
@@ -227,33 +94,6 @@ export function getRoot(path: string, sep: string = '/'): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
export const join: (...parts: string[]) => string = function () {
|
||||
// Not using a function with var-args because of how TS compiles
|
||||
// them to JS - it would result in 2*n runtime cost instead
|
||||
// of 1*n, where n is parts.length.
|
||||
|
||||
let value = '';
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
let part = arguments[i];
|
||||
if (i > 0) {
|
||||
// add the separater between two parts unless
|
||||
// there already is one
|
||||
let last = value.charCodeAt(value.length - 1);
|
||||
if (!isPathSeparator(last)) {
|
||||
let next = part.charCodeAt(0);
|
||||
if (!isPathSeparator(next)) {
|
||||
|
||||
value += sep;
|
||||
}
|
||||
}
|
||||
}
|
||||
value += part;
|
||||
}
|
||||
|
||||
return normalize(value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if the path follows this pattern: `\\hostname\sharename`.
|
||||
*
|
||||
@@ -281,7 +121,7 @@ export function isUNC(path: string): boolean {
|
||||
return false;
|
||||
}
|
||||
let pos = 2;
|
||||
let start = pos;
|
||||
const start = pos;
|
||||
for (; pos < path.length; pos++) {
|
||||
code = path.charCodeAt(pos);
|
||||
if (code === CharCode.Backslash) {
|
||||
@@ -327,6 +167,10 @@ export function isValidBasename(name: string | null | undefined): boolean {
|
||||
return false; // Windows: file cannot end with a whitespace
|
||||
}
|
||||
|
||||
if (name.length > 255) {
|
||||
return false; // most file systems do not allow files > 255 lenth
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -343,7 +187,7 @@ export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boo
|
||||
return equalsIgnoreCase(pathA, pathB);
|
||||
}
|
||||
|
||||
export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = nativeSep): boolean {
|
||||
export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = sep): boolean {
|
||||
if (path === candidate) {
|
||||
return true;
|
||||
}
|
||||
@@ -381,39 +225,6 @@ export function isEqualOrParent(path: string, candidate: string, ignoreCase?: bo
|
||||
return path.indexOf(candidate) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from Node's path.isAbsolute functions
|
||||
*/
|
||||
export function isAbsolute(path: string): boolean {
|
||||
return isWindows ?
|
||||
isAbsolute_win32(path) :
|
||||
isAbsolute_posix(path);
|
||||
}
|
||||
|
||||
export function isAbsolute_win32(path: string): boolean {
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char0 = path.charCodeAt(0);
|
||||
if (isPathSeparator(char0)) {
|
||||
return true;
|
||||
} else if (isWindowsDriveLetter(char0)) {
|
||||
if (path.length > 2 && path.charCodeAt(1) === CharCode.Colon) {
|
||||
const char2 = path.charCodeAt(2);
|
||||
if (isPathSeparator(char2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isAbsolute_posix(path: string): boolean {
|
||||
return !!(path && path.charCodeAt(0) === CharCode.Slash);
|
||||
}
|
||||
|
||||
export function isWindowsDriveLetter(char0: number): boolean {
|
||||
return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export interface IMatch {
|
||||
export function or(...filter: IFilter[]): IFilter {
|
||||
return function (word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
for (let i = 0, len = filter.length; i < len; i++) {
|
||||
let match = filter[i](word, wordToMatchAgainst);
|
||||
const match = filter[i](word, wordToMatchAgainst);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
@@ -64,7 +64,7 @@ function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: s
|
||||
// Contiguous Substring
|
||||
|
||||
export function matchesContiguousSubString(word: string, wordToMatchAgainst: string): IMatch[] | null {
|
||||
let index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
|
||||
const index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ function join(head: IMatch, tail: IMatch[]): IMatch[] {
|
||||
|
||||
function nextAnchor(camelCaseWord: string, start: number): number {
|
||||
for (let i = start; i < camelCaseWord.length; i++) {
|
||||
let c = camelCaseWord.charCodeAt(i);
|
||||
const c = camelCaseWord.charCodeAt(i);
|
||||
if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
@@ -184,10 +184,10 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis {
|
||||
if (isNumber(code)) { numeric++; }
|
||||
}
|
||||
|
||||
let upperPercent = upper / word.length;
|
||||
let lowerPercent = lower / word.length;
|
||||
let alphaPercent = alpha / word.length;
|
||||
let numericPercent = numeric / word.length;
|
||||
const upperPercent = upper / word.length;
|
||||
const lowerPercent = lower / word.length;
|
||||
const alphaPercent = alpha / word.length;
|
||||
const numericPercent = numeric / word.length;
|
||||
|
||||
return { upperPercent, lowerPercent, alphaPercent, numericPercent };
|
||||
}
|
||||
@@ -307,7 +307,7 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti
|
||||
|
||||
function nextWord(word: string, start: number): number {
|
||||
for (let i = start; i < word.length; i++) {
|
||||
let c = word.charCodeAt(i);
|
||||
const c = word.charCodeAt(i);
|
||||
if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
@@ -334,7 +334,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
|
||||
}
|
||||
|
||||
// RegExp Filter
|
||||
let match = regexp.exec(wordToMatchAgainst);
|
||||
const match = regexp.exec(wordToMatchAgainst);
|
||||
if (match) {
|
||||
return [{ start: match.index, end: match.index + match[0].length }];
|
||||
}
|
||||
@@ -348,7 +348,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
|
||||
* powerfull than `matchesFuzzy`
|
||||
*/
|
||||
export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null {
|
||||
let score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true);
|
||||
const score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true);
|
||||
return score ? createMatches(score) : null;
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ function initTable() {
|
||||
row.push(-i);
|
||||
}
|
||||
for (let i = 0; i <= _maxLen; i++) {
|
||||
let thisRow = row.slice(0);
|
||||
const thisRow = row.slice(0);
|
||||
thisRow[0] = -i;
|
||||
table.push(thisRow);
|
||||
}
|
||||
@@ -566,9 +566,9 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb
|
||||
|
||||
_scores[patternPos][wordPos] = score;
|
||||
|
||||
let diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score);
|
||||
let top = _table[patternPos - 1][wordPos] + -1;
|
||||
let left = _table[patternPos][wordPos - 1] + -1;
|
||||
const diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score);
|
||||
const top = _table[patternPos - 1][wordPos] + -1;
|
||||
const left = _table[patternPos][wordPos - 1] + -1;
|
||||
|
||||
if (left >= top) {
|
||||
// left or diag
|
||||
@@ -635,8 +635,8 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma
|
||||
|
||||
while (patternPos > _patternStartPos && wordPos > 0) {
|
||||
|
||||
let score = _scores[patternPos][wordPos];
|
||||
let arrow = _arrows[patternPos][wordPos];
|
||||
const score = _scores[patternPos][wordPos];
|
||||
const arrow = _arrows[patternPos][wordPos];
|
||||
|
||||
if (arrow === Arrow.Left) {
|
||||
// left -> no match, skip a word character
|
||||
@@ -733,11 +733,11 @@ function fuzzyScoreWithPermutations(pattern: string, lowPattern: string, pattern
|
||||
// permutations of the pattern to find a better match. The
|
||||
// permutations only swap neighbouring characters, e.g
|
||||
// `cnoso` becomes `conso`, `cnsoo`, `cnoos`.
|
||||
let tries = Math.min(7, pattern.length - 1);
|
||||
const tries = Math.min(7, pattern.length - 1);
|
||||
for (let movingPatternPos = patternPos + 1; movingPatternPos < tries; movingPatternPos++) {
|
||||
let newPattern = nextTypoPermutation(pattern, movingPatternPos);
|
||||
const newPattern = nextTypoPermutation(pattern, movingPatternPos);
|
||||
if (newPattern) {
|
||||
let candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak);
|
||||
const candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak);
|
||||
if (candidate) {
|
||||
candidate[0] -= 3; // permutation penalty
|
||||
if (!top || candidate[0] > top[0]) {
|
||||
@@ -757,8 +757,8 @@ function nextTypoPermutation(pattern: string, patternPos: number): string | unde
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let swap1 = pattern[patternPos];
|
||||
let swap2 = pattern[patternPos + 1];
|
||||
const swap1 = pattern[patternPos];
|
||||
const swap2 = pattern[patternPos + 1];
|
||||
|
||||
if (swap1 === swap2) {
|
||||
return undefined;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
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 * as extpath from 'vs/base/common/extpath';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { isThenable } from 'vs/base/common/async';
|
||||
@@ -17,7 +18,6 @@ export interface IExpression {
|
||||
export interface IRelativePattern {
|
||||
base: string;
|
||||
pattern: string;
|
||||
pathToRelative(from: string, to: string): string;
|
||||
}
|
||||
|
||||
export function getEmptyExpression(): IExpression {
|
||||
@@ -53,7 +53,7 @@ export function splitGlobAware(pattern: string, splitChar: string): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
let segments: string[] = [];
|
||||
const segments: string[] = [];
|
||||
|
||||
let inBraces = false;
|
||||
let inBrackets = false;
|
||||
@@ -102,7 +102,7 @@ function parseRegExp(pattern: string): string {
|
||||
let regEx = '';
|
||||
|
||||
// Split up into segments for each slash found
|
||||
let segments = splitGlobAware(pattern, GLOB_SPLIT);
|
||||
const segments = splitGlobAware(pattern, GLOB_SPLIT);
|
||||
|
||||
// Special case where we only have globstars
|
||||
if (segments.every(s => s === GLOBSTAR)) {
|
||||
@@ -179,10 +179,10 @@ function parseRegExp(pattern: string): string {
|
||||
continue;
|
||||
|
||||
case '}':
|
||||
let choices = splitGlobAware(braceVal, ',');
|
||||
const choices = splitGlobAware(braceVal, ',');
|
||||
|
||||
// Converts {foo,bar} => [foo|bar]
|
||||
let braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`;
|
||||
const braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`;
|
||||
|
||||
regEx += braceRegExp;
|
||||
|
||||
@@ -302,7 +302,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P
|
||||
if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check
|
||||
const base = pattern.substr(4); // '**/*'.length === 4
|
||||
parsedPattern = function (path, basename) {
|
||||
return path && strings.endsWith(path, base) ? pattern : null;
|
||||
return typeof path === 'string' && strings.endsWith(path, base) ? pattern : null;
|
||||
};
|
||||
} else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check
|
||||
parsedPattern = trivia2(match[1], pattern);
|
||||
@@ -331,11 +331,11 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
|
||||
}
|
||||
|
||||
return function (path, basename) {
|
||||
if (!paths.isEqualOrParent(path, arg2.base)) {
|
||||
if (!extpath.isEqualOrParent(path, arg2.base)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsedPattern(arg2.pathToRelative(arg2.base, path), basename);
|
||||
return parsedPattern(paths.relative(arg2.base, path), basename);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ function trivia2(base: string, originalPattern: string): ParsedStringPattern {
|
||||
const slashBase = `/${base}`;
|
||||
const backslashBase = `\\${base}`;
|
||||
const parsedPattern: ParsedStringPattern = function (path, basename) {
|
||||
if (!path) {
|
||||
if (typeof path !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (basename) {
|
||||
@@ -396,12 +396,12 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern {
|
||||
|
||||
// common patterns: **/something/else just need endsWith check, something/else just needs and equals check
|
||||
function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): ParsedStringPattern {
|
||||
const nativePath = paths.nativeSep !== paths.sep ? path.replace(ALL_FORWARD_SLASHES, paths.nativeSep) : path;
|
||||
const nativePathEnd = paths.nativeSep + nativePath;
|
||||
const nativePath = paths.sep !== paths.posix.sep ? path.replace(ALL_FORWARD_SLASHES, paths.sep) : path;
|
||||
const nativePathEnd = paths.sep + nativePath;
|
||||
const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) {
|
||||
return path && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null;
|
||||
return typeof path === 'string' && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null;
|
||||
} : function (path, basename) {
|
||||
return path && path === nativePath ? pattern : null;
|
||||
return typeof path === 'string' && path === nativePath ? pattern : null;
|
||||
};
|
||||
parsedPattern.allPaths = [(matchPathEnds ? '*/' : './') + path];
|
||||
return parsedPattern;
|
||||
@@ -412,7 +412,7 @@ function toRegExp(pattern: string): ParsedStringPattern {
|
||||
const regExp = new RegExp(`^${parseRegExp(pattern)}$`);
|
||||
return function (path: string, basename: string) {
|
||||
regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it!
|
||||
return path && regExp.test(path) ? pattern : null;
|
||||
return typeof path === 'string' && regExp.test(path) ? pattern : null;
|
||||
};
|
||||
} catch (error) {
|
||||
return NULL;
|
||||
@@ -430,7 +430,7 @@ function toRegExp(pattern: string): ParsedStringPattern {
|
||||
export function match(pattern: string | IRelativePattern, path: string): boolean;
|
||||
export function match(expression: IExpression, path: string, hasSibling?: (name: string) => boolean): string /* the matching pattern */;
|
||||
export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): any {
|
||||
if (!arg1 || !path) {
|
||||
if (!arg1 || typeof path !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -515,7 +515,7 @@ function listToMap(list: string[]) {
|
||||
export function isRelativePattern(obj: any): obj is IRelativePattern {
|
||||
const rp = obj as IRelativePattern;
|
||||
|
||||
return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string' && typeof rp.pathToRelative === 'function';
|
||||
return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -675,7 +675,7 @@ function aggregateBasenameMatches(parsedPatterns: Array<ParsedStringPattern | Pa
|
||||
}, <string[]>[]);
|
||||
}
|
||||
const aggregate: ParsedStringPattern = function (path, basename) {
|
||||
if (!path) {
|
||||
if (typeof path !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (!basename) {
|
||||
|
||||
@@ -69,4 +69,4 @@ export class Hasher {
|
||||
this._value = hash(obj, this._value);
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export class HistoryNavigator<T> implements INavigator<T> {
|
||||
}
|
||||
|
||||
private _reduceToLimit() {
|
||||
let data = this._elements;
|
||||
const data = this._elements;
|
||||
if (data.length > this._limit) {
|
||||
this._initialize(data.slice(data.length - this._limit));
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
|
||||
let digits = 0;
|
||||
let value = 0;
|
||||
while (digits < count) {
|
||||
let ch = text.charCodeAt(pos);
|
||||
const ch = text.charCodeAt(pos);
|
||||
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
|
||||
value = value * 16 + ch - CharacterCodes._0;
|
||||
}
|
||||
@@ -240,7 +240,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
|
||||
}
|
||||
|
||||
function scanNumber(): string {
|
||||
let start = pos;
|
||||
const start = pos;
|
||||
if (text.charCodeAt(pos) === CharacterCodes._0) {
|
||||
pos++;
|
||||
} else {
|
||||
@@ -331,7 +331,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
|
||||
result += '\t';
|
||||
break;
|
||||
case CharacterCodes.u:
|
||||
let ch = scanHexDigits(4);
|
||||
const ch = scanHexDigits(4);
|
||||
if (ch >= 0) {
|
||||
result += String.fromCharCode(ch);
|
||||
} else {
|
||||
@@ -424,7 +424,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
|
||||
|
||||
// comments
|
||||
case CharacterCodes.slash:
|
||||
let start = pos - 1;
|
||||
const start = pos - 1;
|
||||
// Single-line comment
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
@@ -444,10 +444,10 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) {
|
||||
pos += 2;
|
||||
|
||||
let safeLength = len - 1; // For lookahead.
|
||||
const safeLength = len - 1; // For lookahead.
|
||||
let commentClosed = false;
|
||||
while (pos < safeLength) {
|
||||
let ch = text.charCodeAt(pos);
|
||||
const ch = text.charCodeAt(pos);
|
||||
|
||||
if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
@@ -720,8 +720,8 @@ interface NodeImpl extends Node {
|
||||
* 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();
|
||||
const segments: Segment[] = []; // strings or numbers
|
||||
const earlyReturnException = new Object();
|
||||
let previousNode: NodeImpl | undefined = undefined;
|
||||
const previousNodeInst: NodeImpl = {
|
||||
value: {},
|
||||
@@ -800,7 +800,7 @@ export function getLocation(text: string, position: number): Location {
|
||||
isAtPropertyKey = false;
|
||||
previousNode = undefined;
|
||||
} else if (sep === ',') {
|
||||
let last = segments[segments.length - 1];
|
||||
const last = segments[segments.length - 1];
|
||||
if (typeof last === 'number') {
|
||||
segments[segments.length - 1] = last + 1;
|
||||
} else {
|
||||
@@ -843,7 +843,7 @@ export function getLocation(text: string, position: number): Location {
|
||||
export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any {
|
||||
let currentProperty: string | null = null;
|
||||
let currentParent: any = [];
|
||||
let previousParents: any[] = [];
|
||||
const previousParents: any[] = [];
|
||||
|
||||
function onValue(value: any) {
|
||||
if (Array.isArray(currentParent)) {
|
||||
@@ -853,9 +853,9 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt
|
||||
}
|
||||
}
|
||||
|
||||
let visitor: JSONVisitor = {
|
||||
const visitor: JSONVisitor = {
|
||||
onObjectBegin: () => {
|
||||
let object = {};
|
||||
const object = {};
|
||||
onValue(object);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = object;
|
||||
@@ -868,7 +868,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onArrayBegin: () => {
|
||||
let array: any[] = [];
|
||||
const array: any[] = [];
|
||||
onValue(array);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = array;
|
||||
@@ -905,7 +905,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars
|
||||
return valueNode;
|
||||
}
|
||||
|
||||
let visitor: JSONVisitor = {
|
||||
const visitor: JSONVisitor = {
|
||||
onObjectBegin: (offset: number) => {
|
||||
currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });
|
||||
},
|
||||
@@ -945,7 +945,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars
|
||||
};
|
||||
visit(text, visitor, options);
|
||||
|
||||
let result = currentParent.children![0];
|
||||
const result = currentParent.children![0];
|
||||
if (result) {
|
||||
delete result.parent;
|
||||
}
|
||||
@@ -977,7 +977,7 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
let index = <number>segment;
|
||||
const index = <number>segment;
|
||||
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -994,12 +994,12 @@ export function getNodePath(node: Node): JSONPath {
|
||||
if (!node.parent || !node.parent.children) {
|
||||
return [];
|
||||
}
|
||||
let path = getNodePath(node.parent);
|
||||
const path = getNodePath(node.parent);
|
||||
if (node.parent.type === 'property') {
|
||||
let key = node.parent.children[0].value;
|
||||
const key = node.parent.children[0].value;
|
||||
path.push(key);
|
||||
} else if (node.parent.type === 'array') {
|
||||
let index = node.parent.children.indexOf(node);
|
||||
const index = node.parent.children.indexOf(node);
|
||||
if (index !== -1) {
|
||||
path.push(index);
|
||||
}
|
||||
@@ -1015,9 +1015,9 @@ export function getNodeValue(node: Node): any {
|
||||
case 'array':
|
||||
return node.children!.map(getNodeValue);
|
||||
case 'object':
|
||||
let obj = Object.create(null);
|
||||
const obj = Object.create(null);
|
||||
for (let prop of node.children!) {
|
||||
let valueNode = prop.children![1];
|
||||
const valueNode = prop.children![1];
|
||||
if (valueNode) {
|
||||
obj[prop.children![0].value] = getNodeValue(valueNode);
|
||||
}
|
||||
@@ -1043,10 +1043,10 @@ export function contains(node: Node, offset: number, includeRightBound = false):
|
||||
*/
|
||||
export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined {
|
||||
if (contains(node, offset, includeRightBound)) {
|
||||
let children = node.children;
|
||||
const 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);
|
||||
const item = findNodeAtOffset(children[i], offset, includeRightBound);
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
@@ -1064,7 +1064,7 @@ export function findNodeAtOffset(node: Node, offset: number, includeRightBound =
|
||||
*/
|
||||
export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any {
|
||||
|
||||
let _scanner = createScanner(text, false);
|
||||
const _scanner = createScanner(text, false);
|
||||
|
||||
function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void {
|
||||
return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
@@ -1073,7 +1073,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions
|
||||
return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
}
|
||||
|
||||
let onObjectBegin = toNoArgVisit(visitor.onObjectBegin),
|
||||
const onObjectBegin = toNoArgVisit(visitor.onObjectBegin),
|
||||
onObjectProperty = toOneArgVisit(visitor.onObjectProperty),
|
||||
onObjectEnd = toNoArgVisit(visitor.onObjectEnd),
|
||||
onArrayBegin = toNoArgVisit(visitor.onArrayBegin),
|
||||
@@ -1083,11 +1083,11 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions
|
||||
onComment = toNoArgVisit(visitor.onComment),
|
||||
onError = toOneArgVisit(visitor.onError);
|
||||
|
||||
let disallowComments = options && options.disallowComments;
|
||||
let allowTrailingComma = options && options.allowTrailingComma;
|
||||
const disallowComments = options && options.disallowComments;
|
||||
const allowTrailingComma = options && options.allowTrailingComma;
|
||||
function scanNext(): SyntaxKind {
|
||||
while (true) {
|
||||
let token = _scanner.scan();
|
||||
const token = _scanner.scan();
|
||||
switch (_scanner.getTokenError()) {
|
||||
case ScanError.InvalidUnicode:
|
||||
handleError(ParseErrorCode.InvalidUnicode);
|
||||
@@ -1148,7 +1148,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions
|
||||
}
|
||||
|
||||
function parseString(isValue: boolean): boolean {
|
||||
let value = _scanner.getTokenValue();
|
||||
const value = _scanner.getTokenValue();
|
||||
if (isValue) {
|
||||
onLiteralValue(value);
|
||||
} else {
|
||||
|
||||
@@ -12,9 +12,9 @@ export function removeProperty(text: string, path: JSONPath, formattingOptions:
|
||||
}
|
||||
|
||||
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);
|
||||
const path = originalPath.slice();
|
||||
const errors: ParseError[] = [];
|
||||
const root = parseTree(text, errors);
|
||||
let parent: Node | undefined = undefined;
|
||||
|
||||
let lastSegment: Segment | undefined = undefined;
|
||||
@@ -39,24 +39,24 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo
|
||||
}
|
||||
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' && Array.isArray(parent.children)) {
|
||||
let existing = findNodeAtLocation(parent, [lastSegment]);
|
||||
const existing = findNodeAtLocation(parent, [lastSegment]);
|
||||
if (existing !== undefined) {
|
||||
if (value === undefined) { // delete
|
||||
if (!existing.parent) {
|
||||
throw new Error('Malformed AST');
|
||||
}
|
||||
let propertyIndex = parent.children.indexOf(existing.parent);
|
||||
const propertyIndex = parent.children.indexOf(existing.parent);
|
||||
let removeBegin: number;
|
||||
let removeEnd = existing.parent.offset + existing.parent.length;
|
||||
if (propertyIndex > 0) {
|
||||
// remove the comma of the previous node
|
||||
let previous = parent.children[propertyIndex - 1];
|
||||
const previous = parent.children[propertyIndex - 1];
|
||||
removeBegin = previous.offset + previous.length;
|
||||
} else {
|
||||
removeBegin = parent.offset + 1;
|
||||
if (parent.children.length > 1) {
|
||||
// remove the comma of the next node
|
||||
let next = parent.children[1];
|
||||
const next = parent.children[1];
|
||||
removeEnd = next.offset;
|
||||
}
|
||||
}
|
||||
@@ -69,11 +69,11 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo
|
||||
if (value === undefined) { // delete
|
||||
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;
|
||||
const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
|
||||
const 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];
|
||||
const previous = parent.children[index - 1];
|
||||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
|
||||
} else if (parent.children.length === 0) {
|
||||
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
|
||||
@@ -83,32 +83,32 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo
|
||||
return withFormatting(text, edit, formattingOptions);
|
||||
}
|
||||
} else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
|
||||
let insertIndex = lastSegment;
|
||||
const insertIndex = lastSegment;
|
||||
if (insertIndex === -1) {
|
||||
// Insert
|
||||
let newProperty = `${JSON.stringify(value)}`;
|
||||
const newProperty = `${JSON.stringify(value)}`;
|
||||
let edit: Edit;
|
||||
if (parent.children.length === 0) {
|
||||
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
|
||||
} else {
|
||||
let previous = parent.children[parent.children.length - 1];
|
||||
const previous = parent.children[parent.children.length - 1];
|
||||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
|
||||
}
|
||||
return withFormatting(text, edit, formattingOptions);
|
||||
} else {
|
||||
if (value === undefined && parent.children.length >= 0) {
|
||||
//Removal
|
||||
let removalIndex = lastSegment;
|
||||
let toRemove = parent.children[removalIndex];
|
||||
const removalIndex = lastSegment;
|
||||
const toRemove = parent.children[removalIndex];
|
||||
let edit: Edit;
|
||||
if (parent.children.length === 1) {
|
||||
// only item
|
||||
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
|
||||
} else if (parent.children.length - 1 === removalIndex) {
|
||||
// last item
|
||||
let previous = parent.children[removalIndex - 1];
|
||||
let offset = previous.offset + previous.length;
|
||||
let parentEndOffset = parent.offset + parent.length;
|
||||
const previous = parent.children[removalIndex - 1];
|
||||
const offset = previous.offset + previous.length;
|
||||
const parentEndOffset = parent.offset + parent.length;
|
||||
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
|
||||
} else {
|
||||
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
|
||||
@@ -139,18 +139,18 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO
|
||||
}
|
||||
}
|
||||
|
||||
let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions);
|
||||
const edits = format(newText, { offset: begin, length: end - begin }, formattingOptions);
|
||||
|
||||
// apply the formatting edits and track the begin and end offsets of the changes
|
||||
for (let i = edits.length - 1; i >= 0; i--) {
|
||||
let edit = edits[i];
|
||||
const edit = edits[i];
|
||||
newText = applyEdit(newText, edit);
|
||||
begin = Math.min(begin, edit.offset);
|
||||
end = Math.max(end, edit.offset + edit.length);
|
||||
end += edit.content.length - edit.length;
|
||||
}
|
||||
// create a single edit with all changes
|
||||
let editLength = text.length - (newText.length - end) - begin;
|
||||
const editLength = text.length - (newText.length - end) - begin;
|
||||
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
rangeStart = 0;
|
||||
rangeEnd = documentText.length;
|
||||
}
|
||||
let eol = getEOL(options, documentText);
|
||||
const eol = getEOL(options, documentText);
|
||||
|
||||
let lineBreak = false;
|
||||
let indentLevel = 0;
|
||||
@@ -91,7 +91,7 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
indentValue = '\t';
|
||||
}
|
||||
|
||||
let scanner = createScanner(formatText, false);
|
||||
const scanner = createScanner(formatText, false);
|
||||
let hasError = false;
|
||||
|
||||
function newLineAndIndent(): string {
|
||||
@@ -107,7 +107,7 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None;
|
||||
return token;
|
||||
}
|
||||
let editOperations: Edit[] = [];
|
||||
const editOperations: Edit[] = [];
|
||||
function addEdit(text: string, startOffset: number, endOffset: number) {
|
||||
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) {
|
||||
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
|
||||
@@ -117,8 +117,8 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
let firstToken = scanNext();
|
||||
|
||||
if (firstToken !== SyntaxKind.EOF) {
|
||||
let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
let initialIndent = repeat(indentValue, initialIndentLevel);
|
||||
const firstTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
const initialIndent = repeat(indentValue, initialIndentLevel);
|
||||
addEdit(initialIndent, formatTextStart, firstTokenStart);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
let replaceContent = '';
|
||||
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() + formatTextStart;
|
||||
const commentTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
addEdit(' ', firstTokenEnd, commentTokenStart);
|
||||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
|
||||
replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
|
||||
@@ -195,7 +195,7 @@ export function format(documentText: string, range: Range | undefined, options:
|
||||
}
|
||||
|
||||
}
|
||||
let secondTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
|
||||
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
|
||||
firstToken = secondToken;
|
||||
}
|
||||
@@ -213,9 +213,9 @@ function repeat(s: string, count: number): string {
|
||||
function computeIndentLevel(content: string, options: FormattingOptions): number {
|
||||
let i = 0;
|
||||
let nChars = 0;
|
||||
let tabSize = options.tabSize || 4;
|
||||
const tabSize = options.tabSize || 4;
|
||||
while (i < content.length) {
|
||||
let ch = content.charAt(i);
|
||||
const ch = content.charAt(i);
|
||||
if (ch === ' ') {
|
||||
nChars++;
|
||||
} else if (ch === '\t') {
|
||||
@@ -230,7 +230,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number
|
||||
|
||||
function getEOL(options: FormattingOptions, text: string): string {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let ch = text.charAt(i);
|
||||
const ch = text.charAt(i);
|
||||
if (ch === '\r') {
|
||||
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
|
||||
return '\r\n';
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
|
||||
/**
|
||||
* Virtual Key Codes, the value does not hold any inherent meaning.
|
||||
@@ -406,7 +407,7 @@ export const enum KeyMod {
|
||||
}
|
||||
|
||||
export function KeyChord(firstPart: number, secondPart: number): number {
|
||||
let chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0;
|
||||
const chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0;
|
||||
return (firstPart | chordPart) >>> 0;
|
||||
}
|
||||
|
||||
@@ -417,12 +418,12 @@ export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybi
|
||||
const firstPart = (keybinding & 0x0000FFFF) >>> 0;
|
||||
const chordPart = (keybinding & 0xFFFF0000) >>> 16;
|
||||
if (chordPart !== 0) {
|
||||
return new ChordKeybinding(
|
||||
return new ChordKeybinding([
|
||||
createSimpleKeybinding(firstPart, OS),
|
||||
createSimpleKeybinding(chordPart, OS),
|
||||
);
|
||||
createSimpleKeybinding(chordPart, OS)
|
||||
]);
|
||||
}
|
||||
return createSimpleKeybinding(firstPart, OS);
|
||||
return new ChordKeybinding([createSimpleKeybinding(firstPart, OS)]);
|
||||
}
|
||||
|
||||
export function createSimpleKeybinding(keybinding: number, OS: OperatingSystem): SimpleKeybinding {
|
||||
@@ -439,14 +440,7 @@ export function createSimpleKeybinding(keybinding: number, OS: OperatingSystem):
|
||||
return new SimpleKeybinding(ctrlKey, shiftKey, altKey, metaKey, keyCode);
|
||||
}
|
||||
|
||||
export const enum KeybindingType {
|
||||
Simple = 1,
|
||||
Chord = 2
|
||||
}
|
||||
|
||||
export class SimpleKeybinding {
|
||||
public readonly type = KeybindingType.Simple;
|
||||
|
||||
public readonly ctrlKey: boolean;
|
||||
public readonly shiftKey: boolean;
|
||||
public readonly altKey: boolean;
|
||||
@@ -461,10 +455,7 @@ export class SimpleKeybinding {
|
||||
this.keyCode = keyCode;
|
||||
}
|
||||
|
||||
public equals(other: Keybinding): boolean {
|
||||
if (other.type !== KeybindingType.Simple) {
|
||||
return false;
|
||||
}
|
||||
public equals(other: SimpleKeybinding): boolean {
|
||||
return (
|
||||
this.ctrlKey === other.ctrlKey
|
||||
&& this.shiftKey === other.shiftKey
|
||||
@@ -475,10 +466,10 @@ export class SimpleKeybinding {
|
||||
}
|
||||
|
||||
public getHashCode(): string {
|
||||
let ctrl = this.ctrlKey ? '1' : '0';
|
||||
let shift = this.shiftKey ? '1' : '0';
|
||||
let alt = this.altKey ? '1' : '0';
|
||||
let meta = this.metaKey ? '1' : '0';
|
||||
const ctrl = this.ctrlKey ? '1' : '0';
|
||||
const shift = this.shiftKey ? '1' : '0';
|
||||
const alt = this.altKey ? '1' : '0';
|
||||
const meta = this.metaKey ? '1' : '0';
|
||||
return `${ctrl}${shift}${alt}${meta}${this.keyCode}`;
|
||||
}
|
||||
|
||||
@@ -492,6 +483,10 @@ export class SimpleKeybinding {
|
||||
);
|
||||
}
|
||||
|
||||
public toChord(): ChordKeybinding {
|
||||
return new ChordKeybinding([this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this keybinding refer to the key code of a modifier and it also has the modifier flag?
|
||||
*/
|
||||
@@ -506,22 +501,43 @@ export class SimpleKeybinding {
|
||||
}
|
||||
|
||||
export class ChordKeybinding {
|
||||
public readonly type = KeybindingType.Chord;
|
||||
public readonly parts: SimpleKeybinding[];
|
||||
|
||||
public readonly firstPart: SimpleKeybinding;
|
||||
public readonly chordPart: SimpleKeybinding;
|
||||
|
||||
constructor(firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) {
|
||||
this.firstPart = firstPart;
|
||||
this.chordPart = chordPart;
|
||||
constructor(parts: SimpleKeybinding[]) {
|
||||
if (parts.length === 0) {
|
||||
throw illegalArgument(`parts`);
|
||||
}
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
public getHashCode(): string {
|
||||
return `${this.firstPart.getHashCode()};${this.chordPart.getHashCode()}`;
|
||||
let result = '';
|
||||
for (let i = 0, len = this.parts.length; i < len; i++) {
|
||||
if (i !== 0) {
|
||||
result += ';';
|
||||
}
|
||||
result += this.parts[i].getHashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public equals(other: ChordKeybinding | null): boolean {
|
||||
if (other === null) {
|
||||
return false;
|
||||
}
|
||||
if (this.parts.length !== other.parts.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.parts.length; i++) {
|
||||
if (!this.parts[i].equals(other.parts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export type Keybinding = SimpleKeybinding | ChordKeybinding;
|
||||
export type Keybinding = ChordKeybinding;
|
||||
|
||||
export class ResolvedKeybindingPart {
|
||||
readonly ctrlKey: boolean;
|
||||
@@ -574,12 +590,13 @@ export abstract class ResolvedKeybinding {
|
||||
public abstract isChord(): boolean;
|
||||
|
||||
/**
|
||||
* Returns the firstPart, chordPart that should be used for dispatching.
|
||||
* Returns the parts that comprise of the keybinding.
|
||||
* Simple keybindings return one element.
|
||||
*/
|
||||
public abstract getDispatchParts(): [string | null, string | null];
|
||||
public abstract getParts(): ResolvedKeybindingPart[];
|
||||
|
||||
/**
|
||||
* Returns the firstPart, chordPart of the keybinding.
|
||||
* For simple keybindings, the second element will be null.
|
||||
* Returns the parts that should be used for dispatching.
|
||||
*/
|
||||
public abstract getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart | null];
|
||||
public abstract getDispatchParts(): (string | null)[];
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ export interface Modifiers {
|
||||
readonly metaKey: boolean;
|
||||
}
|
||||
|
||||
export interface KeyLabelProvider<T extends Modifiers> {
|
||||
(keybinding: T): string | null;
|
||||
}
|
||||
|
||||
export class ModifierLabelProvider {
|
||||
|
||||
public readonly modifierLabels: ModifierLabels[];
|
||||
@@ -32,11 +36,22 @@ export class ModifierLabelProvider {
|
||||
this.modifierLabels[OperatingSystem.Linux] = linux;
|
||||
}
|
||||
|
||||
public toLabel(firstPartMod: Modifiers | null, firstPartKey: string | null, chordPartMod: Modifiers | null, chordPartKey: string | null, OS: OperatingSystem): string | null {
|
||||
if (firstPartMod === null || firstPartKey === null) {
|
||||
public toLabel<T extends Modifiers>(OS: OperatingSystem, parts: T[], keyLabelProvider: KeyLabelProvider<T>): string | null {
|
||||
if (parts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return _asString(firstPartMod, firstPartKey, chordPartMod, chordPartKey, this.modifierLabels[OS]);
|
||||
|
||||
const result: string[] = [];
|
||||
for (let i = 0, len = parts.length; i < len; i++) {
|
||||
const part = parts[i];
|
||||
const keyLabel = keyLabelProvider(part);
|
||||
if (keyLabel === null) {
|
||||
// this keybinding cannot be expressed...
|
||||
return null;
|
||||
}
|
||||
result[i] = _simpleAsString(part, keyLabel, this.modifierLabels[OS]);
|
||||
}
|
||||
return result.join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +162,7 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe
|
||||
return '';
|
||||
}
|
||||
|
||||
let result: string[] = [];
|
||||
const result: string[] = [];
|
||||
|
||||
// translate modifier keys: Ctrl-Shift-Alt-Meta
|
||||
if (modifiers.ctrlKey) {
|
||||
@@ -171,14 +186,3 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe
|
||||
|
||||
return result.join(labels.separator);
|
||||
}
|
||||
|
||||
function _asString(firstPartMod: Modifiers, firstPartKey: string, chordPartMod: Modifiers | null, chordPartKey: string | null, labels: ModifierLabels): string {
|
||||
let result = _simpleAsString(firstPartMod, firstPartKey, labels);
|
||||
|
||||
if (chordPartMod !== null && chordPartKey !== null) {
|
||||
result += ' ';
|
||||
result += _simpleAsString(chordPartMod, chordPartKey, labels);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -85,16 +85,14 @@ export class KeybindingParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this.parseSimpleKeybinding(input);
|
||||
let chordPart: SimpleKeybinding | null = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this.parseSimpleKeybinding(remains);
|
||||
}
|
||||
const parts: SimpleKeybinding[] = [];
|
||||
let part: SimpleKeybinding;
|
||||
|
||||
if (chordPart) {
|
||||
return new ChordKeybinding(firstPart, chordPart);
|
||||
}
|
||||
return firstPart;
|
||||
do {
|
||||
[part, input] = this.parseSimpleKeybinding(input);
|
||||
parts.push(part);
|
||||
} while (input.length > 0);
|
||||
return new ChordKeybinding(parts);
|
||||
}
|
||||
|
||||
private static parseSimpleUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, string] {
|
||||
@@ -109,16 +107,18 @@ export class KeybindingParser {
|
||||
return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains];
|
||||
}
|
||||
|
||||
static parseUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding | null, SimpleKeybinding | ScanCodeBinding | null] {
|
||||
static parseUserBinding(input: string): (SimpleKeybinding | ScanCodeBinding)[] {
|
||||
if (!input) {
|
||||
return [null, null];
|
||||
return [];
|
||||
}
|
||||
|
||||
let [firstPart, remains] = this.parseSimpleUserBinding(input);
|
||||
let chordPart: SimpleKeybinding | ScanCodeBinding | null = null;
|
||||
if (remains.length > 0) {
|
||||
[chordPart] = this.parseSimpleUserBinding(remains);
|
||||
const parts: (SimpleKeybinding | ScanCodeBinding)[] = [];
|
||||
let part: SimpleKeybinding | ScanCodeBinding;
|
||||
|
||||
while (input.length > 0) {
|
||||
[part, input] = this.parseSimpleUserBinding(input);
|
||||
parts.push(part);
|
||||
}
|
||||
return [firstPart, chordPart];
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { nativeSep, normalize, basename as pathsBasename, sep } from 'vs/base/common/paths';
|
||||
import { sep, posix, normalize } from 'vs/base/common/path';
|
||||
import { endsWith, ltrim, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { isEqual, basename } from 'vs/base/common/resources';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export interface IWorkspaceFolderProvider {
|
||||
getWorkspaceFolder(resource: URI): { uri: URI, name?: string } | null;
|
||||
@@ -36,14 +37,15 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom
|
||||
const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1;
|
||||
|
||||
let pathLabel: string;
|
||||
if (isEqual(baseResource.uri, resource, !isLinux)) {
|
||||
if (isEqual(baseResource.uri, resource)) {
|
||||
pathLabel = ''; // no label if paths are identical
|
||||
} else {
|
||||
pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), sep)!, true);
|
||||
// TODO: isidor use resources.relative
|
||||
pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), posix.sep)!);
|
||||
}
|
||||
|
||||
if (hasMultipleRoots) {
|
||||
const rootName = (baseResource && baseResource.name) ? baseResource.name : pathsBasename(baseResource.uri.fsPath);
|
||||
const rootName = (baseResource && baseResource.name) ? baseResource.name : basename(baseResource.uri);
|
||||
pathLabel = pathLabel ? (rootName + ' • ' + pathLabel) : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
|
||||
@@ -58,11 +60,11 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom
|
||||
|
||||
// convert c:\something => C:\something
|
||||
if (hasDriveLetter(resource.fsPath)) {
|
||||
return normalize(normalizeDriveLetter(resource.fsPath), true);
|
||||
return normalize(normalizeDriveLetter(resource.fsPath));
|
||||
}
|
||||
|
||||
// normalize and tildify (macOS, Linux only)
|
||||
let res = normalize(resource.fsPath, true);
|
||||
let res = normalize(resource.fsPath);
|
||||
if (!isWindows && userHomeProvider) {
|
||||
res = tildify(res, userHomeProvider.userHome);
|
||||
}
|
||||
@@ -81,7 +83,7 @@ export function getBaseLabel(resource: URI | string | undefined): string | undef
|
||||
resource = URI.file(resource);
|
||||
}
|
||||
|
||||
const base = pathsBasename(resource.path) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */;
|
||||
const base = basename(resource) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */;
|
||||
|
||||
// convert c: => C:
|
||||
if (hasDriveLetter(base)) {
|
||||
@@ -112,7 +114,7 @@ export function tildify(path: string, userHome: string): string {
|
||||
// Keep a normalized user home path as cache to prevent accumulated string creation
|
||||
let normalizedUserHome = normalizedUserHomeCached.original === userHome ? normalizedUserHomeCached.normalized : undefined;
|
||||
if (!normalizedUserHome) {
|
||||
normalizedUserHome = `${rtrim(userHome, sep)}${sep}`;
|
||||
normalizedUserHome = `${rtrim(userHome, posix.sep)}${posix.sep}`;
|
||||
normalizedUserHomeCached = { original: userHome, normalized: normalizedUserHome };
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ export function shorten(paths: string[]): string[] {
|
||||
let path = paths[pathIndex];
|
||||
|
||||
if (path === '') {
|
||||
shortenedPaths[pathIndex] = `.${nativeSep}`;
|
||||
shortenedPaths[pathIndex] = `.${sep}`;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -185,20 +187,20 @@ export function shorten(paths: string[]): string[] {
|
||||
if (path.indexOf(unc) === 0) {
|
||||
prefix = path.substr(0, path.indexOf(unc) + unc.length);
|
||||
path = path.substr(path.indexOf(unc) + unc.length);
|
||||
} else if (path.indexOf(nativeSep) === 0) {
|
||||
prefix = path.substr(0, path.indexOf(nativeSep) + nativeSep.length);
|
||||
path = path.substr(path.indexOf(nativeSep) + nativeSep.length);
|
||||
} else if (path.indexOf(sep) === 0) {
|
||||
prefix = path.substr(0, path.indexOf(sep) + sep.length);
|
||||
path = path.substr(path.indexOf(sep) + sep.length);
|
||||
} else if (path.indexOf(home) === 0) {
|
||||
prefix = path.substr(0, path.indexOf(home) + home.length);
|
||||
path = path.substr(path.indexOf(home) + home.length);
|
||||
}
|
||||
|
||||
// pick the first shortest subpath found
|
||||
const segments: string[] = path.split(nativeSep);
|
||||
const segments: string[] = path.split(sep);
|
||||
for (let subpathLength = 1; match && subpathLength <= segments.length; subpathLength++) {
|
||||
for (let start = segments.length - subpathLength; match && start >= 0; start--) {
|
||||
match = false;
|
||||
let subpath = segments.slice(start, start + subpathLength).join(nativeSep);
|
||||
let subpath = segments.slice(start, start + subpathLength).join(sep);
|
||||
|
||||
// that is unique to any other path
|
||||
for (let otherPathIndex = 0; !match && otherPathIndex < paths.length; otherPathIndex++) {
|
||||
@@ -209,7 +211,7 @@ export function shorten(paths: string[]): string[] {
|
||||
|
||||
// Adding separator as prefix for subpath, such that 'endsWith(src, trgt)' considers subpath as directory name instead of plain string.
|
||||
// prefix is not added when either subpath is root directory or path[otherPathIndex] does not have multiple directories.
|
||||
const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(nativeSep) > -1) ? nativeSep + subpath : subpath;
|
||||
const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(sep) > -1) ? sep + subpath : subpath;
|
||||
const isOtherPathEnding: boolean = endsWith(paths[otherPathIndex], subpathWithSep);
|
||||
|
||||
match = !isSubpathEnding || isOtherPathEnding;
|
||||
@@ -226,11 +228,11 @@ export function shorten(paths: string[]): string[] {
|
||||
// extend subpath to include disk drive prefix
|
||||
start = 0;
|
||||
subpathLength++;
|
||||
subpath = segments[0] + nativeSep + subpath;
|
||||
subpath = segments[0] + sep + subpath;
|
||||
}
|
||||
|
||||
if (start > 0) {
|
||||
result = segments[0] + nativeSep;
|
||||
result = segments[0] + sep;
|
||||
}
|
||||
|
||||
result = prefix + result;
|
||||
@@ -238,14 +240,14 @@ export function shorten(paths: string[]): string[] {
|
||||
|
||||
// add ellipsis at the beginning if neeeded
|
||||
if (start > 0) {
|
||||
result = result + ellipsis + nativeSep;
|
||||
result = result + ellipsis + sep;
|
||||
}
|
||||
|
||||
result = result + subpath;
|
||||
|
||||
// add ellipsis at the end if needed
|
||||
if (start + subpathLength < segments.length) {
|
||||
result = result + nativeSep + ellipsis;
|
||||
result = result + sep + ellipsis;
|
||||
}
|
||||
|
||||
shortenedPaths[pathIndex] = result;
|
||||
@@ -282,7 +284,7 @@ interface ISegment {
|
||||
* @param value string to which templating is applied
|
||||
* @param values the values of the templates to use
|
||||
*/
|
||||
export function template(template: string, values: { [key: string]: string | ISeparator } = Object.create(null)): string {
|
||||
export function template(template: string, values: { [key: string]: string | ISeparator | null } = Object.create(null)): string {
|
||||
const segments: ISegment[] = [];
|
||||
|
||||
let inVariable = false;
|
||||
@@ -355,10 +357,10 @@ export function template(template: string, values: { [key: string]: string | ISe
|
||||
*/
|
||||
export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean): string {
|
||||
if (isMacintosh || forceDisableMnemonics) {
|
||||
return label.replace(/\(&&\w\)|&&/g, '');
|
||||
return label.replace(/\(&&\w\)|&&/g, '').replace(/&/g, isMacintosh ? '&' : '&&');
|
||||
}
|
||||
|
||||
return label.replace(/&&/g, '&');
|
||||
return label.replace(/&&|&/g, m => m === '&' ? '&&' : '&');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,3 +384,16 @@ export function mnemonicButtonLabel(label: string): string {
|
||||
export function unmnemonicLabel(label: string): string {
|
||||
return label.replace(/&/g, '&&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a path in name and parent path, supporting both '/' and '\'
|
||||
*/
|
||||
export function splitName(fullPath: string): { name: string, parentPath: string } {
|
||||
for (let i = fullPath.length - 1; i >= 1; i--) {
|
||||
const code = fullPath.charCodeAt(i);
|
||||
if (code === CharCode.Slash || code === CharCode.Backslash) {
|
||||
return { parentPath: fullPath.substr(0, i), name: fullPath.substr(i + 1) };
|
||||
}
|
||||
}
|
||||
return { parentPath: '', name: fullPath };
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export class LinkedList<E> {
|
||||
}
|
||||
if (candidate.prev && candidate.next) {
|
||||
// middle
|
||||
let anchor = candidate.prev;
|
||||
const anchor = candidate.prev;
|
||||
anchor.next = candidate.next;
|
||||
candidate.next.prev = anchor;
|
||||
|
||||
@@ -144,7 +144,7 @@ export class LinkedList<E> {
|
||||
}
|
||||
|
||||
toArray(): E[] {
|
||||
let result: E[] = [];
|
||||
const result: E[] = [];
|
||||
for (let node = this._first; node instanceof Node; node = node.next) {
|
||||
result.push(node.element);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ 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[];
|
||||
export function values<V>(forEachable: { forEach(callback: (value: V, ...more: any[]) => any) }): V[] {
|
||||
export function values<V>(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] {
|
||||
const result: V[] = [];
|
||||
forEachable.forEach(value => result.push(value));
|
||||
return result;
|
||||
@@ -100,8 +100,8 @@ export class StringIterator implements IKeyIterator {
|
||||
}
|
||||
|
||||
cmp(a: string): number {
|
||||
let aCode = a.charCodeAt(0);
|
||||
let thisCode = this._value.charCodeAt(this._pos);
|
||||
const aCode = a.charCodeAt(0);
|
||||
const thisCode = this._value.charCodeAt(this._pos);
|
||||
return aCode - thisCode;
|
||||
}
|
||||
|
||||
@@ -149,11 +149,11 @@ export class PathIterator implements IKeyIterator {
|
||||
cmp(a: string): number {
|
||||
|
||||
let aPos = 0;
|
||||
let aLen = a.length;
|
||||
const aLen = a.length;
|
||||
let thisPos = this._from;
|
||||
|
||||
while (aPos < aLen && thisPos < this._to) {
|
||||
let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
|
||||
const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
|
||||
if (cmp !== 0) {
|
||||
return cmp;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
set(key: string, element: E): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
const iter = this._iter.reset(key);
|
||||
let node: TernarySearchTreeNode<E>;
|
||||
|
||||
if (!this._root) {
|
||||
@@ -220,7 +220,7 @@ export class TernarySearchTree<E> {
|
||||
|
||||
node = this._root;
|
||||
while (true) {
|
||||
let val = iter.cmp(node.segment);
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
if (!node.left) {
|
||||
@@ -256,10 +256,10 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
get(key: string): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.segment);
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
@@ -279,13 +279,13 @@ export class TernarySearchTree<E> {
|
||||
|
||||
delete(key: string): void {
|
||||
|
||||
let iter = this._iter.reset(key);
|
||||
let stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
|
||||
const iter = this._iter.reset(key);
|
||||
const stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
|
||||
let node = this._root;
|
||||
|
||||
// find and unset node
|
||||
while (node) {
|
||||
let val = iter.cmp(node.segment);
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
stack.push([1, node]);
|
||||
@@ -319,11 +319,11 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
findSubstr(key: string): E | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
let candidate: E | undefined = undefined;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.segment);
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
@@ -343,10 +343,10 @@ export class TernarySearchTree<E> {
|
||||
}
|
||||
|
||||
findSuperstr(key: string): Iterator<E> | undefined {
|
||||
let iter = this._iter.reset(key);
|
||||
const iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.segment);
|
||||
const val = iter.cmp(node.segment);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
@@ -373,7 +373,7 @@ export class TernarySearchTree<E> {
|
||||
let res: { done: false; value: E; };
|
||||
let idx: number;
|
||||
let data: E[];
|
||||
let next = (): IteratorResult<E> => {
|
||||
const next = (): IteratorResult<E> => {
|
||||
if (!data) {
|
||||
// lazy till first invocation
|
||||
data = [];
|
||||
@@ -610,7 +610,7 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
|
||||
values(): V[] {
|
||||
let result: V[] = [];
|
||||
const result: V[] = [];
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
result.push(current.value);
|
||||
@@ -620,7 +620,7 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
|
||||
keys(): K[] {
|
||||
let result: K[] = [];
|
||||
const result: K[] = [];
|
||||
let current = this._head;
|
||||
while (current) {
|
||||
result.push(current.key);
|
||||
@@ -631,14 +631,14 @@ export class LinkedMap<K, V> {
|
||||
|
||||
/* VS Code / Monaco editor runs on es5 which has no Symbol.iterator
|
||||
keys(): IterableIterator<K> {
|
||||
let current = this._head;
|
||||
let iterator: IterableIterator<K> = {
|
||||
const current = this._head;
|
||||
const iterator: IterableIterator<K> = {
|
||||
[Symbol.iterator]() {
|
||||
return iterator;
|
||||
},
|
||||
next():IteratorResult<K> {
|
||||
if (current) {
|
||||
let result = { value: current.key, done: false };
|
||||
const result = { value: current.key, done: false };
|
||||
current = current.next;
|
||||
return result;
|
||||
} else {
|
||||
@@ -650,14 +650,14 @@ export class LinkedMap<K, V> {
|
||||
}
|
||||
|
||||
values(): IterableIterator<V> {
|
||||
let current = this._head;
|
||||
let iterator: IterableIterator<V> = {
|
||||
const current = this._head;
|
||||
const iterator: IterableIterator<V> = {
|
||||
[Symbol.iterator]() {
|
||||
return iterator;
|
||||
},
|
||||
next():IteratorResult<V> {
|
||||
if (current) {
|
||||
let result = { value: current.value, done: false };
|
||||
const result = { value: current.value, done: false };
|
||||
current = current.next;
|
||||
return result;
|
||||
} else {
|
||||
@@ -723,9 +723,21 @@ export class LinkedMap<K, V> {
|
||||
this._tail = undefined;
|
||||
}
|
||||
else if (item === this._head) {
|
||||
// This can only happend if size === 1 which is handle
|
||||
// by the case above.
|
||||
if (!item.next) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
item.next.previous = undefined;
|
||||
this._head = item.next;
|
||||
}
|
||||
else if (item === this._tail) {
|
||||
// This can only happend if size === 1 which is handle
|
||||
// by the case above.
|
||||
if (!item.previous) {
|
||||
throw new Error('Invalid list');
|
||||
}
|
||||
item.previous.next = undefined;
|
||||
this._tail = item.previous;
|
||||
}
|
||||
else {
|
||||
@@ -737,6 +749,8 @@ export class LinkedMap<K, V> {
|
||||
next.previous = previous;
|
||||
previous.next = next;
|
||||
}
|
||||
item.next = undefined;
|
||||
item.previous = undefined;
|
||||
}
|
||||
|
||||
private touch(item: Item<K, V>, touch: Touch): void {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 { basename, posix, extname } from 'vs/base/common/path';
|
||||
import { endsWith, startsWithUTF8BOM, startsWith } from 'vs/base/common/strings';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
|
||||
export const MIME_TEXT = 'text/plain';
|
||||
@@ -85,7 +85,7 @@ function toTextMimeAssociationItem(association: ITextMimeAssociation): ITextMime
|
||||
filenameLowercase: association.filename ? association.filename.toLowerCase() : undefined,
|
||||
extensionLowercase: association.extension ? association.extension.toLowerCase() : undefined,
|
||||
filepatternLowercase: association.filepattern ? association.filepattern.toLowerCase() : undefined,
|
||||
filepatternOnPath: association.filepattern ? association.filepattern.indexOf(paths.sep) >= 0 : false
|
||||
filepatternOnPath: association.filepattern ? association.filepattern.indexOf(posix.sep) >= 0 : false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export function guessMimeTypes(path: string | null, firstLine?: string): string[
|
||||
}
|
||||
|
||||
path = path.toLowerCase();
|
||||
const filename = paths.basename(path);
|
||||
const filename = basename(path);
|
||||
|
||||
// 1.) User configured mappings have highest priority
|
||||
const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations);
|
||||
@@ -166,7 +166,7 @@ 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 (endsWith(filename, association.extensionLowercase!)) {
|
||||
extensionMatch = association;
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
|
||||
}
|
||||
|
||||
function guessMimeTypeByFirstline(firstLine: string): string | null {
|
||||
if (strings.startsWithUTF8BOM(firstLine)) {
|
||||
if (startsWithUTF8BOM(firstLine)) {
|
||||
firstLine = firstLine.substr(1);
|
||||
}
|
||||
|
||||
@@ -230,12 +230,12 @@ export function isUnspecific(mime: string[] | string): boolean {
|
||||
* 2. Otherwise, if there are other extensions, suggest the first one.
|
||||
* 3. Otherwise, suggest the prefix.
|
||||
*/
|
||||
export function suggestFilename(langId: string, prefix: string): string {
|
||||
export function suggestFilename(langId: string | null, prefix: string): string {
|
||||
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, '.'));
|
||||
const extensionsWithDotFirst = coalesce(extensions)
|
||||
.filter(assoc => startsWith(assoc, '.'));
|
||||
|
||||
if (extensionsWithDotFirst.length > 0) {
|
||||
return prefix + extensionsWithDotFirst[0];
|
||||
@@ -299,6 +299,6 @@ const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
};
|
||||
|
||||
export function getMediaMime(path: string): string | undefined {
|
||||
const ext = paths.extname(path);
|
||||
const ext = extname(path);
|
||||
return mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ export function deepFreeze<T>(obj: T): T {
|
||||
}
|
||||
const stack: any[] = [obj];
|
||||
while (stack.length > 0) {
|
||||
let obj = stack.shift();
|
||||
const obj = stack.shift();
|
||||
Object.freeze(obj);
|
||||
for (const key in obj) {
|
||||
if (_hasOwnProperty.call(obj, key)) {
|
||||
let prop = obj[key];
|
||||
const prop = obj[key];
|
||||
if (typeof prop === 'object' && !Object.isFrozen(prop)) {
|
||||
stack.push(prop);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ export abstract class Parser {
|
||||
|
||||
protected static merge<T>(destination: T, source: T, overwrite: boolean): void {
|
||||
Object.keys(source).forEach((key: string) => {
|
||||
let destValue = destination[key];
|
||||
let sourceValue = source[key];
|
||||
const destValue = destination[key];
|
||||
const sourceValue = source[key];
|
||||
if (Types.isUndefined(sourceValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
1684
src/vs/base/common/path.ts
Normal file
1684
src/vs/base/common/path.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,39 +5,26 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/*global define*/
|
||||
//@ts-check
|
||||
|
||||
// This module can be loaded in an amd and commonjs-context.
|
||||
// Because we want both instances to use the same perf-data
|
||||
// we store them globally
|
||||
// stores data as: 'name','timestamp'
|
||||
function _factory(sharedObj) {
|
||||
|
||||
if (typeof define !== "function" && typeof module === "object" && typeof module.exports === "object") {
|
||||
// this is commonjs, fake amd
|
||||
global.define = function (_dep, callback) {
|
||||
module.exports = callback();
|
||||
global.define = undefined;
|
||||
};
|
||||
}
|
||||
|
||||
define([], function () {
|
||||
|
||||
global._performanceEntries = global._performanceEntries || [];
|
||||
sharedObj._performanceEntries = sharedObj._performanceEntries || [];
|
||||
|
||||
const _dataLen = 2;
|
||||
const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { };
|
||||
|
||||
function importEntries(entries) {
|
||||
global._performanceEntries.splice(0, 0, ...entries);
|
||||
sharedObj._performanceEntries.splice(0, 0, ...entries);
|
||||
}
|
||||
|
||||
function exportEntries() {
|
||||
return global._performanceEntries.slice(0);
|
||||
return sharedObj._performanceEntries.slice(0);
|
||||
}
|
||||
|
||||
function getEntries() {
|
||||
const result = [];
|
||||
const entries = global._performanceEntries;
|
||||
const entries = sharedObj._performanceEntries;
|
||||
for (let i = 0; i < entries.length; i += _dataLen) {
|
||||
result.push({
|
||||
name: entries[i],
|
||||
@@ -48,7 +35,7 @@ define([], function () {
|
||||
}
|
||||
|
||||
function getEntry(name) {
|
||||
const entries = global._performanceEntries;
|
||||
const entries = sharedObj._performanceEntries;
|
||||
for (let i = 0; i < entries.length; i += _dataLen) {
|
||||
if (entries[i] === name) {
|
||||
return {
|
||||
@@ -60,7 +47,7 @@ define([], function () {
|
||||
}
|
||||
|
||||
function getDuration(from, to) {
|
||||
const entries = global._performanceEntries;
|
||||
const entries = sharedObj._performanceEntries;
|
||||
let target = to;
|
||||
let endIndex = 0;
|
||||
for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) {
|
||||
@@ -79,11 +66,11 @@ define([], function () {
|
||||
}
|
||||
|
||||
function mark(name) {
|
||||
global._performanceEntries.push(name, Date.now());
|
||||
sharedObj._performanceEntries.push(name, Date.now());
|
||||
_timeStamp(name);
|
||||
}
|
||||
|
||||
var exports = {
|
||||
const exports = {
|
||||
mark: mark,
|
||||
getEntries: getEntries,
|
||||
getEntry: getEntry,
|
||||
@@ -93,4 +80,29 @@ define([], function () {
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
}
|
||||
|
||||
// This module can be loaded in an amd and commonjs-context.
|
||||
// Because we want both instances to use the same perf-data
|
||||
// we store them globally
|
||||
|
||||
let sharedObj;
|
||||
if (typeof global === 'object') {
|
||||
// nodejs
|
||||
sharedObj = global;
|
||||
} else if (typeof self === 'object') {
|
||||
// browser
|
||||
sharedObj = self;
|
||||
} else {
|
||||
sharedObj = {};
|
||||
}
|
||||
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define([], function () { return _factory(sharedObj); });
|
||||
} else if (typeof module === "object" && typeof module.exports === "object") {
|
||||
// commonjs
|
||||
module.exports = _factory(sharedObj);
|
||||
} else {
|
||||
// invalid context...
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const LANGUAGE_DEFAULT = 'en';
|
||||
|
||||
let _isWindows = false;
|
||||
let _isMacintosh = false;
|
||||
let _isLinux = false;
|
||||
let _isNative = false;
|
||||
let _isWeb = false;
|
||||
let _locale: string | undefined = undefined;
|
||||
let _language: string | undefined = undefined;
|
||||
let _language: string = LANGUAGE_DEFAULT;
|
||||
let _translationsConfigFile: string | undefined = undefined;
|
||||
|
||||
interface NLSConfig {
|
||||
@@ -32,17 +34,15 @@ interface INodeProcess {
|
||||
};
|
||||
type?: string;
|
||||
}
|
||||
declare let process: INodeProcess;
|
||||
declare let global: any;
|
||||
declare const process: INodeProcess;
|
||||
declare const global: any;
|
||||
|
||||
interface INavigator {
|
||||
userAgent: string;
|
||||
language: string;
|
||||
}
|
||||
declare let navigator: INavigator;
|
||||
declare let self: any;
|
||||
|
||||
export const LANGUAGE_DEFAULT = 'en';
|
||||
declare const navigator: INavigator;
|
||||
declare const self: any;
|
||||
|
||||
const isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer');
|
||||
|
||||
@@ -120,6 +120,27 @@ export function isRootUser(): boolean {
|
||||
*/
|
||||
export const language = _language;
|
||||
|
||||
export namespace Language {
|
||||
|
||||
export function value(): string {
|
||||
return language;
|
||||
}
|
||||
|
||||
export function isDefaultVariant(): boolean {
|
||||
if (language.length === 2) {
|
||||
return language === 'en';
|
||||
} else if (language.length >= 3) {
|
||||
return language[0] === 'e' && language[1] === 'n' && language[2] === '-';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isDefault(): boolean {
|
||||
return language === 'en';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The OS locale or the locale specified by --locale. The format of
|
||||
* the string is all lower case (e.g. zh-tw for Traditional
|
||||
@@ -155,14 +176,3 @@ export const enum OperatingSystem {
|
||||
Linux = 3
|
||||
}
|
||||
export const OS = (_isMacintosh ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux));
|
||||
|
||||
export const enum AccessibilitySupport {
|
||||
/**
|
||||
* This should be the browser case where it is not known if a screen reader is attached or no.
|
||||
*/
|
||||
Unknown = 0,
|
||||
|
||||
Disabled = 1,
|
||||
|
||||
Enabled = 2
|
||||
}
|
||||
|
||||
27
src/vs/base/common/process.ts
Normal file
27
src/vs/base/common/process.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isWindows, isMacintosh, setImmediate } from 'vs/base/common/platform';
|
||||
|
||||
interface IProcess {
|
||||
platform: string;
|
||||
env: object;
|
||||
|
||||
cwd(): string;
|
||||
nextTick(callback: (...args: any[]) => void): number;
|
||||
}
|
||||
|
||||
declare const process: IProcess;
|
||||
const safeProcess: IProcess = (typeof process === 'undefined') ? {
|
||||
cwd(): string { return '/'; },
|
||||
env: Object.create(null),
|
||||
get platform(): string { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; },
|
||||
nextTick(callback: (...args: any[]) => void): number { return setImmediate(callback); }
|
||||
} : process;
|
||||
|
||||
export const cwd = safeProcess.cwd;
|
||||
export const env = safeProcess.env;
|
||||
export const platform = safeProcess.platform;
|
||||
export const nextTick = safeProcess.nextTick;
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
|
||||
/**
|
||||
* Options to be passed to the external program or shell.
|
||||
*/
|
||||
@@ -84,3 +86,30 @@ export const enum TerminateResponseCode {
|
||||
AccessDenied = 2,
|
||||
ProcessNotFound = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a VS Code process environment by removing all Electron/VS Code-related values.
|
||||
*/
|
||||
export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve: string[]): void {
|
||||
const set = preserve.reduce((set, key) => {
|
||||
set[key] = true;
|
||||
return set;
|
||||
}, {} as Record<string, boolean>);
|
||||
const keysToRemove = [
|
||||
/^ELECTRON_.+$/,
|
||||
/^GOOGLE_API_KEY$/,
|
||||
/^VSCODE_.+$/,
|
||||
/^SNAP(|_.*)$/
|
||||
];
|
||||
const envKeys = Object.keys(env);
|
||||
envKeys
|
||||
.filter(key => !set[key])
|
||||
.forEach(envKey => {
|
||||
for (let i = 0; i < keysToRemove.length; i++) {
|
||||
if (envKey.search(keysToRemove[i]) !== -1) {
|
||||
delete env[envKey];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob';
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
|
||||
export function getComparisonKey(resource: URI): string {
|
||||
return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString();
|
||||
@@ -32,22 +35,24 @@ export function basenameOrAuthority(resource: URI): string {
|
||||
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);
|
||||
return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), ignoreCase);
|
||||
}
|
||||
if (isEqualAuthority(base.authority, parentCandidate.authority, ignoreCase)) {
|
||||
return paths.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/');
|
||||
if (isEqualAuthority(base.authority, parentCandidate.authority)) {
|
||||
return extpath.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isEqualAuthority(a1: string, a2: string, ignoreCase?: boolean) {
|
||||
return a1 === a2 || ignoreCase && a1 && a2 && equalsIgnoreCase(a1, a2);
|
||||
/**
|
||||
* Tests wheter the two authorities are the same
|
||||
*/
|
||||
export function isEqualAuthority(a1: string, a2: string) {
|
||||
return a1 === a2 || equalsIgnoreCase(a1, a2);
|
||||
}
|
||||
|
||||
export function isEqual(first: URI | undefined, second: URI | undefined, ignoreCase = hasToIgnoreCase(first)): boolean {
|
||||
const identityEquals = (first === second);
|
||||
if (identityEquals) {
|
||||
if (first === second) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,15 +60,20 @@ export function isEqual(first: URI | undefined, second: URI | undefined, ignoreC
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
return equalsIgnoreCase(first.toString(), second.toString());
|
||||
if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return first.toString() === second.toString();
|
||||
const p1 = first.path || '/', p2 = second.path || '/';
|
||||
return p1 === p2 || ignoreCase && equalsIgnoreCase(p1 || '/', p2 || '/');
|
||||
}
|
||||
|
||||
export function basename(resource: URI): string {
|
||||
return paths.basename(resource.path);
|
||||
return paths.posix.basename(resource.path);
|
||||
}
|
||||
|
||||
export function extname(resource: URI): string {
|
||||
return paths.posix.extname(resource.path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,16 +82,17 @@ export function basename(resource: URI): string {
|
||||
* @param resource The input URI.
|
||||
* @returns The URI representing the directory of the input URI.
|
||||
*/
|
||||
export function dirname(resource: URI): URI | null {
|
||||
export function dirname(resource: URI): URI {
|
||||
if (resource.path.length === 0) {
|
||||
return resource;
|
||||
}
|
||||
if (resource.scheme === Schemas.file) {
|
||||
return URI.file(paths.dirname(fsPath(resource)));
|
||||
return URI.file(paths.dirname(originalFSPath(resource)));
|
||||
}
|
||||
let dirname = paths.dirname(resource.path, '/');
|
||||
let dirname = paths.posix.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
|
||||
console.error(`dirname("${resource.toString})) resulted in a relative path`);
|
||||
dirname = '/'; // 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
|
||||
@@ -89,18 +100,18 @@ export function dirname(resource: URI): URI | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a URI path with a path fragment and normalizes the resulting path.
|
||||
* Join a URI path with path fragments 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 {
|
||||
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;
|
||||
joinedPath = URI.file(paths.join(originalFSPath(resource), ...pathFragment)).path;
|
||||
} else {
|
||||
joinedPath = paths.join(resource.path, pathFragment);
|
||||
joinedPath = paths.posix.join(resource.path || '/', ...pathFragment);
|
||||
}
|
||||
return resource.with({
|
||||
path: joinedPath
|
||||
@@ -119,9 +130,9 @@ export function normalizePath(resource: URI): URI {
|
||||
}
|
||||
let normalizedPath: string;
|
||||
if (resource.scheme === Schemas.file) {
|
||||
normalizedPath = URI.file(paths.normalize(fsPath(resource))).path;
|
||||
normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path;
|
||||
} else {
|
||||
normalizedPath = paths.normalize(resource.path);
|
||||
normalizedPath = paths.posix.normalize(resource.path);
|
||||
}
|
||||
return resource.with({
|
||||
path: normalizedPath
|
||||
@@ -132,7 +143,7 @@ export function normalizePath(resource: URI): URI {
|
||||
* Returns the fsPath of an URI where the drive letter is not normalized.
|
||||
* See #56403.
|
||||
*/
|
||||
export function fsPath(uri: URI): string {
|
||||
export function originalFSPath(uri: URI): string {
|
||||
let value: string;
|
||||
const uriPath = uri.path;
|
||||
if (uri.authority && uriPath.length > 1 && uri.scheme === 'file') {
|
||||
@@ -141,7 +152,7 @@ export function fsPath(uri: URI): string {
|
||||
} else if (
|
||||
isWindows
|
||||
&& uriPath.charCodeAt(0) === CharCode.Slash
|
||||
&& paths.isWindowsDriveLetter(uriPath.charCodeAt(1))
|
||||
&& extpath.isWindowsDriveLetter(uriPath.charCodeAt(1))
|
||||
&& uriPath.charCodeAt(2) === CharCode.Colon
|
||||
) {
|
||||
value = uriPath.substr(1);
|
||||
@@ -159,7 +170,64 @@ export function fsPath(uri: URI): string {
|
||||
* Returns true if the URI path is absolute.
|
||||
*/
|
||||
export function isAbsolutePath(resource: URI): boolean {
|
||||
return paths.isAbsolute(resource.path);
|
||||
return !!resource.path && resource.path[0] === '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the URI path has a trailing path separator
|
||||
*/
|
||||
export function hasTrailingPathSeparator(resource: URI): boolean {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const fsp = originalFSPath(resource);
|
||||
return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === paths.sep;
|
||||
} else {
|
||||
const p = resource.path;
|
||||
return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a trailing path seperator, if theres one.
|
||||
* Important: Doesn't remove the first slash, it would make the URI invalid
|
||||
*/
|
||||
export function removeTrailingPathSeparator(resource: URI): URI {
|
||||
if (hasTrailingPathSeparator(resource)) {
|
||||
return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned.
|
||||
* The returned relative path always uses forward slashes.
|
||||
*/
|
||||
export function relativePath(from: URI, to: URI): string | undefined {
|
||||
if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
|
||||
return undefined;
|
||||
}
|
||||
if (from.scheme === Schemas.file) {
|
||||
const relativePath = paths.relative(from.path, to.path);
|
||||
return isWindows ? extpath.toSlashes(relativePath) : relativePath;
|
||||
}
|
||||
return paths.posix.relative(from.path || '/', to.path || '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a absolute or relative path against a base URI.
|
||||
*/
|
||||
export function resolvePath(base: URI, path: string): URI {
|
||||
if (base.scheme === Schemas.file) {
|
||||
const newURI = URI.file(paths.resolve(originalFSPath(base), path));
|
||||
return base.with({
|
||||
authority: newURI.authority,
|
||||
path: newURI.path
|
||||
});
|
||||
}
|
||||
return base.with({
|
||||
path: paths.posix.resolve(base.path, path)
|
||||
});
|
||||
}
|
||||
|
||||
export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] {
|
||||
@@ -182,21 +250,6 @@ 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 undefined;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data URI related helpers.
|
||||
*/
|
||||
@@ -230,3 +283,31 @@ export namespace DataUri {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ResourceGlobMatcher {
|
||||
|
||||
private readonly globalExpression: ParsedExpression;
|
||||
private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>();
|
||||
|
||||
constructor(
|
||||
globalExpression: IExpression,
|
||||
rootExpressions: { root: URI, expression: IExpression }[]
|
||||
) {
|
||||
this.globalExpression = parse(globalExpression);
|
||||
for (const expression of rootExpressions) {
|
||||
this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) });
|
||||
}
|
||||
}
|
||||
|
||||
matches(resource: URI): boolean {
|
||||
const rootExpression = this.expressionsByRoot.findSubstr(resource.toString());
|
||||
if (rootExpression) {
|
||||
const path = relativePath(rootExpression.root, resource);
|
||||
if (path && !!rootExpression.expression(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return !!this.globalExpression(resource.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,13 +117,13 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
||||
}
|
||||
|
||||
public createScrollEvent(previous: ScrollState): ScrollEvent {
|
||||
let widthChanged = (this.width !== previous.width);
|
||||
let scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth);
|
||||
let scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft);
|
||||
const widthChanged = (this.width !== previous.width);
|
||||
const scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth);
|
||||
const scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft);
|
||||
|
||||
let heightChanged = (this.height !== previous.height);
|
||||
let scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight);
|
||||
let scrollTopChanged = (this.scrollTop !== previous.scrollTop);
|
||||
const heightChanged = (this.height !== previous.height);
|
||||
const scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight);
|
||||
const scrollTopChanged = (this.scrollTop !== previous.scrollTop);
|
||||
|
||||
return {
|
||||
width: this.width,
|
||||
|
||||
@@ -21,8 +21,8 @@ export function isFalsyOrWhitespace(str: string | undefined): boolean {
|
||||
* @returns the provided number with the given number of preceding zeros.
|
||||
*/
|
||||
export function pad(n: number, l: number, char: string = '0'): string {
|
||||
let str = '' + n;
|
||||
let r = [str];
|
||||
const str = '' + n;
|
||||
const r = [str];
|
||||
|
||||
for (let i = str.length; i < l; i++) {
|
||||
r.push(char);
|
||||
@@ -44,7 +44,7 @@ export function format(value: string, ...args: any[]): string {
|
||||
return value;
|
||||
}
|
||||
return value.replace(_formatRegexp, function (match, group) {
|
||||
let idx = parseInt(group, 10);
|
||||
const idx = parseInt(group, 10);
|
||||
return isNaN(idx) || idx < 0 || idx >= args.length ?
|
||||
match :
|
||||
args[idx];
|
||||
@@ -79,7 +79,7 @@ export function escapeRegExpCharacters(value: string): string {
|
||||
* @param needle the thing to trim (default is a blank)
|
||||
*/
|
||||
export function trim(haystack: string, needle: string = ' '): string {
|
||||
let trimmed = ltrim(haystack, needle);
|
||||
const trimmed = ltrim(haystack, needle);
|
||||
return rtrim(trimmed, needle);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export function ltrim(haystack: string, needle: string): string {
|
||||
return haystack;
|
||||
}
|
||||
|
||||
let needleLen = needle.length;
|
||||
const needleLen = needle.length;
|
||||
if (needleLen === 0 || haystack.length === 0) {
|
||||
return haystack;
|
||||
}
|
||||
@@ -116,7 +116,7 @@ export function rtrim(haystack: string, needle: string): string {
|
||||
return haystack;
|
||||
}
|
||||
|
||||
let needleLen = needle.length,
|
||||
const needleLen = needle.length,
|
||||
haystackLen = haystack.length;
|
||||
|
||||
if (needleLen === 0 || haystackLen === 0) {
|
||||
@@ -173,7 +173,7 @@ export function startsWith(haystack: string, needle: string): boolean {
|
||||
* Determines if haystack ends with needle.
|
||||
*/
|
||||
export function endsWith(haystack: string, needle: string): boolean {
|
||||
let diff = haystack.length - needle.length;
|
||||
const diff = haystack.length - needle.length;
|
||||
if (diff > 0) {
|
||||
return haystack.indexOf(needle, diff) === diff;
|
||||
} else if (diff === 0) {
|
||||
@@ -232,7 +232,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('');
|
||||
const match = regexp.exec('');
|
||||
return !!(match && <any>regexp.lastIndex === 0);
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ export function regExpFlags(regexp: RegExp): string {
|
||||
*/
|
||||
export function firstNonWhitespaceIndex(str: string): number {
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
let chCode = str.charCodeAt(i);
|
||||
const chCode = str.charCodeAt(i);
|
||||
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
|
||||
return i;
|
||||
}
|
||||
@@ -267,7 +267,7 @@ export function firstNonWhitespaceIndex(str: string): number {
|
||||
*/
|
||||
export function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string {
|
||||
for (let i = start; i < end; i++) {
|
||||
let chCode = str.charCodeAt(i);
|
||||
const chCode = str.charCodeAt(i);
|
||||
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
|
||||
return str.substring(start, i);
|
||||
}
|
||||
@@ -281,7 +281,7 @@ export function getLeadingWhitespace(str: string, start: number = 0, end: number
|
||||
*/
|
||||
export function lastNonWhitespaceIndex(str: string, startIndex: number = str.length - 1): number {
|
||||
for (let i = startIndex; i >= 0; i--) {
|
||||
let chCode = str.charCodeAt(i);
|
||||
const chCode = str.charCodeAt(i);
|
||||
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
|
||||
return i;
|
||||
}
|
||||
@@ -380,7 +380,7 @@ function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean {
|
||||
|
||||
// a-z A-Z
|
||||
if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) {
|
||||
let diff = Math.abs(codeA - codeB);
|
||||
const diff = Math.abs(codeA - codeB);
|
||||
if (diff !== 0 && diff !== 32) {
|
||||
return false;
|
||||
}
|
||||
@@ -431,8 +431,8 @@ export function commonSuffixLength(a: string, b: string): number {
|
||||
let i: number,
|
||||
len = Math.min(a.length, b.length);
|
||||
|
||||
let aLastIndex = a.length - 1;
|
||||
let bLastIndex = b.length - 1;
|
||||
const aLastIndex = a.length - 1;
|
||||
const bLastIndex = b.length - 1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (a.charCodeAt(aLastIndex - i) !== b.charCodeAt(bLastIndex - i)) {
|
||||
@@ -459,7 +459,7 @@ function substrEquals(a: string, aStart: number, aEnd: number, b: string, bStart
|
||||
* For instance `overlap("foobar", "arr, I'm a pirate") === 2`.
|
||||
*/
|
||||
export function overlap(a: string, b: string): number {
|
||||
let aEnd = a.length;
|
||||
const aEnd = a.length;
|
||||
let bEnd = b.length;
|
||||
let aStart = aEnd - bEnd;
|
||||
|
||||
@@ -486,9 +486,9 @@ export function overlap(a: string, b: string): number {
|
||||
// Code points U+0000 to U+D7FF and U+E000 to U+FFFF are represented on a single character
|
||||
// Code points U+10000 to U+10FFFF are represented on two consecutive characters
|
||||
//export function getUnicodePoint(str:string, index:number, len:number):number {
|
||||
// let chrCode = str.charCodeAt(index);
|
||||
// const chrCode = str.charCodeAt(index);
|
||||
// if (0xD800 <= chrCode && chrCode <= 0xDBFF && index + 1 < len) {
|
||||
// let nextChrCode = str.charCodeAt(index + 1);
|
||||
// const nextChrCode = str.charCodeAt(index + 1);
|
||||
// if (0xDC00 <= nextChrCode && nextChrCode <= 0xDFFF) {
|
||||
// return (chrCode - 0xD800) << 10 + (nextChrCode - 0xDC00) + 0x10000;
|
||||
// }
|
||||
@@ -627,6 +627,21 @@ export function removeAnsiEscapeCodes(str: string): string {
|
||||
return str;
|
||||
}
|
||||
|
||||
export const removeAccents: (str: string) => string = (function () {
|
||||
if (typeof (String.prototype as any).normalize !== 'function') {
|
||||
// ☹️ no ES6 features...
|
||||
return function (str: string) { return str; };
|
||||
} else {
|
||||
// transform into NFD form and remove accents
|
||||
// see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463
|
||||
const regex = /[\u0300-\u036f]/g;
|
||||
return function (str: string) {
|
||||
return (str as any).normalize('NFD').replace(regex, empty);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
// -- UTF-8 BOM
|
||||
|
||||
export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM);
|
||||
@@ -670,7 +685,7 @@ export function fuzzyContains(target: string, query: string): boolean {
|
||||
let index = 0;
|
||||
let lastIndexOf = -1;
|
||||
while (index < queryLen) {
|
||||
let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1);
|
||||
const indexOf = targetLower.indexOf(query[index], lastIndexOf + 1);
|
||||
if (indexOf < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export function isStringArray(value: any): value is string[] {
|
||||
* @returns whether the provided parameter is of type `object` but **not**
|
||||
* `null`, an `array`, a `regexp`, nor a `date`.
|
||||
*/
|
||||
export function isObject(obj: any): boolean {
|
||||
export function isObject(obj: any): obj is Object {
|
||||
// The method can't do a type cast since there are type (like strings) which
|
||||
// are subclasses of any put not positvely matched by the function. Hence type
|
||||
// narrowing results in wrong results.
|
||||
@@ -143,8 +143,12 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi
|
||||
throw new Error(`argument does not match constraint: typeof ${constraint}`);
|
||||
}
|
||||
} else if (isFunction(constraint)) {
|
||||
if (arg instanceof constraint) {
|
||||
return;
|
||||
try {
|
||||
if (arg instanceof constraint) {
|
||||
return;
|
||||
}
|
||||
} catch{
|
||||
// ignore
|
||||
}
|
||||
if (!isUndefinedOrNull(arg) && arg.constructor === constraint) {
|
||||
return;
|
||||
@@ -161,8 +165,42 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi
|
||||
* any additional argument supplied.
|
||||
*/
|
||||
export function create(ctor: Function, ...args: any[]): any {
|
||||
let obj = Object.create(ctor.prototype);
|
||||
ctor.apply(obj, args);
|
||||
|
||||
return obj;
|
||||
if (isNativeClass(ctor)) {
|
||||
return new (ctor as any)(...args);
|
||||
} else {
|
||||
const obj = Object.create(ctor.prototype);
|
||||
ctor.apply(obj, args);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/32235645/1499159
|
||||
function isNativeClass(thing): boolean {
|
||||
return typeof thing === 'function'
|
||||
&& thing.hasOwnProperty('prototype')
|
||||
&& !thing.hasOwnProperty('arguments');
|
||||
}
|
||||
|
||||
export function getAllPropertyNames(obj: object): string[] {
|
||||
let res: string[] = [];
|
||||
let proto = Object.getPrototypeOf(obj);
|
||||
while (Object.prototype !== proto) {
|
||||
res = res.concat(Object.getOwnPropertyNames(proto));
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts null to undefined, passes all other values through.
|
||||
*/
|
||||
export function withNullAsUndefined<T>(x: T | null): T | undefined {
|
||||
return x === null ? undefined : x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts undefined to null, passes all other values through.
|
||||
*/
|
||||
export function withUndefinedAsNull<T>(x: T | undefined): T | null {
|
||||
return typeof x === 'undefined' ? null : x;
|
||||
}
|
||||
112
src/vs/base/common/uint.ts
Normal file
112
src/vs/base/common/uint.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function toUint8ArrayBuffer(str: string): ArrayBuffer {
|
||||
|
||||
if (typeof TextEncoder !== 'undefined') {
|
||||
return new TextEncoder().encode(str).buffer;
|
||||
}
|
||||
|
||||
let i: number, len: number, length = 0, charCode = 0, trailCharCode = 0, codepoint = 0;
|
||||
|
||||
// First pass, for the size
|
||||
for (i = 0, len = str.length; i < len; i++) {
|
||||
charCode = str.charCodeAt(i);
|
||||
|
||||
// Surrogate pair
|
||||
if (charCode >= 0xD800 && charCode < 0xDC00) {
|
||||
trailCharCode = str.charCodeAt(++i);
|
||||
|
||||
if (!(trailCharCode >= 0xDC00 && trailCharCode < 0xE000)) {
|
||||
throw new Error('Invalid char code');
|
||||
}
|
||||
|
||||
// Code point can be obtained by subtracting 0xD800 and 0xDC00 from both char codes respectively
|
||||
// and joining the 10 least significant bits from each, finally adding 0x10000.
|
||||
codepoint = ((((charCode - 0xD800) & 0x3FF) << 10) | ((trailCharCode - 0xDC00) & 0x3FF)) + 0x10000;
|
||||
|
||||
} else {
|
||||
codepoint = charCode;
|
||||
}
|
||||
|
||||
length += byteSizeInUTF8(codepoint);
|
||||
}
|
||||
|
||||
let result = new ArrayBuffer(length);
|
||||
let view = new Uint8Array(result);
|
||||
let pos = 0;
|
||||
|
||||
// Second pass, for the data
|
||||
for (i = 0, len = str.length; i < len; i++) {
|
||||
charCode = str.charCodeAt(i);
|
||||
|
||||
if (charCode >= 0xD800 && charCode < 0xDC00) {
|
||||
trailCharCode = str.charCodeAt(++i);
|
||||
codepoint = ((((charCode - 0xD800) & 0x3FF) << 10) | ((trailCharCode - 0xDC00) & 0x3FF)) + 0x10000;
|
||||
} else {
|
||||
codepoint = charCode;
|
||||
}
|
||||
|
||||
pos += writeUTF8(codepoint, view, pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function byteSizeInUTF8(codePoint: number): number {
|
||||
codePoint = codePoint >>> 0;
|
||||
|
||||
if (codePoint < 0x80) {
|
||||
return 1;
|
||||
} else if (codePoint < 0x800) {
|
||||
return 2;
|
||||
} else if (codePoint < 0x10000) {
|
||||
return 3;
|
||||
} else if (codePoint < 0x200000) {
|
||||
return 4;
|
||||
} else if (codePoint < 0x4000000) {
|
||||
return 5;
|
||||
} else if (codePoint < 0x80000000) {
|
||||
return 6;
|
||||
} else {
|
||||
throw new Error('Code point 0x' + toHexString(codePoint) + ' not encodable in UTF8.');
|
||||
}
|
||||
}
|
||||
|
||||
function writeUTF8(codePoint: number, buffer: Uint8Array, pos: number): number {
|
||||
|
||||
// How many bits needed for codePoint
|
||||
let byteSize = byteSizeInUTF8(codePoint);
|
||||
|
||||
// 0xxxxxxx
|
||||
if (byteSize === 1) {
|
||||
buffer[pos] = codePoint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 110xxxxx 10xxxxxx
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
// 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
// 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
// first byte
|
||||
buffer[pos] = ((0xFC << (6 - byteSize)) | (codePoint >>> (6 * (byteSize - 1)))) & 0xFF;
|
||||
|
||||
// successive bytes
|
||||
for (let i = 1; i < byteSize; i++) {
|
||||
buffer[pos + i] = (0x80 | (0x3F & (codePoint >>> (6 * (byteSize - i - 1))))) & 0xFF;
|
||||
}
|
||||
|
||||
return byteSize;
|
||||
}
|
||||
|
||||
function leftPad(value: string, length: number, char: string = '0'): string {
|
||||
return new Array(length - value.length + 1).join(char) + value;
|
||||
}
|
||||
|
||||
function toHexString(value: number, bitsize: number = 32): string {
|
||||
return leftPad((value >>> 0).toString(16), bitsize / 4);
|
||||
}
|
||||
@@ -56,6 +56,21 @@ function _validateUri(ret: URI, _strict?: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
// for a while we allowed uris *without* schemes and this is the migration
|
||||
// for them, e.g. an uri without scheme and without strict-mode warns and falls
|
||||
// back to the file-scheme. that should cause the least carnage and still be a
|
||||
// clear warning
|
||||
function _schemeFix(scheme: string, _strict: boolean): string {
|
||||
if (_strict || _throwOnMissingSchema) {
|
||||
return scheme || _empty;
|
||||
}
|
||||
if (!scheme) {
|
||||
console.trace('BAD uri lacks scheme, falling back to file-scheme.');
|
||||
scheme = 'file';
|
||||
}
|
||||
return scheme;
|
||||
}
|
||||
|
||||
// implements a bit of https://tools.ietf.org/html/rfc3986#section-5
|
||||
function _referenceResolution(scheme: string, path: string): string {
|
||||
|
||||
@@ -154,7 +169,7 @@ export class URI implements UriComponents {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean) {
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) {
|
||||
|
||||
if (typeof schemeOrData === 'object') {
|
||||
this.scheme = schemeOrData.scheme || _empty;
|
||||
@@ -166,7 +181,7 @@ export class URI implements UriComponents {
|
||||
// that creates uri components.
|
||||
// _validateUri(this);
|
||||
} else {
|
||||
this.scheme = schemeOrData || _empty;
|
||||
this.scheme = _schemeFix(schemeOrData, _strict);
|
||||
this.authority = authority || _empty;
|
||||
this.path = _referenceResolution(this.scheme, path || _empty);
|
||||
this.query = query || _empty;
|
||||
@@ -314,7 +329,7 @@ export class URI implements UriComponents {
|
||||
// check for authority as used in UNC shares
|
||||
// or use the path as given
|
||||
if (path[0] === _slash && path[1] === _slash) {
|
||||
let idx = path.indexOf(_slash, 2);
|
||||
const idx = path.indexOf(_slash, 2);
|
||||
if (idx === -1) {
|
||||
authority = path.substring(2);
|
||||
path = _slash;
|
||||
@@ -364,7 +379,7 @@ export class URI implements UriComponents {
|
||||
} else if (data instanceof URI) {
|
||||
return data;
|
||||
} else {
|
||||
let result = new _URI(data);
|
||||
const result = new _URI(data);
|
||||
result._fsPath = (<UriState>data).fsPath;
|
||||
result._formatted = (<UriState>data).external;
|
||||
return result;
|
||||
@@ -473,7 +488,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
||||
let nativeEncodePos = -1;
|
||||
|
||||
for (let pos = 0; pos < uriComponent.length; pos++) {
|
||||
let code = uriComponent.charCodeAt(pos);
|
||||
const code = uriComponent.charCodeAt(pos);
|
||||
|
||||
// unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3
|
||||
if (
|
||||
@@ -503,7 +518,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
||||
}
|
||||
|
||||
// check with default table first
|
||||
let escaped = encodeTable[code];
|
||||
const escaped = encodeTable[code];
|
||||
if (escaped !== undefined) {
|
||||
|
||||
// check if we are delaying native encode
|
||||
@@ -532,7 +547,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
||||
function encodeURIComponentMinimal(path: string): string {
|
||||
let res: string | undefined = undefined;
|
||||
for (let pos = 0; pos < path.length; pos++) {
|
||||
let code = path.charCodeAt(pos);
|
||||
const code = path.charCodeAt(pos);
|
||||
if (code === CharCode.Hash || code === CharCode.QuestionMark) {
|
||||
if (res === undefined) {
|
||||
res = path.substr(0, pos);
|
||||
@@ -622,12 +637,12 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
||||
if (path) {
|
||||
// lower-case windows drive letters in /C:/fff or C:/fff
|
||||
if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) {
|
||||
let code = path.charCodeAt(1);
|
||||
const code = path.charCodeAt(1);
|
||||
if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3
|
||||
}
|
||||
} else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) {
|
||||
let code = path.charCodeAt(0);
|
||||
const code = path.charCodeAt(0);
|
||||
if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { transformErrorForSerialization } from 'vs/base/common/errors';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { getAllPropertyNames } from 'vs/base/common/types';
|
||||
|
||||
const INITIALIZE = '$initialize';
|
||||
|
||||
@@ -324,7 +325,7 @@ export class SimpleWorkerServer {
|
||||
if (this._requestHandler) {
|
||||
// static request handler
|
||||
let methods: string[] = [];
|
||||
for (let prop in this._requestHandler) {
|
||||
for (const prop of getAllPropertyNames(this._requestHandler)) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
@@ -360,7 +361,7 @@ export class SimpleWorkerServer {
|
||||
}
|
||||
|
||||
let methods: string[] = [];
|
||||
for (let prop in this._requestHandler) {
|
||||
for (const prop of getAllPropertyNames(this._requestHandler)) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user