Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae

This commit is contained in:
ADS Merger
2020-06-18 04:32:54 +00:00
committed by AzureDataStudio
parent a971aee5bd
commit 5e7071e466
1002 changed files with 24201 additions and 13193 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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