mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 19:18:32 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -3,17 +3,27 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ITelemetryData {
|
||||
from?: string;
|
||||
target?: string;
|
||||
readonly from?: string;
|
||||
readonly target?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface IAction extends IDisposable {
|
||||
export type WorkbenchActionExecutedClassification = {
|
||||
id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
from: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
|
||||
export type WorkbenchActionExecutedEvent = {
|
||||
id: string;
|
||||
from: string;
|
||||
};
|
||||
|
||||
export interface IAction extends IDisposable {
|
||||
readonly id: string;
|
||||
label: string;
|
||||
tooltip: string;
|
||||
class: string | undefined;
|
||||
@@ -25,44 +35,44 @@ export interface IAction extends IDisposable {
|
||||
|
||||
export interface IActionRunner extends IDisposable {
|
||||
run(action: IAction, context?: any): Promise<any>;
|
||||
onDidRun: Event<IRunEvent>;
|
||||
onDidBeforeRun: Event<IRunEvent>;
|
||||
readonly onDidRun: Event<IRunEvent>;
|
||||
readonly onDidBeforeRun: Event<IRunEvent>;
|
||||
}
|
||||
|
||||
export interface IActionViewItem {
|
||||
actionRunner: IActionRunner;
|
||||
export interface IActionViewItem extends IDisposable {
|
||||
readonly actionRunner: IActionRunner;
|
||||
setActionContext(context: any): void;
|
||||
render(element: any /* HTMLElement */): void;
|
||||
isEnabled(): boolean;
|
||||
focus(): void;
|
||||
blur(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface IActionChangeEvent {
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
class?: string;
|
||||
enabled?: boolean;
|
||||
checked?: boolean;
|
||||
radio?: boolean;
|
||||
readonly label?: string;
|
||||
readonly tooltip?: string;
|
||||
readonly class?: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly checked?: boolean;
|
||||
readonly radio?: boolean;
|
||||
}
|
||||
|
||||
export class Action implements IAction {
|
||||
export class Action extends Disposable implements IAction {
|
||||
|
||||
protected _onDidChange = new Emitter<IActionChangeEvent>();
|
||||
protected _onDidChange = this._register(new Emitter<IActionChangeEvent>());
|
||||
readonly onDidChange: Event<IActionChangeEvent> = this._onDidChange.event;
|
||||
|
||||
protected _id: string;
|
||||
protected readonly _id: string;
|
||||
protected _label: string;
|
||||
protected _tooltip: string;
|
||||
protected _cssClass: string | undefined;
|
||||
protected _enabled: boolean;
|
||||
protected _checked: boolean;
|
||||
protected _radio: boolean;
|
||||
protected _actionCallback?: (event?: any) => Promise<any>;
|
||||
protected readonly _actionCallback?: (event?: any) => Promise<any>;
|
||||
|
||||
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise<any>) {
|
||||
super();
|
||||
this._id = id;
|
||||
this._label = label;
|
||||
this._cssClass = cssClass;
|
||||
@@ -82,7 +92,7 @@ export class Action implements IAction {
|
||||
this._setLabel(value);
|
||||
}
|
||||
|
||||
protected _setLabel(value: string): void {
|
||||
private _setLabel(value: string): void {
|
||||
if (this._label !== value) {
|
||||
this._label = value;
|
||||
this._onDidChange.fire({ label: value });
|
||||
@@ -171,16 +181,12 @@ export class Action implements IAction {
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRunEvent {
|
||||
action: IAction;
|
||||
result?: any;
|
||||
error?: any;
|
||||
readonly action: IAction;
|
||||
readonly result?: any;
|
||||
readonly error?: any;
|
||||
}
|
||||
|
||||
export class ActionRunner extends Disposable implements IActionRunner {
|
||||
@@ -217,8 +223,8 @@ export class RadioGroup extends Disposable {
|
||||
constructor(readonly actions: Action[]) {
|
||||
super();
|
||||
|
||||
this._register(combinedDisposable(actions.map(action => {
|
||||
return action.onDidChange(e => {
|
||||
for (const action of actions) {
|
||||
this._register(action.onDidChange(e => {
|
||||
if (e.checked && action.checked) {
|
||||
for (const candidate of actions) {
|
||||
if (candidate !== action) {
|
||||
@@ -226,7 +232,7 @@ export class RadioGroup extends Disposable {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})));
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,9 +295,9 @@ function topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, re
|
||||
/**
|
||||
* @returns a new array with all falsy values removed. The original array IS NOT modified.
|
||||
*/
|
||||
export function coalesce<T>(array: Array<T | undefined | null>): T[] {
|
||||
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
|
||||
if (!array) {
|
||||
return array;
|
||||
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-checks
|
||||
}
|
||||
return <T[]>array.filter(e => !!e);
|
||||
}
|
||||
@@ -336,7 +336,9 @@ export function isFalsyOrEmpty(obj: any): boolean {
|
||||
/**
|
||||
* @returns True if the provided object is an array and has at least one element.
|
||||
*/
|
||||
export function isNonEmptyArray<T>(obj: ReadonlyArray<T> | undefined | null): obj is Array<T> {
|
||||
export function isNonEmptyArray<T>(obj: T[] | undefined | null): obj is T[];
|
||||
export function isNonEmptyArray<T>(obj: readonly T[] | undefined | null): obj is readonly T[];
|
||||
export function isNonEmptyArray<T>(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] {
|
||||
return Array.isArray(obj) && obj.length > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function isThenable<T>(obj: any): obj is Promise<T> {
|
||||
@@ -415,11 +415,11 @@ export class Limiter<T> {
|
||||
this._onFinished = new Emitter<void>();
|
||||
}
|
||||
|
||||
public get onFinished(): Event<void> {
|
||||
get onFinished(): Event<void> {
|
||||
return this._onFinished.event;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
get size(): number {
|
||||
return this._size;
|
||||
// return this.runningPromises + this.outstandingPromises.length;
|
||||
}
|
||||
@@ -455,7 +455,7 @@ export class Limiter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this._onFinished.dispose();
|
||||
}
|
||||
}
|
||||
@@ -475,35 +475,30 @@ export class Queue<T> extends Limiter<T> {
|
||||
* by disposing them once the queue is empty.
|
||||
*/
|
||||
export class ResourceQueue {
|
||||
private queues: { [path: string]: Queue<void> };
|
||||
private queues: Map<string, Queue<void>> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.queues = Object.create(null);
|
||||
}
|
||||
|
||||
public queueFor(resource: URI): Queue<void> {
|
||||
queueFor(resource: URI): Queue<void> {
|
||||
const key = resource.toString();
|
||||
if (!this.queues[key]) {
|
||||
if (!this.queues.has(key)) {
|
||||
const queue = new Queue<void>();
|
||||
queue.onFinished(() => {
|
||||
queue.dispose();
|
||||
delete this.queues[key];
|
||||
this.queues.delete(key);
|
||||
});
|
||||
|
||||
this.queues[key] = queue;
|
||||
this.queues.set(key, queue);
|
||||
}
|
||||
|
||||
return this.queues[key];
|
||||
return this.queues.get(key)!;
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeoutTimer extends Disposable {
|
||||
export class TimeoutTimer implements IDisposable {
|
||||
private _token: any;
|
||||
|
||||
constructor();
|
||||
constructor(runner: () => void, timeout: number);
|
||||
constructor(runner?: () => void, timeout?: number) {
|
||||
super();
|
||||
this._token = -1;
|
||||
|
||||
if (typeof runner === 'function' && typeof timeout === 'number') {
|
||||
@@ -513,7 +508,6 @@ export class TimeoutTimer extends Disposable {
|
||||
|
||||
dispose(): void {
|
||||
this.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
@@ -543,18 +537,16 @@ export class TimeoutTimer extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class IntervalTimer extends Disposable {
|
||||
export class IntervalTimer implements IDisposable {
|
||||
|
||||
private _token: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._token = -1;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
|
||||
@@ -78,7 +78,10 @@ export class VSBuffer {
|
||||
}
|
||||
|
||||
slice(start?: number, end?: number): VSBuffer {
|
||||
return new VSBuffer(this.buffer.slice(start, end));
|
||||
// IMPORTANT: use subarray instead of slice because TypedArray#slice
|
||||
// creates shallow copy and NodeBuffer#slice doesn't. The use of subarray
|
||||
// ensures the same, performant, behaviour.
|
||||
return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end));
|
||||
}
|
||||
|
||||
set(array: VSBuffer, offset?: number): void {
|
||||
@@ -138,18 +141,13 @@ export interface VSBufferReadable {
|
||||
read(): VSBuffer | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer readable stream emits data to listeners. The stream
|
||||
* will only start emitting when the first data listener has
|
||||
* been added or the resume() method has been called.
|
||||
*/
|
||||
export interface VSBufferReadableStream {
|
||||
export interface ReadableStream<T> {
|
||||
|
||||
/**
|
||||
* The 'data' event is emitted whenever the stream is
|
||||
* relinquishing ownership of a chunk of data to a consumer.
|
||||
*/
|
||||
on(event: 'data', callback: (chunk: VSBuffer) => void): void;
|
||||
on(event: 'data', callback: (chunk: T) => void): void;
|
||||
|
||||
/**
|
||||
* Emitted when any error occurs.
|
||||
@@ -166,19 +164,34 @@ export interface VSBufferReadableStream {
|
||||
/**
|
||||
* Stops emitting any events until resume() is called.
|
||||
*/
|
||||
pause(): void;
|
||||
pause?(): void;
|
||||
|
||||
/**
|
||||
* Starts emitting events again after pause() was called.
|
||||
*/
|
||||
resume(): void;
|
||||
resume?(): void;
|
||||
|
||||
/**
|
||||
* Destroys the stream and stops emitting any event.
|
||||
*/
|
||||
destroy?(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readable stream that sends data via VSBuffer.
|
||||
*/
|
||||
export interface VSBufferReadableStream extends ReadableStream<VSBuffer> {
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export function isVSBufferReadableStream(obj: any): obj is VSBufferReadableStream {
|
||||
const candidate: VSBufferReadableStream = obj;
|
||||
|
||||
return candidate && [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to fully read a VSBuffer readable into a single buffer.
|
||||
*/
|
||||
@@ -236,6 +249,19 @@ export function bufferToStream(buffer: VSBuffer): VSBufferReadableStream {
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a VSBufferStream from a Uint8Array stream.
|
||||
*/
|
||||
export function toVSBufferReadableStream(stream: ReadableStream<Uint8Array | string>): VSBufferReadableStream {
|
||||
const vsbufferStream = writeableBufferStream();
|
||||
|
||||
stream.on('data', data => vsbufferStream.write(typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data)));
|
||||
stream.on('end', () => vsbufferStream.end());
|
||||
stream.on('error', error => vsbufferStream.error(error));
|
||||
|
||||
return vsbufferStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a VSBufferStream that can be pushed
|
||||
* buffers to. Will only start to emit data when a listener
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface CacheResult<T> {
|
||||
export interface CacheResult<T> extends IDisposable {
|
||||
promise: Promise<T>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class Cache<T> {
|
||||
|
||||
@@ -46,9 +46,9 @@ export function size<T>(from: IStringDictionary<T> | INumberDictionary<T>): numb
|
||||
}
|
||||
|
||||
export function first<T>(from: IStringDictionary<T> | INumberDictionary<T>): T | undefined {
|
||||
for (let key in from) {
|
||||
for (const key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
return from[key];
|
||||
return (from as any)[key];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -99,3 +99,43 @@ export function fromMap<T>(original: Map<string, T>): IStringDictionary<T> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class SetMap<K, V> {
|
||||
|
||||
private map = new Map<K, Set<V>>();
|
||||
|
||||
add(key: K, value: V): void {
|
||||
let values = this.map.get(key);
|
||||
|
||||
if (!values) {
|
||||
values = new Set<V>();
|
||||
this.map.set(key, values);
|
||||
}
|
||||
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
delete(key: K, value: V): void {
|
||||
const values = this.map.get(key);
|
||||
|
||||
if (!values) {
|
||||
return;
|
||||
}
|
||||
|
||||
values.delete(value);
|
||||
|
||||
if (values.size === 0) {
|
||||
this.map.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
forEach(key: K, fn: (value: V) => void): void {
|
||||
const values = this.map.get(key);
|
||||
|
||||
if (!values) {
|
||||
return;
|
||||
}
|
||||
|
||||
values.forEach(fn);
|
||||
}
|
||||
}
|
||||
@@ -7,28 +7,26 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
|
||||
let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>;
|
||||
|
||||
export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>): void {
|
||||
intlFileNameCollator = collator;
|
||||
}
|
||||
const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => {
|
||||
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
||||
return {
|
||||
collator: collator,
|
||||
collatorIsNumeric: collator.resolvedOptions().numeric
|
||||
};
|
||||
});
|
||||
|
||||
export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
|
||||
if (intlFileNameCollator) {
|
||||
const a = one || '';
|
||||
const b = other || '';
|
||||
const result = intlFileNameCollator.getValue().collator.compare(a, b);
|
||||
const a = one || '';
|
||||
const b = other || '';
|
||||
const result = intlFileNameCollator.getValue().collator.compare(a, b);
|
||||
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
return noIntlCompareFileNames(one, other, caseSensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
|
||||
@@ -54,46 +52,27 @@ export function noIntlCompareFileNames(one: string | null, other: string | null,
|
||||
}
|
||||
|
||||
export function compareFileExtensions(one: string | null, other: string | null): number {
|
||||
if (intlFileNameCollator) {
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other);
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one);
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other);
|
||||
|
||||
let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
|
||||
let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
|
||||
|
||||
if (result === 0) {
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
}
|
||||
|
||||
// Extensions are equal, compare filenames
|
||||
result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
|
||||
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
}
|
||||
if (result === 0) {
|
||||
// Using the numeric option in the collator will
|
||||
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
// Extensions are equal, compare filenames
|
||||
result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
|
||||
|
||||
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
|
||||
return oneName < otherName ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return noIntlCompareFileExtensions(one, other);
|
||||
}
|
||||
|
||||
function noIntlCompareFileExtensions(one: string | null, other: string | null): number {
|
||||
const [oneName, oneExtension] = extractNameAndExtension(one && one.toLowerCase());
|
||||
const [otherName, otherExtension] = extractNameAndExtension(other && other.toLowerCase());
|
||||
|
||||
if (oneExtension !== otherExtension) {
|
||||
return oneExtension < otherExtension ? -1 : 1;
|
||||
}
|
||||
|
||||
if (oneName === otherName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return oneName < otherName ? -1 : 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
function extractNameAndExtension(str?: string | null): [string, string] {
|
||||
|
||||
@@ -131,7 +131,10 @@ export function log(entry: IRemoteConsoleLog, label: string): void {
|
||||
}
|
||||
|
||||
// Log it
|
||||
console[entry.severity].apply(console, consoleArgs);
|
||||
if (typeof (console as any)[entry.severity] !== 'function') {
|
||||
throw new Error('Unknown console method');
|
||||
}
|
||||
(console as any)[entry.severity].apply(console, consoleArgs);
|
||||
}
|
||||
|
||||
function color(color: string): string {
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as arrays from 'vs/base/common/arrays';
|
||||
function exceptionToErrorMessage(exception: any, verbose: boolean): string {
|
||||
if (exception.message) {
|
||||
if (verbose && (exception.stack || exception.stacktrace)) {
|
||||
return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), exception.stack || exception.stacktrace);
|
||||
return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace));
|
||||
}
|
||||
|
||||
return detectSystemErrorMessage(exception);
|
||||
@@ -19,6 +19,14 @@ function exceptionToErrorMessage(exception: any, verbose: boolean): string {
|
||||
return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
|
||||
}
|
||||
|
||||
function stackToString(stack: string[] | string | undefined): string | undefined {
|
||||
if (Array.isArray(stack)) {
|
||||
return stack.join('\n');
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
function detectSystemErrorMessage(exception: any): string {
|
||||
|
||||
// See https://nodejs.org/api/errors.html#errors_class_system_error
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
export interface IErrorOptions {
|
||||
actions?: IAction[];
|
||||
actions?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export interface IErrorWithActions {
|
||||
actions?: IAction[];
|
||||
actions?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export function isErrorWithActions(obj: any): obj is IErrorWithActions {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { once as onceFn } from 'vs/base/common/functional';
|
||||
import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
|
||||
/**
|
||||
@@ -13,12 +13,11 @@ import { LinkedList } from 'vs/base/common/linkedList';
|
||||
* can be subscribed. The event is the subscriber function itself.
|
||||
*/
|
||||
export interface Event<T> {
|
||||
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
|
||||
}
|
||||
|
||||
export namespace Event {
|
||||
const _disposable = { dispose() { } };
|
||||
export const None: Event<any> = function () { return _disposable; };
|
||||
export const None: Event<any> = () => Disposable.None;
|
||||
|
||||
/**
|
||||
* Given an event, returns another event which only fires once.
|
||||
@@ -50,7 +49,7 @@ export namespace Event {
|
||||
|
||||
/**
|
||||
* Given an event and a `map` function, returns another event which maps each element
|
||||
* throught the mapping function.
|
||||
* through the mapping function.
|
||||
*/
|
||||
export function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables));
|
||||
@@ -86,12 +85,12 @@ export namespace Event {
|
||||
* whenever any of the provided events emit.
|
||||
*/
|
||||
export function any<T>(...events: Event<T>[]): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
|
||||
return (listener, thisArgs = null, disposables?) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event and a `merge` function, returns another event which maps each element
|
||||
* and the cummulative result throught the `merge` function. Similar to `map`, but with memory.
|
||||
* and the cumulative result through the `merge` function. Similar to `map`, but with memory.
|
||||
*/
|
||||
export function reduce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, initial?: O): Event<O> {
|
||||
let output: O | undefined = initial;
|
||||
@@ -272,7 +271,7 @@ export namespace Event {
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T>;
|
||||
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
|
||||
latch(): IChainableEvent<T>;
|
||||
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
|
||||
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
}
|
||||
|
||||
@@ -300,7 +299,7 @@ export namespace Event {
|
||||
return new ChainableEvent(latch(this.event));
|
||||
}
|
||||
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[] | DisposableStore) {
|
||||
return this.event(listener, thisArgs, disposables);
|
||||
}
|
||||
|
||||
@@ -477,7 +476,7 @@ export class Emitter<T> {
|
||||
*/
|
||||
get event(): Event<T> {
|
||||
if (!this._event) {
|
||||
this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]) => {
|
||||
this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => {
|
||||
if (!this._listeners) {
|
||||
this._listeners = new LinkedList();
|
||||
}
|
||||
@@ -522,7 +521,9 @@ export class Emitter<T> {
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Array.isArray(disposables)) {
|
||||
if (disposables instanceof DisposableStore) {
|
||||
disposables.add(result);
|
||||
} else if (Array.isArray(disposables)) {
|
||||
disposables.push(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,11 @@ const wordSeparators = new Set<number>();
|
||||
.forEach(s => wordSeparators.add(s.charCodeAt(0)));
|
||||
|
||||
function isWordSeparator(code: number): boolean {
|
||||
return wordSeparators.has(code);
|
||||
return isWhitespace(code) || wordSeparators.has(code);
|
||||
}
|
||||
|
||||
function charactersMatch(codeA: number, codeB: number): boolean {
|
||||
return (codeA === codeB) || (isWordSeparator(codeA) && isWordSeparator(codeB));
|
||||
}
|
||||
|
||||
function isAlphanumeric(code: number): boolean {
|
||||
@@ -298,7 +302,7 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti
|
||||
return [];
|
||||
} else if (j === target.length) {
|
||||
return null;
|
||||
} else if (word[i] !== target[j]) {
|
||||
} else if (!charactersMatch(word.charCodeAt(i), target.charCodeAt(j))) {
|
||||
return null;
|
||||
} else {
|
||||
let result: IMatch[] | null = null;
|
||||
@@ -316,9 +320,8 @@ 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++) {
|
||||
const c = word.charCodeAt(i);
|
||||
if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1))) ||
|
||||
isWordSeparator(c) || (i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) {
|
||||
if (isWordSeparator(word.charCodeAt(i)) ||
|
||||
(i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -492,7 +495,7 @@ function isUpperCaseAtPos(pos: number, word: string, wordLow: string): boolean {
|
||||
return word[pos] !== wordLow[pos];
|
||||
}
|
||||
|
||||
function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean {
|
||||
export function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean {
|
||||
while (patternPos < patternLen && wordPos < wordLen) {
|
||||
if (patternLow[patternPos] === wordLow[wordPos]) {
|
||||
patternPos += 1;
|
||||
|
||||
@@ -458,14 +458,14 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG
|
||||
if (parsedPattern === NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
const resultPattern = function (path: string, basename: string) {
|
||||
const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename: string) {
|
||||
return !!parsedPattern(path, basename);
|
||||
};
|
||||
if (parsedPattern.allBasenames) {
|
||||
(<ParsedStringPattern><any>resultPattern).allBasenames = parsedPattern.allBasenames;
|
||||
resultPattern.allBasenames = parsedPattern.allBasenames;
|
||||
}
|
||||
if (parsedPattern.allPaths) {
|
||||
(<ParsedStringPattern><any>resultPattern).allPaths = parsedPattern.allPaths;
|
||||
resultPattern.allPaths = parsedPattern.allPaths;
|
||||
}
|
||||
return resultPattern;
|
||||
}
|
||||
|
||||
@@ -92,3 +92,25 @@ export function removeMarkdownEscapes(text: string): string {
|
||||
}
|
||||
return text.replace(/\\([\\`*_{}[\]()#+\-.!])/g, '$1');
|
||||
}
|
||||
|
||||
export function parseHrefAndDimensions(href: string): { href: string, dimensions: string[] } {
|
||||
const dimensions: string[] = [];
|
||||
const splitted = href.split('|').map(s => s.trim());
|
||||
href = splitted[0];
|
||||
const parameters = splitted[1];
|
||||
if (parameters) {
|
||||
const heightFromParams = /height=(\d+)/.exec(parameters);
|
||||
const widthFromParams = /width=(\d+)/.exec(parameters);
|
||||
const height = heightFromParams ? heightFromParams[1] : '';
|
||||
const width = widthFromParams ? widthFromParams[1] : '';
|
||||
const widthIsFinite = isFinite(parseInt(width));
|
||||
const heightIsFinite = isFinite(parseInt(height));
|
||||
if (widthIsFinite) {
|
||||
dimensions.push(`width="${width}"`);
|
||||
}
|
||||
if (heightIsFinite) {
|
||||
dimensions.push(`height="${height}"`);
|
||||
}
|
||||
}
|
||||
return { href, dimensions };
|
||||
}
|
||||
|
||||
@@ -5,6 +5,45 @@
|
||||
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
/**
|
||||
* Enables logging of potentially leaked disposables.
|
||||
*
|
||||
* A disposable is considered leaked if it is not disposed or not registered as the child of
|
||||
* another disposable. This tracking is very simple an only works for classes that either
|
||||
* extend Disposable or use a DisposableStore. This means there are a lot of false positives.
|
||||
*/
|
||||
const TRACK_DISPOSABLES = false;
|
||||
|
||||
const __is_disposable_tracked__ = '__is_disposable_tracked__';
|
||||
|
||||
function markTracked<T extends IDisposable>(x: T): void {
|
||||
if (!TRACK_DISPOSABLES) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (x && x !== Disposable.None) {
|
||||
try {
|
||||
(x as any)[__is_disposable_tracked__] = true;
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function trackDisposable<T extends IDisposable>(x: T): T {
|
||||
if (!TRACK_DISPOSABLES) {
|
||||
return x;
|
||||
}
|
||||
|
||||
const stack = new Error('Potentially leaked disposable').stack!;
|
||||
setTimeout(() => {
|
||||
if (!(x as any)[__is_disposable_tracked__]) {
|
||||
console.log(stack);
|
||||
}
|
||||
}, 3000);
|
||||
return x;
|
||||
}
|
||||
|
||||
export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
@@ -15,56 +54,156 @@ export function isDisposable<E extends object>(thing: E): thing is E & IDisposab
|
||||
}
|
||||
|
||||
export function dispose<T extends IDisposable>(disposable: T): T;
|
||||
export function dispose<T extends IDisposable>(...disposables: Array<T | undefined>): T[];
|
||||
export function dispose<T extends IDisposable>(disposables: T[]): T[];
|
||||
export function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] | undefined {
|
||||
if (Array.isArray(first)) {
|
||||
first.forEach(d => d && d.dispose());
|
||||
export function dispose<T extends IDisposable>(disposable: T | undefined): T | undefined;
|
||||
export function dispose<T extends IDisposable>(disposables: Array<T>): Array<T>;
|
||||
export function dispose<T extends IDisposable>(disposables: ReadonlyArray<T>): ReadonlyArray<T>;
|
||||
export function dispose<T extends IDisposable>(disposables: T | T[] | undefined): T | T[] | undefined {
|
||||
if (Array.isArray(disposables)) {
|
||||
disposables.forEach(d => {
|
||||
if (d) {
|
||||
markTracked(d);
|
||||
d.dispose();
|
||||
}
|
||||
});
|
||||
return [];
|
||||
} else if (rest.length === 0) {
|
||||
if (first) {
|
||||
first.dispose();
|
||||
return first;
|
||||
}
|
||||
return undefined;
|
||||
} else if (disposables) {
|
||||
markTracked(disposables);
|
||||
disposables.dispose();
|
||||
return disposables;
|
||||
} else {
|
||||
dispose(first);
|
||||
dispose(rest);
|
||||
return [];
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function combinedDisposable(disposables: IDisposable[]): IDisposable {
|
||||
return { dispose: () => dispose(disposables) };
|
||||
export function combinedDisposable(...disposables: IDisposable[]): IDisposable {
|
||||
disposables.forEach(markTracked);
|
||||
return trackDisposable({ dispose: () => dispose(disposables) });
|
||||
}
|
||||
|
||||
export function toDisposable(fn: () => void): IDisposable {
|
||||
return { dispose() { fn(); } };
|
||||
const self = trackDisposable({
|
||||
dispose: () => {
|
||||
markTracked(self);
|
||||
fn();
|
||||
}
|
||||
});
|
||||
return self;
|
||||
}
|
||||
|
||||
export class DisposableStore implements IDisposable {
|
||||
private _toDispose = new Set<IDisposable>();
|
||||
private _isDisposed = false;
|
||||
|
||||
/**
|
||||
* Dispose of all registered disposables and mark this object as disposed.
|
||||
*
|
||||
* Any future disposables added to this object will be disposed of on `add`.
|
||||
*/
|
||||
public dispose(): void {
|
||||
if (this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
markTracked(this);
|
||||
this._isDisposed = true;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose of all registered disposables but do not mark this object as disposed.
|
||||
*/
|
||||
public clear(): void {
|
||||
this._toDispose.forEach(item => item.dispose());
|
||||
this._toDispose.clear();
|
||||
}
|
||||
|
||||
public add<T extends IDisposable>(t: T): T {
|
||||
if (!t) {
|
||||
return t;
|
||||
}
|
||||
if ((t as any as DisposableStore) === this) {
|
||||
throw new Error('Cannot register a disposable on itself!');
|
||||
}
|
||||
|
||||
markTracked(t);
|
||||
if (this._isDisposed) {
|
||||
console.warn(new Error('Registering disposable on object that has already been disposed of').stack);
|
||||
t.dispose();
|
||||
} else {
|
||||
this._toDispose.add(t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Disposable implements IDisposable {
|
||||
|
||||
static None = Object.freeze<IDisposable>({ dispose() { } });
|
||||
|
||||
protected _toDispose: IDisposable[] = [];
|
||||
protected get toDispose(): IDisposable[] { return this._toDispose; }
|
||||
private readonly _store = new DisposableStore();
|
||||
|
||||
private _lifecycle_disposable_isDisposed = false;
|
||||
constructor() {
|
||||
trackDisposable(this);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._lifecycle_disposable_isDisposed = true;
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
markTracked(this);
|
||||
|
||||
this._store.dispose();
|
||||
}
|
||||
|
||||
protected _register<T extends IDisposable>(t: T): T {
|
||||
if (this._lifecycle_disposable_isDisposed) {
|
||||
console.warn('Registering disposable on object that has already been disposed.');
|
||||
t.dispose();
|
||||
} else {
|
||||
this._toDispose.push(t);
|
||||
if ((t as any as Disposable) === this) {
|
||||
throw new Error('Cannot register a disposable on itself!');
|
||||
}
|
||||
return this._store.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the lifecycle of a disposable value that may be changed.
|
||||
*
|
||||
* This ensures that when the the disposable value is changed, the previously held disposable is disposed of. You can
|
||||
* also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
|
||||
*/
|
||||
export class MutableDisposable<T extends IDisposable> implements IDisposable {
|
||||
private _value?: T;
|
||||
private _isDisposed = false;
|
||||
|
||||
constructor() {
|
||||
trackDisposable(this);
|
||||
}
|
||||
|
||||
get value(): T | undefined {
|
||||
return this._isDisposed ? undefined : this._value;
|
||||
}
|
||||
|
||||
set value(value: T | undefined) {
|
||||
if (this._isDisposed || value === this._value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t;
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
if (value) {
|
||||
markTracked(value);
|
||||
}
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.value = undefined;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._isDisposed = true;
|
||||
markTracked(this);
|
||||
if (this._value) {
|
||||
this._value.dispose();
|
||||
}
|
||||
this._value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,22 +213,23 @@ export interface IReference<T> extends IDisposable {
|
||||
|
||||
export abstract class ReferenceCollection<T> {
|
||||
|
||||
private references: { [key: string]: { readonly object: T; counter: number; } } = Object.create(null);
|
||||
private references: Map<string, { readonly object: T; counter: number; }> = new Map();
|
||||
|
||||
constructor() { }
|
||||
|
||||
acquire(key: string): IReference<T> {
|
||||
let reference = this.references[key];
|
||||
let reference = this.references.get(key);
|
||||
|
||||
if (!reference) {
|
||||
reference = this.references[key] = { counter: 0, object: this.createReferencedObject(key) };
|
||||
reference = { counter: 0, object: this.createReferencedObject(key) };
|
||||
this.references.set(key, reference);
|
||||
}
|
||||
|
||||
const { object } = reference;
|
||||
const dispose = once(() => {
|
||||
if (--reference.counter === 0) {
|
||||
this.destroyReferencedObject(key, reference.object);
|
||||
delete this.references[key];
|
||||
if (--reference!.counter === 0) {
|
||||
this.destroyReferencedObject(key, reference!.object);
|
||||
this.references.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ 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';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { DataUri } from 'vs/base/common/resources';
|
||||
|
||||
export const MIME_TEXT = 'text/plain';
|
||||
export const MIME_BINARY = 'application/octet-stream';
|
||||
@@ -106,12 +109,28 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void {
|
||||
/**
|
||||
* Given a file, return the best matching mime type for it
|
||||
*/
|
||||
export function guessMimeTypes(path: string | null, firstLine?: string): string[] {
|
||||
export function guessMimeTypes(resource: URI | null, firstLine?: string): string[] {
|
||||
let path: string | undefined;
|
||||
if (resource) {
|
||||
switch (resource.scheme) {
|
||||
case Schemas.file:
|
||||
path = resource.fsPath;
|
||||
break;
|
||||
case Schemas.data:
|
||||
const metadata = DataUri.parseMetaData(resource);
|
||||
path = metadata.get(DataUri.META_DATA_LABEL);
|
||||
break;
|
||||
default:
|
||||
path = resource.path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
return [MIME_UNKNOWN];
|
||||
}
|
||||
|
||||
path = path.toLowerCase();
|
||||
|
||||
const filename = basename(path);
|
||||
|
||||
// 1.) User configured mappings have highest priority
|
||||
@@ -255,52 +274,54 @@ interface MapExtToMediaMimes {
|
||||
|
||||
// Known media mimes that we can handle
|
||||
const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
'.aac': 'audio/x-aac',
|
||||
'.avi': 'video/x-msvideo',
|
||||
'.bmp': 'image/bmp',
|
||||
'.flv': 'video/x-flv',
|
||||
'.gif': 'image/gif',
|
||||
'.jpg': 'image/jpg',
|
||||
'.jpeg': 'image/jpg',
|
||||
'.jpe': 'image/jpg',
|
||||
'.png': 'image/png',
|
||||
'.tiff': 'image/tiff',
|
||||
'.tif': 'image/tiff',
|
||||
'.ico': 'image/x-icon',
|
||||
'.tga': 'image/x-tga',
|
||||
'.psd': 'image/vnd.adobe.photoshop',
|
||||
'.webp': 'image/webp',
|
||||
'.jpe': 'image/jpg',
|
||||
'.jpeg': 'image/jpg',
|
||||
'.jpg': 'image/jpg',
|
||||
'.m1v': 'video/mpeg',
|
||||
'.m2a': 'audio/mpeg',
|
||||
'.m2v': 'video/mpeg',
|
||||
'.m3a': 'audio/mpeg',
|
||||
'.mid': 'audio/midi',
|
||||
'.midi': 'audio/midi',
|
||||
'.mp4a': 'audio/mp4',
|
||||
'.mpga': 'audio/mpeg',
|
||||
'.mk3d': 'video/x-matroska',
|
||||
'.mks': 'video/x-matroska',
|
||||
'.mkv': 'video/x-matroska',
|
||||
'.mov': 'video/quicktime',
|
||||
'.movie': 'video/x-sgi-movie',
|
||||
'.mp2': 'audio/mpeg',
|
||||
'.mp2a': 'audio/mpeg',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.m2a': 'audio/mpeg',
|
||||
'.m3a': 'audio/mpeg',
|
||||
'.oga': 'audio/ogg',
|
||||
'.ogg': 'audio/ogg',
|
||||
'.spx': 'audio/ogg',
|
||||
'.aac': 'audio/x-aac',
|
||||
'.wav': 'audio/x-wav',
|
||||
'.wma': 'audio/x-ms-wma',
|
||||
'.mp4': 'video/mp4',
|
||||
'.mp4a': 'audio/mp4',
|
||||
'.mp4v': 'video/mp4',
|
||||
'.mpg4': 'video/mp4',
|
||||
'.mpe': 'video/mpeg',
|
||||
'.mpeg': 'video/mpeg',
|
||||
'.mpg': 'video/mpeg',
|
||||
'.mpe': 'video/mpeg',
|
||||
'.m1v': 'video/mpeg',
|
||||
'.m2v': 'video/mpeg',
|
||||
'.mpg4': 'video/mp4',
|
||||
'.mpga': 'audio/mpeg',
|
||||
'.oga': 'audio/ogg',
|
||||
'.ogg': 'audio/ogg',
|
||||
'.ogv': 'video/ogg',
|
||||
'.png': 'image/png',
|
||||
'.psd': 'image/vnd.adobe.photoshop',
|
||||
'.qt': 'video/quicktime',
|
||||
'.mov': 'video/quicktime',
|
||||
'.spx': 'audio/ogg',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.tga': 'image/x-tga',
|
||||
'.tif': 'image/tiff',
|
||||
'.tiff': 'image/tiff',
|
||||
'.wav': 'audio/x-wav',
|
||||
'.webm': 'video/webm',
|
||||
'.mkv': 'video/x-matroska',
|
||||
'.mk3d': 'video/x-matroska',
|
||||
'.mks': 'video/x-matroska',
|
||||
'.webp': 'image/webp',
|
||||
'.wma': 'audio/x-ms-wma',
|
||||
'.wmv': 'video/x-ms-wmv',
|
||||
'.flv': 'video/x-flv',
|
||||
'.avi': 'video/x-msvideo',
|
||||
'.movie': 'video/x-sgi-movie'
|
||||
'.woff': 'application/font-woff',
|
||||
};
|
||||
|
||||
export function getMediaMime(path: string): string | undefined {
|
||||
|
||||
@@ -46,4 +46,6 @@ export namespace Schemas {
|
||||
export const command: string = 'command';
|
||||
|
||||
export const vscodeRemote: string = 'vscode-remote';
|
||||
|
||||
export const userData: string = 'vscode-userdata';
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@ export function deepClone<T>(obj: T): T {
|
||||
return obj as any;
|
||||
}
|
||||
const result: any = Array.isArray(obj) ? [] : {};
|
||||
Object.keys(obj as any).forEach((key: string) => {
|
||||
if (obj[key] && typeof obj[key] === 'object') {
|
||||
result[key] = deepClone(obj[key]);
|
||||
Object.keys(<any>obj).forEach((key: string) => {
|
||||
if ((<any>obj)[key] && typeof (<any>obj)[key] === 'object') {
|
||||
result[key] = deepClone((<any>obj)[key]);
|
||||
} else {
|
||||
result[key] = obj[key];
|
||||
result[key] = (<any>obj)[key];
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Types from 'vs/base/common/types';
|
||||
|
||||
export const enum ValidationState {
|
||||
OK = 0,
|
||||
Info = 1,
|
||||
@@ -78,25 +76,4 @@ export abstract class Parser {
|
||||
public fatal(message: string): void {
|
||||
this._problemReporter.fatal(message);
|
||||
}
|
||||
|
||||
protected static merge<T extends object>(destination: T, source: T, overwrite: boolean): void {
|
||||
Object.keys(source).forEach((key: string) => {
|
||||
const destValue = destination[key];
|
||||
const sourceValue = source[key];
|
||||
if (Types.isUndefined(sourceValue)) {
|
||||
return;
|
||||
}
|
||||
if (Types.isUndefined(destValue)) {
|
||||
destination[key] = sourceValue;
|
||||
} else {
|
||||
if (overwrite) {
|
||||
if (Types.isObject(destValue) && Types.isObject(sourceValue)) {
|
||||
this.merge(destValue, sourceValue, overwrite);
|
||||
} else {
|
||||
destination[key] = sourceValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export const win32: IPath = {
|
||||
// absolute path, get cwd for that drive, or the process cwd if
|
||||
// the drive cwd is not available. We're sure the device is not
|
||||
// a UNC path at this points, because UNC paths are always absolute.
|
||||
path = process.env['=' + resolvedDevice] || process.cwd();
|
||||
path = (process.env as any)['=' + resolvedDevice] || process.cwd();
|
||||
|
||||
// Verify that a cwd was found and that it actually points
|
||||
// to our drive. If not, default to the drive's root.
|
||||
|
||||
@@ -221,7 +221,7 @@ export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep)
|
||||
* 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 {
|
||||
export function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(from)): string | undefined {
|
||||
if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -229,7 +229,20 @@ export function relativePath(from: URI, to: URI): string | undefined {
|
||||
const relativePath = paths.relative(from.path, to.path);
|
||||
return isWindows ? extpath.toSlashes(relativePath) : relativePath;
|
||||
}
|
||||
return paths.posix.relative(from.path || '/', to.path || '/');
|
||||
let fromPath = from.path || '/', toPath = to.path || '/';
|
||||
if (ignoreCase) {
|
||||
// make casing of fromPath match toPath
|
||||
let i = 0;
|
||||
for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
|
||||
if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
|
||||
if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fromPath = toPath.substr(0, i) + fromPath.substr(i);
|
||||
}
|
||||
return paths.posix.relative(fromPath, toPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,6 +169,31 @@ export function getAllPropertyNames(obj: object): string[] {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function getAllMethodNames(obj: object): string[] {
|
||||
const methods: string[] = [];
|
||||
for (const prop of getAllPropertyNames(obj)) {
|
||||
if (typeof (obj as any)[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: any[]) => any): T {
|
||||
const createProxyMethod = (method: string): () => any => {
|
||||
return function () {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
return invoke(method, args);
|
||||
};
|
||||
};
|
||||
|
||||
let result = {} as T;
|
||||
for (const methodName of methodNames) {
|
||||
(<any>result)[methodName] = createProxyMethod(methodName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts null to undefined, passes all other values through.
|
||||
*/
|
||||
|
||||
@@ -385,8 +385,8 @@ export class URI implements UriComponents {
|
||||
return data;
|
||||
} else {
|
||||
const result = new _URI(data);
|
||||
result._fsPath = (<UriState>data).fsPath;
|
||||
result._formatted = (<UriState>data).external;
|
||||
result._fsPath = (<UriState>data)._sep === _pathSepMarker ? (<UriState>data).fsPath : null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -402,10 +402,12 @@ export interface UriComponents {
|
||||
|
||||
interface UriState extends UriComponents {
|
||||
$mid: number;
|
||||
fsPath: string;
|
||||
external: string;
|
||||
fsPath: string;
|
||||
_sep: 1 | undefined;
|
||||
}
|
||||
|
||||
const _pathSepMarker = isWindows ? 1 : undefined;
|
||||
|
||||
// tslint:disable-next-line:class-name
|
||||
class _URI extends URI {
|
||||
@@ -439,6 +441,7 @@ class _URI extends URI {
|
||||
// cached state
|
||||
if (this._fsPath) {
|
||||
res.fsPath = this._fsPath;
|
||||
res._sep = _pathSepMarker;
|
||||
}
|
||||
if (this._formatted) {
|
||||
res.external = this._formatted;
|
||||
|
||||
@@ -10,6 +10,51 @@ export interface IURITransformer {
|
||||
transformIncoming(uri: UriComponents): UriComponents;
|
||||
transformOutgoing(uri: UriComponents): UriComponents;
|
||||
transformOutgoingURI(uri: URI): URI;
|
||||
transformOutgoingScheme(scheme: string): string;
|
||||
}
|
||||
|
||||
export interface UriParts {
|
||||
scheme: string;
|
||||
authority?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface IRawURITransformer {
|
||||
transformIncoming(uri: UriParts): UriParts;
|
||||
transformOutgoing(uri: UriParts): UriParts;
|
||||
transformOutgoingScheme(scheme: string): string;
|
||||
}
|
||||
|
||||
function toJSON(uri: URI): UriComponents {
|
||||
return <UriComponents><any>uri.toJSON();
|
||||
}
|
||||
|
||||
export class URITransformer implements IURITransformer {
|
||||
|
||||
private readonly _uriTransformer: IRawURITransformer;
|
||||
|
||||
constructor(uriTransformer: IRawURITransformer) {
|
||||
this._uriTransformer = uriTransformer;
|
||||
}
|
||||
|
||||
public transformIncoming(uri: UriComponents): UriComponents {
|
||||
const result = this._uriTransformer.transformIncoming(uri);
|
||||
return (result === uri ? uri : toJSON(URI.from(result)));
|
||||
}
|
||||
|
||||
public transformOutgoing(uri: UriComponents): UriComponents {
|
||||
const result = this._uriTransformer.transformOutgoing(uri);
|
||||
return (result === uri ? uri : toJSON(URI.from(result)));
|
||||
}
|
||||
|
||||
public transformOutgoingURI(uri: URI): URI {
|
||||
const result = this._uriTransformer.transformOutgoing(uri);
|
||||
return (result === uri ? uri : URI.from(result));
|
||||
}
|
||||
|
||||
public transformOutgoingScheme(scheme: string): string {
|
||||
return this._uriTransformer.transformOutgoingScheme(scheme);
|
||||
}
|
||||
}
|
||||
|
||||
export const DefaultURITransformer: IURITransformer = new class {
|
||||
@@ -24,6 +69,10 @@ export const DefaultURITransformer: IURITransformer = new class {
|
||||
transformOutgoingURI(uri: URI): URI {
|
||||
return uri;
|
||||
}
|
||||
|
||||
transformOutgoingScheme(scheme: string): string {
|
||||
return scheme;
|
||||
}
|
||||
};
|
||||
|
||||
function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any {
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { transformErrorForSerialization } from 'vs/base/common/errors';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { getAllPropertyNames } from 'vs/base/common/types';
|
||||
import * as types from 'vs/base/common/types';
|
||||
|
||||
const INITIALIZE = '$initialize';
|
||||
|
||||
export interface IWorker {
|
||||
export interface IWorker extends IDisposable {
|
||||
getId(): number;
|
||||
postMessage(message: string): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface IWorkerCallback {
|
||||
@@ -174,17 +173,22 @@ class SimpleWorkerProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkerClient<W> {
|
||||
getProxyObject(): Promise<W>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main thread side
|
||||
*/
|
||||
export class SimpleWorkerClient<T> extends Disposable {
|
||||
export class SimpleWorkerClient<W extends object, H extends object> extends Disposable implements IWorkerClient<W> {
|
||||
|
||||
private _worker: IWorker;
|
||||
private _onModuleLoaded: Promise<string[]>;
|
||||
private _protocol: SimpleWorkerProtocol;
|
||||
private _lazyProxy: Promise<T>;
|
||||
private readonly _worker: IWorker;
|
||||
private readonly _onModuleLoaded: Promise<string[]>;
|
||||
private readonly _protocol: SimpleWorkerProtocol;
|
||||
private readonly _lazyProxy: Promise<W>;
|
||||
|
||||
constructor(workerFactory: IWorkerFactory, moduleId: string) {
|
||||
constructor(workerFactory: IWorkerFactory, moduleId: string, host: H) {
|
||||
super();
|
||||
|
||||
let lazyProxyReject: ((err: any) => void) | null = null;
|
||||
@@ -208,8 +212,15 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
this._worker.postMessage(msg);
|
||||
},
|
||||
handleMessage: (method: string, args: any[]): Promise<any> => {
|
||||
// Intentionally not supporting worker -> main requests
|
||||
return Promise.resolve(null);
|
||||
if (typeof (host as any)[method] !== 'function') {
|
||||
return Promise.reject(new Error('Missing method ' + method + ' on main thread host.'));
|
||||
}
|
||||
|
||||
try {
|
||||
return Promise.resolve((host as any)[method].apply(host, args));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
this._protocol.setWorkerId(this._worker.getId());
|
||||
@@ -224,41 +235,33 @@ export class SimpleWorkerClient<T> extends Disposable {
|
||||
loaderConfiguration = (<any>self).requirejs.s.contexts._.config;
|
||||
}
|
||||
|
||||
const hostMethods = types.getAllMethodNames(host);
|
||||
|
||||
// Send initialize message
|
||||
this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [
|
||||
this._worker.getId(),
|
||||
loaderConfiguration,
|
||||
moduleId,
|
||||
loaderConfiguration
|
||||
hostMethods,
|
||||
]);
|
||||
|
||||
this._lazyProxy = new Promise<T>((resolve, reject) => {
|
||||
lazyProxyReject = reject;
|
||||
this._onModuleLoaded.then((availableMethods: string[]) => {
|
||||
let proxy = <T>{};
|
||||
for (const methodName of availableMethods) {
|
||||
(proxy as any)[methodName] = createProxyMethod(methodName, proxyMethodRequest);
|
||||
}
|
||||
resolve(proxy);
|
||||
}, (e) => {
|
||||
reject(e);
|
||||
this._onError('Worker failed to load ' + moduleId, e);
|
||||
});
|
||||
});
|
||||
|
||||
// Create proxy to loaded code
|
||||
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
|
||||
return this._request(method, args);
|
||||
};
|
||||
|
||||
const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise<any>): () => Promise<any> => {
|
||||
return function () {
|
||||
let args = Array.prototype.slice.call(arguments, 0);
|
||||
return proxyMethodRequest(method, args);
|
||||
};
|
||||
};
|
||||
this._lazyProxy = new Promise<W>((resolve, reject) => {
|
||||
lazyProxyReject = reject;
|
||||
this._onModuleLoaded.then((availableMethods: string[]) => {
|
||||
resolve(types.createProxyObject<W>(availableMethods, proxyMethodRequest));
|
||||
}, (e) => {
|
||||
reject(e);
|
||||
this._onError('Worker failed to load ' + moduleId, e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getProxyObject(): Promise<T> {
|
||||
public getProxyObject(): Promise<W> {
|
||||
return this._lazyProxy;
|
||||
}
|
||||
|
||||
@@ -281,16 +284,22 @@ export interface IRequestHandler {
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
export interface IRequestHandlerFactory<H> {
|
||||
(host: H): IRequestHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Worker side
|
||||
*/
|
||||
export class SimpleWorkerServer {
|
||||
export class SimpleWorkerServer<H extends object> {
|
||||
|
||||
private _requestHandlerFactory: IRequestHandlerFactory<H> | null;
|
||||
private _requestHandler: IRequestHandler | null;
|
||||
private _protocol: SimpleWorkerProtocol;
|
||||
|
||||
constructor(postSerializedMessage: (msg: string) => void, requestHandler: IRequestHandler | null) {
|
||||
this._requestHandler = requestHandler;
|
||||
constructor(postSerializedMessage: (msg: string) => void, requestHandlerFactory: IRequestHandlerFactory<H> | null) {
|
||||
this._requestHandlerFactory = requestHandlerFactory;
|
||||
this._requestHandler = null;
|
||||
this._protocol = new SimpleWorkerProtocol({
|
||||
sendMessage: (msg: string): void => {
|
||||
postSerializedMessage(msg);
|
||||
@@ -305,7 +314,7 @@ export class SimpleWorkerServer {
|
||||
|
||||
private _handleMessage(method: string, args: any[]): Promise<any> {
|
||||
if (method === INITIALIZE) {
|
||||
return this.initialize(<number>args[0], <string>args[1], <any>args[2]);
|
||||
return this.initialize(<number>args[0], <any>args[1], <string>args[2], <string[]>args[3]);
|
||||
}
|
||||
|
||||
if (!this._requestHandler || typeof this._requestHandler[method] !== 'function') {
|
||||
@@ -319,18 +328,19 @@ export class SimpleWorkerServer {
|
||||
}
|
||||
}
|
||||
|
||||
private initialize(workerId: number, moduleId: string, loaderConfig: any): Promise<string[]> {
|
||||
private initialize(workerId: number, loaderConfig: any, moduleId: string, hostMethods: string[]): Promise<string[]> {
|
||||
this._protocol.setWorkerId(workerId);
|
||||
|
||||
if (this._requestHandler) {
|
||||
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
|
||||
return this._protocol.sendMessage(method, args);
|
||||
};
|
||||
|
||||
const hostProxy = types.createProxyObject<H>(hostMethods, proxyMethodRequest);
|
||||
|
||||
if (this._requestHandlerFactory) {
|
||||
// static request handler
|
||||
let methods: string[] = [];
|
||||
for (const prop of getAllPropertyNames(this._requestHandler)) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(methods);
|
||||
this._requestHandler = this._requestHandlerFactory(hostProxy);
|
||||
return Promise.resolve(types.getAllMethodNames(this._requestHandler));
|
||||
}
|
||||
|
||||
if (loaderConfig) {
|
||||
@@ -351,23 +361,15 @@ export class SimpleWorkerServer {
|
||||
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
// Use the global require to be sure to get the global config
|
||||
(<any>self).require([moduleId], (...result: any[]) => {
|
||||
let handlerModule = result[0];
|
||||
this._requestHandler = handlerModule.create();
|
||||
(<any>self).require([moduleId], (module: { create: IRequestHandlerFactory<H> }) => {
|
||||
this._requestHandler = module.create(hostProxy);
|
||||
|
||||
if (!this._requestHandler) {
|
||||
reject(new Error(`No RequestHandler!`));
|
||||
return;
|
||||
}
|
||||
|
||||
let methods: string[] = [];
|
||||
for (const prop of getAllPropertyNames(this._requestHandler)) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(methods);
|
||||
resolve(types.getAllMethodNames(this._requestHandler));
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
@@ -376,6 +378,6 @@ export class SimpleWorkerServer {
|
||||
/**
|
||||
* Called on the worker side
|
||||
*/
|
||||
export function create(postMessage: (msg: string) => void): SimpleWorkerServer {
|
||||
export function create(postMessage: (msg: string) => void): SimpleWorkerServer<any> {
|
||||
return new SimpleWorkerServer(postMessage, null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user