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:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -69,4 +69,4 @@ export class Hasher {
this._value = hash(obj, this._value);
return this._value;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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