mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae
This commit is contained in:
committed by
AzureDataStudio
parent
a971aee5bd
commit
5e7071e466
@@ -489,12 +489,21 @@ export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string,
|
||||
export function insert<T>(array: T[], element: T): () => void {
|
||||
array.push(element);
|
||||
|
||||
return () => {
|
||||
const index = array.indexOf(element);
|
||||
if (index > -1) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
};
|
||||
return () => remove(array, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from an array if it can be found.
|
||||
*/
|
||||
export function remove<T>(array: T[], element: T): T | undefined {
|
||||
const index = array.indexOf(element);
|
||||
if (index > -1) {
|
||||
array.splice(index, 1);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -194,6 +194,8 @@ export interface VSBufferReadableStream extends streams.ReadableStream<VSBuffer>
|
||||
|
||||
export interface VSBufferWriteableStream extends streams.WriteableStream<VSBuffer> { }
|
||||
|
||||
export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream<VSBuffer> { }
|
||||
|
||||
export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
|
||||
return streams.consumeReadable<VSBuffer>(readable, chunks => VSBuffer.concat(chunks));
|
||||
}
|
||||
@@ -206,6 +208,21 @@ export function streamToBuffer(stream: streams.ReadableStream<VSBuffer>): Promis
|
||||
return streams.consumeStream<VSBuffer>(stream, chunks => VSBuffer.concat(chunks));
|
||||
}
|
||||
|
||||
export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream<VSBuffer>): Promise<VSBuffer> {
|
||||
if (bufferedStream.ended) {
|
||||
return VSBuffer.concat(bufferedStream.buffer);
|
||||
}
|
||||
|
||||
return VSBuffer.concat([
|
||||
|
||||
// Include already read chunks...
|
||||
...bufferedStream.buffer,
|
||||
|
||||
// ...and all additional chunks
|
||||
await streamToBuffer(bufferedStream.stream)
|
||||
]);
|
||||
}
|
||||
|
||||
export function bufferToStream(buffer: VSBuffer): streams.ReadableStream<VSBuffer> {
|
||||
return streams.toStream<VSBuffer>(buffer, chunks => VSBuffer.concat(chunks));
|
||||
}
|
||||
@@ -214,6 +231,6 @@ export function streamToBufferReadableStream(stream: streams.ReadableStreamEvent
|
||||
return streams.transform<Uint8Array | string, VSBuffer>(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks));
|
||||
}
|
||||
|
||||
export function newWriteableBufferStream(): streams.WriteableStream<VSBuffer> {
|
||||
return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks));
|
||||
export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {
|
||||
return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);
|
||||
}
|
||||
|
||||
@@ -495,6 +495,7 @@ export function markdownUnescapeCodicons(text: string): string {
|
||||
const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
|
||||
export function renderCodicons(text: string): string {
|
||||
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
|
||||
// If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts
|
||||
return escaped
|
||||
? `$(${codicon})`
|
||||
: `<span class="codicon codicon-${name}${animation ? ` codicon-animation-${animation}` : ''}"></span>`;
|
||||
|
||||
@@ -32,25 +32,6 @@ export function values<T>(from: IStringDictionary<T> | INumberDictionary<T>): T[
|
||||
return result;
|
||||
}
|
||||
|
||||
export function size<T>(from: IStringDictionary<T> | INumberDictionary<T>): number {
|
||||
let count = 0;
|
||||
for (let key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
export function first<T>(from: IStringDictionary<T> | INumberDictionary<T>): T | undefined {
|
||||
for (const key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
return (from as any)[key];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over each entry in the provided dictionary. The iterator allows
|
||||
* to remove elements and will stop when the callback returns {{false}}.
|
||||
|
||||
@@ -440,14 +440,14 @@ class LeakageMonitor {
|
||||
this._warnCountdown = threshold * 0.5;
|
||||
|
||||
// find most frequent listener and print warning
|
||||
let topStack: string;
|
||||
let topStack: string | undefined;
|
||||
let topCount: number = 0;
|
||||
this._stacks.forEach((count, stack) => {
|
||||
for (const [stack, count] of this._stacks) {
|
||||
if (!topStack || topCount < count) {
|
||||
topStack = stack;
|
||||
topCount = count;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`);
|
||||
console.warn(topStack!);
|
||||
|
||||
@@ -285,7 +285,7 @@ export function isRootOrDriveLetter(path: string): boolean {
|
||||
return pathNormalized === posix.sep;
|
||||
}
|
||||
|
||||
export function indexOfPath(path: string, candidate: string, ignoreCase: boolean): number {
|
||||
export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number {
|
||||
if (candidate.length > path.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,9 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe
|
||||
}
|
||||
|
||||
// the actual key
|
||||
result.push(key);
|
||||
if (key !== '') {
|
||||
result.push(key);
|
||||
}
|
||||
|
||||
return result.join(labels.separator);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export function dispose<T extends IDisposable>(arg: T | IterableIterator<T> | un
|
||||
d.dispose();
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
return Array.isArray(arg) ? [] : arg;
|
||||
} else if (arg) {
|
||||
markTracked(arg);
|
||||
arg.dispose();
|
||||
@@ -219,11 +219,11 @@ export abstract class ReferenceCollection<T> {
|
||||
|
||||
private readonly references: Map<string, { readonly object: T; counter: number; }> = new Map();
|
||||
|
||||
acquire(key: string): IReference<T> {
|
||||
acquire(key: string, ...args: any[]): IReference<T> {
|
||||
let reference = this.references.get(key);
|
||||
|
||||
if (!reference) {
|
||||
reference = { counter: 0, object: this.createReferencedObject(key) };
|
||||
reference = { counter: 0, object: this.createReferencedObject(key, ...args) };
|
||||
this.references.set(key, reference);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ export abstract class ReferenceCollection<T> {
|
||||
return { object, dispose };
|
||||
}
|
||||
|
||||
protected abstract createReferencedObject(key: string): T;
|
||||
protected abstract createReferencedObject(key: string, ...args: any[]): T;
|
||||
protected abstract destroyReferencedObject(key: string, object: T): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -481,14 +481,40 @@ export class TernarySearchTree<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
interface ResourceMapKeyFn {
|
||||
(resource: URI): string;
|
||||
}
|
||||
|
||||
export class ResourceMap<T> implements Map<URI, T> {
|
||||
|
||||
private static readonly defaultToKey = (resource: URI) => resource.toString();
|
||||
|
||||
readonly [Symbol.toStringTag] = 'ResourceMap';
|
||||
|
||||
protected readonly map: Map<string, T>;
|
||||
private readonly map: Map<string, T>;
|
||||
private readonly toKey: ResourceMapKeyFn;
|
||||
|
||||
constructor(other?: ResourceMap<T>) {
|
||||
this.map = other ? new Map(other.map) : new Map();
|
||||
/**
|
||||
*
|
||||
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
|
||||
*/
|
||||
constructor(toKey?: ResourceMapKeyFn);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param other Another resource which this maps is created from
|
||||
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
|
||||
*/
|
||||
constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
|
||||
|
||||
constructor(mapOrKeyFn?: ResourceMap<T> | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
|
||||
if (mapOrKeyFn instanceof ResourceMap) {
|
||||
this.map = new Map(mapOrKeyFn.map);
|
||||
this.toKey = toKey ?? ResourceMap.defaultToKey;
|
||||
} else {
|
||||
this.map = new Map();
|
||||
this.toKey = mapOrKeyFn ?? ResourceMap.defaultToKey;
|
||||
}
|
||||
}
|
||||
|
||||
set(resource: URI, value: T): this {
|
||||
@@ -546,10 +572,6 @@ export class ResourceMap<T> implements Map<URI, T> {
|
||||
yield [URI.parse(item[0]), item[1]];
|
||||
}
|
||||
}
|
||||
|
||||
private toKey(resource: URI): string {
|
||||
return resource.toString();
|
||||
}
|
||||
}
|
||||
|
||||
interface Item<K, V> {
|
||||
|
||||
@@ -56,11 +56,18 @@ export namespace Schemas {
|
||||
|
||||
export const vscodeCustomEditor = 'vscode-custom-editor';
|
||||
|
||||
export const vscodeNotebook = 'vscode-notebook';
|
||||
|
||||
export const vscodeSettings = 'vscode-settings';
|
||||
|
||||
export const webviewPanel = 'webview-panel';
|
||||
|
||||
export const vscodeWebviewResource = 'vscode-webview-resource';
|
||||
|
||||
/**
|
||||
* Scheme used for extension pages
|
||||
*/
|
||||
export const extension = 'extension';
|
||||
}
|
||||
|
||||
class RemoteAuthoritiesImpl {
|
||||
|
||||
4
src/vs/base/common/performance.d.ts
vendored
4
src/vs/base/common/performance.d.ts
vendored
@@ -5,7 +5,7 @@
|
||||
|
||||
export interface PerformanceEntry {
|
||||
readonly name: string;
|
||||
readonly timestamp: number;
|
||||
readonly startTime: number;
|
||||
}
|
||||
|
||||
export function mark(name: string): void;
|
||||
@@ -15,8 +15,6 @@ export function mark(name: string): void;
|
||||
*/
|
||||
export function getEntries(): PerformanceEntry[];
|
||||
|
||||
export function getEntry(name: string): PerformanceEntry;
|
||||
|
||||
export function getDuration(from: string, to: string): number;
|
||||
|
||||
type ExportData = any[];
|
||||
|
||||
@@ -28,24 +28,12 @@ function _factory(sharedObj) {
|
||||
for (let i = 0; i < entries.length; i += _dataLen) {
|
||||
result.push({
|
||||
name: entries[i],
|
||||
timestamp: entries[i + 1],
|
||||
startTime: entries[i + 1],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getEntry(name) {
|
||||
const entries = sharedObj._performanceEntries;
|
||||
for (let i = 0; i < entries.length; i += _dataLen) {
|
||||
if (entries[i] === name) {
|
||||
return {
|
||||
name: entries[i],
|
||||
timestamp: entries[i + 1],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDuration(from, to) {
|
||||
const entries = sharedObj._performanceEntries;
|
||||
let target = to;
|
||||
@@ -73,7 +61,6 @@ function _factory(sharedObj) {
|
||||
const exports = {
|
||||
mark: mark,
|
||||
getEntries: getEntries,
|
||||
getEntry: getEntry,
|
||||
getDuration: getDuration,
|
||||
importEntries: importEntries,
|
||||
exportEntries: exportEntries
|
||||
|
||||
@@ -7,7 +7,7 @@ import { memoize } from 'vs/base/common/decorators';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { relativePath, joinPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { PathIterator, values } from 'vs/base/common/map';
|
||||
import { PathIterator } from 'vs/base/common/map';
|
||||
|
||||
export interface IResourceNode<T, C = void> {
|
||||
readonly uri: URI;
|
||||
@@ -30,7 +30,7 @@ class Node<T, C> implements IResourceNode<T, C> {
|
||||
}
|
||||
|
||||
get children(): Iterable<Node<T, C>> {
|
||||
return [...values(this._children)];
|
||||
return this._children.values();
|
||||
}
|
||||
|
||||
@memoize
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { URI, uriToFsPath } from 'vs/base/common/uri';
|
||||
import { equalsIgnoreCase, compare as strCompare, compareIgnoreCase } from 'vs/base/common/strings';
|
||||
import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { isWindows, isLinux } 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';
|
||||
@@ -138,32 +138,10 @@ export class ExtUri implements IExtUri {
|
||||
constructor(private _ignorePathCasing: (uri: URI) => boolean) { }
|
||||
|
||||
compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number {
|
||||
// scheme
|
||||
let ret = strCompare(uri1.scheme, uri2.scheme);
|
||||
if (ret === 0) {
|
||||
// authority
|
||||
ret = compareIgnoreCase(uri1.authority, uri2.authority);
|
||||
if (ret === 0) {
|
||||
// path
|
||||
ret = this._ignorePathCasing(uri1) ? compareIgnoreCase(uri1.path, uri2.path) : strCompare(uri1.path, uri2.path);
|
||||
// query
|
||||
if (ret === 0) {
|
||||
ret = strCompare(uri1.query, uri2.query);
|
||||
// fragment
|
||||
if (ret === 0 && !ignoreFragment) {
|
||||
ret = strCompare(uri1.fragment, uri2.fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uri1 === uri2) {
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
|
||||
return uri.with({
|
||||
path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
|
||||
fragment: ignoreFragment ? null : undefined
|
||||
}).toString();
|
||||
return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
|
||||
}
|
||||
|
||||
isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean {
|
||||
@@ -173,11 +151,14 @@ export class ExtUri implements IExtUri {
|
||||
if (!uri1 || !uri2) {
|
||||
return false;
|
||||
}
|
||||
if (uri1.scheme !== uri2.scheme || !isEqualAuthority(uri1.authority, uri2.authority)) {
|
||||
return false;
|
||||
}
|
||||
const p1 = uri1.path, p2 = uri2.path;
|
||||
return (p1 === p2 || this._ignorePathCasing(uri1) && equalsIgnoreCase(p1, p2)) && uri1.query === uri2.query && (ignoreFragment || uri1.fragment === uri2.fragment);
|
||||
return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment);
|
||||
}
|
||||
|
||||
getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
|
||||
return uri.with({
|
||||
path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
|
||||
fragment: ignoreFragment ? null : undefined
|
||||
}).toString();
|
||||
}
|
||||
|
||||
isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean {
|
||||
@@ -332,6 +313,7 @@ export class ExtUri implements IExtUri {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unbiased utility that takes uris "as they are". This means it can be interchanged with
|
||||
* uri#toString() usages. The following is true
|
||||
@@ -342,36 +324,52 @@ export class ExtUri implements IExtUri {
|
||||
export const extUri = new ExtUri(() => false);
|
||||
|
||||
/**
|
||||
* BIASED utility that always ignores the casing of uris path. ONLY use these util if you
|
||||
* BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you
|
||||
* understand what you are doing.
|
||||
*
|
||||
* Note that `IUriIdentityService#extUri` is a better replacement for this because that utility
|
||||
* knows when path casing matters and when not.
|
||||
* This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
|
||||
*
|
||||
* When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
|
||||
* because those uris come from a "trustworthy source". When creating unknown uris it's always
|
||||
* better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
|
||||
* casing matters.
|
||||
*/
|
||||
export const extUriBiasedIgnorePathCase = new ExtUri(uri => {
|
||||
// A file scheme resource is in the same platform as code, so ignore case for non linux platforms
|
||||
// Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
|
||||
return uri.scheme === Schemas.file ? !isLinux : true;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* BIASED utility that always ignores the casing of uris paths. ONLY use this util if you
|
||||
* understand what you are doing.
|
||||
*
|
||||
* This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
|
||||
*
|
||||
* When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
|
||||
* because those uris come from a "trustworthy source". When creating unknown uris it's always
|
||||
* better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
|
||||
* casing matters.
|
||||
*/
|
||||
export const extUriIgnorePathCase = new ExtUri(_ => true);
|
||||
|
||||
const exturiBiasedIgnorePathCase = new ExtUri(uri => {
|
||||
// A file scheme resource is in the same platform as code, so ignore case for non linux platforms
|
||||
// Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
|
||||
return uri && uri.scheme === Schemas.file ? !isLinux : true;
|
||||
});
|
||||
|
||||
export const isEqual = exturiBiasedIgnorePathCase.isEqual.bind(exturiBiasedIgnorePathCase);
|
||||
export const isEqualOrParent = exturiBiasedIgnorePathCase.isEqualOrParent.bind(exturiBiasedIgnorePathCase);
|
||||
export const getComparisonKey = exturiBiasedIgnorePathCase.getComparisonKey.bind(exturiBiasedIgnorePathCase);
|
||||
export const basenameOrAuthority = exturiBiasedIgnorePathCase.basenameOrAuthority.bind(exturiBiasedIgnorePathCase);
|
||||
export const basename = exturiBiasedIgnorePathCase.basename.bind(exturiBiasedIgnorePathCase);
|
||||
export const extname = exturiBiasedIgnorePathCase.extname.bind(exturiBiasedIgnorePathCase);
|
||||
export const dirname = exturiBiasedIgnorePathCase.dirname.bind(exturiBiasedIgnorePathCase);
|
||||
export const isEqual = extUri.isEqual.bind(extUri);
|
||||
export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri);
|
||||
export const getComparisonKey = extUri.getComparisonKey.bind(extUri);
|
||||
export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
|
||||
export const basename = extUri.basename.bind(extUri);
|
||||
export const extname = extUri.extname.bind(extUri);
|
||||
export const dirname = extUri.dirname.bind(extUri);
|
||||
export const joinPath = extUri.joinPath.bind(extUri);
|
||||
export const normalizePath = exturiBiasedIgnorePathCase.normalizePath.bind(exturiBiasedIgnorePathCase);
|
||||
export const relativePath = exturiBiasedIgnorePathCase.relativePath.bind(exturiBiasedIgnorePathCase);
|
||||
export const resolvePath = exturiBiasedIgnorePathCase.resolvePath.bind(exturiBiasedIgnorePathCase);
|
||||
export const isAbsolutePath = exturiBiasedIgnorePathCase.isAbsolutePath.bind(exturiBiasedIgnorePathCase);
|
||||
export const isEqualAuthority = exturiBiasedIgnorePathCase.isEqualAuthority.bind(exturiBiasedIgnorePathCase);
|
||||
export const hasTrailingPathSeparator = exturiBiasedIgnorePathCase.hasTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
|
||||
export const removeTrailingPathSeparator = exturiBiasedIgnorePathCase.removeTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
|
||||
export const addTrailingPathSeparator = exturiBiasedIgnorePathCase.addTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
|
||||
export const normalizePath = extUri.normalizePath.bind(extUri);
|
||||
export const relativePath = extUri.relativePath.bind(extUri);
|
||||
export const resolvePath = extUri.resolvePath.bind(extUri);
|
||||
export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri);
|
||||
export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
|
||||
export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
|
||||
export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri);
|
||||
export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
enum Severity {
|
||||
@@ -20,11 +19,6 @@ namespace Severity {
|
||||
const _warn = 'warn';
|
||||
const _info = 'info';
|
||||
|
||||
const _displayStrings: { [value: number]: string; } = Object.create(null);
|
||||
_displayStrings[Severity.Error] = nls.localize('sev.error', "Error");
|
||||
_displayStrings[Severity.Warning] = nls.localize('sev.warning', "Warning");
|
||||
_displayStrings[Severity.Info] = nls.localize('sev.info', "Info");
|
||||
|
||||
/**
|
||||
* Parses 'error', 'warning', 'warn', 'info' in call casings
|
||||
* and falls back to ignore.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
/**
|
||||
* The payload that flows in readable stream events.
|
||||
*/
|
||||
@@ -49,6 +51,11 @@ export interface ReadableStream<T> extends ReadableStreamEvents<T> {
|
||||
* Destroys the stream and stops emitting any event.
|
||||
*/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Allows to remove a listener that was previously added.
|
||||
*/
|
||||
removeListener(event: string, callback: Function): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,8 +81,14 @@ export interface WriteableStream<T> extends ReadableStream<T> {
|
||||
* Writing data to the stream will trigger the on('data')
|
||||
* event listener if the stream is flowing and buffer the
|
||||
* data otherwise until the stream is flowing.
|
||||
*
|
||||
* If a `highWaterMark` is configured and writing to the
|
||||
* stream reaches this mark, a promise will be returned
|
||||
* that should be awaited on before writing more data.
|
||||
* Otherwise there is a risk of buffering a large number
|
||||
* of data chunks without consumer.
|
||||
*/
|
||||
write(data: T): void;
|
||||
write(data: T): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Signals an error to the consumer of the stream via the
|
||||
@@ -95,12 +108,43 @@ export interface WriteableStream<T> extends ReadableStream<T> {
|
||||
end(result?: T | Error): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A stream that has a buffer already read. Returns the original stream
|
||||
* that was read as well as the chunks that got read.
|
||||
*
|
||||
* The `ended` flag indicates if the stream has been fully consumed.
|
||||
*/
|
||||
export interface ReadableBufferedStream<T> {
|
||||
|
||||
/**
|
||||
* The original stream that is being read.
|
||||
*/
|
||||
stream: ReadableStream<T>;
|
||||
|
||||
/**
|
||||
* An array of chunks already read from this stream.
|
||||
*/
|
||||
buffer: T[];
|
||||
|
||||
/**
|
||||
* Signals if the stream has ended or not. If not, consumers
|
||||
* should continue to read from the stream until consumed.
|
||||
*/
|
||||
ended: boolean;
|
||||
}
|
||||
|
||||
export function isReadableStream<T>(obj: unknown): obj is ReadableStream<T> {
|
||||
const candidate = obj as ReadableStream<T>;
|
||||
|
||||
return candidate && [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
|
||||
}
|
||||
|
||||
export function isReadableBufferedStream<T>(obj: unknown): obj is ReadableBufferedStream<T> {
|
||||
const candidate = obj as ReadableBufferedStream<T>;
|
||||
|
||||
return candidate && isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean';
|
||||
}
|
||||
|
||||
export interface IReducer<T> {
|
||||
(data: T[]): T;
|
||||
}
|
||||
@@ -118,8 +162,18 @@ export interface ITransformer<Original, Transformed> {
|
||||
error?: IErrorTransformer;
|
||||
}
|
||||
|
||||
export function newWriteableStream<T>(reducer: IReducer<T>): WriteableStream<T> {
|
||||
return new WriteableStreamImpl<T>(reducer);
|
||||
export function newWriteableStream<T>(reducer: IReducer<T>, options?: WriteableStreamOptions): WriteableStream<T> {
|
||||
return new WriteableStreamImpl<T>(reducer, options);
|
||||
}
|
||||
|
||||
export interface WriteableStreamOptions {
|
||||
|
||||
/**
|
||||
* The number of objects to buffer before WriteableStream#write()
|
||||
* signals back that the buffer is full. Can be used to reduce
|
||||
* the memory pressure when the stream is not flowing.
|
||||
*/
|
||||
highWaterMark?: number;
|
||||
}
|
||||
|
||||
class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
@@ -141,7 +195,9 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
end: [] as { (): void }[]
|
||||
};
|
||||
|
||||
constructor(private reducer: IReducer<T>) { }
|
||||
private readonly pendingWritePromises: Function[] = [];
|
||||
|
||||
constructor(private reducer: IReducer<T>, private options?: WriteableStreamOptions) { }
|
||||
|
||||
pause(): void {
|
||||
if (this.state.destroyed) {
|
||||
@@ -166,7 +222,7 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
}
|
||||
}
|
||||
|
||||
write(data: T): void {
|
||||
write(data: T): void | Promise<void> {
|
||||
if (this.state.destroyed) {
|
||||
return;
|
||||
}
|
||||
@@ -179,6 +235,11 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
// not yet flowing: buffer data until flowing
|
||||
else {
|
||||
this.buffer.data.push(data);
|
||||
|
||||
// highWaterMark: if configured, signal back when buffer reached limits
|
||||
if (typeof this.options?.highWaterMark === 'number' && this.buffer.data.length > this.options.highWaterMark) {
|
||||
return new Promise(resolve => this.pendingWritePromises.push(resolve));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +328,35 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
}
|
||||
}
|
||||
|
||||
removeListener(event: string, callback: Function): void {
|
||||
if (this.state.destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let listeners: unknown[] | undefined = undefined;
|
||||
|
||||
switch (event) {
|
||||
case 'data':
|
||||
listeners = this.listeners.data;
|
||||
break;
|
||||
|
||||
case 'end':
|
||||
listeners = this.listeners.end;
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
listeners = this.listeners.error;
|
||||
break;
|
||||
}
|
||||
|
||||
if (listeners) {
|
||||
const index = listeners.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private flowData(): void {
|
||||
if (this.buffer.data.length > 0) {
|
||||
const fullDataBuffer = this.reducer(this.buffer.data);
|
||||
@@ -274,6 +364,11 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
this.listeners.data.forEach(listener => listener(fullDataBuffer));
|
||||
|
||||
this.buffer.data.length = 0;
|
||||
|
||||
// When the buffer is empty, resolve all pending writers
|
||||
const pendingWritePromises = [...this.pendingWritePromises];
|
||||
this.pendingWritePromises.length = 0;
|
||||
pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,6 +403,8 @@ class WriteableStreamImpl<T> implements WriteableStream<T> {
|
||||
this.listeners.data.length = 0;
|
||||
this.listeners.error.length = 0;
|
||||
this.listeners.end.length = 0;
|
||||
|
||||
this.pendingWritePromises.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,7 +428,7 @@ export function consumeReadable<T>(readable: Readable<T>, reducer: IReducer<T>):
|
||||
* reached, will return a readable instead to ensure all data can still
|
||||
* be read.
|
||||
*/
|
||||
export function consumeReadableWithLimit<T>(readable: Readable<T>, reducer: IReducer<T>, maxChunks: number): T | Readable<T> {
|
||||
export function peekReadable<T>(readable: Readable<T>, reducer: IReducer<T>, maxChunks: number): T | Readable<T> {
|
||||
const chunks: T[] = [];
|
||||
|
||||
let chunk: T | null | undefined = undefined;
|
||||
@@ -388,58 +485,50 @@ export function consumeStream<T>(stream: ReadableStream<T>, reducer: IReducer<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to read a T stream up to a maximum of chunks. If the limit is
|
||||
* reached, will return a stream instead to ensure all data can still
|
||||
* be read.
|
||||
* Helper to peek up to `maxChunks` into a stream. The return type signals if
|
||||
* the stream has ended or not. If not, caller needs to add a `data` listener
|
||||
* to continue reading.
|
||||
*/
|
||||
export function consumeStreamWithLimit<T>(stream: ReadableStream<T>, reducer: IReducer<T>, maxChunks: number): Promise<T | ReadableStream<T>> {
|
||||
export function peekStream<T>(stream: ReadableStream<T>, maxChunks: number): Promise<ReadableBufferedStream<T>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: T[] = [];
|
||||
const streamListeners = new DisposableStore();
|
||||
|
||||
let wrapperStream: WriteableStream<T> | undefined = undefined;
|
||||
// Data Listener
|
||||
const buffer: T[] = [];
|
||||
const dataListener = (chunk: T) => {
|
||||
|
||||
stream.on('data', data => {
|
||||
// Add to buffer
|
||||
buffer.push(chunk);
|
||||
|
||||
// If we reach maxChunks, we start to return a stream
|
||||
// and make sure that any data we have already read
|
||||
// is in it as well
|
||||
if (!wrapperStream && chunks.length === maxChunks) {
|
||||
wrapperStream = newWriteableStream(reducer);
|
||||
while (chunks.length) {
|
||||
wrapperStream.write(chunks.shift()!);
|
||||
}
|
||||
// We reached maxChunks and thus need to return
|
||||
if (buffer.length > maxChunks) {
|
||||
|
||||
wrapperStream.write(data);
|
||||
// Dispose any listeners and ensure to pause the
|
||||
// stream so that it can be consumed again by caller
|
||||
streamListeners.dispose();
|
||||
stream.pause();
|
||||
|
||||
return resolve(wrapperStream);
|
||||
return resolve({ stream, buffer, ended: false });
|
||||
}
|
||||
};
|
||||
|
||||
if (wrapperStream) {
|
||||
wrapperStream.write(data);
|
||||
} else {
|
||||
chunks.push(data);
|
||||
}
|
||||
});
|
||||
streamListeners.add(toDisposable(() => stream.removeListener('data', dataListener)));
|
||||
stream.on('data', dataListener);
|
||||
|
||||
stream.on('error', error => {
|
||||
if (wrapperStream) {
|
||||
wrapperStream.error(error);
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
// Error Listener
|
||||
const errorListener = (error: Error) => {
|
||||
return reject(error);
|
||||
};
|
||||
|
||||
stream.on('end', () => {
|
||||
if (wrapperStream) {
|
||||
while (chunks.length) {
|
||||
wrapperStream.write(chunks.shift()!);
|
||||
}
|
||||
streamListeners.add(toDisposable(() => stream.removeListener('error', errorListener)));
|
||||
stream.on('error', errorListener);
|
||||
|
||||
wrapperStream.end();
|
||||
} else {
|
||||
return resolve(reducer(chunks));
|
||||
}
|
||||
});
|
||||
const endListener = () => {
|
||||
return resolve({ stream, buffer, ended: true });
|
||||
};
|
||||
|
||||
streamListeners.add(toDisposable(() => stream.removeListener('end', endListener)));
|
||||
stream.on('end', endListener);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -728,9 +728,9 @@ export function isBasicASCII(str: string): boolean {
|
||||
return IS_BASIC_ASCII.test(str);
|
||||
}
|
||||
|
||||
export const UNUSUAL_LINE_TERMINATORS = /[\u2028\u2029\u0085]/; // LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS), NEXT LINE (NEL)
|
||||
export const UNUSUAL_LINE_TERMINATORS = /[\u2028\u2029]/; // LINE SEPARATOR (LS) or PARAGRAPH SEPARATOR (PS)
|
||||
/**
|
||||
* Returns true if `str` contains unusual line terminators, like LS, PS or NEL
|
||||
* Returns true if `str` contains unusual line terminators, like LS or PS
|
||||
*/
|
||||
export function containsUnusualLineTerminators(str: string): boolean {
|
||||
return UNUSUAL_LINE_TERMINATORS.test(str);
|
||||
|
||||
@@ -64,6 +64,13 @@ export function isUndefined(obj: any): obj is undefined {
|
||||
return (typeof obj === 'undefined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is defined.
|
||||
*/
|
||||
export function isDefined<T>(arg: T | null | undefined): arg is T {
|
||||
return !isUndefinedOrNull(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
|
||||
@@ -413,7 +413,7 @@ interface UriState extends UriComponents {
|
||||
|
||||
const _pathSepMarker = isWindows ? 1 : undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/class-name-casing
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
class _URI extends URI {
|
||||
|
||||
_formatted: string | null = null;
|
||||
|
||||
Reference in New Issue
Block a user