Atlas › Test

event.test|title=Event utils EventMultiplexer hot start works|occurrence=1

Exact test identity: mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/common/event.test|title=Event utils EventMultiplexer hot start works|occurrence=1

Package
mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/common
Suite / test hierarchy
event.test|title=Event utils EventMultiplexer hot start works|occurrence=1
Test
event.test|title=Event utils EventMultiplexer hot start works|occurrence=1
Introduced at
event.test|title=Event utils EventMultiplexer works|occurrence=1, event.test|title=Event utils EventMultiplexer hot start works|occurrence=1 Frontier kind: Test frontier
Covered ranges
1448
Covered lines
10071
Covered files
81

Co-introduced tests

1 other test enter at the same concept.

Covered source

Expand a file to inspect source; the > gutter marks covered lines.

src/vs/base/common/async.ts 1238 covered LOC · 202 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- async.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken, CancellationTokenSource } from './cancellation.js';
7 > import { BugIndicatingError, CancellationError, isCancellationError } from './errors.js';
8 > import { Emitter, Event } from './event.js';
9 > import { Disposable, DisposableMap, DisposableStore, IDisposable, isDisposable, MutableDisposable, toDisposable } from './lifecycle.js';
10 > import { extUri as defaultExtUri, IExtUri } from './resources.js';
11 > import { URI } from './uri.js';
12 > import { setTimeout0 } from './platform.js';
13 > import { MicrotaskDelay } from './symbols.js';
14 > import { Lazy } from './lazy.js';
15 >
16 > export function isThenable<T>(obj: unknown): obj is Promise<T> {
17 return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
18 }
19 > async.ts
20 > export interface CancelablePromise<T> extends Promise<T> {
21 > cancel(): void;
22 > }
23 >
24 > /**
25 > * Returns a promise that can be cancelled using the provided cancellation token.
26 > *
27 > * @remarks When cancellation is requested, the promise will be rejected with a {@link CancellationError}.
28 > * If the promise resolves to a disposable object, it will be automatically disposed when cancellation
29 > * is requested.
30 > *
31 > * @param callback A function that accepts a cancellation token and returns a promise
32 > * @returns A promise that can be cancelled
33 > */
34 > export function createCancelablePromise<T>(callback: (token: CancellationToken) => Promise<T>): CancelablePromise<T> {
35 const source = new CancellationTokenSource();
36
80 };
81 }
82 > async.ts
83 > /**
84 > * Returns a promise that resolves with `undefined` as soon as the passed token is cancelled.
85 > * @see {@link raceCancellationError}
86 > */
87 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken): Promise<T | undefined>;
88 >
89 > /**
90 > * Returns a promise that resolves with `defaultValue` as soon as the passed token is cancelled.
91 > * @see {@link raceCancellationError}
92 > */
93 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue: T): Promise<T>;
94 >
95 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue?: T): Promise<T | undefined> {
96 return new Promise((resolve, reject) => {
97 const ref = token.onCancellationRequested(() => {
102 });
103 }
104 > async.ts
105 > /**
106 > * Returns a promise that rejects with an {@CancellationError} as soon as the passed token is cancelled.
107 > * @see {@link raceCancellation}
108 > */
109 > export function raceCancellationError<T>(promise: Promise<T>, token: CancellationToken): Promise<T> {
110 return new Promise((resolve, reject) => {
111 const ref = token.onCancellationRequested(() => {
116 });
117 }
118 > async.ts
119 > export function rejectIfNotCanceled(err: unknown): undefined {
120 if (isCancellationError(err)) {
121 return undefined;
123 return Promise.reject(err) as never;
124 }
125 > async.ts
126 > /**
127 > * Wraps a cancellable promise such that it is no cancellable. Can be used to
128 > * avoid issues with shared promises that would normally be returned as
129 > * cancellable to consumers.
130 > */
131 > export function notCancellablePromise<T>(promise: CancelablePromise<T>): Promise<T> {
132 return new Promise<T>((resolve, reject) => {
133 promise.then(resolve, reject);
134 });
135 }
136 > async.ts
137 > /**
138 > * Returns as soon as one of the promises resolves or rejects and cancels remaining promises
139 > */
140 > export function raceCancellablePromises<T>(cancellablePromises: (CancelablePromise<T> | Promise<T>)[]): CancelablePromise<T> {
141 let resolvedPromiseIndex = -1;
142 const promises = cancellablePromises.map((promise, index) => promise.then(result => { resolvedPromiseIndex = index; return result; }));
154 return promise;
155 }
156 > async.ts
157 > export function raceTimeout<T>(promise: Promise<T>, timeout: number, onTimeout?: () => void): Promise<T | undefined> {
158 let promiseResolve: ((value: T | undefined) => void) | undefined = undefined;
159
168 ]);
169 }
170 > async.ts
171 > export function asPromise<T>(callback: () => T | Thenable<T>): Promise<T> {
172 return new Promise<T>((resolve, reject) => {
173 const item = callback();
179 });
180 }
181 > async.ts
182 > /**
183 > * Creates and returns a new promise, plus its `resolve` and `reject` callbacks.
184 > *
185 > * Replace with standardized [`Promise.withResolvers`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers) once it is supported
186 > */
187 > export function promiseWithResolvers<T>(): { promise: Promise<T>; resolve: (value: T | PromiseLike<T>) => void; reject: (err?: any) => void } {
188 let resolve: (value: T | PromiseLike<T>) => void;
189 let reject: (reason?: any) => void;
194 return { promise, resolve: resolve!, reject: reject! };
195 }
196 > async.ts
197 > export interface ITask<T> {
198 > (): T;
199 > }
200 >
201 > export interface ICancellableTask<T> {
202 > (token: CancellationToken): T;
203 > }
204 >
205 > /**
206 > * A helper to prevent accumulation of sequential async tasks.
207 > *
208 > * Imagine a mail man with the sole task of delivering letters. As soon as
209 > * a letter submitted for delivery, he drives to the destination, delivers it
210 > * and returns to his base. Imagine that during the trip, N more letters were submitted.
211 > * When the mail man returns, he picks those N letters and delivers them all in a
212 > * single trip. Even though N+1 submissions occurred, only 2 deliveries were made.
213 > *
214 > * The throttler implements this via the queue() method, by providing it a task
215 > * factory. Following the example:
216 > *
217 > * const throttler = new Throttler();
218 > * const letters = [];
219 > *
220 > * function deliver() {
221 > * const lettersToDeliver = letters;
222 > * letters = [];
223 > * return makeTheTrip(lettersToDeliver);
224 > * }
225 > *
226 > * function onLetterReceived(l) {
227 > * letters.push(l);
228 > * throttler.queue(deliver);
229 > * }
230 > */
231 > export class Throttler implements IDisposable {
232 >
233 > private activePromise: Promise<any> | null;
234 > private queuedPromise: Promise<any> | null;
235 > private queuedPromiseFactory: ICancellableTask<Promise<any>> | null;
236 > private cancellationTokenSource: CancellationTokenSource;
237 >
238 > constructor() {
239 this.activePromise = null;
240 this.queuedPromise = null;
243 this.cancellationTokenSource = new CancellationTokenSource();
244 }
245 > async.ts
246 > queue<T>(promiseFactory: ICancellableTask<Promise<T>>): Promise<T> {
247 if (this.cancellationTokenSource.token.isCancellationRequested) {
248 return Promise.reject(new Error('Throttler is disposed'));
288 });
289 }
290 > async.ts
291 > dispose(): void {
292 this.cancellationTokenSource.cancel();
293 }
294 > } async.ts
295 >
296 > export class Sequencer {
297
298 private current: Promise<unknown> = Promise.resolve(null);
299 > async.ts
300 > queue<T>(promiseTask: ITask<Promise<T>>): Promise<T> {
301 return this.current = this.current.then(() => promiseTask(), () => promiseTask());
302 }
303 > } async.ts
304 >
305 > /**
306 > * A {@link Throttler} per key. Calls for the same key coalesce (only the most
307 > * recently queued task runs after the active one settles); calls for different
308 > * keys are independent. Idle keys are cleaned up automatically.
309 > */
310 > export class ThrottlerByKey<TKey> implements IDisposable {
311
312 private readonly throttlers = new Map<TKey, { throttler: Throttler; count: number }>();
313 > async.ts
314 > queue<T>(key: TKey, task: ITask<Promise<T>>): Promise<T> {
315 let entry = this.throttlers.get(key);
316 if (!entry) {
327 });
328 }
329 > async.ts
330 > dispose(): void {
331 for (const { throttler } of this.throttlers.values()) {
332 throttler.dispose();
334 this.throttlers.clear();
335 }
336 > } async.ts
337 >
338 > export class SequencerByKey<TKey> {
339
340 private promiseMap = new Map<TKey, Promise<unknown>>();
341 > async.ts
342 > queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
343 const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
344 const newPromise = runningPromise
353 return newPromise;
354 }
355 > async.ts
356 > peek(key: TKey): Promise<unknown> | undefined {
357 return this.promiseMap.get(key) || undefined;
358 }
359 > async.ts
360 > keys(): IterableIterator<TKey> {
361 return this.promiseMap.keys();
362 }
363 > } async.ts
364 >
365 > interface IScheduledLater extends IDisposable {
366 > isTriggered(): boolean;
367 > }
368 >
369 > const timeoutDeferred = (timeout: number, fn: () => void): IScheduledLater => {
370 let scheduled = true;
371 const handle = setTimeout(() => {
381 };
382 };
383 > async.ts
384 > const microtaskDeferred = (fn: () => void): IScheduledLater => {
385 let scheduled = true;
386 queueMicrotask(() => {
396 };
397 };
398 > async.ts
399 > /**
400 > * A helper to delay (debounce) execution of a task that is being requested often.
401 > *
402 > * Following the throttler, now imagine the mail man wants to optimize the number of
403 > * trips proactively. The trip itself can be long, so he decides not to make the trip
404 > * as soon as a letter is submitted. Instead he waits a while, in case more
405 > * letters are submitted. After said waiting period, if no letters were submitted, he
406 > * decides to make the trip. Imagine that N more letters were submitted after the first
407 > * one, all within a short period of time between each other. Even though N+1
408 > * submissions occurred, only 1 delivery was made.
409 > *
410 > * The delayer offers this behavior via the trigger() method, into which both the task
411 > * to be executed and the waiting period (delay) must be passed in as arguments. Following
412 > * the example:
413 > *
414 > * const delayer = new Delayer(WAITING_PERIOD);
415 > * const letters = [];
416 > *
417 > * function letterReceived(l) {
418 > * letters.push(l);
419 > * delayer.trigger(() => { return makeTheTrip(); });
420 > * }
421 > */
422 > export class Delayer<T> implements IDisposable {
423 >
424 > private deferred: IScheduledLater | null;
425 > private completionPromise: Promise<any> | null;
426 > private doResolve: ((value?: any | Promise<any>) => void) | null;
427 > private doReject: ((err: unknown) => void) | null;
428 > private task: ITask<T | Promise<T>> | null;
429 >
430 > constructor(public defaultDelay: number | typeof MicrotaskDelay) {
431 this.deferred = null;
432 this.completionPromise = null;
435 this.task = null;
436 }
437 > async.ts
438 > trigger(task: ITask<T | Promise<T>>, delay = this.defaultDelay): Promise<T> {
439 this.task = task;
440 this.cancelTimeout();
465 return this.completionPromise;
466 }
467 > async.ts
468 > isTriggered(): boolean {
469 return !!this.deferred?.isTriggered();
470 }
471 > async.ts
472 > cancel(): void {
473 this.cancelTimeout();
474
478 }
479 }
480 > async.ts
481 > private cancelTimeout(): void {
482 this.deferred?.dispose();
483 this.deferred = null;
484 }
485 > async.ts
486 > dispose(): void {
487 this.cancel();
488 }
489 > } async.ts
490 >
491 > /**
492 > * A helper to delay execution of a task that is being requested often, while
493 > * preventing accumulation of consecutive executions, while the task runs.
494 > *
495 > * The mail man is clever and waits for a certain amount of time, before going
496 > * out to deliver letters. While the mail man is going out, more letters arrive
497 > * and can only be delivered once he is back. Once he is back the mail man will
498 > * do one more trip to deliver the letters that have accumulated while he was out.
499 > */
500 > export class ThrottledDelayer<T> {
501 >
502 > private delayer: Delayer<Promise<T>>;
503 > private throttler: Throttler;
504 >
505 > constructor(defaultDelay: number) {
506 this.delayer = new Delayer(defaultDelay);
507 this.throttler = new Throttler();
508 }
509 > async.ts
510 > trigger(promiseFactory: ICancellableTask<Promise<T>>, delay?: number): Promise<T> {
511 return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise<T>;
512 }
513 > async.ts
514 > isTriggered(): boolean {
515 return this.delayer.isTriggered();
516 }
517 > async.ts
518 > cancel(): void {
519 this.delayer.cancel();
520 }
521 > async.ts
522 > dispose(): void {
523 this.delayer.dispose();
524 this.throttler.dispose();
525 }
526 > } async.ts
527 >
528 > /**
529 > * A barrier that is initially closed and then becomes opened permanently.
530 > */
531 > export class Barrier {
532 > private _isOpen: boolean;
533 > private _promise: Promise<boolean>;
534 > private _completePromise!: (v: boolean) => void;
535 >
536 > constructor() {
537 this._isOpen = false;
538 this._promise = new Promise<boolean>((c, e) => {
540 });
541 }
542 > async.ts
543 > isOpen(): boolean {
544 return this._isOpen;
545 }
546 > async.ts
547 > open(): void {
548 this._isOpen = true;
549 this._completePromise(true);
550 }
551 > async.ts
552 > wait(): Promise<boolean> {
553 return this._promise;
554 }
555 > } async.ts
556 >
557 > /**
558 > * A barrier that is initially closed and then becomes opened permanently after a certain period of
559 > * time or when open is called explicitly
560 > */
561 > export class AutoOpenBarrier extends Barrier {
562 >
563 > private readonly _timeout: Timeout;
564 >
565 > constructor(autoOpenTimeMs: number) {
566 super();
567 this._timeout = setTimeout(() => this.open(), autoOpenTimeMs);
568 }
569 > async.ts
570 > override open(): void {
571 clearTimeout(this._timeout);
572 super.open();
573 }
574 > } async.ts
575 >
576 > export function timeout(millis: number): CancelablePromise<void>;
577 > export function timeout(millis: number, token: CancellationToken): Promise<void>;
578 > export function timeout(millis: number, token?: CancellationToken): CancelablePromise<void> | Promise<void> {
579 if (!token) {
580 return createCancelablePromise(token => timeout(millis, token));
593 });
594 }
595 > async.ts
596 > /**
597 > * Creates a timeout that can be disposed using its returned value.
598 > * @param handler The timeout handler.
599 > * @param timeout An optional timeout in milliseconds.
600 > * @param store An optional {@link DisposableStore} that will have the timeout disposable managed automatically.
601 > *
602 > * @example
603 > * const store = new DisposableStore;
604 > * // Call the timeout after 1000ms at which point it will be automatically
605 > * // evicted from the store.
606 > * const timeoutDisposable = disposableTimeout(() => {}, 1000, store);
607 > *
608 > * if (foo) {
609 > * // Cancel the timeout and evict it from store.
610 > * timeoutDisposable.dispose();
611 > * }
612 > */
613 > export function disposableTimeout(handler: () => void, timeout = 0, store?: DisposableStore): IDisposable {
614 const timer = setTimeout(() => {
615 handler();
625 return disposable;
626 }
627 > async.ts
628 > /**
629 > * The largest delay (in milliseconds) a single `setTimeout` can represent.
630 > * Larger values overflow its internal 32-bit signed integer and fire (almost)
631 > * immediately instead of waiting.
632 > */
633 > export const MAX_TIMEOUT_DELAY = 2 ** 31 - 1; // ~24.8 days
634 >
635 > /**
636 > * Like {@link disposableTimeout}, but supports delays larger than
637 > * {@link MAX_TIMEOUT_DELAY} (~24.8 days), which a single `setTimeout` cannot
638 > * represent. The wait is split into chunks and re-armed until the target time is
639 > * reached, so the handler fires at approximately `Date.now() + timeout`.
640 > *
641 > * Note: like `setTimeout`, firing is best-effort and may drift across system
642 > * sleep or wall-clock changes; do not rely on it for precise scheduling.
643 > *
644 > * @param handler The timeout handler.
645 > * @param timeout The timeout in milliseconds. May exceed {@link MAX_TIMEOUT_DELAY}.
646 > * @param store An optional {@link DisposableStore} that will have the timeout disposable managed automatically.
647 > */
648 > export function disposableLongTimeout(handler: () => void, timeout: number, store?: DisposableStore): IDisposable {
649 const target = Date.now() + timeout;
650 let timer: Timeout;
671 return disposable;
672 }
673 > async.ts
674 > /**
675 > * Runs the provided list of promise factories in sequential order. The returned
676 > * promise will complete to an array of results from each promise.
677 > */
678 >
679 > export function sequence<T>(promiseFactories: ITask<Promise<T>>[]): Promise<T[]> {
680 const results: T[] = [];
681 let index = 0;
701 return Promise.resolve(null).then(thenHandler);
702 }
703 > async.ts
704 > export function first<T>(promiseFactories: ITask<Promise<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise<T | null> {
705 let index = 0;
706 const len = promiseFactories.length;
725 return loop();
726 }
727 > async.ts
728 > /**
729 > * Returns the result of the first promise that matches the "shouldStop",
730 > * running all promises in parallel. Supports cancelable promises.
731 > */
732 > export function firstParallel<T>(promiseList: Promise<T>[], shouldStop?: (t: T) => boolean, defaultValue?: T | null): Promise<T | null>;
733 > export function firstParallel<T, R extends T>(promiseList: Promise<T>[], shouldStop: (t: T) => t is R, defaultValue?: R | null): Promise<R | null>;
734 > export function firstParallel<T>(promiseList: Promise<T>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null) {
735 if (promiseList.length === 0) {
736 return Promise.resolve(defaultValue);
764 });
765 }
766 > async.ts
767 > interface ILimitedTaskFactory<T> {
768 > factory: ITask<Promise<T>>;
769 > c: (value: T | Promise<T>) => void;
770 > e: (error?: unknown) => void;
771 > }
772 >
773 > export interface ILimiter<T> {
774 >
775 > readonly size: number;
776 >
777 > queue(factory: ITask<Promise<T>>): Promise<T>;
778 >
779 > clear(): void;
780 > }
781 >
782 > /**
783 > * A helper to queue N promises and run them all with a max degree of parallelism. The helper
784 > * ensures that at any time no more than M promises are running at the same time.
785 > */
786 > export class Limiter<T> implements ILimiter<T> {
787 >
788 > private _size = 0;
789 > private _isDisposed = false;
790 > private runningPromises: number;
791 > private readonly maxDegreeOfParalellism: number;
792 > private readonly outstandingPromises: ILimitedTaskFactory<T>[];
793 > private readonly _onDrained: Emitter<void>;
794 >
795 > constructor(maxDegreeOfParalellism: number) {
796 this.maxDegreeOfParalellism = maxDegreeOfParalellism;
797 this.outstandingPromises = [];
799 this._onDrained = new Emitter<void>();
800 }
801 > async.ts
802 > /**
803 > *
804 > * @returns A promise that resolved when all work is done (onDrained) or when
805 > * there is nothing to do
806 > */
807 > whenIdle(): Promise<void> {
808 return this.size > 0
809 ? Event.toPromise(this.onDrained)
810 : Promise.resolve();
811 }
812 > async.ts
813 > get onDrained(): Event<void> {
814 return this._onDrained.event;
815 }
816 > async.ts
817 > get size(): number {
818 return this._size;
819 }
820 > async.ts
821 > queue(factory: ITask<Promise<T>>): Promise<T> {
822 if (this._isDisposed) {
823 throw new Error('Object has been disposed');
830 });
831 }
832 > async.ts
833 > private consume(): void {
834 while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
835 const iLimitedTask = this.outstandingPromises.shift()!;
841 }
842 }
843 > async.ts
844 > private consumed(): void {
845 if (this._isDisposed) {
846 return;
855 }
856 }
857 > async.ts
858 > clear(): void {
859 if (this._isDisposed) {
860 throw new Error('Object has been disposed');
863 this._size = this.runningPromises;
864 }
865 > async.ts
866 > dispose(): void {
867 this._isDisposed = true;
868 this.outstandingPromises.length = 0; // stop further processing
870 this._onDrained.dispose();
871 }
872 > } async.ts
873 >
874 > /**
875 > * A queue is handles one promise at a time and guarantees that at any time only one promise is executing.
876 > */
877 > export class Queue<T> extends Limiter<T> {
878 >
879 > constructor() {
880 super(1);
881 }
882 > } async.ts
883 >
884 > /**
885 > * Same as `Queue`, ensures that only 1 task is executed at the same time. The difference to `Queue` is that
886 > * there is only 1 task about to be scheduled next. As such, calling `queue` while a task is executing will
887 > * replace the currently queued task until it executes.
888 > *
889 > * As such, the returned promise may not be from the factory that is passed in but from the next factory that
890 > * is running after having called `queue`.
891 > */
892 > export class LimitedQueue {
893
894 private readonly sequentializer = new TaskSequentializer();
895
896 private tasks = 0;
897 > async.ts
898 > queue(factory: ITask<Promise<void>>): Promise<void> {
899 if (!this.sequentializer.isRunning()) {
900 return this.sequentializer.run(this.tasks++, factory());
905 });
906 }
907 > } async.ts
908 >
909 > /**
910 > * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource
911 > * by disposing them once the queue is empty.
912 > */
913 > export class ResourceQueue implements IDisposable {
914
915 private readonly queues = new Map<string, Queue<void>>();
919 private drainListeners: DisposableMap<number> | undefined = undefined;
920 private drainListenerCount = 0;
921 > async.ts
922 > async whenDrained(): Promise<void> {
923 if (this.isDrained()) {
924 return;
930 return promise.p;
931 }
932 > async.ts
933 > private isDrained(): boolean {
934 for (const [, queue] of this.queues) {
935 if (queue.size > 0) {
940 return true;
941 }
942 > async.ts
943 > queueSize(resource: URI, extUri: IExtUri = defaultExtUri): number {
944 const key = extUri.getComparisonKey(resource);
945
946 return this.queues.get(key)?.size ?? 0;
947 }
948 > async.ts
949 > queueFor(resource: URI, factory: ITask<Promise<void>>, extUri: IExtUri = defaultExtUri): Promise<void> {
950 const key = extUri.getComparisonKey(resource);
951
977 return queue.queue(factory);
978 }
979 > async.ts
980 > private onDidQueueDrain(): void {
981 if (!this.isDrained()) {
982 return; // not done yet
985 this.releaseDrainers();
986 }
987 > async.ts
988 > private releaseDrainers(): void {
989 for (const drainer of this.drainers) {
990 drainer.complete();
993 this.drainers.clear();
994 }
995 > async.ts
996 > dispose(): void {
997 for (const [, queue] of this.queues) {
998 queue.dispose();
1011 this.drainListeners?.dispose();
1012 }
1013 > } async.ts
1014 >
1015 > export type Task<T = void> = () => (Promise<T> | T);
1016 >
1017 > /**
1018 > * Wrap a type in an optional promise. This can be useful to avoid the runtime
1019 > * overhead of creating a promise.
1020 > */
1021 > export type MaybePromise<T> = Promise<T> | T;
1022 >
1023 > /**
1024 > * Processes tasks in the order they were scheduled.
1025 > */
1026 > export class TaskQueue {
1027 private _runningTask: Task<any> | undefined = undefined;
1028 private _pendingTasks: { task: Task<any>; deferred: DeferredPromise<any>; setUndefinedWhenCleared: boolean }[] = [];
1029 > async.ts
1030 > /**
1031 > * Waits for the current and pending tasks to finish, then runs and awaits the given task.
1032 > * If the task is skipped because of clearPending, the promise is rejected with a CancellationError.
1033 > */
1034 > public schedule<T>(task: Task<T>): Promise<T> {
1035 const deferred = new DeferredPromise<T>();
1036 this._pendingTasks.push({ task, deferred, setUndefinedWhenCleared: false });
1038 return deferred.p;
1039 }
1040 > async.ts
1041 > /**
1042 > * Waits for the current and pending tasks to finish, then runs and awaits the given task.
1043 > * If the task is skipped because of clearPending, the promise is resolved with undefined.
1044 > */
1045 > public scheduleSkipIfCleared<T>(task: Task<T>): Promise<T | undefined> {
1046 const deferred = new DeferredPromise<T>();
1047 this._pendingTasks.push({ task, deferred, setUndefinedWhenCleared: true });
1049 return deferred.p;
1050 }
1051 > async.ts
1052 > private _runIfNotRunning(): void {
1053 if (this._runningTask === undefined) {
1054 this._processQueue();
1055 }
1056 }
1057 > async.ts
1058 > private async _processQueue(): Promise<void> {
1059 if (this._pendingTasks.length === 0) {
1060 return;
1082 }
1083 }
1084 > async.ts
1085 > /**
1086 > * Clears all pending tasks. Does not cancel the currently running task.
1087 > */
1088 > public clearPending(): void {
1089 const tasks = this._pendingTasks;
1090 this._pendingTasks = [];
1097 }
1098 }
1099 > } async.ts
1100 >
1101 > export class TimeoutTimer implements IDisposable {
1102 > private _token: Timeout | undefined;
1103 > private _isDisposed = false;
1104 >
1105 > constructor();
1106 > constructor(runner: () => void, timeout: number);
1107 > constructor(runner?: () => void, timeout?: number) {
1108 this._token = undefined;
1109
1112 }
1113 }
1114 > async.ts
1115 > dispose(): void {
1116 this.cancel();
1117 this._isDisposed = true;
1118 }
1119 > async.ts
1120 > cancel(): void {
1121 if (this._token !== undefined) {
1122 clearTimeout(this._token);
1124 }
1125 }
1126 > async.ts
1127 > cancelAndSet(runner: () => void, timeout: number): void {
1128 if (this._isDisposed) {
1129 throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed TimeoutTimer`);
1136 }, timeout);
1137 }
1138 > async.ts
1139 > setIfNotSet(runner: () => void, timeout: number): void {
1140 if (this._isDisposed) {
1141 throw new BugIndicatingError(`Calling 'setIfNotSet' on a disposed TimeoutTimer`);
1151 }, timeout);
1152 }
1153 > } async.ts
1154 >
1155 > export class IntervalTimer implements IDisposable {
1156
1157 private disposable: IDisposable | undefined = undefined;
1158 private isDisposed = false;
1159 > async.ts
1160 > cancel(): void {
1161 this.disposable?.dispose();
1162 this.disposable = undefined;
1163 }
1164 > async.ts
1165 > cancelAndSet(runner: () => void, interval: number, context = globalThis): void {
1166 if (this.isDisposed) {
1167 throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed IntervalTimer`);
1178 });
1179 }
1180 > async.ts
1181 > dispose(): void {
1182 this.cancel();
1183 this.isDisposed = true;
1184 }
1185 > } async.ts
1186 >
1187 > export class RunOnceScheduler<Runner extends (...args: any[]) => any = () => any> implements IDisposable {
1188 >
1189 > protected runner: Runner | null;
1190 >
1191 > private timeoutToken: Timeout | undefined;
1192 > private timeout: number;
1193 > private timeoutHandler: () => void;
1194 >
1195 > constructor(runner: Runner, delay: number) {
1196 this.timeoutToken = undefined;
1197 this.runner = runner;
1199 this.timeoutHandler = this.onTimeout.bind(this);
1200 }
1201 > async.ts
1202 > /**
1203 > * Dispose RunOnceScheduler
1204 > */
1205 > dispose(): void {
1206 this.cancel();
1207 this.runner = null;
1208 }
1209 > async.ts
1210 > /**
1211 > * Cancel current scheduled runner (if any).
1212 > */
1213 > cancel(): void {
1214 if (this.isScheduled()) {
1215 clearTimeout(this.timeoutToken);
1217 }
1218 }
1219 > async.ts
1220 > /**
1221 > * Cancel previous runner (if any) & schedule a new runner.
1222 > */
1223 > schedule(delay = this.timeout): void {
1224 this.cancel();
1225 this.timeoutToken = setTimeout(this.timeoutHandler, delay);
1226 }
1227 > async.ts
1228 > get delay(): number {
1229 return this.timeout;
1230 }
1231 > async.ts
1232 > set delay(value: number) {
1233 this.timeout = value;
1234 }
1235 > async.ts
1236 > /**
1237 > * Returns true if scheduled.
1238 > */
1239 > isScheduled(): boolean {
1240 return this.timeoutToken !== undefined;
1241 }
1242 > async.ts
1243 > flush(): void {
1244 if (this.isScheduled()) {
1245 this.cancel();
1247 }
1248 }
1249 > async.ts
1250 > private onTimeout() {
1251 this.timeoutToken = undefined;
1252 if (this.runner) {
1254 }
1255 }
1256 > async.ts
1257 > protected doRun(): void {
1258 this.runner?.();
1259 }
1260 > } async.ts
1261 >
1262 > /**
1263 > * Same as `RunOnceScheduler`, but doesn't count the time spent in sleep mode.
1264 > * > **NOTE**: Only offers 1s resolution.
1265 > *
1266 > * When calling `setTimeout` with 3hrs, and putting the computer immediately to sleep
1267 > * for 8hrs, `setTimeout` will fire **as soon as the computer wakes from sleep**. But
1268 > * this scheduler will execute 3hrs **after waking the computer from sleep**.
1269 > */
1270 > export class ProcessTimeRunOnceScheduler {
1271 >
1272 > private runner: (() => void) | null;
1273 > private timeout: number;
1274 >
1275 > private counter: number;
1276 > private intervalToken: Timeout | undefined;
1277 > private intervalHandler: () => void;
1278 >
1279 > constructor(runner: () => void, delay: number) {
1280 if (delay % 1000 !== 0) {
1281 console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${delay}ms is not a multiple of 1000ms.`);
1287 this.intervalHandler = this.onInterval.bind(this);
1288 }
1289 > async.ts
1290 > dispose(): void {
1291 this.cancel();
1292 this.runner = null;
1293 }
1294 > async.ts
1295 > cancel(): void {
1296 if (this.isScheduled()) {
1297 clearInterval(this.intervalToken);
1299 }
1300 }
1301 > async.ts
1302 > /**
1303 > * Cancel previous runner (if any) & schedule a new runner.
1304 > */
1305 > schedule(delay = this.timeout): void {
1306 if (delay % 1000 !== 0) {
1307 console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${delay}ms is not a multiple of 1000ms.`);
1311 this.intervalToken = setInterval(this.intervalHandler, 1000);
1312 }
1313 > async.ts
1314 > /**
1315 > * Returns true if scheduled.
1316 > */
1317 > isScheduled(): boolean {
1318 return this.intervalToken !== undefined;
1319 }
1320 > async.ts
1321 > private onInterval() {
1322 this.counter--;
1323 if (this.counter > 0) {
1331 this.runner?.();
1332 }
1333 > } async.ts
1334 >
1335 > export class RunOnceWorker<T> extends RunOnceScheduler<(units: T[]) => void> {
1336 >
1337 > private units: T[] = [];
1338 >
1339 > constructor(runner: (units: T[]) => void, timeout: number) {
1340 super(runner, timeout);
1341 }
1342 > async.ts
1343 > work(unit: T): void {
1344 this.units.push(unit);
1345
1348 }
1349 }
1350 > async.ts
1351 > protected override doRun(): void {
1352 const units = this.units;
1353 this.units = [];
1355 this.runner?.(units);
1356 }
1357 > async.ts
1358 > override dispose(): void {
1359 this.units = [];
1360
1361 super.dispose();
1362 }
1363 > } async.ts
1364 >
1365 > export interface IThrottledWorkerOptions {
1366 >
1367 > /**
1368 > * maximum of units the worker will pass onto handler at once
1369 > */
1370 > maxWorkChunkSize: number;
1371 >
1372 > /**
1373 > * maximum of units the worker will keep in memory for processing
1374 > */
1375 > maxBufferedWork: number | undefined;
1376 >
1377 > /**
1378 > * delay before processing the next round of chunks when chunk size exceeds limits
1379 > */
1380 > throttleDelay: number;
1381 >
1382 > /**
1383 > * When enabled will guarantee that two distinct calls to `work()` are not executed
1384 > * without throttle delay between them.
1385 > * Otherwise if the worker isn't currently throttling it will execute work immediately.
1386 > */
1387 > waitThrottleDelayBetweenWorkUnits?: boolean;
1388 > }
1389 >
1390 > /**
1391 > * The `ThrottledWorker` will accept units of work `T`
1392 > * to handle. The contract is:
1393 > * * there is a maximum of units the worker can handle at once (via `maxWorkChunkSize`)
1394 > * * there is a maximum of units the worker will keep in memory for processing (via `maxBufferedWork`)
1395 > * * after having handled `maxWorkChunkSize` units, the worker needs to rest (via `throttleDelay`)
1396 > */
1397 > export class ThrottledWorker<T> extends Disposable {
1398 >
1399 > private readonly pendingWork: T[] = [];
1400 >
1401 > private readonly throttler = this._register(new MutableDisposable<RunOnceScheduler>());
1402 > private disposed = false;
1403 > private lastExecutionTime = 0;
1404 >
1405 > constructor(
1406 private options: IThrottledWorkerOptions,
1407 private readonly handler: (units: T[]) => void
1409 super();
1410 }
1411 > async.ts
1412 > /**
1413 > * The number of work units that are pending to be processed.
1414 > */
1415 > get pending(): number { return this.pendingWork.length; }
1416 >
1417 > /**
1418 > * Add units to be worked on. Use `pending` to figure out
1419 > * how many units are not yet processed after this method
1420 > * was called.
1421 > *
1422 > * @returns whether the work was accepted or not. If the
1423 > * worker is disposed, it will not accept any more work.
1424 > * If the number of pending units would become larger
1425 > * than `maxPendingWork`, more work will also not be accepted.
1426 > */
1427 > work(units: readonly T[]): boolean {
1428 if (this.disposed) {
1429 return false; // work not accepted: disposed
1469 return true; // work accepted
1470 }
1471 > async.ts
1472 > private doWork(): void {
1473 this.lastExecutionTime = Date.now();
1474
1481 }
1482 }
1483 > async.ts
1484 > private scheduleThrottler(delay = this.options.throttleDelay): void {
1485 this.throttler.value = new RunOnceScheduler(() => {
1486 this.throttler.clear();
1490 this.throttler.value.schedule();
1491 }
1492 > async.ts
1493 > override dispose(): void {
1494 super.dispose();
1495
1497 this.disposed = true;
1498 }
1499 > } async.ts
1500 >
1501 > //#region -- run on idle tricks ------------
1502 >
1503 > export interface IdleDeadline {
1504 > readonly didTimeout: boolean;
1505 > timeRemaining(): number;
1506 > }
1507 >
1508 > type IdleApi = Pick<typeof globalThis, 'requestIdleCallback' | 'cancelIdleCallback'>;
1509 >
1510 >
1511 > /**
1512 > * Execute the callback the next time the browser is idle, returning an
1513 > * {@link IDisposable} that will cancel the callback when disposed. This wraps
1514 > * [requestIdleCallback] so it will fallback to [setTimeout] if the environment
1515 > * doesn't support it.
1516 > *
1517 > * @param callback The callback to run when idle, this includes an
1518 > * [IdleDeadline] that provides the time alloted for the idle callback by the
1519 > * browser. Not respecting this deadline will result in a degraded user
1520 > * experience.
1521 > * @param timeout A timeout at which point to queue no longer wait for an idle
1522 > * callback but queue it on the regular event loop (like setTimeout). Typically
1523 > * this should not be used.
1524 > *
1525 > * [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline
1526 > * [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
1527 > * [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
1528 > *
1529 > * **Note** that there is `dom.ts#runWhenWindowIdle` which is better suited when running inside a browser
1530 > * context
1531 > */
1532 > export let runWhenGlobalIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable;
1533 >
1534 > export let _runWhenIdle: (targetWindow: IdleApi, callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable;
1535 >
1536 > (function () {
1537 > const safeGlobal: any = globalThis;
1538 > if (typeof safeGlobal.requestIdleCallback !== 'function' || typeof safeGlobal.cancelIdleCallback !== 'function') {
1539 > _runWhenIdle = (_targetWindow, runner, timeout?) => {
1540 setTimeout0(() => {
1541 if (disposed) {
1561 };
1562 };
1563 > } else { async.ts
1564 _runWhenIdle = (targetWindow: typeof safeGlobal, runner, timeout?) => {
1565 const handle: number = targetWindow.requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
1576 };
1577 }
1578 > runWhenGlobalIdle = (runner, timeout) => _runWhenIdle(globalThis, runner, timeout); async.ts
1579 > })();
1580 >
1581 > export function installFakeRunWhenIdle(fakeImpl: typeof _runWhenIdle): IDisposable {
1582 const origRunWhenIdle = _runWhenIdle;
1583 const origRunWhenGlobalIdle = runWhenGlobalIdle;
1589 });
1590 }
1591 > async.ts
1592 > export abstract class AbstractIdleValue<T> {
1593 >
1594 > private readonly _executor: () => void;
1595 > private readonly _handle: IDisposable;
1596 >
1597 > private _didRun: boolean = false;
1598 > private _value?: T;
1599 > private _error: unknown;
1600 >
1601 > constructor(targetWindow: IdleApi, executor: () => T) {
1602 this._executor = () => {
1603 try {
1611 this._handle = _runWhenIdle(targetWindow, () => this._executor());
1612 }
1613 > async.ts
1614 > dispose(): void {
1615 this._handle.dispose();
1616 }
1617 > async.ts
1618 > get value(): T {
1619 if (!this._didRun) {
1620 this._handle.dispose();
1626 return this._value!;
1627 }
1628 > async.ts
1629 > get isInitialized(): boolean {
1630 return this._didRun;
1631 }
1632 > } async.ts
1633 >
1634 > /**
1635 > * An `IdleValue` that always uses the current window (which might be throttled or inactive)
1636 > *
1637 > * **Note** that there is `dom.ts#WindowIdleValue` which is better suited when running inside a browser
1638 > * context
1639 > */
1640 > export class GlobalIdleValue<T> extends AbstractIdleValue<T> {
1641 >
1642 > constructor(executor: () => T) {
1643 super(globalThis, executor);
1644 }
1645 > } async.ts
1646 >
1647 > //#endregion
1648 >
1649 export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries: number): Promise<T> {
1650 let lastError: Error | undefined;
1662 throw lastError;
1663 }
1664 > async.ts
1665 > //#region Task Sequentializer
1666 >
1667 > interface IRunningTask {
1668 > readonly taskId: number;
1669 > readonly cancel: () => void;
1670 > readonly promise: Promise<void>;
1671 > }
1672 >
1673 > interface IQueuedTask {
1674 > readonly promise: Promise<void>;
1675 > readonly promiseResolve: () => void;
1676 > readonly promiseReject: (error: Error) => void;
1677 > run: ITask<Promise<void>>;
1678 > }
1679 >
1680 > export interface ITaskSequentializerWithRunningTask {
1681 > readonly running: Promise<void>;
1682 > }
1683 >
1684 > export interface ITaskSequentializerWithQueuedTask {
1685 > readonly queued: IQueuedTask;
1686 > }
1687 >
1688 > /**
1689 > * @deprecated use `LimitedQueue` instead for an easier to use API
1690 > */
1691 > export class TaskSequentializer {
1692 >
1693 > private _running?: IRunningTask;
1694 > private _queued?: IQueuedTask;
1695 >
1696 > isRunning(taskId?: number): this is ITaskSequentializerWithRunningTask {
1697 if (typeof taskId === 'number') {
1698 return this._running?.taskId === taskId;
1701 return !!this._running;
1702 }
1703 > async.ts
1704 > get running(): Promise<void> | undefined {
1705 return this._running?.promise;
1706 }
1707 > async.ts
1708 > cancelRunning(): void {
1709 this._running?.cancel();
1710 }
1711 > async.ts
1712 > run(taskId: number, promise: Promise<void>, onCancel?: () => void,): Promise<void> {
1713 this._running = { taskId, cancel: () => onCancel?.(), promise };
1714
1717 return promise;
1718 }
1719 > async.ts
1720 > private doneRunning(taskId: number): void {
1721 if (this._running && taskId === this._running.taskId) {
1722
1728 }
1729 }
1730 > async.ts
1731 > private runQueued(): void {
1732 if (this._queued) {
1733 const queued = this._queued;
1738 }
1739 }
1740 > async.ts
1741 > /**
1742 > * Note: the promise to schedule as next run MUST itself call `run`.
1743 > * Otherwise, this sequentializer will report `false` for `isRunning`
1744 > * even when this task is running. Missing this detail means that
1745 > * suddenly multiple tasks will run in parallel.
1746 > */
1747 > queue(run: ITask<Promise<void>>): Promise<void> {
1748
1749 // this is our first queued task, so we create associated promise with it
1767 return this._queued.promise;
1768 }
1769 > async.ts
1770 > hasQueued(): this is ITaskSequentializerWithQueuedTask {
1771 return !!this._queued;
1772 }
1773 > async.ts
1774 > async join(): Promise<void> {
1775 return this._queued?.promise ?? this._running?.promise;
1776 }
1777 > } async.ts
1778 >
1779 > //#endregion
1780 >
1781 > //#region
1782 >
1783 > /**
1784 > * The `IntervalCounter` allows to count the number
1785 > * of calls to `increment()` over a duration of
1786 > * `interval`. This utility can be used to conditionally
1787 > * throttle a frequent task when a certain threshold
1788 > * is reached.
1789 > */
1790 > export class IntervalCounter {
1791 >
1792 > private lastIncrementTime = 0;
1793 >
1794 > private value = 0;
1795 >
1796 > constructor(private readonly interval: number, private readonly nowFn = () => Date.now()) { }
1797 >
1798 > increment(): number {
1799 const now = this.nowFn();
1800
1810 return this.value;
1811 }
1812 > } async.ts
1813 >
1814 > //#endregion
1815 >
1816 > //#region
1817 >
1818 > export type ValueCallback<T = unknown> = (value: T | Promise<T>) => void;
1819 >
1820 > const enum DeferredOutcome {
1821 > Resolved,
1822 > Rejected
1823 > }
1824 >
1825 > /**
1826 > * Creates a promise whose resolution or rejection can be controlled imperatively.
1827 > */
1828 > export class DeferredPromise<T> {
1829 >
1830 > public static fromPromise<T>(promise: Promise<T>): DeferredPromise<T> {
1831 const deferred = new DeferredPromise<T>();
1832 deferred.settleWith(promise);
1833 return deferred;
1834 }
1835 > async.ts
1836 > private completeCallback!: ValueCallback<T>;
1837 > private errorCallback!: (err: unknown) => void;
1838 > private outcome?: { outcome: DeferredOutcome.Rejected; value: unknown } | { outcome: DeferredOutcome.Resolved; value: T };
1839 >
1840 > public get isRejected() {
1841 return this.outcome?.outcome === DeferredOutcome.Rejected;
1842 }
1843 > async.ts
1844 > public get isResolved() {
1845 return this.outcome?.outcome === DeferredOutcome.Resolved;
1846 }
1847 > async.ts
1848 > public get isSettled() {
1849 return !!this.outcome;
1850 }
1851 > async.ts
1852 > public get value() {
1853 return this.outcome?.outcome === DeferredOutcome.Resolved ? this.outcome?.value : undefined;
1854 }
1855 > async.ts
1856 > public readonly p: Promise<T>;
1857 >
1858 > constructor() {
1859 this.p = new Promise<T>((c, e) => {
1860 this.completeCallback = c;
1862 });
1863 }
1864 > async.ts
1865 > public complete(value: T) {
1866 if (this.isSettled) {
1867 return Promise.resolve();
1874 });
1875 }
1876 > async.ts
1877 > public error(err: unknown) {
1878 if (this.isSettled) {
1879 return Promise.resolve();
1886 });
1887 }
1888 > async.ts
1889 > public settleWith(promise: Promise<T>): Promise<void> {
1890 return promise.then(
1891 value => this.complete(value),
1893 );
1894 }
1895 > async.ts
1896 > public cancel() {
1897 return this.error(new CancellationError());
1898 }
1899 > } async.ts
1900 >
1901 > //#endregion
1902 >
1903 > //#region Promises
1904 >
1905 > export namespace Promises {
1906 >
1907 > /**
1908 > * A drop-in replacement for `Promise.all` with the only difference
1909 > * that the method awaits every promise to either fulfill or reject.
1910 > *
1911 > * Similar to `Promise.all`, only the first error will be returned
1912 > * if any.
1913 > */
1914 > export async function settled<T>(promises: Promise<T>[]): Promise<T[]> {
1915 let firstError: Error | undefined = undefined;
1916
1929 return result as unknown as T[]; // cast is needed and protected by the `throw` above
1930 }
1931 > async.ts
1932 > /**
1933 > * A helper to create a new `Promise<T>` with a body that is a promise
1934 > * itself. By default, an error that raises from the async body will
1935 > * end up as a unhandled rejection, so this utility properly awaits the
1936 > * body and rejects the promise as a normal promise does without async
1937 > * body.
1938 > *
1939 > * This method should only be used in rare cases where otherwise `async`
1940 > * cannot be used (e.g. when callbacks are involved that require this).
1941 > */
1942 > export function withAsyncBody<T, E = Error>(bodyFn: (resolve: (value: T) => unknown, reject: (error: E) => unknown) => Promise<unknown>): Promise<T> {
1943 // eslint-disable-next-line no-async-promise-executor
1944 return new Promise<T>(async (resolve, reject) => {
1950 });
1951 }
1952 > } async.ts
1953 >
1954 > export class StatefulPromise<T> {
1955 > private _value: T | undefined = undefined;
1956 > get value(): T | undefined { return this._value; }
1957 >
1958 > private _error: unknown = undefined;
1959 > get error(): unknown { return this._error; }
1960 >
1961 > private _isResolved = false;
1962 > get isResolved() { return this._isResolved; }
1963 >
1964 > public readonly promise: Promise<T>;
1965 >
1966 > constructor(promise: Promise<T>) {
1967 this.promise = promise.then(
1968 value => {
1978 );
1979 }
1980 > async.ts
1981 > /**
1982 > * Returns the resolved value.
1983 > * Throws if the promise is not resolved yet.
1984 > */
1985 > public requireValue(): T {
1986 if (!this._isResolved) {
1987 throw new BugIndicatingError('Promise is not resolved yet');
1992 return this._value!;
1993 }
1994 > } async.ts
1995 >
1996 > export class LazyStatefulPromise<T> {
1997 > private readonly _promise = new Lazy(() => new StatefulPromise(this._compute()));
1998 >
1999 > constructor(
2000 private readonly _compute: () => Promise<T>,
2001 ) { }
2002 > async.ts
2003 > /**
2004 > * Returns the resolved value.
2005 > * Throws if the promise is not resolved yet.
2006 > */
2007 > public requireValue(): T {
2008 return this._promise.value.requireValue();
2009 }
2010 > async.ts
2011 > /**
2012 > * Returns the promise (and triggers a computation of the promise if not yet done so).
2013 > */
2014 > public getPromise(): Promise<T> {
2015 return this._promise.value.promise;
2016 }
2017 > async.ts
2018 > /**
2019 > * Reads the current value without triggering a computation of the promise.
2020 > */
2021 > public get currentValue(): T | undefined {
2022 return this._promise.rawValue?.value;
2023 }
2024 > } async.ts
2025 >
2026 > //#endregion
2027 >
2028 > //#region
2029 >
2030 > const enum AsyncIterableSourceState {
2031 > Initial,
2032 > DoneOK,
2033 > DoneError,
2034 > }
2035 >
2036 > /**
2037 > * An object that allows to emit async values asynchronously or bring the iterable to an error state using `reject()`.
2038 > * This emitter is valid only for the duration of the executor (until the promise returned by the executor settles).
2039 > */
2040 > export interface AsyncIterableEmitter<T> {
2041 > /**
2042 > * The value will be appended at the end.
2043 > *
2044 > * **NOTE** If `reject()` has already been called, this method has no effect.
2045 > */
2046 > emitOne(value: T): void;
2047 > /**
2048 > * The values will be appended at the end.
2049 > *
2050 > * **NOTE** If `reject()` has already been called, this method has no effect.
2051 > */
2052 > emitMany(values: T[]): void;
2053 > /**
2054 > * Writing an error will permanently invalidate this iterable.
2055 > * The current users will receive an error thrown, as will all future users.
2056 > *
2057 > * **NOTE** If `reject()` have already been called, this method has no effect.
2058 > */
2059 > reject(error: Error): void;
2060 > }
2061 >
2062 > /**
2063 > * An executor for the `AsyncIterableObject` that has access to an emitter.
2064 > */
2065 > export interface AsyncIterableExecutor<T> {
2066 > /**
2067 > * @param emitter An object that allows to emit async values valid only for the duration of the executor.
2068 > */
2069 > (emitter: AsyncIterableEmitter<T>): unknown | Promise<unknown>;
2070 > }
2071 >
2072 > /**
2073 > * A rich implementation for an `AsyncIterable<T>`.
2074 > */
2075 > export class AsyncIterableObject<T> implements AsyncIterable<T> {
2076 >
2077 > public static fromArray<T>(items: T[]): AsyncIterableObject<T> {
2078 > return new AsyncIterableObject<T>((writer) => {
2079 > writer.emitMany(items);
2080 > });
2081 > }
2082 >
2083 > public static fromPromise<T>(promise: Promise<T[]>): AsyncIterableObject<T> {
2084 return new AsyncIterableObject<T>(async (emitter) => {
2085 emitter.emitMany(await promise);
2086 });
2087 }
2088 > async.ts
2089 > public static fromPromisesResolveOrder<T>(promises: Promise<T>[]): AsyncIterableObject<T> {
2090 return new AsyncIterableObject<T>(async (emitter) => {
2091 await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
2092 });
2093 }
2094 > async.ts
2095 > public static merge<T>(iterables: AsyncIterable<T>[]): AsyncIterableObject<T> {
2096 return new AsyncIterableObject(async (emitter) => {
2097 await Promise.all(iterables.map(async (iterable) => {
2102 });
2103 }
2104 > async.ts
2105 > public static EMPTY = AsyncIterableObject.fromArray<any>([]);
2106 >
2107 > private _state: AsyncIterableSourceState;
2108 > private _results: T[];
2109 > private _error: Error | null;
2110 > private readonly _onReturn?: () => void | Promise<void>;
2111 > private readonly _onStateChanged: Emitter<void>;
2112 >
2113 > constructor(executor: AsyncIterableExecutor<T>, onReturn?: () => void | Promise<void>) {
2114 > this._state = AsyncIterableSourceState.Initial;
2115 > this._results = [];
2116 > this._error = null;
2117 > this._onReturn = onReturn;
2118 > this._onStateChanged = new Emitter<void>();
2119 >
2120 > queueMicrotask(async () => {
2121 > const writer: AsyncIterableEmitter<T> = {
2122 > emitOne: (item) => this.emitOne(item),
2123 > emitMany: (items) => this.emitMany(items),
2124 > reject: (error) => this.reject(error)
2125 > };
2126 > try {
2127 > await Promise.resolve(executor(writer));
2128 > this.resolve();
2129 > } catch (err) {
2130 this.reject(err);
2131 > } finally { async.ts
2132 > // The executor has settled; emitting afterwards must be a no-op per the
2133 > // documented "no effect after resolve()/reject()" contract (see emitOne).
2134 > writer.emitOne = () => { };
2135 > writer.emitMany = () => { };
2136 > writer.reject = () => { };
2137 > }
2138 > });
2139 > }
2140 >
2141 > [Symbol.asyncIterator](): AsyncIterator<T, undefined, undefined> {
2142 let i = 0;
2143 return {
2162 };
2163 }
2164 > async.ts
2165 > public static map<T, R>(iterable: AsyncIterable<T>, mapFn: (item: T) => R): AsyncIterableObject<R> {
2166 return new AsyncIterableObject<R>(async (emitter) => {
2167 for await (const item of iterable) {
2170 });
2171 }
2172 > async.ts
2173 > public map<R>(mapFn: (item: T) => R): AsyncIterableObject<R> {
2174 return AsyncIterableObject.map(this, mapFn);
2175 }
2176 > async.ts
2177 > public static filter<T>(iterable: AsyncIterable<T>, filterFn: (item: T) => boolean): AsyncIterableObject<T> {
2178 return new AsyncIterableObject<T>(async (emitter) => {
2179 for await (const item of iterable) {
2184 });
2185 }
2186 > async.ts
2187 > public filter<T2 extends T>(filterFn: (item: T) => item is T2): AsyncIterableObject<T2>;
2188 > public filter(filterFn: (item: T) => boolean): AsyncIterableObject<T>;
2189 > public filter(filterFn: (item: T) => boolean): AsyncIterableObject<T> {
2190 return AsyncIterableObject.filter(this, filterFn);
2191 }
2192 > async.ts
2193 > public static coalesce<T>(iterable: AsyncIterable<T | undefined | null>): AsyncIterableObject<T> {
2194 return <AsyncIterableObject<T>>AsyncIterableObject.filter(iterable, item => !!item);
2195 }
2196 > async.ts
2197 > public coalesce(): AsyncIterableObject<NonNullable<T>> {
2198 return AsyncIterableObject.coalesce(this) as AsyncIterableObject<NonNullable<T>>;
2199 }
2200 > async.ts
2201 > public static async toPromise<T>(iterable: AsyncIterable<T>): Promise<T[]> {
2202 const result: T[] = [];
2203 for await (const item of iterable) {
2206 return result;
2207 }
2208 > async.ts
2209 > public toPromise(): Promise<T[]> {
2210 return AsyncIterableObject.toPromise(this);
2211 }
2212 > async.ts
2213 > /**
2214 > * The value will be appended at the end.
2215 > *
2216 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2217 > */
2218 > private emitOne(value: T): void {
2219 if (this._state !== AsyncIterableSourceState.Initial) {
2220 return;
2225 this._onStateChanged.fire();
2226 }
2227 > async.ts
2228 > /**
2229 > * The values will be appended at the end.
2230 > *
2231 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2232 > */
2233 > private emitMany(values: T[]): void {
2234 > if (this._state !== AsyncIterableSourceState.Initial) {
2235 return;
2236 }
2237 > // it is important to add new values at the end, async.ts
2238 > // as we may have iterators already running on the array
2239 > this._results = this._results.concat(values);
2240 > this._onStateChanged.fire();
2241 > }
2242 >
2243 > /**
2244 > * Calling `resolve()` will mark the result array as complete.
2245 > *
2246 > * **NOTE** `resolve()` must be called, otherwise all consumers of this iterable will hang indefinitely, similar to a non-resolved promise.
2247 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2248 > */
2249 > private resolve(): void {
2250 > if (this._state !== AsyncIterableSourceState.Initial) {
2251 return;
2252 }
2253 > this._state = AsyncIterableSourceState.DoneOK; async.ts
2254 > this._onStateChanged.fire();
2255 > }
2256 >
2257 > /**
2258 > * Writing an error will permanently invalidate this iterable.
2259 > * The current users will receive an error thrown, as will all future users.
2260 > *
2261 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2262 > */
2263 > private reject(error: Error) {
2264 if (this._state !== AsyncIterableSourceState.Initial) {
2265 return;
2269 this._onStateChanged.fire();
2270 }
2271 > } async.ts
2272 >
2273 >
2274 > export function createCancelableAsyncIterableProducer<T>(callback: (token: CancellationToken) => AsyncIterable<T>): CancelableAsyncIterableProducer<T> {
2275 const source = new CancellationTokenSource();
2276 const innerIterable = callback(source.token);
2299 });
2300 }
2301 > async.ts
2302 > export class AsyncIterableSource<T> {
2303 >
2304 > private readonly _deferred = new DeferredPromise<void>();
2305 > private readonly _asyncIterable: AsyncIterableObject<T>;
2306 >
2307 > private _errorFn: (error: Error) => void;
2308 > private _emitOneFn: (item: T) => void;
2309 > private _emitManyFn: (item: T[]) => void;
2310 >
2311 > /**
2312 > *
2313 > * @param onReturn A function that will be called when consuming the async iterable
2314 > * has finished by the consumer, e.g the for-await-loop has be existed (break, return) early.
2315 > * This is NOT called when resolving this source by its owner.
2316 > */
2317 > constructor(onReturn?: () => Promise<void> | void) {
2318 this._asyncIterable = new AsyncIterableObject(emitter => {
2319
2354 };
2355 }
2356 > async.ts
2357 > get asyncIterable(): AsyncIterableObject<T> {
2358 return this._asyncIterable;
2359 }
2360 > async.ts
2361 > resolve(): void {
2362 this._deferred.complete();
2363 }
2364 > async.ts
2365 > reject(error: Error): void {
2366 this._errorFn(error);
2367 this._deferred.complete();
2368 }
2369 > async.ts
2370 > emitOne(item: T): void {
2371 this._emitOneFn(item);
2372 }
2373 > async.ts
2374 > emitMany(items: T[]) {
2375 this._emitManyFn(items);
2376 }
2377 > } async.ts
2378 >
2379 > export function cancellableIterable<T>(iterableOrIterator: AsyncIterator<T> | AsyncIterable<T>, token: CancellationToken): AsyncIterableIterator<T> {
2380 const iterator = Symbol.asyncIterator in iterableOrIterator ? iterableOrIterator[Symbol.asyncIterator]() : iterableOrIterator;
2381
2395 };
2396 }
2397 > async.ts
2398 > type ProducerConsumerValue<T> = {
2399 > ok: true;
2400 > value: T;
2401 > } | {
2402 > ok: false;
2403 > error: Error;
2404 > };
2405 >
2406 > class ProducerConsumer<T> {
2407 > private readonly _unsatisfiedConsumers: DeferredPromise<T>[] = [];
2408 > private readonly _unconsumedValues: ProducerConsumerValue<T>[] = [];
2409 > private _finalValue: ProducerConsumerValue<T> | undefined;
2410 >
2411 > public get hasFinalValue(): boolean {
2412 > return !!this._finalValue;
2413 > }
2414 >
2415 > produce(value: ProducerConsumerValue<T>): void {
2416 this._ensureNoFinalValue();
2417 if (this._unsatisfiedConsumers.length > 0) {
2422 }
2423 }
2424 > async.ts
2425 > produceFinal(value: ProducerConsumerValue<T>): void {
2426 > this._ensureNoFinalValue();
2427 > this._finalValue = value;
2428 > for (const deferred of this._unsatisfiedConsumers) {
2429 this._resolveOrRejectDeferred(deferred, value);
2430 }
2431 > this._unsatisfiedConsumers.length = 0; async.ts
2432 > }
2433 >
2434 > private _ensureNoFinalValue(): void {
2435 > if (this._finalValue) {
2436 throw new BugIndicatingError('ProducerConsumer: cannot produce after final value has been set');
2437 }
2438 > } async.ts
2439 >
2440 > private _resolveOrRejectDeferred(deferred: DeferredPromise<T>, value: ProducerConsumerValue<T>): void {
2441 if (value.ok) {
2442 deferred.complete(value.value);
2445 }
2446 }
2447 > async.ts
2448 > consume(): Promise<T> {
2449 if (this._unconsumedValues.length > 0 || this._finalValue) {
2450 const value = this._unconsumedValues.length > 0 ? this._unconsumedValues.shift()! : this._finalValue!;
2460 }
2461 }
2462 > } async.ts
2463 >
2464 > /**
2465 > * Important difference to AsyncIterableObject:
2466 > * If it is iterated two times, the second iterator will not see the values emitted by the first iterator.
2467 > */
2468 > export class AsyncIterableProducer<T> implements AsyncIterable<T> {
2469 > private readonly _producerConsumer = new ProducerConsumer<IteratorResult<T>>();
2470 >
2471 > constructor(executor: AsyncIterableExecutor<T>, private readonly _onReturn?: () => void) {
2472 > queueMicrotask(async () => {
2473 > const p = executor({
2474 > emitOne: value => this._producerConsumer.produce({ ok: true, value: { done: false, value: value } }),
2475 > emitMany: values => {
2476 > for (const value of values) {
2477 this._producerConsumer.produce({ ok: true, value: { done: false, value: value } });
2478 }
2479 > }, async.ts
2480 > reject: error => this._finishError(error),
2481 > });
2482 >
2483 > if (!this._producerConsumer.hasFinalValue) {
2484 > try {
2485 > await p;
2486 > this._finishOk();
2487 > } catch (error) {
2488 this._finishError(error);
2489 }
2490 > } async.ts
2491 > });
2492 > }
2493 >
2494 > public static fromArray<T>(items: T[]): AsyncIterableProducer<T> {
2495 > return new AsyncIterableProducer<T>((writer) => {
2496 > writer.emitMany(items);
2497 > });
2498 > }
2499 >
2500 > public static fromPromise<T>(promise: Promise<T[]>): AsyncIterableProducer<T> {
2501 return new AsyncIterableProducer<T>(async (emitter) => {
2502 emitter.emitMany(await promise);
2503 });
2504 }
2505 > async.ts
2506 > public static fromPromisesResolveOrder<T>(promises: Promise<T>[]): AsyncIterableProducer<T> {
2507 return new AsyncIterableProducer<T>(async (emitter) => {
2508 await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
2509 });
2510 }
2511 > async.ts
2512 > public static merge<T>(iterables: AsyncIterable<T>[]): AsyncIterableProducer<T> {
2513 return new AsyncIterableProducer(async (emitter) => {
2514 await Promise.all(iterables.map(async (iterable) => {
2519 });
2520 }
2521 > async.ts
2522 > public static EMPTY = AsyncIterableProducer.fromArray<any>([]);
2523 >
2524 > public static map<T, R>(iterable: AsyncIterable<T>, mapFn: (item: T) => R): AsyncIterableProducer<R> {
2525 return new AsyncIterableProducer<R>(async (emitter) => {
2526 for await (const item of iterable) {
2529 });
2530 }
2531 > async.ts
2532 > public static tee<T>(iterable: AsyncIterable<T>): [AsyncIterableProducer<T>, AsyncIterableProducer<T>] {
2533 let emitter1: AsyncIterableEmitter<T> | undefined;
2534 let emitter2: AsyncIterableEmitter<T> | undefined;
2565 return [p1, p2];
2566 }
2567 > async.ts
2568 > public map<R>(mapFn: (item: T) => R): AsyncIterableProducer<R> {
2569 return AsyncIterableProducer.map(this, mapFn);
2570 }
2571 > async.ts
2572 > public static coalesce<T>(iterable: AsyncIterable<T | undefined | null>): AsyncIterableProducer<T> {
2573 return <AsyncIterableProducer<T>>AsyncIterableProducer.filter(iterable, item => !!item);
2574 }
2575 > async.ts
2576 > public coalesce(): AsyncIterableProducer<NonNullable<T>> {
2577 return AsyncIterableProducer.coalesce(this) as AsyncIterableProducer<NonNullable<T>>;
2578 }
2579 > async.ts
2580 > public static filter<T>(iterable: AsyncIterable<T>, filterFn: (item: T) => boolean): AsyncIterableProducer<T> {
2581 return new AsyncIterableProducer<T>(async (emitter) => {
2582 for await (const item of iterable) {
2587 });
2588 }
2589 > async.ts
2590 > public filter<T2 extends T>(filterFn: (item: T) => item is T2): AsyncIterableProducer<T2>;
2591 > public filter(filterFn: (item: T) => boolean): AsyncIterableProducer<T>;
2592 > public filter(filterFn: (item: T) => boolean): AsyncIterableProducer<T> {
2593 return AsyncIterableProducer.filter(this, filterFn);
2594 }
2595 > async.ts
2596 > private _finishOk(): void {
2597 > if (!this._producerConsumer.hasFinalValue) {
2598 > this._producerConsumer.produceFinal({ ok: true, value: { done: true, value: undefined } });
2599 > }
2600 > }
2601 >
2602 > private _finishError(error: Error): void {
2603 if (!this._producerConsumer.hasFinalValue) {
2604 this._producerConsumer.produceFinal({ ok: false, error: error });
2606 // Warning: this can cause to dropped errors.
2607 }
2608 > async.ts
2609 > private readonly _iterator: AsyncIterator<T, void, void> = {
2610 > next: () => this._producerConsumer.consume(),
2611 > return: () => {
2612 this._onReturn?.();
2613 return Promise.resolve({ done: true, value: undefined });
2614 },
2615 > throw: async (e) => { async.ts
2616 this._finishError(e);
2617 return { done: true, value: undefined };
2618 },
2619 > }; async.ts
2620 >
2621 > [Symbol.asyncIterator](): AsyncIterator<T, void, void> {
2622 return this._iterator;
2623 }
2624 > } async.ts
2625 >
2626 > export class CancelableAsyncIterableProducer<T> extends AsyncIterableProducer<T> {
2627 > constructor(
2628 private readonly _source: CancellationTokenSource,
2629 executor: AsyncIterableExecutor<T>
2631 super(executor);
2632 }
2633 > async.ts
2634 > cancel(): void {
2635 this._source.cancel();
2636 }
2637 > } async.ts
2638 >
2639 > //#endregion
2640 >
2641 > export const AsyncReaderEndOfStream = Symbol('AsyncReaderEndOfStream');
2642 >
2643 > export class AsyncReader<T> {
2644 > private _buffer: T[] = [];
2645 > private _atEnd = false;
2646 >
2647 > public get endOfStream(): boolean { return this._buffer.length === 0 && this._atEnd; }
2648 > private _extendBufferPromise: Promise<void> | undefined;
2649 >
2650 > constructor(
2651 private readonly _source: AsyncIterator<T>
2652 ) {
2653 }
2654 > async.ts
2655 > public async read(): Promise<T | typeof AsyncReaderEndOfStream> {
2656 if (this._buffer.length === 0 && !this._atEnd) {
2657 await this._extendBuffer();
2662 return this._buffer.shift()!;
2663 }
2664 > async.ts
2665 > public async readWhile(predicate: (value: T) => boolean, callback: (element: T) => unknown): Promise<void> {
2666 do {
2667 const piece = await this.peek();
2676 } while (true);
2677 }
2678 > async.ts
2679 > public readBufferedOrThrow(): T | typeof AsyncReaderEndOfStream {
2680 const value = this.peekBufferedOrThrow();
2681 this._buffer.shift();
2682 return value;
2683 }
2684 > async.ts
2685 > public async consumeToEnd(): Promise<void> {
2686 while (!this.endOfStream) {
2687 await this.read();
2688 }
2689 }
2690 > async.ts
2691 > public async peek(): Promise<T | typeof AsyncReaderEndOfStream> {
2692 if (this._buffer.length === 0 && !this._atEnd) {
2693 await this._extendBuffer();
2698 return this._buffer[0];
2699 }
2700 > async.ts
2701 > public peekBufferedOrThrow(): T | typeof AsyncReaderEndOfStream {
2702 if (this._buffer.length === 0) {
2703 if (this._atEnd) {
2709 return this._buffer[0];
2710 }
2711 > async.ts
2712 > public async peekTimeout(timeoutMs: number): Promise<T | typeof AsyncReaderEndOfStream | undefined> {
2713 if (this._buffer.length === 0 && !this._atEnd) {
2714 await raceTimeout(this._extendBuffer(), timeoutMs);
2722 return this._buffer[0];
2723 }
2724 > async.ts
2725 > private _extendBuffer(): Promise<void> {
2726 if (this._atEnd) {
2727 return Promise.resolve();
2742 return this._extendBufferPromise;
2743 }
2744 > } async.ts
2745 >
2746 > export function createTimeout(ms: number, cb: () => void): IDisposable {
2747 const t = setTimeout(cb, ms);
2748 return toDisposable(() => clearTimeout(t));
src/vs/base/common/event.ts 970 covered LOC · 137 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- event.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancelablePromise } from './async.js';
7 > import { CancellationToken } from './cancellation.js';
8 > import { diffSets } from './collections.js';
9 > import { onUnexpectedError } from './errors.js';
10 > import { createSingleCallFunction } from './functional.js';
11 > import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from './lifecycle.js';
12 > import { LinkedList } from './linkedList.js';
13 > import { IObservable, IObservableWithChange, IObserver } from './observable.js';
14 > import { env } from './process.js';
15 > import { StopWatch } from './stopwatch.js';
16 > import { MicrotaskDelay } from './symbols.js';
17 >
18 >
19 > // -----------------------------------------------------------------------------------------------------------------------
20 > // Uncomment the next line to print warnings whenever an emitter with listeners is disposed. That is a sign of code smell.
21 > // -----------------------------------------------------------------------------------------------------------------------
22 > const _enableDisposeWithListenerWarning = false
23 > // || Boolean("TRUE") // causes a linter warning so that it cannot be pushed
24 > ;
25 >
26 >
27 > // -----------------------------------------------------------------------------------------------------------------------
28 > // Uncomment the next line to print warnings whenever a snapshotted event is used repeatedly without cleanup.
29 > // See https://github.com/microsoft/vscode/issues/142851
30 > // -----------------------------------------------------------------------------------------------------------------------
31 > const _enableSnapshotPotentialLeakWarning = false
32 > // || Boolean("TRUE") // causes a linter warning so that it cannot be pushed
33 > ;
34 >
35 >
36 > const _bufferLeakWarnCountThreshold = 100;
37 > const _bufferLeakWarnTimeThreshold = 60_000; // 1 minute
38 >
39 function _isBufferLeakWarningEnabled(): boolean {
40 return !!env['VSCODE_DEV'];
41 }
42 > event.ts
43 > /**
44 > * An event with zero or one parameters that can be subscribed to. The event is a function itself.
45 > */
46 > export interface Event<T> {
47 > (listener: (e: T) => unknown, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
48 > }
49 >
50 > export namespace Event {
51 > export const None: Event<any> = () => Disposable.None;
52 >
53 > function _addLeakageTraceLogic(options: EmitterOptions) {
54 if (_enableSnapshotPotentialLeakWarning) {
55 const { onDidAddListener: origListenerDidAdd } = options;
65 }
66 }
67 > event.ts
68 > /**
69 > * Given an event, returns another event which debounces calls and defers the listeners to a later task via a shared
70 > * `setTimeout`. The event is converted into a signal (`Event<void>`) to avoid additional object creation as a
71 > * result of merging events and to try prevent race conditions that could arise when using related deferred and
72 > * non-deferred events.
73 > *
74 > * This is useful for deferring non-critical work (eg. general UI updates) to ensure it does not block critical work
75 > * (eg. latency of keypress to text rendered).
76 > *
77 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
78 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
79 > * returned event causes this utility to leak a listener on the original event.
80 > *
81 > * @param event The event source for the new event.
82 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
83 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
84 > * listener gets disposed before the debounced event fires.
85 > * @param disposable A disposable store to add the new EventEmitter to.
86 > */
87 > export function defer(event: Event<unknown>, flushOnListenerRemove?: boolean, disposable?: DisposableStore): Event<void> {
88 return debounce<unknown, void>(event, () => void 0, 0, undefined, flushOnListenerRemove ?? true, undefined, disposable);
89 }
90 > event.ts
91 > /**
92 > * Given an event, returns another event which only fires once.
93 > *
94 > * @param event The event source for the new event.
95 > */
96 > export function once<T>(event: Event<T>): Event<T> {
97 return (listener, thisArgs = null, disposables?) => {
98 // we need this, in case the event fires during the listener call
118 };
119 }
120 > event.ts
121 > /**
122 > * Given an event, returns another event which only fires once, and only when the condition is met.
123 > *
124 > * @param event The event source for the new event.
125 > */
126 > export function onceIf<T>(event: Event<T>, condition: (e: T) => boolean): Event<T> {
127 return Event.once(Event.filter(event, condition));
128 }
129 > event.ts
130 > /**
131 > * Maps an event of one type into an event of another type using a mapping function, similar to how
132 > * `Array.prototype.map` works.
133 > *
134 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
135 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
136 > * returned event causes this utility to leak a listener on the original event.
137 > *
138 > * @param event The event source for the new event.
139 > * @param map The mapping function.
140 > * @param disposable A disposable store to add the new EventEmitter to.
141 > */
142 > export function map<I, O>(event: Event<I>, map: (i: I) => O, disposable?: DisposableStore): Event<O> {
143 return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables), disposable);
144 }
145 > event.ts
146 > /**
147 > * Wraps an event in another event that performs some function on the event object before firing.
148 > *
149 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
150 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
151 > * returned event causes this utility to leak a listener on the original event.
152 > *
153 > * @param event The event source for the new event.
154 > * @param each The function to perform on the event object.
155 > * @param disposable A disposable store to add the new EventEmitter to.
156 > */
157 > export function forEach<I>(event: Event<I>, each: (i: I) => void, disposable?: DisposableStore): Event<I> {
158 return snapshot((listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables), disposable);
159 }
160 > event.ts
161 > /**
162 > * Wraps an event in another event that fires only when some condition is met.
163 > *
164 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
165 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
166 > * returned event causes this utility to leak a listener on the original event.
167 > *
168 > * @param event The event source for the new event.
169 > * @param filter The filter function that defines the condition. The event will fire for the object if this function
170 > * returns true.
171 > * @param disposable A disposable store to add the new EventEmitter to.
172 > */
173 > export function filter<T, U>(event: Event<T | U>, filter: (e: T | U) => e is T, disposable?: DisposableStore): Event<T>;
174 > export function filter<T>(event: Event<T>, filter: (e: T) => boolean, disposable?: DisposableStore): Event<T>;
175 > export function filter<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R, disposable?: DisposableStore): Event<R>;
176 > export function filter<T>(event: Event<T>, filter: (e: T) => boolean, disposable?: DisposableStore): Event<T> {
177 return snapshot((listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables), disposable);
178 }
179 > event.ts
180 > /**
181 > * Given an event, returns the same event but typed as `Event<void>`.
182 > */
183 > export function signal<T>(event: Event<T>): Event<void> {
184 return event as Event<any> as Event<void>;
185 }
186 > event.ts
187 > /**
188 > * Given a collection of events, returns a single event which emits whenever any of the provided events emit.
189 > */
190 > export function any<T>(...events: Event<T>[]): Event<T>;
191 > export function any(...events: Event<any>[]): Event<void>;
192 > export function any<T>(...events: Event<T>[]): Event<T> {
193 return (listener, thisArgs = null, disposables?) => {
194 const disposable = combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e))));
196 };
197 }
198 > event.ts
199 > /**
200 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
201 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
202 > * returned event causes this utility to leak a listener on the original event.
203 > */
204 > export function reduce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, initial?: O, disposable?: DisposableStore): Event<O> {
205 let output: O | undefined = initial;
206
210 }, disposable);
211 }
212 > event.ts
213 > function snapshot<T>(event: Event<T>, disposable: DisposableStore | undefined): Event<T> {
214 let listener: IDisposable | undefined;
215
233 return emitter.event;
234 }
235 > event.ts
236 > /**
237 > * Adds the IDisposable to the store if it's set, and returns it. Useful to
238 > * Event function implementation.
239 > */
240 > function addAndReturnDisposable<T extends IDisposable>(d: T, store: DisposableStore | IDisposable[] | undefined): T {
241 if (store instanceof Array) {
242 store.push(d);
246 return d;
247 }
248 > event.ts
249 > /**
250 > * Given an event, creates a new emitter that event that will debounce events based on {@link delay} and give an
251 > * array event object of all events that fired.
252 > *
253 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
254 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
255 > * returned event causes this utility to leak a listener on the original event.
256 > *
257 > * @param event The original event to debounce.
258 > * @param merge A function that reduces all events into a single event.
259 > * @param delay The number of milliseconds to debounce.
260 > * @param leading Whether to fire a leading event without debouncing.
261 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
262 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
263 > * listener gets disposed before the debounced event fires.
264 > * @param leakWarningThreshold See {@link EmitterOptions.leakWarningThreshold}.
265 > * @param disposable A disposable store to register the debounce emitter to.
266 > */
267 > export function debounce<T>(event: Event<T>, merge: (last: T | undefined, event: T) => T, delay?: number | typeof MicrotaskDelay, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<T>;
268 > export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay?: number | typeof MicrotaskDelay, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O>;
269 > export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay: number | typeof MicrotaskDelay = 100, leading = false, flushOnListenerRemove = false, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O> {
270 let subscription: IDisposable;
271 let output: O | undefined = undefined;
330 return emitter.event;
331 }
332 > event.ts
333 > /**
334 > * Debounces an event, firing after some delay (default=0) with an array of all event original objects.
335 > *
336 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
337 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
338 > * returned event causes this utility to leak a listener on the original event.
339 > *
340 > * @param event The event source for the new event.
341 > * @param delay The number of milliseconds to debounce.
342 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
343 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
344 > * listener gets disposed before the debounced event fires.
345 > * @param disposable A disposable store to add the new EventEmitter to.
346 > */
347 > export function accumulate<T>(event: Event<T>, delay: number | typeof MicrotaskDelay = 0, flushOnListenerRemove?: boolean, disposable?: DisposableStore): Event<T[]> {
348 return Event.debounce<T, T[]>(event, (last, e) => {
349 if (!last) {
354 }, delay, undefined, flushOnListenerRemove ?? true, undefined, disposable);
355 }
356 > event.ts
357 > /**
358 > * Throttles an event, ensuring the event is fired at most once during the specified delay period.
359 > * Unlike debounce, throttle will fire immediately on the leading edge and/or after the delay on the trailing edge.
360 > *
361 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
362 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
363 > * returned event causes this utility to leak a listener on the original event.
364 > *
365 > * @param event The event source for the new event.
366 > * @param merge An accumulator function that merges events if multiple occur during the throttle period.
367 > * @param delay The number of milliseconds to throttle.
368 > * @param leading Whether to fire on the leading edge (immediately on first event).
369 > * @param trailing Whether to fire on the trailing edge (after delay with the last value).
370 > * @param leakWarningThreshold See {@link EmitterOptions.leakWarningThreshold}.
371 > * @param disposable A disposable store to register the throttle emitter to.
372 > */
373 > export function throttle<T>(event: Event<T>, merge: (last: T | undefined, event: T) => T, delay?: number | typeof MicrotaskDelay, leading?: boolean, trailing?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<T>;
374 > export function throttle<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay?: number | typeof MicrotaskDelay, leading?: boolean, trailing?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O>;
375 > export function throttle<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay: number | typeof MicrotaskDelay = 100, leading = true, trailing = true, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O> {
376 let subscription: IDisposable;
377 let output: O | undefined = undefined;
437 return emitter.event;
438 }
439 > event.ts
440 > /**
441 > * Filters an event such that some condition is _not_ met more than once in a row, effectively ensuring duplicate
442 > * event objects from different sources do not fire the same event object.
443 > *
444 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
445 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
446 > * returned event causes this utility to leak a listener on the original event.
447 > *
448 > * @param event The event source for the new event.
449 > * @param equals The equality condition.
450 > * @param disposable A disposable store to add the new EventEmitter to.
451 > *
452 > * @example
453 > * ```
454 > * // Fire only one time when a single window is opened or focused
455 > * Event.latch(Event.any(onDidOpenWindow, onDidFocusWindow))
456 > * ```
457 > */
458 > export function latch<T>(event: Event<T>, equals: (a: T, b: T) => boolean = (a, b) => a === b, disposable?: DisposableStore): Event<T> {
459 let firstCall = true;
460 let cache: T;
467 }, disposable);
468 }
469 > event.ts
470 > /**
471 > * Splits an event whose parameter is a union type into 2 separate events for each type in the union.
472 > *
473 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
474 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
475 > * returned event causes this utility to leak a listener on the original event.
476 > *
477 > * @example
478 > * ```
479 > * const event = new EventEmitter<number | undefined>().event;
480 > * const [numberEvent, undefinedEvent] = Event.split(event, isUndefined);
481 > * ```
482 > *
483 > * @param event The event source for the new event.
484 > * @param isT A function that determines what event is of the first type.
485 > * @param disposable A disposable store to add the new EventEmitter to.
486 > */
487 > export function split<T, U>(event: Event<T | U>, isT: (e: T | U) => e is T, disposable?: DisposableStore): [Event<T>, Event<U>] {
488 return [
489 Event.filter(event, isT, disposable),
491 ];
492 }
493 > event.ts
494 > /**
495 > * Buffers an event until it has a listener attached.
496 > *
497 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
498 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
499 > * returned event causes this utility to leak a listener on the original event.
500 > *
501 > * @param event The event source for the new event.
502 > * @param debugName A name for this buffer, used in leak detection warnings.
503 > * @param flushAfterTimeout Determines whether to flush the buffer after a timeout immediately or after a
504 > * `setTimeout` when the first event listener is added.
505 > * @param _buffer Internal: A source event array used for tests.
506 > *
507 > * @example
508 > * ```
509 > * // Start accumulating events, when the first listener is attached, flush
510 > * // the event after a timeout such that multiple listeners attached before
511 > * // the timeout would receive the event
512 > * this.onInstallExtension = Event.buffer(service.onInstallExtension, 'onInstallExtension', true);
513 > * ```
514 > */
515 > export function buffer<T>(event: Event<T>, debugName: string, flushAfterTimeout = false, _buffer: T[] = [], disposable?: DisposableStore): Event<T> {
516 let buffer: T[] | null = _buffer.slice();
517
600 return emitter.event;
601 }
602 > /** event.ts
603 > * Wraps the event in an {@link IChainableEvent}, allowing a more functional programming style.
604 > *
605 > * @example
606 > * ```
607 > * // Normal
608 > * const onEnterPressNormal = Event.filter(
609 > * Event.map(onKeyPress.event, e => new StandardKeyboardEvent(e)),
610 > * e.keyCode === KeyCode.Enter
611 > * ).event;
612 > *
613 > * // Using chain
614 > * const onEnterPressChain = Event.chain(onKeyPress.event, $ => $
615 > * .map(e => new StandardKeyboardEvent(e))
616 > * .filter(e => e.keyCode === KeyCode.Enter)
617 > * );
618 > * ```
619 > */
620 > export function chain<T, R>(event: Event<T>, sythensize: ($: IChainableSythensis<T>) => IChainableSythensis<R>): Event<R> {
621 const fn: Event<R> = (listener, thisArgs, disposables) => {
622 const cs = sythensize(new ChainableSynthesis()) as ChainableSynthesis;
631 return fn;
632 }
633 > event.ts
634 > const HaltChainable = Symbol('HaltChainable');
635 >
636 > class ChainableSynthesis implements IChainableSythensis<any> {
637 private readonly steps: ((input: any) => unknown)[] = [];
638 > event.ts
639 > map<O>(fn: (i: any) => O): this {
640 this.steps.push(fn);
641 return this;
642 }
643 > event.ts
644 > forEach(fn: (i: any) => void): this {
645 this.steps.push(v => {
646 fn(v);
649 return this;
650 }
651 > event.ts
652 > filter(fn: (e: any) => boolean): this {
653 this.steps.push(v => fn(v) ? v : HaltChainable);
654 return this;
655 }
656 > event.ts
657 > reduce<R>(merge: (last: R | undefined, event: any) => R, initial?: R | undefined): this {
658 let last = initial;
659 this.steps.push(v => {
663 return this;
664 }
665 > event.ts
666 > latch(equals: (a: any, b: any) => boolean = (a, b) => a === b): ChainableSynthesis {
667 let firstCall = true;
668 let cache: any;
676 return this;
677 }
678 > event.ts
679 > public evaluate(value: any) {
680 for (const step of this.steps) {
681 value = step(value);
687 return value;
688 }
689 > } event.ts
690 >
691 > export interface IChainableSythensis<T> {
692 > map<O>(fn: (i: T) => O): IChainableSythensis<O>;
693 > forEach(fn: (i: T) => void): IChainableSythensis<T>;
694 > filter<R extends T>(fn: (e: T) => e is R): IChainableSythensis<R>;
695 > filter(fn: (e: T) => boolean): IChainableSythensis<T>;
696 > reduce<R>(merge: (last: R, event: T) => R, initial: R): IChainableSythensis<R>;
697 > reduce<R>(merge: (last: R | undefined, event: T) => R): IChainableSythensis<R>;
698 > latch(equals?: (a: T, b: T) => boolean): IChainableSythensis<T>;
699 > }
700 >
701 > export interface NodeEventEmitter {
702 > on(event: string | symbol, listener: Function): unknown;
703 > removeListener(event: string | symbol, listener: Function): unknown;
704 > }
705 >
706 > /**
707 > * Creates an {@link Event} from a node event emitter.
708 > */
709 > export function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
710 const fn = (...args: unknown[]) => result.fire(map(...args));
711 const onFirstListenerAdd = () => emitter.on(eventName, fn);
715 return result.event;
716 }
717 > event.ts
718 > export interface DOMEventEmitter {
719 > addEventListener(event: string | symbol, listener: Function): void;
720 > removeEventListener(event: string | symbol, listener: Function): void;
721 > }
722 >
723 > /**
724 > * Creates an {@link Event} from a DOM event emitter.
725 > */
726 > export function fromDOMEventEmitter<T>(emitter: DOMEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
727 const fn = (...args: unknown[]) => result.fire(map(...args));
728 const onFirstListenerAdd = () => emitter.addEventListener(eventName, fn);
732 return result.event;
733 }
734 > event.ts
735 > /**
736 > * Creates a promise out of an event, using the {@link Event.once} helper.
737 > */
738 > export function toPromise<T>(event: Event<T>, disposables?: IDisposable[] | DisposableStore): CancelablePromise<T> {
739 let cancelRef: () => void;
740 let listener: IDisposable;
756 return promise;
757 }
758 > event.ts
759 > /**
760 > * A convenience function for forwarding an event to another emitter which
761 > * improves readability.
762 > *
763 > * This is similar to {@link Relay} but allows instantiating and forwarding
764 > * on a single line and also allows for multiple source events.
765 > * @param from The event to forward.
766 > * @param to The emitter to forward the event to.
767 > * @example
768 > * Event.forward(event, emitter);
769 > * // equivalent to
770 > * event(e => emitter.fire(e));
771 > * // equivalent to
772 > * event(emitter.fire, emitter);
773 > */
774 > export function forward<T>(from: Event<T>, to: Emitter<T>): IDisposable {
775 return from(e => to.fire(e));
776 }
777 > event.ts
778 > /**
779 > * Adds a listener to an event and calls the listener immediately with undefined as the event object.
780 > *
781 > * @example
782 > * ```
783 > * // Initialize the UI and update it when dataChangeEvent fires
784 > * runAndSubscribe(dataChangeEvent, () => this._updateUI());
785 > * ```
786 > */
787 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T) => unknown, initial: T): IDisposable;
788 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => unknown): IDisposable;
789 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => unknown, initial?: T): IDisposable {
790 handler(initial);
791 return event(e => handler(e));
792 }
793 > event.ts
794 > class EmitterObserver<T> implements IObserver {
795 >
796 > readonly emitter: Emitter<T>;
797 >
798 > private _counter = 0;
799 > private _hasChanged = false;
800 >
801 > constructor(readonly _observable: IObservable<T>, store: DisposableStore | undefined) {
802 const options: EmitterOptions = {
803 onWillAddFirstListener: () => {
819 }
820 }
821 > event.ts
822 > beginUpdate<T>(_observable: IObservable<T>): void {
823 // assert(_observable === this.obs);
824 this._counter++;
825 }
826 > event.ts
827 > handlePossibleChange<T>(_observable: IObservable<T>): void {
828 // assert(_observable === this.obs);
829 }
830 > event.ts
831 > handleChange<T, TChange>(_observable: IObservableWithChange<T, TChange>, _change: TChange): void {
832 // assert(_observable === this.obs);
833 this._hasChanged = true;
834 }
835 > event.ts
836 > endUpdate<T>(_observable: IObservable<T>): void {
837 // assert(_observable === this.obs);
838 this._counter--;
845 }
846 }
847 > } event.ts
848 >
849 > /**
850 > * Creates an event emitter that is fired when the observable changes.
851 > * Each listeners subscribes to the emitter.
852 > */
853 > export function fromObservable<T>(obs: IObservable<T>, store?: DisposableStore): Event<T> {
854 const observer = new EmitterObserver(obs, store);
855 return observer.emitter.event;
856 }
857 > event.ts
858 > /**
859 > * Each listener is attached to the observable directly.
860 > */
861 > export function fromObservableLight(observable: IObservable<unknown>): Event<void> {
862 return (listener, thisArgs, disposables) => {
863 let count = 0;
897 };
898 }
899 > } event.ts
900 >
901 > export interface EmitterOptions {
902 > /**
903 > * Optional function that's called *before* the very first listener is added
904 > */
905 > onWillAddFirstListener?: Function;
906 > /**
907 > * Optional function that's called *after* the very first listener is added
908 > */
909 > onDidAddFirstListener?: Function;
910 > /**
911 > * Optional function that's called after a listener is added
912 > */
913 > onDidAddListener?: Function;
914 > /**
915 > * Optional function that's called *after* remove the very last listener
916 > */
917 > onDidRemoveLastListener?: Function;
918 > /**
919 > * Optional function that's called *before* a listener is removed
920 > */
921 > onWillRemoveListener?: Function;
922 > /**
923 > * Optional function that's called when a listener throws an error. Defaults to
924 > * {@link onUnexpectedError}
925 > */
926 > onListenerError?: (e: any) => void;
927 > /**
928 > * Number of listeners that are allowed before assuming a leak. Default to
929 > * a globally configured value
930 > *
931 > * @see setGlobalLeakWarningThreshold
932 > */
933 > leakWarningThreshold?: number;
934 > /**
935 > * Human-readable name for the emitter, included in leak warning error
936 > * messages to help identify which emitter is leaking in telemetry.
937 > */
938 > leakWarningName?: string;
939 > /**
940 > * Pass in a delivery queue, which is useful for ensuring
941 > * in order event delivery across multiple emitters.
942 > */
943 > deliveryQueue?: EventDeliveryQueue;
944 >
945 > /** ONLY enable this during development */
946 > _profName?: string;
947 > }
948 >
949 >
950 > export class EventProfiling {
951 >
952 > static readonly all = new Set<EventProfiling>();
953 >
954 > private static _idPool = 0;
955 >
956 > readonly name: string;
957 > public listenerCount: number = 0;
958 > public invocationCount = 0;
959 > public elapsedOverall = 0;
960 > public durations: number[] = [];
961 >
962 > private _stopWatch?: StopWatch;
963 >
964 > constructor(name: string) {
965 this.name = `${name}_${EventProfiling._idPool++}`;
966 EventProfiling.all.add(this);
967 }
968 > event.ts
969 > start(listenerCount: number): void {
970 this._stopWatch = new StopWatch();
971 this.listenerCount = listenerCount;
972 }
973 > event.ts
974 > stop(): void {
975 if (this._stopWatch) {
976 const elapsed = this._stopWatch.elapsed();
981 }
982 }
983 > } event.ts
984 >
985 > let _globalLeakWarningThreshold = -1;
986 > export function setGlobalLeakWarningThreshold(n: number): IDisposable {
987 const oldValue = _globalLeakWarningThreshold;
988 _globalLeakWarningThreshold = n;
993 };
994 }
995 > event.ts
996 > class LeakageMonitor {
997 >
998 > private static _idPool = 1;
999 >
1000 > private _stacks: Map<string, number> | undefined;
1001 > private _warnCountdown: number = 0;
1002 >
1003 > constructor(
1004 private readonly _errorHandler: (err: Error) => void,
1005 readonly threshold: number,
1006 readonly name: string = (LeakageMonitor._idPool++).toString(16).padStart(3, '0')
1007 ) { }
1008 > event.ts
1009 > dispose(): void {
1010 this._stacks?.clear();
1011 }
1012 > event.ts
1013 > check(stack: Stacktrace, listenerCount: number): undefined | (() => void) {
1014
1015 const threshold = this.threshold;
1046 };
1047 }
1048 > event.ts
1049 > getMostFrequentStack(): [string, number] | undefined {
1050 if (!this._stacks) {
1051 return undefined;
1061 return topStack;
1062 }
1063 > } event.ts
1064 >
1065 > class Stacktrace {
1066 >
1067 > static create() {
1068 > const err = new Error();
1069 > return new Stacktrace(err.stack ?? '');
1070 > }
1071 >
1072 > private constructor(readonly value: string) { }
1073 >
1074 > print() {
1075 console.warn(this.value.split('\n').slice(2).join('\n'));
1076 }
1077 > } event.ts
1078 >
1079 > // error that is logged when going over the configured listener threshold
1080 > export class ListenerLeakError extends Error {
1081 > readonly kind: string;
1082 > readonly listenerCount: number;
1083 > /**
1084 > * The detailed message including listener count and most frequent stack.
1085 > * Available locally for debugging but intentionally not used as the error
1086 > * `message`. When `emitterName` is provided, errors group by emitter name
1087 > * and kind in telemetry; otherwise they group by kind alone.
1088 > */
1089 > readonly details: string;
1090 > constructor(kind: 'dominated' | 'popular', details: string, stack: string, listenerCount: number, emitterName?: string) {
1091 super(emitterName
1092 ? `[${emitterName}] potential listener LEAK detected, ${kind}`
1098 this.stack = stack;
1099 }
1100 > event.ts
1101 > static is(err: unknown): err is ListenerLeakError {
1102 return err instanceof ListenerLeakError
1103 || (err instanceof Error && typeof (err as Error & { kind: unknown; listenerCount: unknown }).kind === 'string' && typeof (err as Error & { kind: unknown; listenerCount: unknown }).listenerCount === 'number');
1104 }
1105 > } event.ts
1106 >
1107 > // SEVERE error that is logged when having gone way over the configured listener
1108 > // threshold so that the emitter refuses to accept more listeners
1109 > export class ListenerRefusalError extends ListenerLeakError {
1110 > constructor(kind: 'dominated' | 'popular', details: string, stack: string, listenerCount: number, emitterName?: string) {
1111 super(kind, details, stack, listenerCount, emitterName);
1112 this.name = 'ListenerRefusalError';
1113 }
1114 > } event.ts
1115 >
1116 > let id = 0;
1117 > class UniqueContainer<T> {
1118 > stack?: Stacktrace;
1119 > public id = id++;
1120 > constructor(public readonly value: T) { }
1121 > }
1122 > const compactionThreshold = 2;
1123 >
1124 > type ListenerContainer<T> = UniqueContainer<(data: T) => void>;
1125 > type ListenerOrListeners<T> = (ListenerContainer<T> | undefined)[] | ListenerContainer<T>;
1126 >
1127 > const forEachListener = <T>(listeners: ListenerOrListeners<T>, fn: (c: ListenerContainer<T>) => void) => {
1128 if (listeners instanceof UniqueContainer) {
1129 fn(listeners);
1137 }
1138 };
1139 > event.ts
1140 > /**
1141 > * The Emitter can be used to expose an Event to the public
1142 > * to fire it from the insides.
1143 > * Sample:
1144 > class Document {
1145 >
1146 > private readonly _onDidChange = new Emitter<(value:string)=>any>();
1147 >
1148 > public onDidChange = this._onDidChange.event;
1149 >
1150 > // getter-style
1151 > // get onDidChange(): Event<(value:string)=>any> {
1152 > // return this._onDidChange.event;
1153 > // }
1154 >
1155 > private _doIt() {
1156 > //...
1157 > this._onDidChange.fire(value);
1158 > }
1159 > }
1160 > */
1161 > export class Emitter<T> {
1162 >
1163 > private readonly _options?: EmitterOptions;
1164 > private readonly _leakageMon?: LeakageMonitor;
1165 > private readonly _perfMon?: EventProfiling;
1166 > private _disposed?: true;
1167 > private _event?: Event<T>;
1168 >
1169 > /**
1170 > * A listener, or list of listeners. A single listener is the most common
1171 > * for event emitters (#185789), so we optimize that special case to avoid
1172 > * wrapping it in an array (just like Node.js itself.)
1173 > *
1174 > * A list of listeners never 'downgrades' back to a plain function if
1175 > * listeners are removed, for two reasons:
1176 > *
1177 > * 1. That's complicated (especially with the deliveryQueue)
1178 > * 2. A listener with >1 listener is likely to have >1 listener again at
1179 > * some point, and swapping between arrays and functions may[citation needed]
1180 > * introduce unnecessary work and garbage.
1181 > *
1182 > * The array listeners can be 'sparse', to avoid reallocating the array
1183 > * whenever any listener is added or removed. If more than `1 / compactionThreshold`
1184 > * of the array is empty, only then is it resized.
1185 > */
1186 > protected _listeners?: ListenerOrListeners<T>;
1187 >
1188 > /**
1189 > * Always to be defined if _listeners is an array. It's no longer a true
1190 > * queue, but holds the dispatching 'state'. If `fire()` is called on an
1191 > * emitter, any work left in the _deliveryQueue is finished first.
1192 > */
1193 > private _deliveryQueue?: EventDeliveryQueuePrivate;
1194 > protected _size = 0;
1195 >
1196 > constructor(options?: EmitterOptions) {
1197 > this._options = options; event.ts
1198 > this._leakageMon = (_globalLeakWarningThreshold > 0 || this._options?.leakWarningThreshold)
1199 ? new LeakageMonitor(options?.onListenerError ?? onUnexpectedError, this._options?.leakWarningThreshold ?? _globalLeakWarningThreshold, this._options?.leakWarningName) :
1200 > undefined; event.ts
1201 > this._perfMon = this._options?._profName ? new EventProfiling(this._options._profName) : undefined;
1202 > this._deliveryQueue = this._options?.deliveryQueue as EventDeliveryQueuePrivate | undefined;
1203 > }
1204 > event.ts
1205 > dispose() {
1206 > if (!this._disposed) { event.ts
1207 > this._disposed = true;
1208 >
1209 > // It is bad to have listeners at the time of disposing an emitter, it is worst to have listeners keep the emitter
1210 > // alive via the reference that's embedded in their disposables. Therefore we loop over all remaining listeners and
1211 > // unset their subscriptions/disposables. Looping and blaming remaining listeners is done on next tick because the
1212 > // the following programming pattern is very popular:
1213 > //
1214 > // const someModel = this._disposables.add(new ModelObject()); // (1) create and register model
1215 > // this._disposables.add(someModel.onDidChange(() => { ... }); // (2) subscribe and register model-event listener
1216 > // ...later...
1217 > // this._disposables.dispose(); disposes (1) then (2): don't warn after (1) but after the "overall dispose" is done
1218 >
1219 > if (this._deliveryQueue?.current === this) {
1220 this._deliveryQueue.reset();
1221 }
1222 > if (this._listeners) { event.ts
1223 if (_enableDisposeWithListenerWarning) {
1224 const listeners = this._listeners;
1231 this._size = 0;
1232 }
1233 > this._options?.onDidRemoveLastListener?.(); event.ts
1234 > this._leakageMon?.dispose();
1235 > }
1236 > }
1237 > event.ts
1238 > /**
1239 > * For the public to allow to subscribe
1240 > * to events from this Emitter
1241 > */
1242 > get event(): Event<T> {
1243 > this._event ??= (callback: (e: T) => unknown, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => { event.ts
1244 > if (this._leakageMon && this._size > this._leakageMon.threshold ** 2) { event.ts
1245 const message = `[${this._leakageMon.name}] REFUSES to accept new listeners because it exceeded its threshold by far (${this._size} vs ${this._leakageMon.threshold})`;
1246 console.warn(message);
1254 return Disposable.None;
1255 }
1256 > event.ts
1257 > if (this._disposed) {
1258 // todo: should we warn if a listener is added to a disposed emitter? This happens often
1259 return Disposable.None;
1260 }
1261 > event.ts
1262 > if (thisArgs) {
1263 callback = callback.bind(thisArgs);
1264 }
1265 > event.ts
1266 > const contained = new UniqueContainer(callback);
1267 >
1268 > let removeMonitor: Function | undefined;
1269 > let stack: Stacktrace | undefined;
1270 > if (this._leakageMon && this._size >= Math.ceil(this._leakageMon.threshold * 0.2)) {
1271 // check and record this emitter for potential leakage
1272 contained.stack = Stacktrace.create();
1273 removeMonitor = this._leakageMon.check(contained.stack, this._size + 1);
1274 }
1275 > event.ts
1276 > if (_enableDisposeWithListenerWarning) {
1277 contained.stack = stack ?? Stacktrace.create();
1278 }
1279 > event.ts
1280 > if (!this._listeners) {
1281 > this._options?.onWillAddFirstListener?.(this);
1282 > this._listeners = contained;
1283 > this._options?.onDidAddFirstListener?.(this);
1284 > } else if (this._listeners instanceof UniqueContainer) {
1285 this._deliveryQueue ??= new EventDeliveryQueuePrivate();
1286 this._listeners = [this._listeners, contained];
1288 this._listeners.push(contained);
1289 }
1290 > this._options?.onDidAddListener?.(this); event.ts
1291 >
1292 > this._size++;
1293 >
1294 >
1295 > const result = toDisposable(() => {
1296 > removeMonitor?.(); event.ts
1297 > this._removeListener(contained);
1298 > }); event.ts
1299 > addToDisposables(result, disposables);
1300 >
1301 > return result;
1302 > };
1303 > event.ts
1304 > return this._event;
1305 > }
1306 > event.ts
1307 > private _removeListener(listener: ListenerContainer<T>) {
1308 > this._options?.onWillRemoveListener?.(this); event.ts
1309 >
1310 > if (!this._listeners) {
1311 return; // expected if a listener gets disposed
1312 }
1313 > event.ts
1314 > if (this._size === 1) {
1315 > this._listeners = undefined; event.ts
1316 > this._options?.onDidRemoveLastListener?.(this);
1317 > this._size = 0;
1318 > return;
1319 > }
1320
1321 // size > 1 which requires that listeners be a list:
1348 listeners.length = n;
1349 }
1350 > } event.ts
1351 > event.ts
1352 > private _deliver(listener: undefined | UniqueContainer<(value: T) => void>, value: T) {
1353 > if (!listener) { event.ts
1354 return;
1355 }
1356 > event.ts
1357 > const errorHandler = this._options?.onListenerError || onUnexpectedError;
1358 > if (!errorHandler) {
1359 listener.value(value);
1360 return;
1361 }
1362 > event.ts
1363 > try {
1364 > listener.value(value);
1365 > } catch (e) {
1366 errorHandler(e);
1367 }
1368 > } event.ts
1369 > event.ts
1370 > /** Delivers items in the queue. Assumes the queue is ready to go. */
1371 > private _deliverQueue(dq: EventDeliveryQueuePrivate) {
1372 const listeners = dq.current!._listeners! as (ListenerContainer<T> | undefined)[];
1373 while (dq.i < dq.end) {
1377 dq.reset();
1378 }
1379 > event.ts
1380 > /**
1381 > * To be kept private to fire an event to
1382 > * subscribers
1383 > */
1384 > fire(event: T): void {
1385 > if (this._deliveryQueue?.current) { event.ts
1386 this._deliverQueue(this._deliveryQueue);
1387 this._perfMon?.stop(); // last fire() will have starting perfmon, stop it before starting the next dispatch
1388 }
1389 > event.ts
1390 > this._perfMon?.start(this._size);
1391 >
1392 > if (!this._listeners) {
1393 > // no-op event.ts
1394 > } else if (this._listeners instanceof UniqueContainer) { event.ts
1395 > this._deliver(this._listeners, event); event.ts
1396 > } else { event.ts
1397 const dq = this._deliveryQueue!;
1398 dq.enqueue(this, event, this._listeners.length);
1399 this._deliverQueue(dq);
1400 }
1401 > event.ts
1402 > this._perfMon?.stop();
1403 > }
1404 > event.ts
1405 > hasListeners(): boolean {
1406 return this._size > 0;
1407 }
1408 > } event.ts
1409 >
1410 > export interface EventDeliveryQueue {
1411 > _isEventDeliveryQueue: true;
1412 > }
1413 >
1414 > export const createEventDeliveryQueue = (): EventDeliveryQueue => new EventDeliveryQueuePrivate();
1415 >
1416 class EventDeliveryQueuePrivate implements EventDeliveryQueue {
1417 declare _isEventDeliveryQueue: true;
1426 */
1427 public end = 0;
1428 > event.ts
1429 > /**
1430 > * Emitter currently being dispatched on. Emitter._listeners is always an array.
1431 > */
1432 > public current?: Emitter<any>;
1433 > /**
1434 > * Currently emitting value. Defined whenever `current` is.
1435 > */
1436 > public value?: unknown;
1437 >
1438 > public enqueue<T>(emitter: Emitter<T>, value: T, end: number) {
1439 this.i = 0;
1440 this.end = end;
1442 this.value = value;
1443 }
1444 > event.ts
1445 > public reset() {
1446 this.i = this.end; // force any current emission loop to stop, mainly for during dispose
1447 this.current = undefined;
1448 this.value = undefined;
1449 }
1450 > } event.ts
1451 >
1452 > export interface IWaitUntil {
1453 > token: CancellationToken;
1454 > waitUntil(thenable: Promise<unknown>): void;
1455 > }
1456 >
1457 > export type IWaitUntilData<T> = Omit<Omit<T, 'waitUntil'>, 'token'>;
1458 >
1459 > export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
1460 >
1461 > private _asyncDeliveryQueue?: LinkedList<[(ev: T) => void, IWaitUntilData<T>]>;
1462 >
1463 > async fireAsync(data: IWaitUntilData<T>, token: CancellationToken, promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>): Promise<void> {
1464 if (!this._listeners) {
1465 return;
1512 }
1513 }
1514 > } event.ts
1515 >
1516 >
1517 > export class PauseableEmitter<T> extends Emitter<T> {
1518 >
1519 > private _isPaused = 0;
1520 > protected _eventQueue = new LinkedList<T>();
1521 > private _mergeFn?: (input: T[]) => T;
1522 >
1523 > public get isPaused(): boolean {
1524 > return this._isPaused !== 0;
1525 > }
1526 >
1527 > constructor(options?: EmitterOptions & { merge?: (input: T[]) => T }) {
1528 super(options);
1529 this._mergeFn = options?.merge;
1530 }
1531 > event.ts
1532 > pause(): void {
1533 this._isPaused++;
1534 }
1535 > event.ts
1536 > resume(): void {
1537 if (this._isPaused !== 0 && --this._isPaused === 0) {
1538 if (this._mergeFn) {
1554 }
1555 }
1556 > event.ts
1557 > override fire(event: T): void {
1558 if (this._size) {
1559 if (this._isPaused !== 0) {
1564 }
1565 }
1566 > } event.ts
1567 >
1568 > export class DebounceEmitter<T> extends PauseableEmitter<T> {
1569 >
1570 > private readonly _delay: number;
1571 > private _handle: Timeout | undefined;
1572 >
1573 > constructor(options: EmitterOptions & { merge: (input: T[]) => T; delay?: number }) {
1574 super(options);
1575 this._delay = options.delay ?? 100;
1576 }
1577 > event.ts
1578 > override fire(event: T): void {
1579 if (!this._handle) {
1580 this.pause();
1586 super.fire(event);
1587 }
1588 > } event.ts
1589 >
1590 > /**
1591 > * An emitter which queue all events and then process them at the
1592 > * end of the event loop.
1593 > */
1594 > export class MicrotaskEmitter<T> extends Emitter<T> {
1595 > private _queuedEvents: T[] = [];
1596 > private _mergeFn?: (input: T[]) => T;
1597 >
1598 > constructor(options?: EmitterOptions & { merge?: (input: T[]) => T }) {
1599 super(options);
1600 this._mergeFn = options?.merge;
1601 }
1602 > override fire(event: T): void { event.ts
1603
1604 if (!this.hasListeners()) {
1618 }
1619 }
1620 > } event.ts
1621 >
1622 > /**
1623 > * An event emitter that multiplexes many events into a single event.
1624 > *
1625 > * @example Listen to the `onData` event of all `Thing`s, dynamically adding and removing `Thing`s
1626 > * to the multiplexer as needed.
1627 > *
1628 > * ```typescript
1629 > * const anythingDataMultiplexer = new EventMultiplexer<{ data: string }>();
1630 > *
1631 > * const thingListeners = DisposableMap<Thing, IDisposable>();
1632 > *
1633 > * thingService.onDidAddThing(thing => {
1634 > * thingListeners.set(thing, anythingDataMultiplexer.add(thing.onData);
1635 > * });
1636 > * thingService.onDidRemoveThing(thing => {
1637 > * thingListeners.deleteAndDispose(thing);
1638 > * });
1639 > *
1640 > * anythingDataMultiplexer.event(e => {
1641 > * console.log('Something fired data ' + e.data)
1642 > * });
1643 > * ```
1644 > */
1645 > export class EventMultiplexer<T> implements IDisposable {
1646 >
1647 > private readonly emitter: Emitter<T>;
1648 > private hasListeners = false;
1649 > private events: { event: Event<T>; listener: IDisposable | null }[] = [];
1650 >
1651 > constructor() {
1652 > this.emitter = new Emitter<T>({ event.ts
1653 > onWillAddFirstListener: () => this.onFirstListenerAdd(),
1654 > onDidRemoveLastListener: () => this.onLastListenerRemove()
1655 > });
1656 > }
1657 > event.ts
1658 > get event(): Event<T> {
1659 > return this.emitter.event; event.ts
1660 > }
1661 > event.ts
1662 > add(event: Event<T>): IDisposable {
1663 > const e = { event: event, listener: null }; event.ts
1664 > this.events.push(e);
1665 >
1666 > if (this.hasListeners) {
1667 > this.hook(e); event.ts
1668 > }
1669 > event.ts
1670 > const dispose = () => {
1671 > if (this.hasListeners) {
1672 this.unhook(e);
1673 }
1674 > event.ts
1675 > const idx = this.events.indexOf(e);
1676 > this.events.splice(idx, 1);
1677 > };
1678 >
1679 > return toDisposable(createSingleCallFunction(dispose));
1680 > }
1681 > event.ts
1682 > private onFirstListenerAdd(): void {
1683 > this.hasListeners = true; event.ts
1684 > this.events.forEach(e => this.hook(e));
1685 > }
1686 > event.ts
1687 > private onLastListenerRemove(): void {
1688 > this.hasListeners = false; event.ts
1689 > this.events.forEach(e => this.unhook(e));
1690 > }
1691 > event.ts
1692 > private hook(e: { event: Event<T>; listener: IDisposable | null }): void {
1693 > e.listener = e.event(r => this.emitter.fire(r)); event.ts
1694 > }
1695 > event.ts
1696 > private unhook(e: { event: Event<T>; listener: IDisposable | null }): void {
1697 > e.listener?.dispose(); event.ts
1698 > e.listener = null;
1699 > }
1700 > event.ts
1701 > dispose(): void {
1702 this.emitter.dispose();
1703
1707 this.events = [];
1708 }
1709 > } event.ts
1710 >
1711 > export interface IDynamicListEventMultiplexer<TEventType> extends IDisposable {
1712 > readonly event: Event<TEventType>;
1713 > }
1714 > export class DynamicListEventMultiplexer<TItem, TEventType> implements IDynamicListEventMultiplexer<TEventType> {
1715 > private readonly _store = new DisposableStore();
1716 >
1717 > readonly event: Event<TEventType>;
1718 >
1719 > constructor(
1720 items: TItem[],
1721 onAddItem: Event<TItem>,
1747 this.event = multiplexer.event;
1748 }
1749 > event.ts
1750 > dispose() {
1751 this._store.dispose();
1752 }
1753 > } event.ts
1754 >
1755 > /**
1756 > * The EventBufferer is useful in situations in which you want
1757 > * to delay firing your events during some code.
1758 > * You can wrap that code and be sure that the event will not
1759 > * be fired during that wrap.
1760 > *
1761 > * ```
1762 > * const emitter: Emitter;
1763 > * const delayer = new EventDelayer();
1764 > * const delayedEvent = delayer.wrapEvent(emitter.event);
1765 > *
1766 > * delayedEvent(console.log);
1767 > *
1768 > * delayer.bufferEvents(() => {
1769 > * emitter.fire(); // event will not be fired yet
1770 > * });
1771 > *
1772 > * // event will only be fired at this point
1773 > * ```
1774 > */
1775 > export class EventBufferer {
1776
1777 private data: { buffers: Function[] }[] = [];
1778 > event.ts
1779 > wrapEvent<T>(event: Event<T>): Event<T>;
1780 > wrapEvent<T>(event: Event<T>, reduce: (last: T | undefined, event: T) => T): Event<T>;
1781 > wrapEvent<T, O>(event: Event<T>, reduce: (last: O | undefined, event: T) => O, initial: O): Event<O>;
1782 > wrapEvent<T, O>(event: Event<T>, reduce?: (last: T | O | undefined, event: T) => T | O, initial?: O): Event<O | T> {
1783 return (listener, thisArgs?, disposables?) => {
1784 return event(i => {
1832 };
1833 }
1834 > event.ts
1835 > bufferEvents<R = void>(fn: () => R): R {
1836 const data = { buffers: new Array<Function>() };
1837 this.data.push(data);
1841 return r;
1842 }
1843 > } event.ts
1844 >
1845 > /**
1846 > * A Relay is an event forwarder which functions as a replugabble event pipe.
1847 > * Once created, you can connect an input event to it and it will simply forward
1848 > * events from that input event through its own `event` property. The `input`
1849 > * can be changed at any point in time.
1850 > */
1851 > export class Relay<T> implements IDisposable {
1852
1853 private listening = false;
1867
1868 readonly event: Event<T> = this.emitter.event;
1869 > event.ts
1870 > set input(event: Event<T>) {
1871 this.inputEvent = event;
1872
1876 }
1877 }
1878 > event.ts
1879 > dispose() {
1880 this.inputEventListener.dispose();
1881 this.emitter.dispose();
1882 }
1883 > } event.ts
1884 >
1885 > export interface IValueWithChangeEvent<T> {
1886 > readonly onDidChange: Event<void>;
1887 > get value(): T;
1888 > }
1889 >
1890 > export class ValueWithChangeEvent<T> implements IValueWithChangeEvent<T> {
1891 > public static const<T>(value: T): IValueWithChangeEvent<T> {
1892 > return new ConstValueWithChangeEvent(value);
1893 > }
1894 >
1895 > private readonly _onDidChange = new Emitter<void>();
1896 > readonly onDidChange: Event<void> = this._onDidChange.event;
1897 >
1898 > constructor(private _value: T) { }
1899 >
1900 > get value(): T {
1901 return this._value;
1902 }
1903 > event.ts
1904 > set value(value: T) {
1905 if (value !== this._value) {
1906 this._value = value;
1908 }
1909 }
1910 > } event.ts
1911 >
1912 > class ConstValueWithChangeEvent<T> implements IValueWithChangeEvent<T> {
1913 > public readonly onDidChange: Event<void> = Event.None;
1914 >
1915 > constructor(readonly value: T) { }
1916 > }
1917 >
1918 > /**
1919 > * @param handleItem Is called for each item in the set (but only the first time the item is seen in the set).
1920 > * The returned disposable is disposed if the item is no longer in the set.
1921 > */
1922 > export function trackSetChanges<T>(getData: () => ReadonlySet<T>, onDidChangeData: Event<unknown>, handleItem: (d: T) => IDisposable): IDisposable {
1923 const map = new DisposableMap<T, IDisposable>();
1924 let oldData = new Set(getData());
1942 return store;
1943 }
1944 > event.ts
1945 >
1946 > function addToDisposables(result: IDisposable, disposables: DisposableStore | IDisposable[] | undefined) { event.ts
1947 > if (disposables instanceof DisposableStore) {
1948 disposables.add(result);
1949 > } else if (Array.isArray(disposables)) { event.ts
1950 disposables.push(result);
1951 }
1952 > } event.ts
1953 > event.ts
1954 function disposeAndRemove(result: IDisposable, disposables: DisposableStore | IDisposable[] | undefined) {
1955 if (disposables instanceof DisposableStore) {
src/vs/base/common/lifecycle.ts 542 covered LOC · 122 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lifecycle.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { compareBy, numberComparator } from './arrays.js';
7 > import { groupBy } from './collections.js';
8 > import { SetMap, ResourceMap } from './map.js';
9 > import { URI } from './uri.js';
10 > import { createSingleCallFunction } from './functional.js';
11 > import { Iterable } from './iterator.js';
12 > import { BugIndicatingError, onUnexpectedError } from './errors.js';
13 >
14 > // #region Disposable Tracking
15 >
16 > /**
17 > * Enables logging of potentially leaked disposables.
18 > *
19 > * A disposable is considered leaked if it is not disposed or not registered as the child of
20 > * another disposable. This tracking is very simple an only works for classes that either
21 > * extend Disposable or use a DisposableStore. This means there are a lot of false positives.
22 > */
23 > const TRACK_DISPOSABLES = false;
24 > let disposableTracker: IDisposableTracker | null = null;
25 >
26 > export interface IDisposableTracker {
27 > /**
28 > * Is called on construction of a disposable.
29 > */
30 > trackDisposable(disposable: IDisposable): void;
31 >
32 > /**
33 > * Is called when a disposable is registered as child of another disposable (e.g. {@link DisposableStore}).
34 > * If parent is `null`, the disposable is removed from its former parent.
35 > */
36 > setParent(child: IDisposable, parent: IDisposable | null): void;
37 >
38 > /**
39 > * Is called after a disposable is disposed.
40 > */
41 > markAsDisposed(disposable: IDisposable): void;
42 >
43 > /**
44 > * Indicates that the given object is a singleton which does not need to be disposed.
45 > */
46 > markAsSingleton(disposable: IDisposable): void;
47 > }
48 >
49 > export class GCBasedDisposableTracker implements IDisposableTracker {
50
51 private readonly _registry = new FinalizationRegistry<string>(heldValue => {
52 console.warn(`[LEAKED DISPOSABLE] ${heldValue}`);
53 });
55 > trackDisposable(disposable: IDisposable): void {
56 const stack = new Error('CREATED via:').stack!;
57 this._registry.register(disposable, stack, disposable);
58 }
60 > setParent(child: IDisposable, parent: IDisposable | null): void {
61 if (parent) {
62 this._registry.unregister(child);
65 }
66 }
68 > markAsDisposed(disposable: IDisposable): void {
69 this._registry.unregister(disposable);
70 }
72 > markAsSingleton(disposable: IDisposable): void {
73 this._registry.unregister(disposable);
74 }
75 > } lifecycle.ts
76 >
77 > export interface DisposableInfo {
78 > value: IDisposable;
79 > source: string | null;
80 > parent: IDisposable | null;
81 > isSingleton: boolean;
82 > idx: number;
83 > }
84 >
85 > export class DisposableTracker implements IDisposableTracker {
86 > private static idx = 0; lifecycle.ts
87 >
88 > private readonly livingDisposables = new Map<IDisposable, DisposableInfo>();
90 > private getDisposableData(d: IDisposable): DisposableInfo {
91 > let val = this.livingDisposables.get(d); lifecycle.ts
92 > if (!val) {
93 > val = { parent: null, source: null, isSingleton: false, value: d, idx: DisposableTracker.idx++ };
94 > this.livingDisposables.set(d, val);
95 > }
96 > return val;
97 > }
99 > trackDisposable(d: IDisposable): void {
100 > const data = this.getDisposableData(d); lifecycle.ts
101 > if (!data.source) {
102 > data.source =
103 > new Error().stack!;
104 > }
105 > }
106 > lifecycle.ts
107 > setParent(child: IDisposable, parent: IDisposable | null): void {
108 > const data = this.getDisposableData(child); lifecycle.ts
109 > data.parent = parent;
110 > }
111 > lifecycle.ts
112 > markAsDisposed(x: IDisposable): void {
113 > this.livingDisposables.delete(x); lifecycle.ts
114 > }
115 > lifecycle.ts
116 > markAsSingleton(disposable: IDisposable): void {
117 this.getDisposableData(disposable).isSingleton = true;
118 }
119 > lifecycle.ts
120 > private getRootParent(data: DisposableInfo, cache: Map<DisposableInfo, DisposableInfo>): DisposableInfo {
121 const cacheValue = cache.get(data);
122 if (cacheValue) {
128 return result;
129 }
130 > lifecycle.ts
131 > getTrackedDisposables(): IDisposable[] {
132 const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
133
138 return leaking;
139 }
140 > lifecycle.ts
141 > computeLeakingDisposables(maxReported = 10, preComputedLeaks?: DisposableInfo[]): { leaks: DisposableInfo[]; details: string } | undefined {
142 > let uncoveredLeakingObjs: DisposableInfo[] | undefined; lifecycle.ts
143 > if (preComputedLeaks) {
144 uncoveredLeakingObjs = preComputedLeaks;
145 > } else { lifecycle.ts
146 > const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
147 >
148 > const leakingObjects = [...this.livingDisposables.values()]
149 > .filter((info) => info.source !== null && !this.getRootParent(info, rootParentCache).isSingleton);
150 >
151 > if (leakingObjects.length === 0) {
152 > return; lifecycle.ts
153 > }
154 const leakingObjsSet = new Set(leakingObjects.map(o => o.value));
155
162 throw new Error('There are cyclic diposable chains!');
163 }
164 > } lifecycle.ts
165
166 if (!uncoveredLeakingObjs) {
224
225 return { leaks: uncoveredLeakingObjs, details: message };
226 > } lifecycle.ts
227 > } lifecycle.ts
228 >
229 > export function setDisposableTracker(tracker: IDisposableTracker | null): void {
230 > disposableTracker = tracker; lifecycle.ts
231 > }
232 > lifecycle.ts
233 > if (TRACK_DISPOSABLES) {
234 const __is_disposable_tracked__ = '__is_disposable_tracked__';
235 setDisposableTracker(new class implements IDisposableTracker {
268 });
269 }
270 > lifecycle.ts
271 > export function trackDisposable<T extends IDisposable>(x: T): T {
272 > disposableTracker?.trackDisposable(x); lifecycle.ts
273 > return x;
274 > }
275 > lifecycle.ts
276 > export function markAsDisposed(disposable: IDisposable): void {
277 > disposableTracker?.markAsDisposed(disposable); lifecycle.ts
278 > }
279 > lifecycle.ts
280 > function setParentOfDisposable(child: IDisposable, parent: IDisposable | null): void { lifecycle.ts
281 > disposableTracker?.setParent(child, parent);
282 > }
283 > lifecycle.ts
284 function setParentOfDisposables(children: IDisposable[], parent: IDisposable | null): void {
285 if (!disposableTracker) {
290 }
291 }
292 > lifecycle.ts
293 > /**
294 > * Indicates that the given object is a singleton which does not need to be disposed.
295 > */
296 > export function markAsSingleton<T extends IDisposable>(singleton: T): T {
297 disposableTracker?.markAsSingleton(singleton);
298 return singleton;
299 }
300 > lifecycle.ts
301 > // #endregion
302 >
303 > /**
304 > * An object that performs a cleanup operation when `.dispose()` is called.
305 > *
306 > * Some examples of how disposables are used:
307 > *
308 > * - An event listener that removes itself when `.dispose()` is called.
309 > * - A resource such as a file system watcher that cleans up the resource when `.dispose()` is called.
310 > * - The return value from registering a provider. When `.dispose()` is called, the provider is unregistered.
311 > */
312 > export interface IDisposable {
313 > dispose(): void;
314 > }
315 >
316 > /**
317 > * Check if `thing` is {@link IDisposable disposable}.
318 > */
319 > export function isDisposable<E>(thing: E): thing is E & IDisposable {
320 // eslint-disable-next-line local/code-no-any-casts
321 return typeof thing === 'object' && thing !== null && typeof (<IDisposable><any>thing).dispose === 'function' && (<IDisposable><any>thing).dispose.length === 0;
322 }
323 > lifecycle.ts
324 > /**
325 > * Disposes of the value(s) passed in.
326 > */
327 > export function dispose<T extends IDisposable>(disposable: T): T;
328 > export function dispose<T extends IDisposable>(disposable: T | undefined): T | undefined;
329 > export function dispose<T extends IDisposable, A extends Iterable<T> = Iterable<T>>(disposables: A): A;
330 > export function dispose<T extends IDisposable>(disposables: Array<T>): Array<T>;
331 > export function dispose<T extends IDisposable>(disposables: ReadonlyArray<T>): ReadonlyArray<T>;
332 > export function dispose<T extends IDisposable>(arg: T | Iterable<T> | undefined): any {
333 > if (Iterable.is(arg)) { lifecycle.ts
334 > const errors: any[] = []; lifecycle.ts
335 >
336 > for (const d of arg) {
337 > if (d) { lifecycle.ts
338 > try {
339 > d.dispose();
340 > } catch (e) {
341 errors.push(e);
342 }
343 > } lifecycle.ts
344 > }
345 > lifecycle.ts
346 > if (errors.length === 1) {
347 throw errors[0];
348 > } else if (errors.length > 1) { lifecycle.ts
349 throw new AggregateError(errors, 'Encountered errors while disposing of store');
350 }
351 > lifecycle.ts
352 > return Array.isArray(arg) ? [] : arg; lifecycle.ts
353 > } else if (arg) { lifecycle.ts
354 arg.dispose();
355 return arg;
356 }
357 > } lifecycle.ts
358 > lifecycle.ts
359 > export function disposeIfDisposable<T extends IDisposable | object>(disposables: Array<T>): Array<T> {
360 for (const d of disposables) {
361 if (isDisposable(d)) {
365 return [];
366 }
367 > lifecycle.ts
368 > /**
369 > * Combine multiple disposable values into a single {@link IDisposable}.
370 > */
371 > export function combinedDisposable(...disposables: IDisposable[]): IDisposable {
372 const parent = toDisposable(() => dispose(disposables));
373 setParentOfDisposables(disposables, parent);
374 return parent;
375 }
376 > lifecycle.ts
377 > class FunctionDisposable implements IDisposable {
378 > private _isDisposed: boolean;
379 > private readonly _fn: () => void;
380 >
381 > constructor(fn: () => void) {
382 > this._isDisposed = false; lifecycle.ts
383 > this._fn = fn;
384 > trackDisposable(this);
385 > }
386 > lifecycle.ts
387 > dispose() {
388 > if (this._isDisposed) { lifecycle.ts
389 return;
390 }
391 > if (!this._fn) { lifecycle.ts
392 throw new Error(`Unbound disposable context: Need to use an arrow function to preserve the value of this`);
393 }
394 > this._isDisposed = true; lifecycle.ts
395 > markAsDisposed(this);
396 > this._fn();
397 > }
398 > } lifecycle.ts
399 >
400 > /**
401 > * Turn a function that implements dispose into an {@link IDisposable}.
402 > *
403 > * @param fn Clean up function, guaranteed to be called only **once**.
404 > */
405 > export function toDisposable(fn: () => void): IDisposable {
406 > return new FunctionDisposable(fn); lifecycle.ts
407 > }
408 > lifecycle.ts
409 > /**
410 > * Manages a collection of disposable values.
411 > *
412 > * This is the preferred way to manage multiple disposables. A `DisposableStore` is safer to work with than an
413 > * `IDisposable[]` as it considers edge cases, such as registering the same value multiple times or adding an item to a
414 > * store that has already been disposed of.
415 > */
416 > export class DisposableStore implements IDisposable {
417 >
418 > static DISABLE_DISPOSED_WARNING = false;
419 >
420 > private readonly _toDispose = new Set<IDisposable>();
421 > private _isDisposed = false;
422 >
423 > constructor() {
424 > trackDisposable(this); lifecycle.ts
425 > }
426 > lifecycle.ts
427 > /**
428 > * Dispose of all registered disposables and mark this object as disposed.
429 > *
430 > * Any future disposables added to this object will be disposed of on `add`.
431 > */
432 > public dispose(): void {
433 > if (this._isDisposed) { lifecycle.ts
434 return;
435 }
436 > lifecycle.ts
437 > markAsDisposed(this);
438 > this._isDisposed = true;
439 > this.clear();
440 > }
441 > lifecycle.ts
442 > /**
443 > * @return `true` if this object has been disposed of.
444 > */
445 > public get isDisposed(): boolean {
446 return this._isDisposed;
447 }
448 > lifecycle.ts
449 > /**
450 > * Dispose of all registered disposables but do not mark this object as disposed.
451 > */
452 > public clear(): void {
453 > if (this._toDispose.size === 0) { lifecycle.ts
454 return;
455 }
456 > lifecycle.ts
457 > try {
458 > dispose(this._toDispose);
459 > } finally {
460 > this._toDispose.clear();
461 > }
462 > } lifecycle.ts
463 > lifecycle.ts
464 > /**
465 > * Add a new {@link IDisposable disposable} to the collection.
466 > */
467 > public add<T extends IDisposable>(o: T): T {
468 > if (!o || o === Disposable.None) { lifecycle.ts
469 return o;
470 }
471 > if ((o as unknown as DisposableStore) === this) { lifecycle.ts
472 throw new Error('Cannot register a disposable on itself!');
473 }
474 > lifecycle.ts
475 > setParentOfDisposable(o, this);
476 > if (this._isDisposed) {
477 if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
478 console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
479 }
480 > } else { lifecycle.ts
481 > this._toDispose.add(o);
482 > }
483 >
484 > return o;
485 > } lifecycle.ts
486 > lifecycle.ts
487 > /**
488 > * Deletes a disposable from store and disposes of it. This will not throw or warn and proceed to dispose the
489 > * disposable even when the disposable is not part in the store.
490 > */
491 > public delete<T extends IDisposable>(o: T): void {
492 if (!o) {
493 return;
499 o.dispose();
500 }
501 > lifecycle.ts
502 > /**
503 > * Deletes the value from the store, but does not dispose it.
504 > */
505 > public deleteAndLeak<T extends IDisposable>(o: T): void {
506 if (!o) {
507 return;
511 }
512 }
513 > lifecycle.ts
514 > public assertNotDisposed(): void {
515 if (this._isDisposed) {
516 onUnexpectedError(new BugIndicatingError('Object disposed'));
517 }
518 }
519 > } lifecycle.ts
520 >
521 > /**
522 > * Abstract base class for a {@link IDisposable disposable} object.
523 > *
524 > * Subclasses can {@linkcode _register} disposables that will be automatically cleaned up when this object is disposed of.
525 > */
526 > export abstract class Disposable implements IDisposable {
527 >
528 > /**
529 > * A disposable that does nothing when it is disposed of.
530 > *
531 > * TODO: This should not be a static property.
532 > */
533 > static readonly None = Object.freeze<IDisposable>({ dispose() { } });
534 >
535 > protected readonly _store = new DisposableStore();
536 >
537 > constructor() {
538 trackDisposable(this);
539 setParentOfDisposable(this._store, this);
540 }
541 > lifecycle.ts
542 > public dispose(): void {
543 markAsDisposed(this);
544
545 this._store.dispose();
546 }
547 > lifecycle.ts
548 > /**
549 > * Adds `o` to the collection of disposables managed by this object.
550 > */
551 > protected _register<T extends IDisposable>(o: T): T {
552 if ((o as unknown as Disposable) === this) {
553 throw new Error('Cannot register a disposable on itself!');
555 return this._store.add(o);
556 }
557 > } lifecycle.ts
558 >
559 > /**
560 > * Manages the lifecycle of a disposable value that may be changed.
561 > *
562 > * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
563 > * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
564 > */
565 > export class MutableDisposable<T extends IDisposable> implements IDisposable {
566 > private _value?: T;
567 > private _isDisposed = false;
568 >
569 > constructor() {
570 trackDisposable(this);
571 }
572 > lifecycle.ts
573 > /**
574 > * Get the currently held disposable value, or `undefined` if this MutableDisposable has been disposed
575 > */
576 > get value(): T | undefined {
577 return this._isDisposed ? undefined : this._value;
578 }
579 > lifecycle.ts
580 > /**
581 > * Set a new disposable value.
582 > *
583 > * Behaviour:
584 > * - If the MutableDisposable has been disposed, the setter is a no-op.
585 > * - If the new value is strictly equal to the current value, the setter is a no-op.
586 > * - Otherwise the previous value (if any) is disposed and the new value is stored.
587 > *
588 > * Related helpers:
589 > * - clear() resets the value to `undefined` (and disposes the previous value).
590 > * - clearAndLeak() returns the old value without disposing it and removes its parent.
591 > */
592 > set value(value: T | undefined) {
593 if (this._isDisposed || value === this._value) {
594 return;
601 this._value = value;
602 }
603 > lifecycle.ts
604 > /**
605 > * Resets the stored value and disposed of the previously stored value.
606 > */
607 > clear(): void {
608 this.value = undefined;
609 }
610 > lifecycle.ts
611 > dispose(): void {
612 this._isDisposed = true;
613 markAsDisposed(this);
615 this._value = undefined;
616 }
617 > lifecycle.ts
618 > /**
619 > * Clears the value, but does not dispose it.
620 > * The old value is returned.
621 > */
622 > clearAndLeak(): T | undefined {
623 const oldValue = this._value;
624 this._value = undefined;
628 return oldValue;
629 }
630 > } lifecycle.ts
631 >
632 > /**
633 > * Manages the lifecycle of a disposable value that may be changed like {@link MutableDisposable}, but the value must
634 > * exist and cannot be undefined.
635 > */
636 > export class MandatoryMutableDisposable<T extends IDisposable> implements IDisposable {
637 > private readonly _disposable = new MutableDisposable<T>();
638 > private _isDisposed = false;
639 >
640 > constructor(initialValue: T) {
641 this._disposable.value = initialValue;
642 }
643 > lifecycle.ts
644 > get value(): T {
645 return this._disposable.value!;
646 }
647 > lifecycle.ts
648 > set value(value: T) {
649 if (this._isDisposed || value === this._disposable.value) {
650 return;
652 this._disposable.value = value;
653 }
654 > lifecycle.ts
655 > dispose() {
656 this._isDisposed = true;
657 this._disposable.dispose();
658 }
659 > } lifecycle.ts
660 >
661 > export class RefCountedDisposable {
662 >
663 > private _counter: number = 1;
664 >
665 > constructor(
666 private readonly _disposable: IDisposable,
667 ) { }
668 > lifecycle.ts
669 > acquire() {
670 this._counter++;
671 return this;
672 }
673 > lifecycle.ts
674 > release() {
675 if (--this._counter === 0) {
676 this._disposable.dispose();
678 return this;
679 }
680 > } lifecycle.ts
681 >
682 > export interface IReference<T> extends IDisposable {
683 > readonly object: T;
684 > }
685 >
686 > export abstract class ReferenceCollection<T> {
687
688 private readonly references: Map<string, { readonly object: T; counter: number }> = new Map();
689 > lifecycle.ts
690 > acquire(key: string, ...args: unknown[]): IReference<T> {
691 let reference = this.references.get(key);
692
708 return { object, dispose };
709 }
710 > lifecycle.ts
711 > protected abstract createReferencedObject(key: string, ...args: unknown[]): T;
712 > protected abstract destroyReferencedObject(key: string, object: T): void;
713 > }
714 >
715 > /**
716 > * Unwraps a reference collection of promised values. Makes sure
717 > * references are disposed whenever promises get rejected.
718 > */
719 > export class AsyncReferenceCollection<T> {
720 >
721 > constructor(private referenceCollection: ReferenceCollection<Promise<T>>) { }
722 >
723 > async acquire(key: string, ...args: unknown[]): Promise<IReference<T>> {
724 const ref = this.referenceCollection.acquire(key, ...args);
725
736 }
737 }
738 > } lifecycle.ts
739 >
740 > export class ImmortalReference<T> implements IReference<T> {
741 > constructor(public object: T) { }
742 > dispose(): void { /* noop */ }
743 > }
744 >
745 > export function disposeOnReturn(fn: (store: DisposableStore) => void): void {
746 const store = new DisposableStore();
747 try {
751 }
752 }
753 > lifecycle.ts
754 > /**
755 > * A map the manages the lifecycle of the values that it stores.
756 > */
757 > export class DisposableMap<K, V extends IDisposable = IDisposable> implements IDisposable {
758 >
759 > private readonly _store: Map<K, V>;
760 > private _isDisposed = false;
761 >
762 > constructor(store: Map<K, V> = new Map<K, V>()) {
763 this._store = store;
764 trackDisposable(this);
765 }
766 > lifecycle.ts
767 > /**
768 > * Disposes of all stored values and mark this object as disposed.
769 > *
770 > * Trying to use this object after it has been disposed of is an error.
771 > */
772 > dispose(): void {
773 markAsDisposed(this);
774 this._isDisposed = true;
775 this.clearAndDisposeAll();
776 }
777 > lifecycle.ts
778 > /**
779 > * Disposes of all stored values and clear the map, but DO NOT mark this object as disposed.
780 > */
781 > clearAndDisposeAll(): void {
782 if (!this._store.size) {
783 return;
790 }
791 }
792 > lifecycle.ts
793 > has(key: K): boolean {
794 return this._store.has(key);
795 }
796 > lifecycle.ts
797 > get size(): number {
798 return this._store.size;
799 }
800 > lifecycle.ts
801 > get(key: K): V | undefined {
802 return this._store.get(key);
803 }
804 > lifecycle.ts
805 > set(key: K, value: V, skipDisposeOnOverwrite = false): void {
806 if (this._isDisposed) {
807 console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack);
815 setParentOfDisposable(value, this);
816 }
817 > lifecycle.ts
818 > /**
819 > * Delete the value stored for `key` from this map and also dispose of it.
820 > */
821 > deleteAndDispose(key: K): void {
822 this._store.get(key)?.dispose();
823 this._store.delete(key);
824 }
825 > lifecycle.ts
826 > /**
827 > * Delete the value stored for `key` from this map but return it. The caller is
828 > * responsible for disposing of the value.
829 > */
830 > deleteAndLeak(key: K): V | undefined {
831 const value = this._store.get(key);
832 if (value) {
836 return value;
837 }
838 > lifecycle.ts
839 > keys(): IterableIterator<K> {
840 return this._store.keys();
841 }
842 > lifecycle.ts
843 > values(): IterableIterator<V> {
844 return this._store.values();
845 }
846 > lifecycle.ts
847 > [Symbol.iterator](): IterableIterator<[K, V]> {
848 return this._store[Symbol.iterator]();
849 }
850 > } lifecycle.ts
851 >
852 > /**
853 > * A set that manages the lifecycle of the values that it stores.
854 > */
855 > export class DisposableSet<V extends IDisposable = IDisposable> implements IDisposable {
856 >
857 > private readonly _store: Set<V>;
858 > private _isDisposed = false;
859 >
860 > constructor(store: Set<V> = new Set<V>()) {
861 this._store = store;
862 trackDisposable(this);
863 }
864 > lifecycle.ts
865 > /**
866 > * Disposes of all stored values and mark this object as disposed.
867 > *
868 > * Trying to use this object after it has been disposed of is an error.
869 > */
870 > dispose(): void {
871 markAsDisposed(this);
872 this._isDisposed = true;
873 this.clearAndDisposeAll();
874 }
875 > lifecycle.ts
876 > /**
877 > * Disposes of all stored values and clear the set, but DO NOT mark this object as disposed.
878 > */
879 > clearAndDisposeAll(): void {
880 if (!this._store.size) {
881 return;
888 }
889 }
890 > lifecycle.ts
891 > has(value: V): boolean {
892 return this._store.has(value);
893 }
894 > lifecycle.ts
895 > get size(): number {
896 return this._store.size;
897 }
898 > lifecycle.ts
899 > add(value: V): void {
900 if (this._isDisposed) {
901 console.warn(new Error('Trying to add a disposable to a DisposableSet that has already been disposed of. The added object will be leaked!').stack);
905 setParentOfDisposable(value, this);
906 }
907 > lifecycle.ts
908 > /**
909 > * Delete the value from this set and also dispose of it.
910 > */
911 > deleteAndDispose(value: V): void {
912 if (this._store.delete(value)) {
913 value.dispose();
914 }
915 }
916 > lifecycle.ts
917 > /**
918 > * Delete the value from this set but return it. The caller is
919 > * responsible for disposing of the value.
920 > */
921 > deleteAndLeak(value: V): V | undefined {
922 if (this._store.delete(value)) {
923 setParentOfDisposable(value, null);
926 return undefined;
927 }
928 > lifecycle.ts
929 > values(): IterableIterator<V> {
930 return this._store.values();
931 }
932 > lifecycle.ts
933 > [Symbol.iterator](): IterableIterator<V> {
934 return this._store[Symbol.iterator]();
935 }
936 > } lifecycle.ts
937 >
938 > /**
939 > * Call `then` on a Promise, unless the returned disposable is disposed.
940 > */
941 > export function thenIfNotDisposed<T>(promise: Promise<T>, then: (result: T) => void): IDisposable {
942 let disposed = false;
943 promise.then(result => {
951 });
952 }
953 > lifecycle.ts
954 > /**
955 > * Call `then` on a promise that resolves to a {@link IDisposable}, then either register the
956 > * disposable or register it to the {@link DisposableStore}, depending on whether the store is
957 > * disposed or not.
958 > */
959 > export function thenRegisterOrDispose<T extends IDisposable>(promise: Promise<T>, store: DisposableStore): Promise<T> {
960 return promise.then(disposable => {
961 if (store.isDisposed) {
967 });
968 }
969 > lifecycle.ts
970 > export class DisposableResourceMap<V extends IDisposable = IDisposable> extends DisposableMap<URI, V> {
971 > constructor() {
972 super(new ResourceMap());
973 }
974 > } lifecycle.ts
src/vs/base/common/strings.ts 454 covered LOC · 101 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- strings.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { LRUCachedFunction } from './cache.js';
7 > import { CharCode } from './charCode.js';
8 > import { Lazy } from './lazy.js';
9 > import { Constants } from './uint.js';
10 >
11 > export function isFalsyOrWhitespace(str: string | undefined): boolean {
12 if (!str || typeof str !== 'string') {
13 return true;
15 return str.trim().length === 0;
16 }
17 > strings.ts
18 > const _formatRegexp = /{(\d+)}/g;
19 >
20 > /**
21 > * Helper to produce a string with a variable number of arguments. Insert variable segments
22 > * into the string using the {n} notation where N is the index of the argument following the string.
23 > * @param value string to which formatting is applied
24 > * @param args replacements for {n}-entries
25 > */
26 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
27 > export function format(value: string, ...args: any[]): string {
28 if (args.length === 0) {
29 return value;
36 });
37 }
38 > strings.ts
39 > const _format2Regexp = /{([^}]+)}/g;
40 >
41 > /**
42 > * Helper to create a string from a template and a string record.
43 > * Similar to `format` but with objects instead of positional arguments.
44 > */
45 > export function format2(template: string, values: Record<string, unknown>): string {
46 if (Object.keys(values).length === 0) {
47 return template;
49 return template.replace(_format2Regexp, (match, group) => (values[group] ?? match) as string);
50 }
51 > strings.ts
52 > /**
53 > * Encodes the given value so that it can be used as literal value in html attributes.
54 > *
55 > * In other words, computes `$val`, such that `attr` in `<div attr="$val" />` has the runtime value `value`.
56 > * This prevents XSS injection.
57 > */
58 > export function htmlAttributeEncodeValue(value: string): string {
59 return value.replace(/[<>"'&]/g, ch => {
60 switch (ch) {
68 });
69 }
70 > strings.ts
71 > /**
72 > * Converts HTML characters inside the string to use entities instead. Makes the string safe from
73 > * being used e.g. in HTMLElement.innerHTML.
74 > */
75 > export function escape(html: string): string {
76 return html.replace(/[<>&]/g, function (match) {
77 switch (match) {
83 });
84 }
85 > strings.ts
86 > /**
87 > * Escapes regular expression characters in a given string
88 > */
89 > export function escapeRegExpCharacters(value: string): string {
90 return value.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&');
91 }
92 > strings.ts
93 > /**
94 > * Counts how often `substr` occurs inside `value`.
95 > */
96 > export function count(value: string, substr: string): number {
97 let result = 0;
98 let index = value.indexOf(substr);
103 return result;
104 }
105 > strings.ts
106 > export function truncate(value: string, maxLength: number, suffix = Ellipsis): string {
107 if (value.length <= maxLength) {
108 return value;
111 return `${value.substr(0, maxLength)}${suffix}`;
112 }
113 > strings.ts
114 > export function truncateMiddle(value: string, maxLength: number, suffix = Ellipsis): string {
115 if (value.length <= maxLength) {
116 return value;
122 return `${value.substr(0, prefixLength)}${suffix}${value.substr(value.length - suffixLength)}`;
123 }
124 > strings.ts
125 > /**
126 > * Removes all occurrences of needle from the beginning and end of haystack.
127 > * @param haystack string to trim
128 > * @param needle the thing to trim (default is a blank)
129 > */
130 > export function trim(haystack: string, needle: string = ' '): string {
131 const trimmed = ltrim(haystack, needle);
132 return rtrim(trimmed, needle);
133 }
134 > strings.ts
135 > /**
136 > * Removes all occurrences of needle from the beginning of haystack.
137 > * @param haystack string to trim
138 > * @param needle the thing to trim
139 > */
140 > export function ltrim(haystack: string, needle: string): string {
141 if (!haystack || !needle) {
142 return haystack;
157 return haystack.substring(offset);
158 }
159 > strings.ts
160 > /**
161 > * Removes all occurrences of needle from the end of haystack.
162 > * @param haystack string to trim
163 > * @param needle the thing to trim
164 > */
165 > export function rtrim(haystack: string, needle: string): string {
166 if (!haystack || !needle) {
167 return haystack;
187 return haystack.substring(0, offset);
188 }
189 > strings.ts
190 > export function convertSimple2RegExpPattern(pattern: string): string {
191 return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
192 }
193 > strings.ts
194 > export interface RegExpOptions {
195 > matchCase?: boolean;
196 > wholeWord?: boolean;
197 > multiline?: boolean;
198 > global?: boolean;
199 > unicode?: boolean;
200 > }
201 >
202 > export function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp {
203 if (!searchString) {
204 throw new Error('Cannot create regex from empty string');
231 return new RegExp(searchString, modifiers);
232 }
233 > strings.ts
234 > export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
235 // Exit early if it's one of these special cases which are meant to match
236 // against an empty string
244 return !!(match && regexp.lastIndex === 0);
245 }
246 > strings.ts
247 > export function joinStrings(items: (string | undefined | null | false)[], separator: string): string {
248 return items.filter(item => item !== undefined && item !== null && item !== false).join(separator);
249 }
250 > strings.ts
251 > export function splitLines(str: string): string[] {
252 return str.split(/\r\n|\r|\n/);
253 }
254 > strings.ts
255 > export function splitLinesIncludeSeparators(str: string): string[] {
256 const linesWithSeparators: string[] = [];
257 const splitLinesAndSeparators = str.split(/(\r\n|\r|\n)/);
261 return linesWithSeparators;
262 }
263 > strings.ts
264 > export function indexOfPattern(str: string, re: RegExp) {
265 const match = re.exec(str);
266 if (match) {
269 return -1;
270 }
271 > strings.ts
272 > /**
273 > * Returns first index of the string that is not whitespace.
274 > * If string is empty or contains only whitespaces, returns -1
275 > */
276 > export function firstNonWhitespaceIndex(str: string): number {
277 for (let i = 0, len = str.length; i < len; i++) {
278 const chCode = str.charCodeAt(i);
283 return -1;
284 }
285 > strings.ts
286 > /**
287 > * Returns the leading whitespace of the string.
288 > * If the string contains only whitespaces, returns entire string
289 > */
290 > export function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string {
291 for (let i = start; i < end; i++) {
292 const chCode = str.charCodeAt(i);
297 return str.substring(start, end);
298 }
299 > strings.ts
300 > /**
301 > * Returns last index of the string that is not whitespace.
302 > * If string is empty or contains only whitespaces, returns -1
303 > */
304 > export function lastNonWhitespaceIndex(str: string, startIndex: number = str.length - 1): number {
305 for (let i = startIndex; i >= 0; i--) {
306 const chCode = str.charCodeAt(i);
311 return -1;
312 }
313 > strings.ts
314 > export function getIndentationLength(str: string): number {
315 const idx = firstNonWhitespaceIndex(str);
316 if (idx === -1) { return str.length; }
317 return idx;
318 }
319 > strings.ts
320 > /**
321 > * Function that works identically to String.prototype.replace, except, the
322 > * replace function is allowed to be async and return a Promise.
323 > */
324 > export function replaceAsync(str: string, search: RegExp, replacer: (match: string, ...args: unknown[]) => Promise<string>): Promise<string> {
325 const parts: (string | Promise<string>)[] = [];
326
340 return Promise.all(parts).then(p => p.join(''));
341 }
342 > strings.ts
343 > export function compare(a: string, b: string): number {
344 if (a < b) {
345 return -1;
350 }
351 }
352 > strings.ts
353 > export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
354 for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
355 const codeA = a.charCodeAt(aStart);
370 return 0;
371 }
372 > strings.ts
373 > export function compareIgnoreCase(a: string, b: string): number {
374 return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length);
375 }
376 > strings.ts
377 > export function compareSubstringIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
378
379 for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
421 return 0;
422 }
423 > strings.ts
424 > export function isAsciiDigit(code: number): boolean {
425 return code >= CharCode.Digit0 && code <= CharCode.Digit9;
426 }
427 > strings.ts
428 > export function isLowerAsciiLetter(code: number): boolean {
429 return code >= CharCode.a && code <= CharCode.z;
430 }
431 > strings.ts
432 > export function isUpperAsciiLetter(code: number): boolean {
433 return code >= CharCode.A && code <= CharCode.Z;
434 }
435 > strings.ts
436 > export function equalsIgnoreCase(a: string, b: string): boolean {
437 return a.length === b.length && compareSubstringIgnoreCase(a, b) === 0;
438 }
439 > strings.ts
440 > export function equals(a: string | undefined, b: string | undefined, ignoreCase?: boolean): boolean {
441 return a === b || (!!ignoreCase && a !== undefined && b !== undefined && equalsIgnoreCase(a, b));
442 }
443 > strings.ts
444 > export function startsWithIgnoreCase(str: string, candidate: string): boolean {
445 const len = candidate.length;
446 return len <= str.length && compareSubstringIgnoreCase(str, candidate, 0, len) === 0;
447 }
448 > strings.ts
449 > export function endsWithIgnoreCase(str: string, candidate: string): boolean {
450 const len = str.length;
451 const start = len - candidate.length;
452 return start >= 0 && compareSubstringIgnoreCase(str, candidate, start, len) === 0;
453 }
454 > strings.ts
455 > /**
456 > * @returns the length of the common prefix of the two strings.
457 > */
458 > export function commonPrefixLength(a: string, b: string): number {
459
460 const len = Math.min(a.length, b.length);
469 return len;
470 }
471 > strings.ts
472 > /**
473 > * @returns the length of the common suffix of the two strings.
474 > */
475 > export function commonSuffixLength(a: string, b: string): number {
476
477 const len = Math.min(a.length, b.length);
489 return len;
490 }
491 > strings.ts
492 > /**
493 > * See http://en.wikipedia.org/wiki/Surrogate_pair
494 > */
495 > export function isHighSurrogate(charCode: number): boolean {
496 return (0xD800 <= charCode && charCode <= 0xDBFF);
497 }
498 > strings.ts
499 > /**
500 > * See http://en.wikipedia.org/wiki/Surrogate_pair
501 > */
502 > export function isLowSurrogate(charCode: number): boolean {
503 return (0xDC00 <= charCode && charCode <= 0xDFFF);
504 }
505 > strings.ts
506 > /**
507 > * See http://en.wikipedia.org/wiki/Surrogate_pair
508 > */
509 > export function computeCodePoint(highSurrogate: number, lowSurrogate: number): number {
510 return ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00) + 0x10000;
511 }
512 > strings.ts
513 > /**
514 > * get the code point that begins at offset `offset`
515 > */
516 > export function getNextCodePoint(str: string, len: number, offset: number): number {
517 const charCode = str.charCodeAt(offset);
518 if (isHighSurrogate(charCode) && offset + 1 < len) {
524 return charCode;
525 }
526 > strings.ts
527 > /**
528 > * get the code point that ends right before offset `offset`
529 > */
530 function getPrevCodePoint(str: string, offset: number): number {
531 const charCode = str.charCodeAt(offset - 1);
538 return charCode;
539 }
540 > strings.ts
541 > export class CodePointIterator {
542 >
543 > private readonly _str: string;
544 > private readonly _len: number;
545 > private _offset: number;
546 >
547 > public get offset(): number {
548 return this._offset;
549 }
550 > strings.ts
551 > constructor(str: string, offset: number = 0) {
552 this._str = str;
553 this._len = str.length;
554 this._offset = offset;
555 }
556 > strings.ts
557 > public setOffset(offset: number): void {
558 this._offset = offset;
559 }
560 > strings.ts
561 > public prevCodePoint(): number {
562 const codePoint = getPrevCodePoint(this._str, this._offset);
563 this._offset -= (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
564 return codePoint;
565 }
566 > strings.ts
567 > public nextCodePoint(): number {
568 const codePoint = getNextCodePoint(this._str, this._len, this._offset);
569 this._offset += (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
570 return codePoint;
571 }
572 > strings.ts
573 > public eol(): boolean {
574 return (this._offset >= this._len);
575 }
576 > } strings.ts
577 >
578 > export class GraphemeIterator {
579 >
580 > private readonly _iterator: CodePointIterator;
581 >
582 > public get offset(): number {
583 return this._iterator.offset;
584 }
585 > strings.ts
586 > constructor(str: string, offset: number = 0) {
587 this._iterator = new CodePointIterator(str, offset);
588 }
589 > strings.ts
590 > public nextGraphemeLength(): number {
591 const graphemeBreakTree = GraphemeBreakTree.getInstance();
592 const iterator = this._iterator;
606 return (iterator.offset - initialOffset);
607 }
608 > strings.ts
609 > public prevGraphemeLength(): number {
610 const graphemeBreakTree = GraphemeBreakTree.getInstance();
611 const iterator = this._iterator;
625 return (initialOffset - iterator.offset);
626 }
627 > strings.ts
628 > public eol(): boolean {
629 return this._iterator.eol();
630 }
631 > } strings.ts
632 >
633 > export function nextCharLength(str: string, initialOffset: number): number {
634 const iterator = new GraphemeIterator(str, initialOffset);
635 return iterator.nextGraphemeLength();
636 }
637 > strings.ts
638 > export function prevCharLength(str: string, initialOffset: number): number {
639 const iterator = new GraphemeIterator(str, initialOffset);
640 return iterator.prevGraphemeLength();
641 }
642 > strings.ts
643 > export function getCharContainingOffset(str: string, offset: number): [number, number] {
644 if (offset > 0 && isLowSurrogate(str.charCodeAt(offset))) {
645 offset--;
649 return [startOffset, endOffset];
650 }
651 > strings.ts
652 > export function charCount(str: string): number {
653 const iterator = new GraphemeIterator(str);
654 let length = 0;
659 return length;
660 }
661 > strings.ts
662 > let CONTAINS_RTL: RegExp | undefined = undefined;
663 >
664 function makeContainsRtl() {
665 // Generated using https://github.com/alexdima/unicode-utils/blob/main/rtl-test.js
666 return /(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA\u07FE-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u088E\u08A0-\u08C9\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDC7\uFDF0-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE35\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDD23\uDE80-\uDEA9\uDEAD-\uDF45\uDF51-\uDF81\uDF86-\uDFF6]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD4B-\uDFFF]|\uD83B[\uDC00-\uDEBB])/;
667 }
668 > strings.ts
669 > /**
670 > * Returns true if `str` contains any Unicode character that is classified as "R" or "AL".
671 > */
672 > export function containsRTL(str: string): boolean {
673 if (!CONTAINS_RTL) {
674 CONTAINS_RTL = makeContainsRtl();
677 return CONTAINS_RTL.test(str);
678 }
679 > strings.ts
680 > const IS_BASIC_ASCII = /^[\t\n\r\x20-\x7E]*$/;
681 > /**
682 > * Returns true if `str` contains only basic ASCII characters in the range 32 - 126 (including 32 and 126) or \n, \r, \t
683 > */
684 > export function isBasicASCII(str: string): boolean {
685 return IS_BASIC_ASCII.test(str);
686 }
687 > strings.ts
688 > export const UNUSUAL_LINE_TERMINATORS = /[\u2028\u2029]/; // LINE SEPARATOR (LS) or PARAGRAPH SEPARATOR (PS)
689 > /**
690 > * Returns true if `str` contains unusual line terminators, like LS or PS
691 > */
692 > export function containsUnusualLineTerminators(str: string): boolean {
693 return UNUSUAL_LINE_TERMINATORS.test(str);
694 }
695 > strings.ts
696 > export function isFullWidthCharacter(charCode: number): boolean {
697 // Do a cheap trick to better support wrapping of wide characters, treat them as 2 columns
698 // http://jrgraphix.net/research/unicode_blocks.php
741 );
742 }
743 > strings.ts
744 > /**
745 > * A fast function (therefore imprecise) to check if code points are emojis.
746 > * Generated using https://github.com/alexdima/unicode-utils/blob/main/emoji-test.js
747 > */
748 > export function isEmojiImprecise(x: number): boolean {
749 return (
750 (x >= 0x1F1E6 && x <= 0x1F1FF) || (x === 8986) || (x === 8987) || (x === 9200)
755 );
756 }
757 > strings.ts
758 > /**
759 > * Given a string and a max length returns a shorted version. Shorting
760 > * happens at favorable positions - such as whitespace or punctuation characters.
761 > * The return value can be longer than the given value of `n`. Leading whitespace is always trimmed.
762 > */
763 > export function lcut(text: string, n: number, prefix = ''): string {
764 const trimmed = text.trimStart();
765
785 return prefix + trimmed.substring(i).trimStart();
786 }
787 > strings.ts
788 > /**
789 > * Given a string and a max length returns a shortened version keeping the beginning.
790 > * Shortening happens at favorable positions - such as whitespace or punctuation characters.
791 > * Trailing whitespace is always trimmed.
792 > */
793 > export function rcut(text: string, n: number, suffix = ''): string {
794 const trimmed = text.trimEnd();
795
832 return result + suffix;
833 }
834 > strings.ts
835 > // Defacto standard: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
836 > const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/;
837 > const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/;
838 > const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/;
839 > const CONTROL_SEQUENCES = new RegExp('(?:' + [
840 > CSI_SEQUENCE.source,
841 > OSC_SEQUENCE.source,
842 > ESC_SEQUENCE.source,
843 > ].join('|') + ')', 'g');
844 >
845 > /** Iterates over parts of a string with CSI sequences */
846 > export function* forAnsiStringParts(str: string) {
847 let last = 0;
848 for (const match of str.matchAll(CONTROL_SEQUENCES)) {
859 }
860 }
861 > strings.ts
862 > /**
863 > * Strips ANSI escape sequences from a string.
864 > * @param str The dastringa stringo strip the ANSI escape sequences from.
865 > *
866 > * @example
867 > * removeAnsiEscapeCodes('\u001b[31mHello, World!\u001b[0m');
868 > * // 'Hello, World!'
869 > */
870 > export function removeAnsiEscapeCodes(str: string): string {
871 if (str) {
872 str = str.replace(CONTROL_SEQUENCES, '');
875 return str;
876 }
877 > strings.ts
878 > const PROMPT_NON_PRINTABLE = /\\\[.*?\\\]/g;
879 >
880 > /**
881 > * Strips ANSI escape sequences from a UNIX-style prompt string (eg. `$PS1`).
882 > * @param str The string to strip the ANSI escape sequences from.
883 > *
884 > * @example
885 > * removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]');
886 > * // '\n\\w\n> '
887 > */
888 > export function removeAnsiEscapeCodesFromPrompt(str: string): string {
889 return removeAnsiEscapeCodes(str).replace(PROMPT_NON_PRINTABLE, '');
890 }
891 > strings.ts
892 >
893 > // -- UTF-8 BOM
894 >
895 > export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM);
896 >
897 > export function startsWithUTF8BOM(str: string): boolean {
898 return !!(str && str.length > 0 && str.charCodeAt(0) === CharCode.UTF8_BOM);
899 }
900 > strings.ts
901 > export function stripUTF8BOM(str: string): string {
902 return startsWithUTF8BOM(str) ? str.substr(1) : str;
903 }
904 > strings.ts
905 > /**
906 > * Checks if the characters of the provided query string are included in the
907 > * target string. The characters do not have to be contiguous within the string.
908 > */
909 > export function fuzzyContains(target: string, query: string): boolean {
910 if (!target || !query) {
911 return false; // return early if target or query are undefined
936 return true;
937 }
938 > strings.ts
939 > export function containsUppercaseCharacter(target: string, ignoreEscapedChars = false): boolean {
940 if (!target) {
941 return false;
948 return target.toLowerCase() !== target;
949 }
950 > strings.ts
951 > export function uppercaseFirstLetter(str: string): string {
952 return str.charAt(0).toUpperCase() + str.slice(1);
953 }
954 > strings.ts
955 > export function getNLines(str: string, n = 1): string {
956 if (n === 0) {
957 return '';
974 return str.substr(0, idx);
975 }
976 > strings.ts
977 > /**
978 > * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc.
979 > */
980 > export function singleLetterHash(n: number): string {
981 const LETTERS_CNT = (CharCode.Z - CharCode.A + 1);
982
989 return String.fromCharCode(CharCode.A + n - LETTERS_CNT);
990 }
991 > strings.ts
992 > //#region Unicode Grapheme Break
993 >
994 > export function getGraphemeBreakType(codePoint: number): GraphemeBreakType {
995 const graphemeBreakTree = GraphemeBreakTree.getInstance();
996 return graphemeBreakTree.getGraphemeBreakType(codePoint);
997 }
998 > strings.ts
999 function breakBetweenGraphemeBreakType(breakTypeA: GraphemeBreakType, breakTypeB: GraphemeBreakType): boolean {
1000 // http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
1076 return true;
1077 }
1078 > strings.ts
1079 > export const enum GraphemeBreakType {
1080 > Other = 0,
1081 > Prepend = 1,
1082 > CR = 2,
1083 > LF = 3,
1084 > Control = 4,
1085 > Extend = 5,
1086 > Regional_Indicator = 6,
1087 > SpacingMark = 7,
1088 > L = 8,
1089 > V = 9,
1090 > T = 10,
1091 > LV = 11,
1092 > LVT = 12,
1093 > ZWJ = 13,
1094 > Extended_Pictographic = 14
1095 > }
1096 >
1097 > class GraphemeBreakTree {
1098 >
1099 > private static _INSTANCE: GraphemeBreakTree | null = null;
1100 > public static getInstance(): GraphemeBreakTree {
1101 if (!GraphemeBreakTree._INSTANCE) {
1102 GraphemeBreakTree._INSTANCE = new GraphemeBreakTree();
1104 return GraphemeBreakTree._INSTANCE;
1105 }
1106 > strings.ts
1107 > private readonly _data: number[];
1108 >
1109 > constructor() {
1110 this._data = getGraphemeBreakRawData();
1111 }
1112 > strings.ts
1113 > public getGraphemeBreakType(codePoint: number): GraphemeBreakType {
1114 // !!! Let's make 7bit ASCII a bit faster: 0..31
1115 if (codePoint < 32) {
1145 return GraphemeBreakType.Other;
1146 }
1147 > } strings.ts
1148 >
1149 function getGraphemeBreakRawData(): number[] {
1150 // generated using https://github.com/alexdima/unicode-utils/blob/main/grapheme-break.js
1151 return JSON.parse('[0,0,0,51229,51255,12,44061,44087,12,127462,127487,6,7083,7085,5,47645,47671,12,54813,54839,12,128678,128678,14,3270,3270,5,9919,9923,14,45853,45879,12,49437,49463,12,53021,53047,12,71216,71218,7,128398,128399,14,129360,129374,14,2519,2519,5,4448,4519,9,9742,9742,14,12336,12336,14,44957,44983,12,46749,46775,12,48541,48567,12,50333,50359,12,52125,52151,12,53917,53943,12,69888,69890,5,73018,73018,5,127990,127990,14,128558,128559,14,128759,128760,14,129653,129655,14,2027,2035,5,2891,2892,7,3761,3761,5,6683,6683,5,8293,8293,4,9825,9826,14,9999,9999,14,43452,43453,5,44509,44535,12,45405,45431,12,46301,46327,12,47197,47223,12,48093,48119,12,48989,49015,12,49885,49911,12,50781,50807,12,51677,51703,12,52573,52599,12,53469,53495,12,54365,54391,12,65279,65279,4,70471,70472,7,72145,72147,7,119173,119179,5,127799,127818,14,128240,128244,14,128512,128512,14,128652,128652,14,128721,128722,14,129292,129292,14,129445,129450,14,129734,129743,14,1476,1477,5,2366,2368,7,2750,2752,7,3076,3076,5,3415,3415,5,4141,4144,5,6109,6109,5,6964,6964,5,7394,7400,5,9197,9198,14,9770,9770,14,9877,9877,14,9968,9969,14,10084,10084,14,43052,43052,5,43713,43713,5,44285,44311,12,44733,44759,12,45181,45207,12,45629,45655,12,46077,46103,12,46525,46551,12,46973,46999,12,47421,47447,12,47869,47895,12,48317,48343,12,48765,48791,12,49213,49239,12,49661,49687,12,50109,50135,12,50557,50583,12,51005,51031,12,51453,51479,12,51901,51927,12,52349,52375,12,52797,52823,12,53245,53271,12,53693,53719,12,54141,54167,12,54589,54615,12,55037,55063,12,69506,69509,5,70191,70193,5,70841,70841,7,71463,71467,5,72330,72342,5,94031,94031,5,123628,123631,5,127763,127765,14,127941,127941,14,128043,128062,14,128302,128317,14,128465,128467,14,128539,128539,14,128640,128640,14,128662,128662,14,128703,128703,14,128745,128745,14,129004,129007,14,129329,129330,14,129402,129402,14,129483,129483,14,129686,129704,14,130048,131069,14,173,173,4,1757,1757,1,2200,2207,5,2434,2435,7,2631,2632,5,2817,2817,5,3008,3008,5,3201,3201,5,3387,3388,5,3542,3542,5,3902,3903,7,4190,4192,5,6002,6003,5,6439,6440,5,6765,6770,7,7019,7027,5,7154,7155,7,8205,8205,13,8505,8505,14,9654,9654,14,9757,9757,14,9792,9792,14,9852,9853,14,9890,9894,14,9937,9937,14,9981,9981,14,10035,10036,14,11035,11036,14,42654,42655,5,43346,43347,7,43587,43587,5,44006,44007,7,44173,44199,12,44397,44423,12,44621,44647,12,44845,44871,12,45069,45095,12,45293,45319,12,45517,45543,12,45741,45767,12,45965,45991,12,46189,46215,12,46413,46439,12,46637,46663,12,46861,46887,12,47085,47111,12,47309,47335,12,47533,47559,12,47757,47783,12,47981,48007,12,48205,48231,12,48429,48455,12,48653,48679,12,48877,48903,12,49101,49127,12,49325,49351,12,49549,49575,12,49773,49799,12,49997,50023,12,50221,50247,12,50445,50471,12,50669,50695,12,50893,50919,12,51117,51143,12,51341,51367,12,51565,51591,12,51789,51815,12,52013,52039,12,52237,52263,12,52461,52487,12,52685,52711,12,52909,52935,12,53133,53159,12,53357,53383,12,53581,53607,12,53805,53831,12,54029,54055,12,54253,54279,12,54477,54503,12,54701,54727,12,54925,54951,12,55149,55175,12,68101,68102,5,69762,69762,7,70067,70069,7,70371,70378,5,70720,70721,7,71087,71087,5,71341,71341,5,71995,71996,5,72249,72249,7,72850,72871,5,73109,73109,5,118576,118598,5,121505,121519,5,127245,127247,14,127568,127569,14,127777,127777,14,127872,127891,14,127956,127967,14,128015,128016,14,128110,128172,14,128259,128259,14,128367,128368,14,128424,128424,14,128488,128488,14,128530,128532,14,128550,128551,14,128566,128566,14,128647,128647,14,128656,128656,14,128667,128673,14,128691,128693,14,128715,128715,14,128728,128732,14,128752,128752,14,128765,128767,14,129096,129103,14,129311,129311,14,129344,129349,14,129394,129394,14,129413,129425,14,129466,129471,14,129511,129535,14,129664,129666,14,129719,129722,14,129760,129767,14,917536,917631,5,13,13,2,1160,1161,5,1564,1564,4,1807,1807,1,2085,2087,5,2307,2307,7,2382,2383,7,2497,2500,5,2563,2563,7,2677,2677,5,2763,2764,7,2879,2879,5,2914,2915,5,3021,3021,5,3142,3144,5,3263,3263,5,3285,3286,5,3398,3400,7,3530,3530,5,3633,3633,5,3864,3865,5,3974,3975,5,4155,4156,7,4229,4230,5,5909,5909,7,6078,6085,7,6277,6278,5,6451,6456,7,6744,6750,5,6846,6846,5,6972,6972,5,7074,7077,5,7146,7148,7,7222,7223,5,7416,7417,5,8234,8238,4,8417,8417,5,9000,9000,14,9203,9203,14,9730,9731,14,9748,9749,14,9762,9763,14,9776,9783,14,9800,9811,14,9831,9831,14,9872,9873,14,9882,9882,14,9900,9903,14,9929,9933,14,9941,9960,14,9974,9974,14,9989,9989,14,10006,10006,14,10062,10062,14,10160,10160,14,11647,11647,5,12953,12953,14,43019,43019,5,43232,43249,5,43443,43443,5,43567,43568,7,43696,43696,5,43765,43765,7,44013,44013,5,44117,44143,12,44229,44255,12,44341,44367,12,44453,44479,12,44565,44591,12,44677,44703,12,44789,44815,12,44901,44927,12,45013,45039,12,45125,45151,12,45237,45263,12,45349,45375,12,45461,45487,12,45573,45599,12,45685,45711,12,45797,45823,12,45909,45935,12,46021,46047,12,46133,46159,12,46245,46271,12,46357,46383,12,46469,46495,12,46581,46607,12,46693,46719,12,46805,46831,12,46917,46943,12,47029,47055,12,47141,47167,12,47253,47279,12,47365,47391,12,47477,47503,12,47589,47615,12,47701,47727,12,47813,47839,12,47925,47951,12,48037,48063,12,48149,48175,12,48261,48287,12,48373,48399,12,48485,48511,12,48597,48623,12,48709,48735,12,48821,48847,12,48933,48959,12,49045,49071,12,49157,49183,12,49269,49295,12,49381,49407,12,49493,49519,12,49605,49631,12,49717,49743,12,49829,49855,12,49941,49967,12,50053,50079,12,50165,50191,12,50277,50303,12,50389,50415,12,50501,50527,12,50613,50639,12,50725,50751,12,50837,50863,12,50949,50975,12,51061,51087,12,51173,51199,12,51285,51311,12,51397,51423,12,51509,51535,12,51621,51647,12,51733,51759,12,51845,51871,12,51957,51983,12,52069,52095,12,52181,52207,12,52293,52319,12,52405,52431,12,52517,52543,12,52629,52655,12,52741,52767,12,52853,52879,12,52965,52991,12,53077,53103,12,53189,53215,12,53301,53327,12,53413,53439,12,53525,53551,12,53637,53663,12,53749,53775,12,53861,53887,12,53973,53999,12,54085,54111,12,54197,54223,12,54309,54335,12,54421,54447,12,54533,54559,12,54645,54671,12,54757,54783,12,54869,54895,12,54981,55007,12,55093,55119,12,55243,55291,10,66045,66045,5,68325,68326,5,69688,69702,5,69817,69818,5,69957,69958,7,70089,70092,5,70198,70199,5,70462,70462,5,70502,70508,5,70750,70750,5,70846,70846,7,71100,71101,5,71230,71230,7,71351,71351,5,71737,71738,5,72000,72000,7,72160,72160,5,72273,72278,5,72752,72758,5,72882,72883,5,73031,73031,5,73461,73462,7,94192,94193,7,119149,119149,7,121403,121452,5,122915,122916,5,126980,126980,14,127358,127359,14,127535,127535,14,127759,127759,14,127771,127771,14,127792,127793,14,127825,127867,14,127897,127899,14,127945,127945,14,127985,127986,14,128000,128007,14,128021,128021,14,128066,128100,14,128184,128235,14,128249,128252,14,128266,128276,14,128335,128335,14,128379,128390,14,128407,128419,14,128444,128444,14,128481,128481,14,128499,128499,14,128526,128526,14,128536,128536,14,128543,128543,14,128556,128556,14,128564,128564,14,128577,128580,14,128643,128645,14,128649,128649,14,128654,128654,14,128660,128660,14,128664,128664,14,128675,128675,14,128686,128689,14,128695,128696,14,128705,128709,14,128717,128719,14,128725,128725,14,128736,128741,14,128747,128748,14,128755,128755,14,128762,128762,14,128981,128991,14,129009,129023,14,129160,129167,14,129296,129304,14,129320,129327,14,129340,129342,14,129356,129356,14,129388,129392,14,129399,129400,14,129404,129407,14,129432,129442,14,129454,129455,14,129473,129474,14,129485,129487,14,129648,129651,14,129659,129660,14,129671,129679,14,129709,129711,14,129728,129730,14,129751,129753,14,129776,129782,14,917505,917505,4,917760,917999,5,10,10,3,127,159,4,768,879,5,1471,1471,5,1536,1541,1,1648,1648,5,1767,1768,5,1840,1866,5,2070,2073,5,2137,2139,5,2274,2274,1,2363,2363,7,2377,2380,7,2402,2403,5,2494,2494,5,2507,2508,7,2558,2558,5,2622,2624,7,2641,2641,5,2691,2691,7,2759,2760,5,2786,2787,5,2876,2876,5,2881,2884,5,2901,2902,5,3006,3006,5,3014,3016,7,3072,3072,5,3134,3136,5,3157,3158,5,3260,3260,5,3266,3266,5,3274,3275,7,3328,3329,5,3391,3392,7,3405,3405,5,3457,3457,5,3536,3537,7,3551,3551,5,3636,3642,5,3764,3772,5,3895,3895,5,3967,3967,7,3993,4028,5,4146,4151,5,4182,4183,7,4226,4226,5,4253,4253,5,4957,4959,5,5940,5940,7,6070,6070,7,6087,6088,7,6158,6158,4,6432,6434,5,6448,6449,7,6679,6680,5,6742,6742,5,6754,6754,5,6783,6783,5,6912,6915,5,6966,6970,5,6978,6978,5,7042,7042,7,7080,7081,5,7143,7143,7,7150,7150,7,7212,7219,5,7380,7392,5,7412,7412,5,8203,8203,4,8232,8232,4,8265,8265,14,8400,8412,5,8421,8432,5,8617,8618,14,9167,9167,14,9200,9200,14,9410,9410,14,9723,9726,14,9733,9733,14,9745,9745,14,9752,9752,14,9760,9760,14,9766,9766,14,9774,9774,14,9786,9786,14,9794,9794,14,9823,9823,14,9828,9828,14,9833,9850,14,9855,9855,14,9875,9875,14,9880,9880,14,9885,9887,14,9896,9897,14,9906,9916,14,9926,9927,14,9935,9935,14,9939,9939,14,9962,9962,14,9972,9972,14,9978,9978,14,9986,9986,14,9997,9997,14,10002,10002,14,10017,10017,14,10055,10055,14,10071,10071,14,10133,10135,14,10548,10549,14,11093,11093,14,12330,12333,5,12441,12442,5,42608,42610,5,43010,43010,5,43045,43046,5,43188,43203,7,43302,43309,5,43392,43394,5,43446,43449,5,43493,43493,5,43571,43572,7,43597,43597,7,43703,43704,5,43756,43757,5,44003,44004,7,44009,44010,7,44033,44059,12,44089,44115,12,44145,44171,12,44201,44227,12,44257,44283,12,44313,44339,12,44369,44395,12,44425,44451,12,44481,44507,12,44537,44563,12,44593,44619,12,44649,44675,12,44705,44731,12,44761,44787,12,44817,44843,12,44873,44899,12,44929,44955,12,44985,45011,12,45041,45067,12,45097,45123,12,45153,45179,12,45209,45235,12,45265,45291,12,45321,45347,12,45377,45403,12,45433,45459,12,45489,45515,12,45545,45571,12,45601,45627,12,45657,45683,12,45713,45739,12,45769,45795,12,45825,45851,12,45881,45907,12,45937,45963,12,45993,46019,12,46049,46075,12,46105,46131,12,46161,46187,12,46217,46243,12,46273,46299,12,46329,46355,12,46385,46411,12,46441,46467,12,46497,46523,12,46553,46579,12,46609,46635,12,46665,46691,12,46721,46747,12,46777,46803,12,46833,46859,12,46889,46915,12,46945,46971,12,47001,47027,12,47057,47083,12,47113,47139,12,47169,47195,12,47225,47251,12,47281,47307,12,47337,47363,12,47393,47419,12,47449,47475,12,47505,47531,12,47561,47587,12,47617,47643,12,47673,47699,12,47729,47755,12,47785,47811,12,47841,47867,12,47897,47923,12,47953,47979,12,48009,48035,12,48065,48091,12,48121,48147,12,48177,48203,12,48233,48259,12,48289,48315,12,48345,48371,12,48401,48427,12,48457,48483,12,48513,48539,12,48569,48595,12,48625,48651,12,48681,48707,12,48737,48763,12,48793,48819,12,48849,48875,12,48905,48931,12,48961,48987,12,49017,49043,12,49073,49099,12,49129,49155,12,49185,49211,12,49241,49267,12,49297,49323,12,49353,49379,12,49409,49435,12,49465,49491,12,49521,49547,12,49577,49603,12,49633,49659,12,49689,49715,12,49745,49771,12,49801,49827,12,49857,49883,12,49913,49939,12,49969,49995,12,50025,50051,12,50081,50107,12,50137,50163,12,50193,50219,12,50249,50275,12,50305,50331,12,50361,50387,12,50417,50443,12,50473,50499,12,50529,50555,12,50585,50611,12,50641,50667,12,50697,50723,12,50753,50779,12,50809,50835,12,50865,50891,12,50921,50947,12,50977,51003,12,51033,51059,12,51089,51115,12,51145,51171,12,51201,51227,12,51257,51283,12,51313,51339,12,51369,51395,12,51425,51451,12,51481,51507,12,51537,51563,12,51593,51619,12,51649,51675,12,51705,51731,12,51761,51787,12,51817,51843,12,51873,51899,12,51929,51955,12,51985,52011,12,52041,52067,12,52097,52123,12,52153,52179,12,52209,52235,12,52265,52291,12,52321,52347,12,52377,52403,12,52433,52459,12,52489,52515,12,52545,52571,12,52601,52627,12,52657,52683,12,52713,52739,12,52769,52795,12,52825,52851,12,52881,52907,12,52937,52963,12,52993,53019,12,53049,53075,12,53105,53131,12,53161,53187,12,53217,53243,12,53273,53299,12,53329,53355,12,53385,53411,12,53441,53467,12,53497,53523,12,53553,53579,12,53609,53635,12,53665,53691,12,53721,53747,12,53777,53803,12,53833,53859,12,53889,53915,12,53945,53971,12,54001,54027,12,54057,54083,12,54113,54139,12,54169,54195,12,54225,54251,12,54281,54307,12,54337,54363,12,54393,54419,12,54449,54475,12,54505,54531,12,54561,54587,12,54617,54643,12,54673,54699,12,54729,54755,12,54785,54811,12,54841,54867,12,54897,54923,12,54953,54979,12,55009,55035,12,55065,55091,12,55121,55147,12,55177,55203,12,65024,65039,5,65520,65528,4,66422,66426,5,68152,68154,5,69291,69292,5,69633,69633,5,69747,69748,5,69811,69814,5,69826,69826,5,69932,69932,7,70016,70017,5,70079,70080,7,70095,70095,5,70196,70196,5,70367,70367,5,70402,70403,7,70464,70464,5,70487,70487,5,70709,70711,7,70725,70725,7,70833,70834,7,70843,70844,7,70849,70849,7,71090,71093,5,71103,71104,5,71227,71228,7,71339,71339,5,71344,71349,5,71458,71461,5,71727,71735,5,71985,71989,7,71998,71998,5,72002,72002,7,72154,72155,5,72193,72202,5,72251,72254,5,72281,72283,5,72344,72345,5,72766,72766,7,72874,72880,5,72885,72886,5,73023,73029,5,73104,73105,5,73111,73111,5,92912,92916,5,94095,94098,5,113824,113827,4,119142,119142,7,119155,119162,4,119362,119364,5,121476,121476,5,122888,122904,5,123184,123190,5,125252,125258,5,127183,127183,14,127340,127343,14,127377,127386,14,127491,127503,14,127548,127551,14,127744,127756,14,127761,127761,14,127769,127769,14,127773,127774,14,127780,127788,14,127796,127797,14,127820,127823,14,127869,127869,14,127894,127895,14,127902,127903,14,127943,127943,14,127947,127950,14,127972,127972,14,127988,127988,14,127992,127994,14,128009,128011,14,128019,128019,14,128023,128041,14,128064,128064,14,128102,128107,14,128174,128181,14,128238,128238,14,128246,128247,14,128254,128254,14,128264,128264,14,128278,128299,14,128329,128330,14,128348,128359,14,128371,128377,14,128392,128393,14,128401,128404,14,128421,128421,14,128433,128434,14,128450,128452,14,128476,128478,14,128483,128483,14,128495,128495,14,128506,128506,14,128519,128520,14,128528,128528,14,128534,128534,14,128538,128538,14,128540,128542,14,128544,128549,14,128552,128555,14,128557,128557,14,128560,128563,14,128565,128565,14,128567,128576,14,128581,128591,14,128641,128642,14,128646,128646,14,128648,128648,14,128650,128651,14,128653,128653,14,128655,128655,14,128657,128659,14,128661,128661,14,128663,128663,14,128665,128666,14,128674,128674,14,128676,128677,14,128679,128685,14,128690,128690,14,128694,128694,14,128697,128702,14,128704,128704,14,128710,128714,14,128716,128716,14,128720,128720,14,128723,128724,14,128726,128727,14,128733,128735,14,128742,128744,14,128746,128746,14,128749,128751,14,128753,128754,14,128756,128758,14,128761,128761,14,128763,128764,14,128884,128895,14,128992,129003,14,129008,129008,14,129036,129039,14,129114,129119,14,129198,129279,14,129293,129295,14,129305,129310,14,129312,129319,14,129328,129328,14,129331,129338,14,129343,129343,14,129351,129355,14,129357,129359,14,129375,129387,14,129393,129393,14,129395,129398,14,129401,129401,14,129403,129403,14,129408,129412,14,129426,129431,14,129443,129444,14,129451,129453,14,129456,129465,14,129472,129472,14,129475,129482,14,129484,129484,14,129488,129510,14,129536,129647,14,129652,129652,14,129656,129658,14,129661,129663,14,129667,129670,14,129680,129685,14,129705,129708,14,129712,129718,14,129723,129727,14,129731,129733,14,129744,129750,14,129754,129759,14,129768,129775,14,129783,129791,14,917504,917504,4,917506,917535,4,917632,917759,4,918000,921599,4,0,9,4,11,12,4,14,31,4,169,169,14,174,174,14,1155,1159,5,1425,1469,5,1473,1474,5,1479,1479,5,1552,1562,5,1611,1631,5,1750,1756,5,1759,1764,5,1770,1773,5,1809,1809,5,1958,1968,5,2045,2045,5,2075,2083,5,2089,2093,5,2192,2193,1,2250,2273,5,2275,2306,5,2362,2362,5,2364,2364,5,2369,2376,5,2381,2381,5,2385,2391,5,2433,2433,5,2492,2492,5,2495,2496,7,2503,2504,7,2509,2509,5,2530,2531,5,2561,2562,5,2620,2620,5,2625,2626,5,2635,2637,5,2672,2673,5,2689,2690,5,2748,2748,5,2753,2757,5,2761,2761,7,2765,2765,5,2810,2815,5,2818,2819,7,2878,2878,5,2880,2880,7,2887,2888,7,2893,2893,5,2903,2903,5,2946,2946,5,3007,3007,7,3009,3010,7,3018,3020,7,3031,3031,5,3073,3075,7,3132,3132,5,3137,3140,7,3146,3149,5,3170,3171,5,3202,3203,7,3262,3262,7,3264,3265,7,3267,3268,7,3271,3272,7,3276,3277,5,3298,3299,5,3330,3331,7,3390,3390,5,3393,3396,5,3402,3404,7,3406,3406,1,3426,3427,5,3458,3459,7,3535,3535,5,3538,3540,5,3544,3550,7,3570,3571,7,3635,3635,7,3655,3662,5,3763,3763,7,3784,3789,5,3893,3893,5,3897,3897,5,3953,3966,5,3968,3972,5,3981,3991,5,4038,4038,5,4145,4145,7,4153,4154,5,4157,4158,5,4184,4185,5,4209,4212,5,4228,4228,7,4237,4237,5,4352,4447,8,4520,4607,10,5906,5908,5,5938,5939,5,5970,5971,5,6068,6069,5,6071,6077,5,6086,6086,5,6089,6099,5,6155,6157,5,6159,6159,5,6313,6313,5,6435,6438,7,6441,6443,7,6450,6450,5,6457,6459,5,6681,6682,7,6741,6741,7,6743,6743,7,6752,6752,5,6757,6764,5,6771,6780,5,6832,6845,5,6847,6862,5,6916,6916,7,6965,6965,5,6971,6971,7,6973,6977,7,6979,6980,7,7040,7041,5,7073,7073,7,7078,7079,7,7082,7082,7,7142,7142,5,7144,7145,5,7149,7149,5,7151,7153,5,7204,7211,7,7220,7221,7,7376,7378,5,7393,7393,7,7405,7405,5,7415,7415,7,7616,7679,5,8204,8204,5,8206,8207,4,8233,8233,4,8252,8252,14,8288,8292,4,8294,8303,4,8413,8416,5,8418,8420,5,8482,8482,14,8596,8601,14,8986,8987,14,9096,9096,14,9193,9196,14,9199,9199,14,9201,9202,14,9208,9210,14,9642,9643,14,9664,9664,14,9728,9729,14,9732,9732,14,9735,9741,14,9743,9744,14,9746,9746,14,9750,9751,14,9753,9756,14,9758,9759,14,9761,9761,14,9764,9765,14,9767,9769,14,9771,9773,14,9775,9775,14,9784,9785,14,9787,9791,14,9793,9793,14,9795,9799,14,9812,9822,14,9824,9824,14,9827,9827,14,9829,9830,14,9832,9832,14,9851,9851,14,9854,9854,14,9856,9861,14,9874,9874,14,9876,9876,14,9878,9879,14,9881,9881,14,9883,9884,14,9888,9889,14,9895,9895,14,9898,9899,14,9904,9905,14,9917,9918,14,9924,9925,14,9928,9928,14,9934,9934,14,9936,9936,14,9938,9938,14,9940,9940,14,9961,9961,14,9963,9967,14,9970,9971,14,9973,9973,14,9975,9977,14,9979,9980,14,9982,9985,14,9987,9988,14,9992,9996,14,9998,9998,14,10000,10001,14,10004,10004,14,10013,10013,14,10024,10024,14,10052,10052,14,10060,10060,14,10067,10069,14,10083,10083,14,10085,10087,14,10145,10145,14,10175,10175,14,11013,11015,14,11088,11088,14,11503,11505,5,11744,11775,5,12334,12335,5,12349,12349,14,12951,12951,14,42607,42607,5,42612,42621,5,42736,42737,5,43014,43014,5,43043,43044,7,43047,43047,7,43136,43137,7,43204,43205,5,43263,43263,5,43335,43345,5,43360,43388,8,43395,43395,7,43444,43445,7,43450,43451,7,43454,43456,7,43561,43566,5,43569,43570,5,43573,43574,5,43596,43596,5,43644,43644,5,43698,43700,5,43710,43711,5,43755,43755,7,43758,43759,7,43766,43766,5,44005,44005,5,44008,44008,5,44012,44012,7,44032,44032,11,44060,44060,11,44088,44088,11,44116,44116,11,44144,44144,11,44172,44172,11,44200,44200,11,44228,44228,11,44256,44256,11,44284,44284,11,44312,44312,11,44340,44340,11,44368,44368,11,44396,44396,11,44424,44424,11,44452,44452,11,44480,44480,11,44508,44508,11,44536,44536,11,44564,44564,11,44592,44592,11,44620,44620,11,44648,44648,11,44676,44676,11,44704,44704,11,44732,44732,11,44760,44760,11,44788,44788,11,44816,44816,11,44844,44844,11,44872,44872,11,44900,44900,11,44928,44928,11,44956,44956,11,44984,44984,11,45012,45012,11,45040,45040,11,45068,45068,11,45096,45096,11,45124,45124,11,45152,45152,11,45180,45180,11,45208,45208,11,45236,45236,11,45264,45264,11,45292,45292,11,45320,45320,11,45348,45348,11,45376,45376,11,45404,45404,11,45432,45432,11,45460,45460,11,45488,45488,11,45516,45516,11,45544,45544,11,45572,45572,11,45600,45600,11,45628,45628,11,45656,45656,11,45684,45684,11,45712,45712,11,45740,45740,11,45768,45768,11,45796,45796,11,45824,45824,11,45852,45852,11,45880,45880,11,45908,45908,11,45936,45936,11,45964,45964,11,45992,45992,11,46020,46020,11,46048,46048,11,46076,46076,11,46104,46104,11,46132,46132,11,46160,46160,11,46188,46188,11,46216,46216,11,46244,46244,11,46272,46272,11,46300,46300,11,46328,46328,11,46356,46356,11,46384,46384,11,46412,46412,11,46440,46440,11,46468,46468,11,46496,46496,11,46524,46524,11,46552,46552,11,46580,46580,11,46608,46608,11,46636,46636,11,46664,46664,11,46692,46692,11,46720,46720,11,46748,46748,11,46776,46776,11,46804,46804,11,46832,46832,11,46860,46860,11,46888,46888,11,46916,46916,11,46944,46944,11,46972,46972,11,47000,47000,11,47028,47028,11,47056,47056,11,47084,47084,11,47112,47112,11,47140,47140,11,47168,47168,11,47196,47196,11,47224,47224,11,47252,47252,11,47280,47280,11,47308,47308,11,47336,47336,11,47364,47364,11,47392,47392,11,47420,47420,11,47448,47448,11,47476,47476,11,47504,47504,11,47532,47532,11,47560,47560,11,47588,47588,11,47616,47616,11,47644,47644,11,47672,47672,11,47700,47700,11,47728,47728,11,47756,47756,11,47784,47784,11,47812,47812,11,47840,47840,11,47868,47868,11,47896,47896,11,47924,47924,11,47952,47952,11,47980,47980,11,48008,48008,11,48036,48036,11,48064,48064,11,48092,48092,11,48120,48120,11,48148,48148,11,48176,48176,11,48204,48204,11,48232,48232,11,48260,48260,11,48288,48288,11,48316,48316,11,48344,48344,11,48372,48372,11,48400,48400,11,48428,48428,11,48456,48456,11,48484,48484,11,48512,48512,11,48540,48540,11,48568,48568,11,48596,48596,11,48624,48624,11,48652,48652,11,48680,48680,11,48708,48708,11,48736,48736,11,48764,48764,11,48792,48792,11,48820,48820,11,48848,48848,11,48876,48876,11,48904,48904,11,48932,48932,11,48960,48960,11,48988,48988,11,49016,49016,11,49044,49044,11,49072,49072,11,49100,49100,11,49128,49128,11,49156,49156,11,49184,49184,11,49212,49212,11,49240,49240,11,49268,49268,11,49296,49296,11,49324,49324,11,49352,49352,11,49380,49380,11,49408,49408,11,49436,49436,11,49464,49464,11,49492,49492,11,49520,49520,11,49548,49548,11,49576,49576,11,49604,49604,11,49632,49632,11,49660,49660,11,49688,49688,11,49716,49716,11,49744,49744,11,49772,49772,11,49800,49800,11,49828,49828,11,49856,49856,11,49884,49884,11,49912,49912,11,49940,49940,11,49968,49968,11,49996,49996,11,50024,50024,11,50052,50052,11,50080,50080,11,50108,50108,11,50136,50136,11,50164,50164,11,50192,50192,11,50220,50220,11,50248,50248,11,50276,50276,11,50304,50304,11,50332,50332,11,50360,50360,11,50388,50388,11,50416,50416,11,50444,50444,11,50472,50472,11,50500,50500,11,50528,50528,11,50556,50556,11,50584,50584,11,50612,50612,11,50640,50640,11,50668,50668,11,50696,50696,11,50724,50724,11,50752,50752,11,50780,50780,11,50808,50808,11,50836,50836,11,50864,50864,11,50892,50892,11,50920,50920,11,50948,50948,11,50976,50976,11,51004,51004,11,51032,51032,11,51060,51060,11,51088,51088,11,51116,51116,11,51144,51144,11,51172,51172,11,51200,51200,11,51228,51228,11,51256,51256,11,51284,51284,11,51312,51312,11,51340,51340,11,51368,51368,11,51396,51396,11,51424,51424,11,51452,51452,11,51480,51480,11,51508,51508,11,51536,51536,11,51564,51564,11,51592,51592,11,51620,51620,11,51648,51648,11,51676,51676,11,51704,51704,11,51732,51732,11,51760,51760,11,51788,51788,11,51816,51816,11,51844,51844,11,51872,51872,11,51900,51900,11,51928,51928,11,51956,51956,11,51984,51984,11,52012,52012,11,52040,52040,11,52068,52068,11,52096,52096,11,52124,52124,11,52152,52152,11,52180,52180,11,52208,52208,11,52236,52236,11,52264,52264,11,52292,52292,11,52320,52320,11,52348,52348,11,52376,52376,11,52404,52404,11,52432,52432,11,52460,52460,11,52488,52488,11,52516,52516,11,52544,52544,11,52572,52572,11,52600,52600,11,52628,52628,11,52656,52656,11,52684,52684,11,52712,52712,11,52740,52740,11,52768,52768,11,52796,52796,11,52824,52824,11,52852,52852,11,52880,52880,11,52908,52908,11,52936,52936,11,52964,52964,11,52992,52992,11,53020,53020,11,53048,53048,11,53076,53076,11,53104,53104,11,53132,53132,11,53160,53160,11,53188,53188,11,53216,53216,11,53244,53244,11,53272,53272,11,53300,53300,11,53328,53328,11,53356,53356,11,53384,53384,11,53412,53412,11,53440,53440,11,53468,53468,11,53496,53496,11,53524,53524,11,53552,53552,11,53580,53580,11,53608,53608,11,53636,53636,11,53664,53664,11,53692,53692,11,53720,53720,11,53748,53748,11,53776,53776,11,53804,53804,11,53832,53832,11,53860,53860,11,53888,53888,11,53916,53916,11,53944,53944,11,53972,53972,11,54000,54000,11,54028,54028,11,54056,54056,11,54084,54084,11,54112,54112,11,54140,54140,11,54168,54168,11,54196,54196,11,54224,54224,11,54252,54252,11,54280,54280,11,54308,54308,11,54336,54336,11,54364,54364,11,54392,54392,11,54420,54420,11,54448,54448,11,54476,54476,11,54504,54504,11,54532,54532,11,54560,54560,11,54588,54588,11,54616,54616,11,54644,54644,11,54672,54672,11,54700,54700,11,54728,54728,11,54756,54756,11,54784,54784,11,54812,54812,11,54840,54840,11,54868,54868,11,54896,54896,11,54924,54924,11,54952,54952,11,54980,54980,11,55008,55008,11,55036,55036,11,55064,55064,11,55092,55092,11,55120,55120,11,55148,55148,11,55176,55176,11,55216,55238,9,64286,64286,5,65056,65071,5,65438,65439,5,65529,65531,4,66272,66272,5,68097,68099,5,68108,68111,5,68159,68159,5,68900,68903,5,69446,69456,5,69632,69632,7,69634,69634,7,69744,69744,5,69759,69761,5,69808,69810,7,69815,69816,7,69821,69821,1,69837,69837,1,69927,69931,5,69933,69940,5,70003,70003,5,70018,70018,7,70070,70078,5,70082,70083,1,70094,70094,7,70188,70190,7,70194,70195,7,70197,70197,7,70206,70206,5,70368,70370,7,70400,70401,5,70459,70460,5,70463,70463,7,70465,70468,7,70475,70477,7,70498,70499,7,70512,70516,5,70712,70719,5,70722,70724,5,70726,70726,5,70832,70832,5,70835,70840,5,70842,70842,5,70845,70845,5,70847,70848,5,70850,70851,5,71088,71089,7,71096,71099,7,71102,71102,7,71132,71133,5,71219,71226,5,71229,71229,5,71231,71232,5,71340,71340,7,71342,71343,7,71350,71350,7,71453,71455,5,71462,71462,7,71724,71726,7,71736,71736,7,71984,71984,5,71991,71992,7,71997,71997,7,71999,71999,1,72001,72001,1,72003,72003,5,72148,72151,5,72156,72159,7,72164,72164,7,72243,72248,5,72250,72250,1,72263,72263,5,72279,72280,7,72324,72329,1,72343,72343,7,72751,72751,7,72760,72765,5,72767,72767,5,72873,72873,7,72881,72881,7,72884,72884,7,73009,73014,5,73020,73021,5,73030,73030,1,73098,73102,7,73107,73108,7,73110,73110,7,73459,73460,5,78896,78904,4,92976,92982,5,94033,94087,7,94180,94180,5,113821,113822,5,118528,118573,5,119141,119141,5,119143,119145,5,119150,119154,5,119163,119170,5,119210,119213,5,121344,121398,5,121461,121461,5,121499,121503,5,122880,122886,5,122907,122913,5,122918,122922,5,123566,123566,5,125136,125142,5,126976,126979,14,126981,127182,14,127184,127231,14,127279,127279,14,127344,127345,14,127374,127374,14,127405,127461,14,127489,127490,14,127514,127514,14,127538,127546,14,127561,127567,14,127570,127743,14,127757,127758,14,127760,127760,14,127762,127762,14,127766,127768,14,127770,127770,14,127772,127772,14,127775,127776,14,127778,127779,14,127789,127791,14,127794,127795,14,127798,127798,14,127819,127819,14,127824,127824,14,127868,127868,14,127870,127871,14,127892,127893,14,127896,127896,14,127900,127901,14,127904,127940,14,127942,127942,14,127944,127944,14,127946,127946,14,127951,127955,14,127968,127971,14,127973,127984,14,127987,127987,14,127989,127989,14,127991,127991,14,127995,127999,5,128008,128008,14,128012,128014,14,128017,128018,14,128020,128020,14,128022,128022,14,128042,128042,14,128063,128063,14,128065,128065,14,128101,128101,14,128108,128109,14,128173,128173,14,128182,128183,14,128236,128237,14,128239,128239,14,128245,128245,14,128248,128248,14,128253,128253,14,128255,128258,14,128260,128263,14,128265,128265,14,128277,128277,14,128300,128301,14,128326,128328,14,128331,128334,14,128336,128347,14,128360,128366,14,128369,128370,14,128378,128378,14,128391,128391,14,128394,128397,14,128400,128400,14,128405,128406,14,128420,128420,14,128422,128423,14,128425,128432,14,128435,128443,14,128445,128449,14,128453,128464,14,128468,128475,14,128479,128480,14,128482,128482,14,128484,128487,14,128489,128494,14,128496,128498,14,128500,128505,14,128507,128511,14,128513,128518,14,128521,128525,14,128527,128527,14,128529,128529,14,128533,128533,14,128535,128535,14,128537,128537,14]');
1152 }
1153 > strings.ts
1154 > //#endregion
1155 >
1156 > /**
1157 > * Computes the offset after performing a left delete on the given string,
1158 > * while considering unicode grapheme/emoji rules.
1159 > */
1160 > export function getLeftDeleteOffset(offset: number, str: string): number {
1161 if (offset === 0) {
1162 return 0;
1174 return iterator.offset;
1175 }
1176 > strings.ts
1177 function getOffsetBeforeLastEmojiComponent(initialOffset: number, str: string): number | undefined {
1178 // See https://www.unicode.org/reports/tr51/tr51-14.html#EBNF_and_Regex for the
1210 return resultOffset;
1211 }
1212 > strings.ts
1213 function isEmojiModifier(codePoint: number): boolean {
1214 return 0x1F3FB <= codePoint && codePoint <= 0x1F3FF;
1215 }
1216 > strings.ts
1217 > const enum CodePoint {
1218 > zwj = 0x200D,
1219 >
1220 > /**
1221 > * Variation Selector-16 (VS16)
1222 > */
1223 > emojiVariantSelector = 0xFE0F,
1224 >
1225 > /**
1226 > * Combining Enclosing Keycap
1227 > */
1228 > enclosingKeyCap = 0x20E3,
1229 >
1230 > space = 0x0020,
1231 > }
1232 >
1233 > export const noBreakWhitespace = '\xa0';
1234 >
1235 > export class AmbiguousCharacters {
1236 > private static readonly ambiguousCharacterData = new Lazy<
1237 > Record<
1238 > string | '_common' | '_default',
1239 > /* code point -> ascii code point */ number[]
1240 > >
1241 > >(() => {
1242 // Generated using https://github.com/hediet/vscode-unicode-data
1243 // Stored as key1, value1, key2, value2, ...
1245 '{\"_common\":[8232,32,8233,32,5760,32,8192,32,8193,32,8194,32,8195,32,8196,32,8197,32,8198,32,8200,32,8201,32,8202,32,8287,32,8199,32,8239,32,2042,95,65101,95,65102,95,65103,95,8208,45,8209,45,8210,45,65112,45,1748,45,8259,45,727,45,8722,45,10134,45,11450,45,1549,44,1643,44,184,44,42233,44,894,59,2307,58,2691,58,1417,58,1795,58,1796,58,5868,58,65072,58,6147,58,6153,58,8282,58,1475,58,760,58,42889,58,8758,58,720,58,42237,58,451,33,11601,33,660,63,577,63,2429,63,5038,63,42731,63,119149,46,8228,46,1793,46,1794,46,42510,46,68176,46,1632,46,1776,46,42232,46,1373,96,65287,96,8219,96,1523,96,8242,96,1370,96,8175,96,65344,96,900,96,8189,96,8125,96,8127,96,8190,96,697,96,884,96,712,96,714,96,715,96,756,96,699,96,701,96,700,96,702,96,42892,96,1497,96,2036,96,2037,96,5194,96,5836,96,94033,96,94034,96,65339,91,10088,40,10098,40,12308,40,64830,40,65341,93,10089,41,10099,41,12309,41,64831,41,10100,123,119060,123,10101,125,65342,94,8270,42,1645,42,8727,42,66335,42,5941,47,8257,47,8725,47,8260,47,9585,47,10187,47,10744,47,119354,47,12755,47,12339,47,11462,47,20031,47,12035,47,65340,92,65128,92,8726,92,10189,92,10741,92,10745,92,119311,92,119355,92,12756,92,20022,92,12034,92,42872,38,708,94,710,94,5869,43,10133,43,66203,43,8249,60,10094,60,706,60,119350,60,5176,60,5810,60,5120,61,11840,61,12448,61,42239,61,8250,62,10095,62,707,62,119351,62,5171,62,94015,62,8275,126,732,126,8128,126,8764,126,65372,124,65293,45,118002,50,120784,50,120794,50,120804,50,120814,50,120824,50,130034,50,42842,50,423,50,1000,50,42564,50,5311,50,42735,50,119302,51,118003,51,120785,51,120795,51,120805,51,120815,51,120825,51,130035,51,42923,51,540,51,439,51,42858,51,11468,51,1248,51,94011,51,71882,51,118004,52,120786,52,120796,52,120806,52,120816,52,120826,52,130036,52,5070,52,71855,52,118005,53,120787,53,120797,53,120807,53,120817,53,120827,53,130037,53,444,53,71867,53,118006,54,120788,54,120798,54,120808,54,120818,54,120828,54,130038,54,11474,54,5102,54,71893,54,119314,55,118007,55,120789,55,120799,55,120809,55,120819,55,120829,55,130039,55,66770,55,71878,55,2819,56,2538,56,2666,56,125131,56,118008,56,120790,56,120800,56,120810,56,120820,56,120830,56,130040,56,547,56,546,56,66330,56,2663,57,2920,57,2541,57,3437,57,118009,57,120791,57,120801,57,120811,57,120821,57,120831,57,130041,57,42862,57,11466,57,71884,57,71852,57,71894,57,9082,97,65345,97,119834,97,119886,97,119938,97,119990,97,120042,97,120094,97,120146,97,120198,97,120250,97,120302,97,120354,97,120406,97,120458,97,593,97,945,97,120514,97,120572,97,120630,97,120688,97,120746,97,65313,65,117974,65,119808,65,119860,65,119912,65,119964,65,120016,65,120068,65,120120,65,120172,65,120224,65,120276,65,120328,65,120380,65,120432,65,913,65,120488,65,120546,65,120604,65,120662,65,120720,65,5034,65,5573,65,42222,65,94016,65,66208,65,119835,98,119887,98,119939,98,119991,98,120043,98,120095,98,120147,98,120199,98,120251,98,120303,98,120355,98,120407,98,120459,98,388,98,5071,98,5234,98,5551,98,65314,66,8492,66,117975,66,119809,66,119861,66,119913,66,120017,66,120069,66,120121,66,120173,66,120225,66,120277,66,120329,66,120381,66,120433,66,42932,66,914,66,120489,66,120547,66,120605,66,120663,66,120721,66,5108,66,5623,66,42192,66,66178,66,66209,66,66305,66,65347,99,8573,99,119836,99,119888,99,119940,99,119992,99,120044,99,120096,99,120148,99,120200,99,120252,99,120304,99,120356,99,120408,99,120460,99,7428,99,1010,99,11429,99,43951,99,66621,99,128844,67,71913,67,71922,67,65315,67,8557,67,8450,67,8493,67,117976,67,119810,67,119862,67,119914,67,119966,67,120018,67,120174,67,120226,67,120278,67,120330,67,120382,67,120434,67,1017,67,11428,67,5087,67,42202,67,66210,67,66306,67,66581,67,66844,67,8574,100,8518,100,119837,100,119889,100,119941,100,119993,100,120045,100,120097,100,120149,100,120201,100,120253,100,120305,100,120357,100,120409,100,120461,100,1281,100,5095,100,5231,100,42194,100,8558,68,8517,68,117977,68,119811,68,119863,68,119915,68,119967,68,120019,68,120071,68,120123,68,120175,68,120227,68,120279,68,120331,68,120383,68,120435,68,5024,68,5598,68,5610,68,42195,68,8494,101,65349,101,8495,101,8519,101,119838,101,119890,101,119942,101,120046,101,120098,101,120150,101,120202,101,120254,101,120306,101,120358,101,120410,101,120462,101,43826,101,1213,101,8959,69,65317,69,8496,69,117978,69,119812,69,119864,69,119916,69,120020,69,120072,69,120124,69,120176,69,120228,69,120280,69,120332,69,120384,69,120436,69,917,69,120492,69,120550,69,120608,69,120666,69,120724,69,11577,69,5036,69,42224,69,71846,69,71854,69,66182,69,119839,102,119891,102,119943,102,119995,102,120047,102,120099,102,120151,102,120203,102,120255,102,120307,102,120359,102,120411,102,120463,102,43829,102,42905,102,383,102,7837,102,1412,102,119315,70,8497,70,117979,70,119813,70,119865,70,119917,70,120021,70,120073,70,120125,70,120177,70,120229,70,120281,70,120333,70,120385,70,120437,70,42904,70,988,70,120778,70,5556,70,42205,70,71874,70,71842,70,66183,70,66213,70,66853,70,65351,103,8458,103,119840,103,119892,103,119944,103,120048,103,120100,103,120152,103,120204,103,120256,103,120308,103,120360,103,120412,103,120464,103,609,103,7555,103,397,103,1409,103,117980,71,119814,71,119866,71,119918,71,119970,71,120022,71,120074,71,120126,71,120178,71,120230,71,120282,71,120334,71,120386,71,120438,71,1292,71,5056,71,5107,71,42198,71,65352,104,8462,104,119841,104,119945,104,119997,104,120049,104,120101,104,120153,104,120205,104,120257,104,120309,104,120361,104,120413,104,120465,104,1211,104,1392,104,5058,104,65320,72,8459,72,8460,72,8461,72,117981,72,119815,72,119867,72,119919,72,120023,72,120179,72,120231,72,120283,72,120335,72,120387,72,120439,72,919,72,120494,72,120552,72,120610,72,120668,72,120726,72,11406,72,5051,72,5500,72,42215,72,66255,72,731,105,9075,105,65353,105,8560,105,8505,105,8520,105,119842,105,119894,105,119946,105,119998,105,120050,105,120102,105,120154,105,120206,105,120258,105,120310,105,120362,105,120414,105,120466,105,120484,105,618,105,617,105,953,105,8126,105,890,105,120522,105,120580,105,120638,105,120696,105,120754,105,1110,105,42567,105,1231,105,43893,105,5029,105,71875,105,65354,106,8521,106,119843,106,119895,106,119947,106,119999,106,120051,106,120103,106,120155,106,120207,106,120259,106,120311,106,120363,106,120415,106,120467,106,1011,106,1112,106,65322,74,117983,74,119817,74,119869,74,119921,74,119973,74,120025,74,120077,74,120129,74,120181,74,120233,74,120285,74,120337,74,120389,74,120441,74,42930,74,895,74,1032,74,5035,74,5261,74,42201,74,119844,107,119896,107,119948,107,120000,107,120052,107,120104,107,120156,107,120208,107,120260,107,120312,107,120364,107,120416,107,120468,107,8490,75,65323,75,117984,75,119818,75,119870,75,119922,75,119974,75,120026,75,120078,75,120130,75,120182,75,120234,75,120286,75,120338,75,120390,75,120442,75,922,75,120497,75,120555,75,120613,75,120671,75,120729,75,11412,75,5094,75,5845,75,42199,75,66840,75,1472,108,8739,73,9213,73,65512,73,1633,108,1777,73,66336,108,125127,108,118001,108,120783,73,120793,73,120803,73,120813,73,120823,73,130033,73,65321,73,8544,73,8464,73,8465,73,117982,108,119816,73,119868,73,119920,73,120024,73,120128,73,120180,73,120232,73,120284,73,120336,73,120388,73,120440,73,65356,108,8572,73,8467,108,119845,108,119897,108,119949,108,120001,108,120053,108,120105,73,120157,73,120209,73,120261,73,120313,73,120365,73,120417,73,120469,73,448,73,120496,73,120554,73,120612,73,120670,73,120728,73,11410,73,1030,73,1216,73,1493,108,1503,108,1575,108,126464,108,126592,108,65166,108,65165,108,1994,108,11599,73,5825,73,42226,73,93992,73,66186,124,66313,124,119338,76,8556,76,8466,76,117985,76,119819,76,119871,76,119923,76,120027,76,120079,76,120131,76,120183,76,120235,76,120287,76,120339,76,120391,76,120443,76,11472,76,5086,76,5290,76,42209,76,93974,76,71843,76,71858,76,66587,76,66854,76,65325,77,8559,77,8499,77,117986,77,119820,77,119872,77,119924,77,120028,77,120080,77,120132,77,120184,77,120236,77,120288,77,120340,77,120392,77,120444,77,924,77,120499,77,120557,77,120615,77,120673,77,120731,77,1018,77,11416,77,5047,77,5616,77,5846,77,42207,77,66224,77,66321,77,119847,110,119899,110,119951,110,120003,110,120055,110,120107,110,120159,110,120211,110,120263,110,120315,110,120367,110,120419,110,120471,110,1400,110,1404,110,65326,78,8469,78,117987,78,119821,78,119873,78,119925,78,119977,78,120029,78,120081,78,120185,78,120237,78,120289,78,120341,78,120393,78,120445,78,925,78,120500,78,120558,78,120616,78,120674,78,120732,78,11418,78,42208,78,66835,78,3074,111,3202,111,3330,111,3458,111,2406,111,2662,111,2790,111,3046,111,3174,111,3302,111,3430,111,3664,111,3792,111,4160,111,1637,111,1781,111,65359,111,8500,111,119848,111,119900,111,119952,111,120056,111,120108,111,120160,111,120212,111,120264,111,120316,111,120368,111,120420,111,120472,111,7439,111,7441,111,43837,111,959,111,120528,111,120586,111,120644,111,120702,111,120760,111,963,111,120532,111,120590,111,120648,111,120706,111,120764,111,11423,111,4351,111,1413,111,1505,111,1607,111,126500,111,126564,111,126596,111,65259,111,65260,111,65258,111,65257,111,1726,111,64428,111,64429,111,64427,111,64426,111,1729,111,64424,111,64425,111,64423,111,64422,111,1749,111,3360,111,4125,111,66794,111,71880,111,71895,111,66604,111,1984,79,2534,79,2918,79,12295,79,70864,79,71904,79,118000,79,120782,79,120792,79,120802,79,120812,79,120822,79,130032,79,65327,79,117988,79,119822,79,119874,79,119926,79,119978,79,120030,79,120082,79,120134,79,120186,79,120238,79,120290,79,120342,79,120394,79,120446,79,927,79,120502,79,120560,79,120618,79,120676,79,120734,79,11422,79,1365,79,11604,79,4816,79,2848,79,66754,79,42227,79,71861,79,66194,79,66219,79,66564,79,66838,79,9076,112,65360,112,119849,112,119901,112,119953,112,120005,112,120057,112,120109,112,120161,112,120213,112,120265,112,120317,112,120369,112,120421,112,120473,112,961,112,120530,112,120544,112,120588,112,120602,112,120646,112,120660,112,120704,112,120718,112,120762,112,120776,112,11427,112,65328,80,8473,80,117989,80,119823,80,119875,80,119927,80,119979,80,120031,80,120083,80,120187,80,120239,80,120291,80,120343,80,120395,80,120447,80,929,80,120504,80,120562,80,120620,80,120678,80,120736,80,11426,80,5090,80,5229,80,42193,80,66197,80,119850,113,119902,113,119954,113,120006,113,120058,113,120110,113,120162,113,120214,113,120266,113,120318,113,120370,113,120422,113,120474,113,1307,113,1379,113,1382,113,8474,81,117990,81,119824,81,119876,81,119928,81,119980,81,120032,81,120084,81,120188,81,120240,81,120292,81,120344,81,120396,81,120448,81,11605,81,119851,114,119903,114,119955,114,120007,114,120059,114,120111,114,120163,114,120215,114,120267,114,120319,114,120371,114,120423,114,120475,114,43847,114,43848,114,7462,114,11397,114,43905,114,119318,82,8475,82,8476,82,8477,82,117991,82,119825,82,119877,82,119929,82,120033,82,120189,82,120241,82,120293,82,120345,82,120397,82,120449,82,422,82,5025,82,5074,82,66740,82,5511,82,42211,82,94005,82,65363,115,119852,115,119904,115,119956,115,120008,115,120060,115,120112,115,120164,115,120216,115,120268,115,120320,115,120372,115,120424,115,120476,115,42801,115,445,115,1109,115,43946,115,71873,115,66632,115,65331,83,117992,83,119826,83,119878,83,119930,83,119982,83,120034,83,120086,83,120138,83,120190,83,120242,83,120294,83,120346,83,120398,83,120450,83,1029,83,1359,83,5077,83,5082,83,42210,83,94010,83,66198,83,66592,83,119853,116,119905,116,119957,116,120009,116,120061,116,120113,116,120165,116,120217,116,120269,116,120321,116,120373,116,120425,116,120477,116,8868,84,10201,84,128872,84,65332,84,117993,84,119827,84,119879,84,119931,84,119983,84,120035,84,120087,84,120139,84,120191,84,120243,84,120295,84,120347,84,120399,84,120451,84,932,84,120507,84,120565,84,120623,84,120681,84,120739,84,11430,84,5026,84,42196,84,93962,84,71868,84,66199,84,66225,84,66325,84,119854,117,119906,117,119958,117,120010,117,120062,117,120114,117,120166,117,120218,117,120270,117,120322,117,120374,117,120426,117,120478,117,42911,117,7452,117,43854,117,43858,117,651,117,965,117,120534,117,120592,117,120650,117,120708,117,120766,117,1405,117,66806,117,71896,117,8746,85,8899,85,117994,85,119828,85,119880,85,119932,85,119984,85,120036,85,120088,85,120140,85,120192,85,120244,85,120296,85,120348,85,120400,85,120452,85,1357,85,4608,85,66766,85,5196,85,42228,85,94018,85,71864,85,8744,118,8897,118,65366,118,8564,118,119855,118,119907,118,119959,118,120011,118,120063,118,120115,118,120167,118,120219,118,120271,118,120323,118,120375,118,120427,118,120479,118,7456,118,957,118,120526,118,120584,118,120642,118,120700,118,120758,118,1141,118,1496,118,71430,118,43945,118,71872,118,119309,86,1639,86,1783,86,8548,86,117995,86,119829,86,119881,86,119933,86,119985,86,120037,86,120089,86,120141,86,120193,86,120245,86,120297,86,120349,86,120401,86,120453,86,1140,86,11576,86,5081,86,5167,86,42719,86,42214,86,93960,86,71840,86,66845,86,623,119,119856,119,119908,119,119960,119,120012,119,120064,119,120116,119,120168,119,120220,119,120272,119,120324,119,120376,119,120428,119,120480,119,7457,119,1121,119,1309,119,1377,119,71434,119,71438,119,71439,119,43907,119,71910,87,71919,87,117996,87,119830,87,119882,87,119934,87,119986,87,120038,87,120090,87,120142,87,120194,87,120246,87,120298,87,120350,87,120402,87,120454,87,1308,87,5043,87,5076,87,42218,87,5742,120,10539,120,10540,120,10799,120,65368,120,8569,120,119857,120,119909,120,119961,120,120013,120,120065,120,120117,120,120169,120,120221,120,120273,120,120325,120,120377,120,120429,120,120481,120,5441,120,5501,120,5741,88,9587,88,66338,88,71916,88,65336,88,8553,88,117997,88,119831,88,119883,88,119935,88,119987,88,120039,88,120091,88,120143,88,120195,88,120247,88,120299,88,120351,88,120403,88,120455,88,42931,88,935,88,120510,88,120568,88,120626,88,120684,88,120742,88,11436,88,11613,88,5815,88,42219,88,66192,88,66228,88,66327,88,66855,88,611,121,7564,121,65369,121,119858,121,119910,121,119962,121,120014,121,120066,121,120118,121,120170,121,120222,121,120274,121,120326,121,120378,121,120430,121,120482,121,655,121,7935,121,43866,121,947,121,8509,121,120516,121,120574,121,120632,121,120690,121,120748,121,1199,121,4327,121,71900,121,65337,89,117998,89,119832,89,119884,89,119936,89,119988,89,120040,89,120092,89,120144,89,120196,89,120248,89,120300,89,120352,89,120404,89,120456,89,933,89,978,89,120508,89,120566,89,120624,89,120682,89,120740,89,11432,89,1198,89,5033,89,5053,89,42220,89,94019,89,71844,89,66226,89,119859,122,119911,122,119963,122,120015,122,120067,122,120119,122,120171,122,120223,122,120275,122,120327,122,120379,122,120431,122,120483,122,7458,122,43923,122,71876,122,71909,90,66293,90,65338,90,8484,90,8488,90,117999,90,119833,90,119885,90,119937,90,119989,90,120041,90,120197,90,120249,90,120301,90,120353,90,120405,90,120457,90,918,90,120493,90,120551,90,120609,90,120667,90,120725,90,5059,90,42204,90,71849,90,65282,34,65283,35,65284,36,65285,37,65286,38,65290,42,65291,43,65294,46,65295,47,65296,48,65298,50,65299,51,65300,52,65301,53,65302,54,65303,55,65304,56,65305,57,65308,60,65309,61,65310,62,65312,64,65316,68,65318,70,65319,71,65324,76,65329,81,65330,82,65333,85,65334,86,65335,87,65343,95,65346,98,65348,100,65350,102,65355,107,65357,109,65358,110,65361,113,65362,114,65364,116,65365,117,65367,119,65370,122,65371,123,65373,125,119846,109],\"_default\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"cs\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"de\":[65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"es\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"fr\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"it\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ja\":[8211,45,8218,44,65281,33,8216,96,8245,96,180,96,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65292,44,65297,49,65307,59],\"ko\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pl\":[65374,126,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pt-BR\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"qps-ploc\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ru\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,305,105,921,73,1009,112,215,120,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"tr\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"zh-hans\":[160,32,65374,126,8218,44,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65297,49],\"zh-hant\":[8211,45,65374,126,8218,44,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89]}'
1246 );
1247 > }); strings.ts
1248 >
1249 > private static readonly cache = new LRUCachedFunction<string, AmbiguousCharacters>((localesStr) => {
1250 const locales = localesStr.split(',');
1251
1304
1305 return new AmbiguousCharacters(map);
1306 > }); strings.ts
1307 >
1308 > public static getInstance(locales: Iterable<string>): AmbiguousCharacters {
1309 return AmbiguousCharacters.cache.get(Array.from(locales).join(','));
1310 }
1311 > strings.ts
1312 > private static _locales = new Lazy<string[]>(() =>
1313 Object.keys(AmbiguousCharacters.ambiguousCharacterData.value).filter(
1314 (k) => !k.startsWith('_')
1315 )
1316 > ); strings.ts
1317 > public static getLocales(): string[] {
1318 return AmbiguousCharacters._locales.value;
1319 }
1320 > strings.ts
1321 > private constructor(
1322 private readonly confusableDictionary: Map<number, number>
1323 ) { }
1324 > strings.ts
1325 > public isAmbiguous(codePoint: number): boolean {
1326 return this.confusableDictionary.has(codePoint);
1327 }
1328 > strings.ts
1329 > public containsAmbiguousCharacter(str: string): boolean {
1330 for (let i = 0; i < str.length; i++) {
1331 const codePoint = str.codePointAt(i);
1336 return false;
1337 }
1338 > strings.ts
1339 > /**
1340 > * Returns the non basic ASCII code point that the given code point can be confused,
1341 > * or undefined if such code point does note exist.
1342 > */
1343 > public getPrimaryConfusable(codePoint: number): number | undefined {
1344 return this.confusableDictionary.get(codePoint);
1345 }
1346 > strings.ts
1347 > public getConfusableCodePoints(): ReadonlySet<number> {
1348 return new Set(this.confusableDictionary.keys());
1349 }
1350 > } strings.ts
1351 >
1352 > export class InvisibleCharacters {
1353 > private static getRawData(): Record<string | '_common', number[]> {
1354 // Generated using https://github.com/hediet/vscode-unicode-data
1355 return JSON.parse('{\"_common\":[11,12,13,127,847,1564,4447,4448,6068,6069,6155,6156,6157,6158,7355,7356,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8204,8205,8206,8207,8234,8235,8236,8237,8238,8239,8287,8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,10240,12644,65024,65025,65026,65027,65028,65029,65030,65031,65032,65033,65034,65035,65036,65037,65038,65039,65279,65440,65520,65521,65522,65523,65524,65525,65526,65527,65528,65532,78844,119155,119156,119157,119158,119159,119160,119161,119162,917504,917505,917506,917507,917508,917509,917510,917511,917512,917513,917514,917515,917516,917517,917518,917519,917520,917521,917522,917523,917524,917525,917526,917527,917528,917529,917530,917531,917532,917533,917534,917535,917536,917537,917538,917539,917540,917541,917542,917543,917544,917545,917546,917547,917548,917549,917550,917551,917552,917553,917554,917555,917556,917557,917558,917559,917560,917561,917562,917563,917564,917565,917566,917567,917568,917569,917570,917571,917572,917573,917574,917575,917576,917577,917578,917579,917580,917581,917582,917583,917584,917585,917586,917587,917588,917589,917590,917591,917592,917593,917594,917595,917596,917597,917598,917599,917600,917601,917602,917603,917604,917605,917606,917607,917608,917609,917610,917611,917612,917613,917614,917615,917616,917617,917618,917619,917620,917621,917622,917623,917624,917625,917626,917627,917628,917629,917630,917631,917760,917761,917762,917763,917764,917765,917766,917767,917768,917769,917770,917771,917772,917773,917774,917775,917776,917777,917778,917779,917780,917781,917782,917783,917784,917785,917786,917787,917788,917789,917790,917791,917792,917793,917794,917795,917796,917797,917798,917799,917800,917801,917802,917803,917804,917805,917806,917807,917808,917809,917810,917811,917812,917813,917814,917815,917816,917817,917818,917819,917820,917821,917822,917823,917824,917825,917826,917827,917828,917829,917830,917831,917832,917833,917834,917835,917836,917837,917838,917839,917840,917841,917842,917843,917844,917845,917846,917847,917848,917849,917850,917851,917852,917853,917854,917855,917856,917857,917858,917859,917860,917861,917862,917863,917864,917865,917866,917867,917868,917869,917870,917871,917872,917873,917874,917875,917876,917877,917878,917879,917880,917881,917882,917883,917884,917885,917886,917887,917888,917889,917890,917891,917892,917893,917894,917895,917896,917897,917898,917899,917900,917901,917902,917903,917904,917905,917906,917907,917908,917909,917910,917911,917912,917913,917914,917915,917916,917917,917918,917919,917920,917921,917922,917923,917924,917925,917926,917927,917928,917929,917930,917931,917932,917933,917934,917935,917936,917937,917938,917939,917940,917941,917942,917943,917944,917945,917946,917947,917948,917949,917950,917951,917952,917953,917954,917955,917956,917957,917958,917959,917960,917961,917962,917963,917964,917965,917966,917967,917968,917969,917970,917971,917972,917973,917974,917975,917976,917977,917978,917979,917980,917981,917982,917983,917984,917985,917986,917987,917988,917989,917990,917991,917992,917993,917994,917995,917996,917997,917998,917999],\"cs\":[173,8203,12288],\"de\":[173,8203,12288],\"es\":[8203,12288],\"fr\":[173,8203,12288],\"it\":[160,173,12288],\"ja\":[173],\"ko\":[173,12288],\"pl\":[173,8203,12288],\"pt-BR\":[173,8203,12288],\"qps-ploc\":[160,173,8203,12288],\"ru\":[173,12288],\"tr\":[160,173,8203,12288],\"zh-hans\":[160,173,8203,12288],\"zh-hant\":[173,12288]}');
1356 }
1357 > strings.ts
1358 > private static _data: Set<number> | undefined = undefined;
1359 >
1360 > private static getData() {
1361 if (!this._data) {
1362 this._data = new Set([...Object.values(InvisibleCharacters.getRawData())].flat());
1364 return this._data;
1365 }
1366 > strings.ts
1367 > public static isInvisibleCharacter(codePoint: number): boolean {
1368 return InvisibleCharacters.getData().has(codePoint);
1369 }
1370 > strings.ts
1371 > public static containsInvisibleCharacter(str: string): boolean {
1372 for (let i = 0; i < str.length; i++) {
1373 const codePoint = str.codePointAt(i);
1378 return false;
1379 }
1380 > strings.ts
1381 > public static get codePoints(): ReadonlySet<number> {
1382 return InvisibleCharacters.getData();
1383 }
1384 > } strings.ts
1385 >
1386 > export const Ellipsis = '\u2026';
1387 >
1388 > /**
1389 > * Convert a Unicode string to a string in which each 16-bit unit occupies only one byte
1390 > *
1391 > * From https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa
1392 > */
1393 function toBinary(str: string): string {
1394 const codeUnits = new Uint16Array(str.length);
1403 return binary;
1404 }
1405 > strings.ts
1406 > /**
1407 > * Version of the global `btoa` function that handles multi-byte characters instead
1408 > * of throwing an exception.
1409 > */
1410 >
1411 > export function multibyteAwareBtoa(str: string): string {
1412 return btoa(toBinary(str));
1413 }
src/vs/base/common/charCode.ts 450 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- charCode.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/
7 >
8 > /**
9 > * An inlined enum containing useful character codes (to be used with String.charCodeAt).
10 > * Please leave the const keyword such that it gets inlined when compiled to JavaScript!
11 > */
12 > export const enum CharCode {
13 > Null = 0,
14 > /**
15 > * The `\b` character.
16 > */
17 > Backspace = 8,
18 > /**
19 > * The `\t` character.
20 > */
21 > Tab = 9,
22 > /**
23 > * The `\n` character.
24 > */
25 > LineFeed = 10,
26 > /**
27 > * The `\r` character.
28 > */
29 > CarriageReturn = 13,
30 > Space = 32,
31 > /**
32 > * The `!` character.
33 > */
34 > ExclamationMark = 33,
35 > /**
36 > * The `"` character.
37 > */
38 > DoubleQuote = 34,
39 > /**
40 > * The `#` character.
41 > */
42 > Hash = 35,
43 > /**
44 > * The `$` character.
45 > */
46 > DollarSign = 36,
47 > /**
48 > * The `%` character.
49 > */
50 > PercentSign = 37,
51 > /**
52 > * The `&` character.
53 > */
54 > Ampersand = 38,
55 > /**
56 > * The `'` character.
57 > */
58 > SingleQuote = 39,
59 > /**
60 > * The `(` character.
61 > */
62 > OpenParen = 40,
63 > /**
64 > * The `)` character.
65 > */
66 > CloseParen = 41,
67 > /**
68 > * The `*` character.
69 > */
70 > Asterisk = 42,
71 > /**
72 > * The `+` character.
73 > */
74 > Plus = 43,
75 > /**
76 > * The `,` character.
77 > */
78 > Comma = 44,
79 > /**
80 > * The `-` character.
81 > */
82 > Dash = 45,
83 > /**
84 > * The `.` character.
85 > */
86 > Period = 46,
87 > /**
88 > * The `/` character.
89 > */
90 > Slash = 47,
91 >
92 > Digit0 = 48,
93 > Digit1 = 49,
94 > Digit2 = 50,
95 > Digit3 = 51,
96 > Digit4 = 52,
97 > Digit5 = 53,
98 > Digit6 = 54,
99 > Digit7 = 55,
100 > Digit8 = 56,
101 > Digit9 = 57,
102 >
103 > /**
104 > * The `:` character.
105 > */
106 > Colon = 58,
107 > /**
108 > * The `;` character.
109 > */
110 > Semicolon = 59,
111 > /**
112 > * The `<` character.
113 > */
114 > LessThan = 60,
115 > /**
116 > * The `=` character.
117 > */
118 > Equals = 61,
119 > /**
120 > * The `>` character.
121 > */
122 > GreaterThan = 62,
123 > /**
124 > * The `?` character.
125 > */
126 > QuestionMark = 63,
127 > /**
128 > * The `@` character.
129 > */
130 > AtSign = 64,
131 >
132 > A = 65,
133 > B = 66,
134 > C = 67,
135 > D = 68,
136 > E = 69,
137 > F = 70,
138 > G = 71,
139 > H = 72,
140 > I = 73,
141 > J = 74,
142 > K = 75,
143 > L = 76,
144 > M = 77,
145 > N = 78,
146 > O = 79,
147 > P = 80,
148 > Q = 81,
149 > R = 82,
150 > S = 83,
151 > T = 84,
152 > U = 85,
153 > V = 86,
154 > W = 87,
155 > X = 88,
156 > Y = 89,
157 > Z = 90,
158 >
159 > /**
160 > * The `[` character.
161 > */
162 > OpenSquareBracket = 91,
163 > /**
164 > * The `\` character.
165 > */
166 > Backslash = 92,
167 > /**
168 > * The `]` character.
169 > */
170 > CloseSquareBracket = 93,
171 > /**
172 > * The `^` character.
173 > */
174 > Caret = 94,
175 > /**
176 > * The `_` character.
177 > */
178 > Underline = 95,
179 > /**
180 > * The ``(`)`` character.
181 > */
182 > BackTick = 96,
183 >
184 > a = 97,
185 > b = 98,
186 > c = 99,
187 > d = 100,
188 > e = 101,
189 > f = 102,
190 > g = 103,
191 > h = 104,
192 > i = 105,
193 > j = 106,
194 > k = 107,
195 > l = 108,
196 > m = 109,
197 > n = 110,
198 > o = 111,
199 > p = 112,
200 > q = 113,
201 > r = 114,
202 > s = 115,
203 > t = 116,
204 > u = 117,
205 > v = 118,
206 > w = 119,
207 > x = 120,
208 > y = 121,
209 > z = 122,
210 >
211 > /**
212 > * The `{` character.
213 > */
214 > OpenCurlyBrace = 123,
215 > /**
216 > * The `|` character.
217 > */
218 > Pipe = 124,
219 > /**
220 > * The `}` character.
221 > */
222 > CloseCurlyBrace = 125,
223 > /**
224 > * The `~` character.
225 > */
226 > Tilde = 126,
227 >
228 > /**
229 > * The &nbsp; (no-break space) character.
230 > * Unicode Character 'NO-BREAK SPACE' (U+00A0)
231 > */
232 > NoBreakSpace = 160,
233 >
234 > U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent
235 > U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent
236 > U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent
237 > U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde
238 > U_Combining_Macron = 0x0304, // U+0304 Combining Macron
239 > U_Combining_Overline = 0x0305, // U+0305 Combining Overline
240 > U_Combining_Breve = 0x0306, // U+0306 Combining Breve
241 > U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above
242 > U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis
243 > U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above
244 > U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above
245 > U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent
246 > U_Combining_Caron = 0x030C, // U+030C Combining Caron
247 > U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above
248 > U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above
249 > U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent
250 > U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu
251 > U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve
252 > U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above
253 > U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above
254 > U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above
255 > U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right
256 > U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below
257 > U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below
258 > U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below
259 > U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below
260 > U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above
261 > U_Combining_Horn = 0x031B, // U+031B Combining Horn
262 > U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below
263 > U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below
264 > U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below
265 > U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below
266 > U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below
267 > U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below
268 > U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below
269 > U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below
270 > U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below
271 > U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below
272 > U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below
273 > U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla
274 > U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek
275 > U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below
276 > U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below
277 > U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below
278 > U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below
279 > U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below
280 > U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below
281 > U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below
282 > U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below
283 > U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below
284 > U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line
285 > U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line
286 > U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay
287 > U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay
288 > U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay
289 > U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay
290 > U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay
291 > U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below
292 > U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below
293 > U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below
294 > U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below
295 > U_Combining_X_Above = 0x033D, // U+033D Combining X Above
296 > U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde
297 > U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline
298 > U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark
299 > U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark
300 > U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni
301 > U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis
302 > U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos
303 > U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni
304 > U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above
305 > U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below
306 > U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below
307 > U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below
308 > U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above
309 > U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above
310 > U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above
311 > U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below
312 > U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below
313 > U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner
314 > U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above
315 > U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above
316 > U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata
317 > U_Combining_X_Below = 0x0353, // U+0353 Combining X Below
318 > U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below
319 > U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below
320 > U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below
321 > U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above
322 > U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right
323 > U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below
324 > U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below
325 > U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above
326 > U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below
327 > U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve
328 > U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron
329 > U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below
330 > U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde
331 > U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve
332 > U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below
333 > U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A
334 > U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E
335 > U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I
336 > U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O
337 > U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U
338 > U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C
339 > U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D
340 > U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H
341 > U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M
342 > U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R
343 > U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T
344 > U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V
345 > U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X
346 >
347 > /**
348 > * Unicode Character 'LINE SEPARATOR' (U+2028)
349 > * http://www.fileformat.info/info/unicode/char/2028/index.htm
350 > */
351 > LINE_SEPARATOR = 0x2028,
352 > /**
353 > * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029)
354 > * http://www.fileformat.info/info/unicode/char/2029/index.htm
355 > */
356 > PARAGRAPH_SEPARATOR = 0x2029,
357 > /**
358 > * Unicode Character 'NEXT LINE' (U+0085)
359 > * http://www.fileformat.info/info/unicode/char/0085/index.htm
360 > */
361 > NEXT_LINE = 0x0085,
362 >
363 > // http://www.fileformat.info/info/unicode/category/Sk/list.htm
364 > U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX
365 > U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT
366 > U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS
367 > U_MACRON = 0x00AF, // U+00AF MACRON
368 > U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT
369 > U_CEDILLA = 0x00B8, // U+00B8 CEDILLA
370 > U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD
371 > U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD
372 > U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD
373 > U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD
374 > U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
375 > U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
376 > U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK
377 > U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK
378 > U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN
379 > U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN
380 > U_BREVE = 0x02D8, // U+02D8 BREVE
381 > U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE
382 > U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE
383 > U_OGONEK = 0x02DB, // U+02DB OGONEK
384 > U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE
385 > U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT
386 > U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK
387 > U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT
388 > U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
389 > U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR
390 > U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR
391 > U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR
392 > U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
393 > U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK
394 > U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK
395 > U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED
396 > U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD
397 > U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD
398 > U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD
399 > U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD
400 > U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING
401 > U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT
402 > U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
403 > U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
404 > U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE
405 > U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON
406 > U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE
407 > U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE
408 > U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE
409 > U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE
410 > U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF
411 > U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF
412 > U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW
413 > U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN
414 > U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS
415 > U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS
416 > U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS
417 > U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI
418 > U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI
419 > U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI
420 > U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA
421 > U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA
422 > U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI
423 > U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA
424 > U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA
425 > U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI
426 > U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA
427 > U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA
428 > U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA
429 > U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA
430 > U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA
431 >
432 > U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002 IDEOGRAPHIC FULL STOP
433 > U_LEFT_CORNER_BRACKET = 0x300C, // U+300C LEFT CORNER BRACKET
434 > U_RIGHT_CORNER_BRACKET = 0x300D, // U+300D RIGHT CORNER BRACKET
435 > U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010 LEFT BLACK LENTICULAR BRACKET
436 > U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011 RIGHT BLACK LENTICULAR BRACKET
437 >
438 >
439 > U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE'
440 >
441 > /**
442 > * UTF-8 BOM
443 > * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
444 > * http://www.fileformat.info/info/unicode/char/feff/index.htm
445 > */
446 > UTF8_BOM = 65279,
447 >
448 > U_FULLWIDTH_SEMICOLON = 0xFF1B, // U+FF1B FULLWIDTH SEMICOLON
449 > U_FULLWIDTH_COMMA = 0xFF0C, // U+FF0C FULLWIDTH COMMA
450 > }
src/vs/base/common/arrays.ts 408 covered LOC · 75 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- arrays.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { findFirstIdxMonotonousOrArrLen } from './arraysFind.js';
7 > import { CancellationToken } from './cancellation.js';
8 > import { CancellationError } from './errors.js';
9 > import { ISplice } from './sequence.js';
10 >
11 > /**
12 > * Returns the last entry and the initial N-1 entries of the array, as a tuple of [rest, last].
13 > *
14 > * The array must have at least one element.
15 > *
16 > * @param arr The input array
17 > * @returns A tuple of [rest, last] where rest is all but the last element and last is the last element
18 > * @throws Error if the array is empty
19 > */
20 > export function tail<T>(arr: T[]): [T[], T] {
21 if (arr.length === 0) {
22 throw new Error('Invalid tail call');
25 return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
26 }
27 > arrays.ts
28 > export function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
29 if (one === other) {
30 return true;
47 return true;
48 }
49 > arrays.ts
50 > /**
51 > * Remove the element at `index` by replacing it with the last element. This is faster than `splice`
52 > * but changes the order of the array
53 > */
54 > export function removeFastWithoutKeepingOrder<T>(array: T[], index: number) {
55 const last = array.length - 1;
56 if (index < last) {
59 array.pop();
60 }
61 > arrays.ts
62 > /**
63 > * Performs a binary search algorithm over a sorted array.
64 > *
65 > * @param array The array being searched.
66 > * @param key The value we search for.
67 > * @param comparator A function that takes two array elements and returns zero
68 > * if they are equal, a negative number if the first element precedes the
69 > * second one in the sorting order, or a positive number if the second element
70 > * precedes the first one.
71 > * @return See {@link binarySearch2}
72 > */
73 > export function binarySearch<T>(array: ReadonlyArray<T>, key: T, comparator: (op1: T, op2: T) => number): number {
74 return binarySearch2(array.length, i => comparator(array[i], key));
75 }
76 > arrays.ts
77 > /**
78 > * Performs a binary search algorithm over a sorted collection. Useful for cases
79 > * when we need to perform a binary search over something that isn't actually an
80 > * array, and converting data to an array would defeat the use of binary search
81 > * in the first place.
82 > *
83 > * @param length The collection length.
84 > * @param compareToKey A function that takes an index of an element in the
85 > * collection and returns zero if the value at this index is equal to the
86 > * search key, a negative number if the value precedes the search key in the
87 > * sorting order, or a positive number if the search key precedes the value.
88 > * @return A non-negative index of an element, if found. If not found, the
89 > * result is -(n+1) (or ~n, using bitwise notation), where n is the index
90 > * where the key should be inserted to maintain the sorting order.
91 > */
92 > export function binarySearch2(length: number, compareToKey: (index: number) => number): number {
93 let low = 0,
94 high = length - 1;
107 return -(low + 1);
108 }
109 > arrays.ts
110 > type Compare<T> = (a: T, b: T) => number;
111 >
112 > /**
113 > * Finds the nth smallest element in the array using quickselect algorithm.
114 > * The data does not need to be sorted.
115 > *
116 > * @param nth The zero-based index of the element to find (0 = smallest, 1 = second smallest, etc.)
117 > * @param data The unsorted array
118 > * @param compare A comparator function that defines the sort order
119 > * @returns The nth smallest element
120 > * @throws TypeError if nth is >= data.length
121 > */
122 > export function quickSelect<T>(nth: number, data: T[], compare: Compare<T>): T {
123
124 nth = nth | 0;
152 }
153 }
154 > arrays.ts
155 > export function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {
156 const result: T[][] = [];
157 let currentGroup: T[] | undefined = undefined;
166 return result;
167 }
168 > arrays.ts
169 > /**
170 > * Splits the given items into a list of (non-empty) groups.
171 > * `shouldBeGrouped` is used to decide if two consecutive items should be in the same group.
172 > * The order of the items is preserved.
173 > */
174 > export function* groupAdjacentBy<T>(items: Iterable<T>, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable<T[]> {
175 let currentGroup: T[] | undefined;
176 let last: T | undefined;
190 }
191 }
192 > arrays.ts
193 > export function forEachAdjacent<T>(arr: T[], f: (item1: T | undefined, item2: T | undefined) => void): void {
194 for (let i = 0; i <= arr.length; i++) {
195 f(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]);
196 }
197 }
198 > arrays.ts
199 > export function forEachWithNeighbors<T>(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void {
200 for (let i = 0; i < arr.length; i++) {
201 f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);
202 }
203 }
204 > arrays.ts
205 > export function concatArrays<T extends any[]>(...arrays: T): T[number][number][] {
206 return [].concat(...arrays);
207 }
208 > arrays.ts
209 > interface IMutableSplice<T> extends ISplice<T> {
210 > readonly toInsert: T[];
211 > deleteCount: number;
212 > }
213 >
214 > /**
215 > * Diffs two *sorted* arrays and computes the splices which apply the diff.
216 > */
217 > export function sortedDiff<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): ISplice<T>[] {
218 const result: IMutableSplice<T>[] = [];
219
266 return result;
267 }
268 > arrays.ts
269 > /**
270 > * Takes two *sorted* arrays and computes their delta (removed, added elements).
271 > * Finishes in `Math.min(before.length, after.length)` steps.
272 > */
273 > export function delta<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): { removed: T[]; added: T[] } {
274 const splices = sortedDiff(before, after, compare);
275 const removed: T[] = [];
283 return { removed, added };
284 }
285 > arrays.ts
286 > /**
287 > * Returns the top N elements from the array.
288 > *
289 > * Faster than sorting the entire array when the array is a lot larger than N.
290 > *
291 > * @param array The unsorted array.
292 > * @param compare A sort function for the elements.
293 > * @param n The number of elements to return.
294 > * @return The first n elements from array when sorted with compare.
295 > */
296 > export function top<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, n: number): T[] {
297 if (n === 0) {
298 return [];
302 return result;
303 }
304 > arrays.ts
305 > /**
306 > * Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run.
307 > *
308 > * Returns the top N elements from the array.
309 > *
310 > * Faster than sorting the entire array when the array is a lot larger than N.
311 > *
312 > * @param array The unsorted array.
313 > * @param compare A sort function for the elements.
314 > * @param n The number of elements to return.
315 > * @param batch The number of elements to examine before yielding to the event loop.
316 > * @return The first n elements from array when sorted with compare.
317 > */
318 > export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise<T[]> {
319 if (n === 0) {
320 return Promise.resolve([]);
339 });
340 }
341 > arrays.ts
342 function topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, result: T[], i: number, m: number): void {
343 for (const n = result.length; i < m; i++) {
350 }
351 }
352 > arrays.ts
353 > /**
354 > * @returns New array with all falsy values removed. The original array IS NOT modified.
355 > */
356 > export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
357 return array.filter((e): e is T => !!e);
358 }
359 > arrays.ts
360 > /**
361 > * Remove all falsy values from `array`. The original array IS modified.
362 > */
363 > export function coalesceInPlace<T>(array: Array<T | undefined | null>): asserts array is Array<T> {
364 let to = 0;
365 for (let i = 0; i < array.length; i++) {
371 array.length = to;
372 }
373 > arrays.ts
374 > /**
375 > * @deprecated Use `Array.copyWithin` instead
376 > */
377 > export function move(array: unknown[], from: number, to: number): void {
378 array.splice(to, 0, array.splice(from, 1)[0]);
379 }
380 > arrays.ts
381 > /**
382 > * @returns false if the provided object is an array and not empty.
383 > */
384 > export function isFalsyOrEmpty(obj: unknown): boolean {
385 return !Array.isArray(obj) || obj.length === 0;
386 }
387 > arrays.ts
388 > /**
389 > * @returns True if the provided object is an array and has at least one element.
390 > */
391 > export function isNonEmptyArray<T>(obj: T[] | undefined | null): obj is T[];
392 > export function isNonEmptyArray<T>(obj: readonly T[] | undefined | null): obj is readonly T[];
393 > export function isNonEmptyArray<T>(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] {
394 return Array.isArray(obj) && obj.length > 0;
395 }
396 > arrays.ts
397 > /**
398 > * Removes duplicates from the given array. The optional keyFn allows to specify
399 > * how elements are checked for equality by returning an alternate value for each.
400 > */
401 > export function distinct<T>(array: ReadonlyArray<T>, keyFn: (value: T) => unknown = value => value): T[] {
402 const seen = new Set<any>();
403
411 });
412 }
413 > arrays.ts
414 > export function uniqueFilter<T, R>(keyFn: (t: T) => R): (t: T) => boolean {
415 const seen = new Set<R>();
416
426 };
427 }
428 > arrays.ts
429 > export function commonPrefixLength<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, equals: (a: T, b: T) => boolean = (a, b) => a === b): number {
430 let result = 0;
431
436 return result;
437 }
438 > arrays.ts
439 > export function range(to: number): number[];
440 > export function range(from: number, to: number): number[];
441 > export function range(arg: number, to?: number): number[] {
442 let from = typeof to === 'number' ? arg : 0;
443
463 return result;
464 }
465 > arrays.ts
466 > export function index<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T };
467 > export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R };
468 > export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper?: (t: T) => R): { [key: string]: R } {
469 return array.reduce((r, t) => {
470 r[indexer(t)] = mapper ? mapper(t) : t;
472 }, Object.create(null));
473 }
474 > arrays.ts
475 > /**
476 > * Inserts an element into an array. Returns a function which, when
477 > * called, will remove that element from the array.
478 > *
479 > * @deprecated In almost all cases, use a `Set<T>` instead.
480 > */
481 > export function insert<T>(array: T[], element: T): () => void {
482 array.push(element);
483
484 return () => remove(array, element);
485 }
486 > arrays.ts
487 > /**
488 > * Removes an element from an array if it can be found.
489 > *
490 > * @deprecated In almost all cases, use a `Set<T>` instead.
491 > */
492 > export function remove<T>(array: T[], element: T): T | undefined {
493 const index = array.indexOf(element);
494 if (index > -1) {
500 return undefined;
501 }
502 > arrays.ts
503 > /**
504 > * Insert `insertArr` inside `target` at `insertIndex`.
505 > * Please don't touch unless you understand https://jsperf.com/inserting-an-array-within-an-array
506 > */
507 > export function arrayInsert<T>(target: T[], insertIndex: number, insertArr: T[]): T[] {
508 const before = target.slice(0, insertIndex);
509 const after = target.slice(insertIndex);
510 return before.concat(insertArr, after);
511 }
512 > arrays.ts
513 > /**
514 > * Uses Fisher-Yates shuffle to shuffle the given array
515 > */
516 > export function shuffle<T>(array: T[], _seed?: number): void {
517 let rand: () => number;
518
536 }
537 }
538 > arrays.ts
539 > /**
540 > * Pushes an element to the start of the array, if found.
541 > */
542 > export function pushToStart<T>(arr: T[], value: T): void {
543 const index = arr.indexOf(value);
544
548 }
549 }
550 > arrays.ts
551 > /**
552 > * Pushes an element to the end of the array, if found.
553 > */
554 > export function pushToEnd<T>(arr: T[], value: T): void {
555 const index = arr.indexOf(value);
556
560 }
561 }
562 > arrays.ts
563 > export function pushMany<T>(arr: T[], items: ReadonlyArray<T>): void {
564 for (const item of items) {
565 arr.push(item);
566 }
567 }
568 > arrays.ts
569 > export function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {
570 return Array.isArray(items) ?
571 items.map(fn) :
572 fn(items);
573 }
574 > arrays.ts
575 > export function mapFilter<T, U>(array: ReadonlyArray<T>, fn: (t: T) => U | undefined): U[] {
576 const result: U[] = [];
577 for (const item of array) {
583 return result;
584 }
585 > arrays.ts
586 > export function withoutDuplicates<T>(array: ReadonlyArray<T>): T[] {
587 const s = new Set(array);
588 return Array.from(s);
589 }
590 > arrays.ts
591 > export function asArray<T>(x: T | T[]): T[];
592 > export function asArray<T>(x: T | readonly T[]): readonly T[];
593 > export function asArray<T>(x: T | T[]): T[] {
594 return Array.isArray(x) ? x : [x];
595 }
596 > arrays.ts
597 > export function getRandomElement<T>(arr: T[]): T | undefined {
598 return arr[Math.floor(Math.random() * arr.length)];
599 }
600 > arrays.ts
601 > /**
602 > * Insert the new items in the array.
603 > * @param array The original array.
604 > * @param start The zero-based location in the array from which to start inserting elements.
605 > * @param newItems The items to be inserted
606 > */
607 > export function insertInto<T>(array: T[], start: number, newItems: T[]): void {
608 const startIdx = getActualStartIndex(array, start);
609 const originalLength = array.length;
619 }
620 }
621 > arrays.ts
622 > /**
623 > * Removes elements from an array and inserts new elements in their place, returning the deleted elements. Alternative to the native Array.splice method, it
624 > * can only support limited number of items due to the maximum call stack size limit.
625 > * @param array The original array.
626 > * @param start The zero-based location in the array from which to start removing elements.
627 > * @param deleteCount The number of elements to remove.
628 > * @returns An array containing the elements that were deleted.
629 > */
630 > export function splice<T>(array: T[], start: number, deleteCount: number, newItems: T[]): T[] {
631 const index = getActualStartIndex(array, start);
632 let result = array.splice(index, deleteCount);
638 return result;
639 }
640 > arrays.ts
641 > /**
642 > * Determine the actual start index (same logic as the native splice() or slice())
643 > * If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided.
644 > * If negative, it will begin that many elements from the end of the array. (In this case, the origin -1, meaning -n is the index of the nth last element, and is therefore equivalent to the index of array.length - n.) If array.length + start is less than 0, it will begin from index 0.
645 > * @param array The target array.
646 > * @param start The operation index.
647 > */
648 function getActualStartIndex<T>(array: T[], start: number): number {
649 return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length);
650 }
651 > arrays.ts
652 >
653 >
654 > /**
655 > * When comparing two values,
656 > * a negative number indicates that the first value is less than the second,
657 > * a positive number indicates that the first value is greater than the second,
658 > * and zero indicates that neither is the case.
659 > */
660 > export type CompareResult = number;
661 >
662 > export namespace CompareResult {
663 > export function isLessThan(result: CompareResult): boolean {
664 return result < 0;
665 }
666 > arrays.ts
667 > export function isLessThanOrEqual(result: CompareResult): boolean {
668 return result <= 0;
669 }
670 > arrays.ts
671 > export function isGreaterThan(result: CompareResult): boolean {
672 return result > 0;
673 }
674 > arrays.ts
675 > export function isNeitherLessOrGreaterThan(result: CompareResult): boolean {
676 return result === 0;
677 }
678 > arrays.ts
679 > export const greaterThan = 1;
680 > export const lessThan = -1;
681 > export const neitherLessOrGreaterThan = 0;
682 > }
683 >
684 > /**
685 > * A comparator `c` defines a total order `<=` on `T` as following:
686 > * `c(a, b) <= 0` iff `a` <= `b`.
687 > * We also have `c(a, b) == 0` iff `c(b, a) == 0`.
688 > */
689 > export type Comparator<T> = (a: T, b: T) => CompareResult;
690 >
691 > export function compareBy<TItem, TCompareBy>(selector: (item: TItem) => TCompareBy, comparator: Comparator<TCompareBy>): Comparator<TItem> {
692 > return (a, b) => comparator(selector(a), selector(b)); arrays.ts
693 > }
694 > arrays.ts
695 > export function tieBreakComparators<TItem>(...comparators: Comparator<TItem>[]): Comparator<TItem> {
696 > return (item1, item2) => { arrays.ts
697 for (const comparator of comparators) {
698 const result = comparator(item1, item2);
703 return CompareResult.neitherLessOrGreaterThan;
704 };
705 > } arrays.ts
706 > arrays.ts
707 > /**
708 > * The natural order on numbers.
709 > */
710 > export const numberComparator: Comparator<number> = (a, b) => a - b;
711 >
712 > export const booleanComparator: Comparator<boolean> = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0);
713 >
714 > export function reverseOrder<TItem>(comparator: Comparator<TItem>): Comparator<TItem> {
715 return (a, b) => -comparator(a, b);
716 }
717 > arrays.ts
718 > /**
719 > * Returns a new comparator that treats `undefined` as the smallest value.
720 > * All other values are compared using the given comparator.
721 > */
722 > export function compareUndefinedSmallest<T>(comparator: Comparator<T>): Comparator<T | undefined> {
723 return (a, b) => {
724 if (a === undefined) {
731 };
732 }
733 > arrays.ts
734 > export class ArrayQueue<T> {
735 > private readonly items: readonly T[];
736 > private firstIdx = 0;
737 > private lastIdx: number;
738 >
739 > /**
740 > * Constructs a queue that is backed by the given array. Runtime is O(1).
741 > */
742 > constructor(items: readonly T[]) {
743 this.items = items;
744 this.lastIdx = this.items.length - 1;
745 }
746 > arrays.ts
747 > get length(): number {
748 return this.lastIdx - this.firstIdx + 1;
749 }
750 > arrays.ts
751 > /**
752 > * Consumes elements from the beginning of the queue as long as the predicate returns true.
753 > * If no elements were consumed, `null` is returned. Has a runtime of O(result.length).
754 > */
755 > takeWhile(predicate: (value: T) => boolean): T[] | null {
756 // P(k) := k <= this.lastIdx && predicate(this.items[k])
757 // Find s := min { k | k >= this.firstIdx && !P(k) } and return this.data[this.firstIdx...s)
765 return result;
766 }
767 > arrays.ts
768 > /**
769 > * Consumes elements from the end of the queue as long as the predicate returns true.
770 > * If no elements were consumed, `null` is returned.
771 > * The result has the same order as the underlying array!
772 > */
773 > takeFromEndWhile(predicate: (value: T) => boolean): T[] | null {
774 // P(k) := this.firstIdx >= k && predicate(this.items[k])
775 // Find s := max { k | k <= this.lastIdx && !P(k) } and return this.data(s...this.lastIdx]
783 return result;
784 }
785 > arrays.ts
786 > peek(): T | undefined {
787 if (this.length === 0) {
788 return undefined;
790 return this.items[this.firstIdx];
791 }
792 > arrays.ts
793 > peekLast(): T | undefined {
794 if (this.length === 0) {
795 return undefined;
797 return this.items[this.lastIdx];
798 }
799 > arrays.ts
800 > dequeue(): T | undefined {
801 const result = this.items[this.firstIdx];
802 this.firstIdx++;
803 return result;
804 }
805 > arrays.ts
806 > removeLast(): T | undefined {
807 const result = this.items[this.lastIdx];
808 this.lastIdx--;
809 return result;
810 }
811 > arrays.ts
812 > takeCount(count: number): T[] {
813 const result = this.items.slice(this.firstIdx, this.firstIdx + count);
814 this.firstIdx += count;
815 return result;
816 }
817 > } arrays.ts
818 >
819 > /**
820 > * This class is faster than an iterator and array for lazy computed data.
821 > */
822 > export class CallbackIterable<T> {
823 > public static readonly empty = new CallbackIterable<never>(_callback => { });
824 >
825 > constructor(
826 > /**
827 > * Calls the callback for every item.
828 > * Stops when the callback returns false.
829 > */
830 > public readonly iterate: (callback: (item: T) => boolean) => void
831 > ) {
832 > }
833 >
834 > forEach(handler: (item: T) => void) {
835 this.iterate(item => { handler(item); return true; });
836 }
837 > arrays.ts
838 > toArray(): T[] {
839 const result: T[] = [];
840 this.iterate(item => { result.push(item); return true; });
841 return result;
842 }
843 > arrays.ts
844 > filter(predicate: (item: T) => boolean): CallbackIterable<T> {
845 return new CallbackIterable(cb => this.iterate(item => predicate(item) ? cb(item) : true));
846 }
847 > arrays.ts
848 > map<TResult>(mapFn: (item: T) => TResult): CallbackIterable<TResult> {
849 return new CallbackIterable<TResult>(cb => this.iterate(item => cb(mapFn(item))));
850 }
851 > arrays.ts
852 > some(predicate: (item: T) => boolean): boolean {
853 let result = false;
854 this.iterate(item => { result = predicate(item); return !result; });
855 return result;
856 }
857 > arrays.ts
858 > findFirst(predicate: (item: T) => boolean): T | undefined {
859 let result: T | undefined;
860 this.iterate(item => {
867 return result;
868 }
869 > arrays.ts
870 > findLast(predicate: (item: T) => boolean): T | undefined {
871 let result: T | undefined;
872 this.iterate(item => {
878 return result;
879 }
880 > arrays.ts
881 > findLastMaxBy(comparator: Comparator<T>): T | undefined {
882 let result: T | undefined;
883 let first = true;
891 return result;
892 }
893 > } arrays.ts
894 >
895 > /**
896 > * Represents a re-arrangement of items in an array.
897 > */
898 > export class Permutation {
899 > constructor(private readonly _indexMap: readonly number[]) { }
900 >
901 > /**
902 > * Returns a permutation that sorts the given array according to the given compare function.
903 > */
904 > public static createSortPermutation<T>(arr: readonly T[], compareFn: (a: T, b: T) => number): Permutation {
905 const sortIndices = Array.from(arr.keys()).sort((index1, index2) => compareFn(arr[index1], arr[index2]));
906 return new Permutation(sortIndices);
907 }
908 > arrays.ts
909 > /**
910 > * Returns a new array with the elements of the given array re-arranged according to this permutation.
911 > */
912 > apply<T>(arr: readonly T[]): T[] {
913 return arr.map((_, index) => arr[this._indexMap[index]]);
914 }
915 > arrays.ts
916 > /**
917 > * Returns a new permutation that undoes the re-arrangement of this permutation.
918 > */
919 > inverse(): Permutation {
920 const inverseIndexMap = this._indexMap.slice();
921 for (let i = 0; i < this._indexMap.length; i++) {
924 return new Permutation(inverseIndexMap);
925 }
926 > } arrays.ts
927 >
928 > /**
929 > * Asynchronous variant of `Array.find()`, returning the first element in
930 > * the array for which the predicate returns true.
931 > *
932 > * This implementation does not bail early and waits for all promises to
933 > * resolve before returning.
934 > */
935 export async function findAsync<T>(array: readonly T[], predicate: (element: T, index: number) => Promise<boolean>): Promise<T | undefined> {
936 const results = await Promise.all(array.map(
940 return results.find(r => r.ok)?.element;
941 }
942 > arrays.ts
943 > export function sum(array: readonly number[]): number {
944 return array.reduce((acc, value) => acc + value, 0);
945 }
946 > arrays.ts
947 > export function sumBy<T>(array: readonly T[], selector: (value: T) => number): number {
948 return array.reduce((acc, value) => acc + selector(value), 0);
949 }
src/vs/base/common/map.ts 313 covered LOC · 97 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- map.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { URI } from './uri.js';
7 >
8 > export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
9 let result = map.get(key);
10 if (result === undefined) {
15 return result;
16 }
17 > map.ts
18 > export function mapToString<K, V>(map: Map<K, V>): string {
19 const entries: string[] = [];
20 map.forEach((value, key) => {
24 return `Map(${map.size}) {${entries.join(', ')}}`;
25 }
26 > map.ts
27 > export function setToString<K>(set: Set<K>): string {
28 const entries: K[] = [];
29 set.forEach(value => {
33 return `Set(${set.size}) {${entries.join(', ')}}`;
34 }
35 > map.ts
36 > interface ResourceMapKeyFn {
37 > (resource: URI): string;
38 > }
39 >
40 > class ResourceMapEntry<T> {
41 > constructor(readonly uri: URI, readonly value: T) { }
42 > }
43 >
44 function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {
45 return Array.isArray(arg);
46 }
47 > map.ts
48 > export class ResourceMap<T> implements Map<URI, T> {
49 >
50 > private static readonly defaultToKey = (resource: URI) => resource.toString();
51 >
52 > readonly [Symbol.toStringTag] = 'ResourceMap';
53 >
54 > private readonly map: Map<string, ResourceMapEntry<T>>;
55 > private readonly toKey: ResourceMapKeyFn;
56 >
57 > /**
58 > *
59 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
60 > */
61 > constructor(toKey?: ResourceMapKeyFn);
62 >
63 > /**
64 > *
65 > * @param other Another resource which this maps is created from
66 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
67 > */
68 > constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
69 >
70 > /**
71 > *
72 > * @param other Another resource which this maps is created from
73 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
74 > */
75 > constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
76 >
77 > constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
78 if (arg instanceof ResourceMap) {
79 this.map = new Map(arg.map);
91 }
92 }
93 > map.ts
94 > set(resource: URI, value: T): this {
95 this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));
96 return this;
97 }
98 > map.ts
99 > get(resource: URI): T | undefined {
100 return this.map.get(this.toKey(resource))?.value;
101 }
102 > map.ts
103 > has(resource: URI): boolean {
104 return this.map.has(this.toKey(resource));
105 }
106 > map.ts
107 > get size(): number {
108 return this.map.size;
109 }
110 > map.ts
111 > clear(): void {
112 this.map.clear();
113 }
114 > map.ts
115 > delete(resource: URI): boolean {
116 return this.map.delete(this.toKey(resource));
117 }
118 > map.ts
119 > forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {
120 if (typeof thisArg !== 'undefined') {
121 clb = clb.bind(thisArg);
125 }
126 }
127 > map.ts
128 > *values(): MapIterator<T> {
129 for (const entry of this.map.values()) {
130 yield entry.value;
131 }
132 }
133 > map.ts
134 > *keys(): MapIterator<URI> {
135 for (const entry of this.map.values()) {
136 yield entry.uri;
137 }
138 }
139 > map.ts
140 > *entries(): MapIterator<[URI, T]> {
141 for (const entry of this.map.values()) {
142 yield [entry.uri, entry.value];
143 }
144 }
145 > map.ts
146 > *[Symbol.iterator](): MapIterator<[URI, T]> {
147 for (const [, entry] of this.map) {
148 yield [entry.uri, entry.value];
149 }
150 }
151 > } map.ts
152 >
153 > export class ResourceSet implements Set<URI> {
154 >
155 > readonly [Symbol.toStringTag]: string = 'ResourceSet';
156 >
157 > private readonly _map: ResourceMap<URI>;
158 >
159 > constructor(toKey?: ResourceMapKeyFn);
160 > constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);
161 > constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
162 if (!entriesOrKey || typeof entriesOrKey === 'function') {
163 this._map = new ResourceMap(entriesOrKey);
167 }
168 }
169 > map.ts
170 >
171 > get size(): number {
172 return this._map.size;
173 }
174 > map.ts
175 > add(value: URI): this {
176 this._map.set(value, value);
177 return this;
178 }
179 > map.ts
180 > clear(): void {
181 this._map.clear();
182 }
183 > map.ts
184 > delete(value: URI): boolean {
185 return this._map.delete(value);
186 }
187 > map.ts
188 > forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {
189 this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
190 }
191 > map.ts
192 > has(value: URI): boolean {
193 return this._map.has(value);
194 }
195 > map.ts
196 > entries(): SetIterator<[URI, URI]> {
197 return this._map.entries() as unknown as SetIterator<[URI, URI]>;
198 }
199 > map.ts
200 > keys(): SetIterator<URI> {
201 return this._map.keys() as unknown as SetIterator<URI>;
202 }
203 > map.ts
204 > values(): SetIterator<URI> {
205 return this._map.keys() as unknown as SetIterator<URI>;
206 }
207 > map.ts
208 > [Symbol.iterator](): SetIterator<URI> {
209 return this.keys();
210 }
211 > } map.ts
212 >
213 >
214 > interface Item<K, V> {
215 > previous: Item<K, V> | undefined;
216 > next: Item<K, V> | undefined;
217 > key: K;
218 > value: V;
219 > }
220 >
221 > export const enum Touch {
222 > None = 0,
223 > AsOld = 1,
224 > AsNew = 2
225 > }
226 >
227 > export class LinkedMap<K, V> implements Map<K, V> {
228 >
229 > readonly [Symbol.toStringTag] = 'LinkedMap';
230 >
231 > private _map: Map<K, Item<K, V>>;
232 > private _head: Item<K, V> | undefined;
233 > private _tail: Item<K, V> | undefined;
234 > private _size: number;
235 >
236 > private _state: number;
237 >
238 > constructor() {
239 this._map = new Map<K, Item<K, V>>();
240 this._head = undefined;
243 this._state = 0;
244 }
245 > map.ts
246 > clear(): void {
247 this._map.clear();
248 this._head = undefined;
251 this._state++;
252 }
253 > map.ts
254 > isEmpty(): boolean {
255 return !this._head && !this._tail;
256 }
257 > map.ts
258 > get size(): number {
259 return this._size;
260 }
261 > map.ts
262 > get first(): V | undefined {
263 return this._head?.value;
264 }
265 > map.ts
266 > get last(): V | undefined {
267 return this._tail?.value;
268 }
269 > map.ts
270 > has(key: K): boolean {
271 return this._map.has(key);
272 }
273 > map.ts
274 > get(key: K, touch: Touch = Touch.None): V | undefined {
275 const item = this._map.get(key);
276 if (!item) {
282 return item.value;
283 }
284 > map.ts
285 > set(key: K, value: V, touch: Touch = Touch.None): this {
286 let item = this._map.get(key);
287 if (item) {
311 return this;
312 }
313 > map.ts
314 > delete(key: K): boolean {
315 return !!this.remove(key);
316 }
317 > map.ts
318 > remove(key: K): V | undefined {
319 const item = this._map.get(key);
320 if (!item) {
326 return item.value;
327 }
328 > map.ts
329 > shift(): V | undefined {
330 if (!this._head && !this._tail) {
331 return undefined;
340 return item.value;
341 }
342 > map.ts
343 > forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
344 const state = this._state;
345 let current = this._head;
356 }
357 }
358 > map.ts
359 > keys(): MapIterator<K> {
360 const map = this;
361 const state = this._state;
381 return iterator;
382 }
383 > map.ts
384 > values(): MapIterator<V> {
385 const map = this;
386 const state = this._state;
406 return iterator;
407 }
408 > map.ts
409 > entries(): MapIterator<[K, V]> {
410 const map = this;
411 const state = this._state;
431 return iterator;
432 }
433 > map.ts
434 > [Symbol.iterator](): MapIterator<[K, V]> {
435 return this.entries();
436 }
437 > map.ts
438 > protected trimOld(newSize: number) {
439 if (newSize >= this.size) {
440 return;
458 this._state++;
459 }
460 > map.ts
461 > protected trimNew(newSize: number) {
462 if (newSize >= this.size) {
463 return;
481 this._state++;
482 }
483 > map.ts
484 > private addItemFirst(item: Item<K, V>): void {
485 // First time Insert
486 if (!this._head && !this._tail) {
495 this._state++;
496 }
497 > map.ts
498 > private addItemLast(item: Item<K, V>): void {
499 // First time Insert
500 if (!this._head && !this._tail) {
509 this._state++;
510 }
511 > map.ts
512 > private removeItem(item: Item<K, V>): void {
513 if (item === this._head && item === this._tail) {
514 this._head = undefined;
546 this._state++;
547 }
548 > map.ts
549 > private touch(item: Item<K, V>, touch: Touch): void {
550 if (!this._head || !this._tail) {
551 throw new Error('Invalid list');
608 }
609 }
610 > map.ts
611 > toJSON(): [K, V][] {
612 const data: [K, V][] = [];
613
618 return data;
619 }
620 > map.ts
621 > fromJSON(data: [K, V][]): void {
622 this.clear();
623
626 }
627 }
628 > } map.ts
629 >
630 > abstract class Cache<K, V> extends LinkedMap<K, V> {
631 >
632 > protected _limit: number;
633 > protected _ratio: number;
634 >
635 > constructor(limit: number, ratio: number = 1) {
636 super();
637 this._limit = limit;
638 this._ratio = Math.min(Math.max(0, ratio), 1);
639 }
640 > map.ts
641 > get limit(): number {
642 return this._limit;
643 }
644 > map.ts
645 > set limit(limit: number) {
646 this._limit = limit;
647 this.checkTrim();
648 }
649 > map.ts
650 > get ratio(): number {
651 return this._ratio;
652 }
653 > map.ts
654 > set ratio(ratio: number) {
655 this._ratio = Math.min(Math.max(0, ratio), 1);
656 this.checkTrim();
657 }
658 > map.ts
659 > override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
660 return super.get(key, touch);
661 }
662 > map.ts
663 > peek(key: K): V | undefined {
664 return super.get(key, Touch.None);
665 }
666 > map.ts
667 > override set(key: K, value: V): this {
668 super.set(key, value, Touch.AsNew);
669 return this;
670 }
671 > map.ts
672 > protected checkTrim() {
673 if (this.size > this._limit) {
674 this.trim(Math.round(this._limit * this._ratio));
675 }
676 }
677 > map.ts
678 > protected abstract trim(newSize: number): void;
679 > }
680 >
681 > export class LRUCache<K, V> extends Cache<K, V> {
682 >
683 > constructor(limit: number, ratio: number = 1) {
684 super(limit, ratio);
685 }
686 > map.ts
687 > protected override trim(newSize: number) {
688 this.trimOld(newSize);
689 }
690 > map.ts
691 > override set(key: K, value: V): this {
692 super.set(key, value);
693 this.checkTrim();
694 return this;
695 }
696 > } map.ts
697 >
698 > export class MRUCache<K, V> extends Cache<K, V> {
699 >
700 > constructor(limit: number, ratio: number = 1) {
701 super(limit, ratio);
702 }
703 > map.ts
704 > protected override trim(newSize: number) {
705 this.trimNew(newSize);
706 }
707 > map.ts
708 > override set(key: K, value: V): this {
709 if (this._limit <= this.size && !this.has(key)) {
710 this.trim(Math.round(this._limit * this._ratio) - 1);
714 return this;
715 }
716 > } map.ts
717 >
718 > export class CounterSet<T> {
719
720 private map = new Map<T, number>();
721 > map.ts
722 > add(value: T): CounterSet<T> {
723 this.map.set(value, (this.map.get(value) || 0) + 1);
724 return this;
725 }
726 > map.ts
727 > delete(value: T): boolean {
728 let counter = this.map.get(value) || 0;
729
742 return true;
743 }
744 > map.ts
745 > has(value: T): boolean {
746 return this.map.has(value);
747 }
748 > } map.ts
749 >
750 > /**
751 > * A map that allows access both by keys and values.
752 > * **NOTE**: values need to be unique.
753 > */
754 > export class BidirectionalMap<K, V> {
755 >
756 > private readonly _m1 = new Map<K, V>();
757 > private readonly _m2 = new Map<V, K>();
758 >
759 > constructor(entries?: readonly (readonly [K, V])[]) {
760 if (entries) {
761 for (const [key, value] of entries) {
764 }
765 }
766 > map.ts
767 > clear(): void {
768 this._m1.clear();
769 this._m2.clear();
770 }
771 > map.ts
772 > set(key: K, value: V): void {
773 this._m1.set(key, value);
774 this._m2.set(value, key);
775 }
776 > map.ts
777 > get(key: K): V | undefined {
778 return this._m1.get(key);
779 }
780 > map.ts
781 > getKey(value: V): K | undefined {
782 return this._m2.get(value);
783 }
784 > map.ts
785 > delete(key: K): boolean {
786 const value = this._m1.get(key);
787 if (value === undefined) {
792 return true;
793 }
794 > map.ts
795 > forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {
796 this._m1.forEach((value, key) => {
797 callbackfn.call(thisArg, value, key, this);
798 });
799 }
800 > map.ts
801 > keys(): IterableIterator<K> {
802 return this._m1.keys();
803 }
804 > map.ts
805 > values(): IterableIterator<V> {
806 return this._m1.values();
807 }
808 > } map.ts
809 >
810 > export class SetMap<K, V> {
811
812 private map = new Map<K, Set<V>>();
813 > map.ts
814 > add(key: K, value: V): void {
815 let values = this.map.get(key);
816
822 values.add(value);
823 }
824 > map.ts
825 > delete(key: K, value: V): void {
826 const values = this.map.get(key);
827
836 }
837 }
838 > map.ts
839 > forEach(key: K, fn: (value: V) => void): void {
840 const values = this.map.get(key);
841
846 values.forEach(fn);
847 }
848 > map.ts
849 > get(key: K): ReadonlySet<V> {
850 const values = this.map.get(key);
851 if (!values) {
854 return values;
855 }
856 > } map.ts
857 >
858 > export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
859 if (a === b) {
860 return true;
879 return true;
880 }
881 > map.ts
882 > /**
883 > * A map that is addressable with an arbitrary number of keys. This is useful in high performance
884 > * scenarios where creating a composite key whenever the data is accessed is too expensive. For
885 > * example for a very hot function, constructing a string like `first-second-third` for every call
886 > * will cause a significant hit to performance.
887 > */
888 > export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {
889 private _data: Map<any, any> = new Map();
890 > map.ts
891 > /**
892 > * Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
893 > * This is because the spread operator is used for the keys and must be last..
894 > * @param value The value to set.
895 > * @param keys The keys for the value.
896 > */
897 > public set(value: TValue, ...keys: [...TKeys]): void {
898 let currentMap = this._data;
899 for (let i = 0; i < keys.length - 1; i++) {
907 currentMap.set(keys[keys.length - 1], value);
908 }
909 > map.ts
910 > public get(...keys: [...TKeys]): TValue | undefined {
911 let currentMap = this._data;
912 for (let i = 0; i < keys.length - 1; i++) {
919 return currentMap.get(keys[keys.length - 1]);
920 }
921 > map.ts
922 > public delete(...keys: [...TKeys]): boolean {
923 const maps: Map<any, any>[] = [this._data];
924 let currentMap = this._data;
939 return deleted;
940 }
941 > map.ts
942 > public deleteAll(...keys: Partial<TKeys>): boolean {
943 if (keys.length === 0) {
944 const hadData = this._data.size > 0;
964 return deleted;
965 }
966 > map.ts
967 > public clear(): void {
968 this._data.clear();
969 }
970 > map.ts
971 > public *getAll(...keys: Partial<TKeys>): IterableIterator<TValue> {
972 let currentMap = this._data;
973 for (const key of keys) {
980 yield* this._values(currentMap);
981 }
982 > map.ts
983 > public *values(): IterableIterator<TValue> {
984 yield* this._values(this._data);
985 }
986 > map.ts
987 > private *_values(map: Map<any, any>): IterableIterator<TValue> {
988 for (const value of map.values()) {
989 if (value instanceof Map) {
994 }
995 }
996 > map.ts
997 > /**
998 > * Get a textual representation of the map for debugging purposes.
999 > */
1000 > public toString(): string {
1001 const printMap = (map: Map<any, any>, depth: number): string => {
1002 let result = '';
1014 return printMap(this._data, 0);
1015 }
1016 > } map.ts
src/vs/base/common/network.ts 292 covered LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- network.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as errors from './errors.js';
7 > import * as platform from './platform.js';
8 > import { equalsIgnoreCase, startsWithIgnoreCase } from './strings.js';
9 > import { URI } from './uri.js';
10 > import * as paths from './path.js';
11 >
12 > export namespace Schemas {
13 >
14 > /**
15 > * A schema that is used for models that exist in memory
16 > * only and that have no correspondence on a server or such.
17 > */
18 > export const inMemory = 'inmemory';
19 >
20 > /**
21 > * A schema that is used for setting files
22 > */
23 > export const vscode = 'vscode';
24 >
25 > /**
26 > * A schema that is used for internal private files
27 > */
28 > export const internal = 'private';
29 >
30 > /**
31 > * A walk-through document.
32 > */
33 > export const walkThrough = 'walkThrough';
34 >
35 > /**
36 > * An embedded code snippet.
37 > */
38 > export const walkThroughSnippet = 'walkThroughSnippet';
39 >
40 > export const http = 'http';
41 >
42 > export const https = 'https';
43 >
44 > export const file = 'file';
45 >
46 > export const mailto = 'mailto';
47 >
48 > export const untitled = 'untitled';
49 >
50 > export const data = 'data';
51 >
52 > export const command = 'command';
53 >
54 > export const vscodeRemote = 'vscode-remote';
55 >
56 > export const vscodeRemoteResource = 'vscode-remote-resource';
57 >
58 > export const vscodeManagedRemoteResource = 'vscode-managed-remote-resource';
59 >
60 > export const vscodeUserData = 'vscode-userdata';
61 >
62 > export const vscodeCustomEditor = 'vscode-custom-editor';
63 >
64 > export const vscodeNotebookCell = 'vscode-notebook-cell';
65 > export const vscodeNotebookCellMetadata = 'vscode-notebook-cell-metadata';
66 > export const vscodeNotebookCellMetadataDiff = 'vscode-notebook-cell-metadata-diff';
67 > export const vscodeNotebookCellOutput = 'vscode-notebook-cell-output';
68 > export const vscodeNotebookCellOutputDiff = 'vscode-notebook-cell-output-diff';
69 > export const vscodeNotebookMetadata = 'vscode-notebook-metadata';
70 > export const vscodeInteractiveInput = 'vscode-interactive-input';
71 >
72 > export const vscodeSettings = 'vscode-settings';
73 >
74 > export const vscodeWorkspaceTrust = 'vscode-workspace-trust';
75 >
76 > export const vscodeTerminal = 'vscode-terminal';
77 >
78 > /** Scheme used for the image carousel editor. */
79 > export const vscodeImageCarousel = 'vscode-image-carousel';
80 >
81 > /** Scheme used for code blocks in chat. */
82 > export const vscodeChatCodeBlock = 'vscode-chat-code-block';
83 >
84 > /** Scheme used for LHS of code compare (aka diff) blocks in chat. */
85 > export const vscodeChatCodeCompareBlock = 'vscode-chat-code-compare-block';
86 >
87 > /** Scheme used for the chat input editor. */
88 > export const vscodeChatEditor = 'vscode-chat-editor';
89 >
90 > /** Scheme used for the chat input part */
91 > export const vscodeChatInput = 'chatSessionInput';
92 >
93 > /** Scheme used for local chat session content */
94 > export const vscodeLocalChatSession = 'vscode-chat-session';
95 >
96 > /**
97 > * Scheme used internally for webviews that aren't linked to a resource (i.e. not custom editors)
98 > */
99 > export const webviewPanel = 'webview-panel';
100 >
101 > /**
102 > * Scheme used for loading the wrapper html and script in webviews.
103 > */
104 > export const vscodeWebview = 'vscode-webview';
105 >
106 > /**
107 > * Scheme used for integrated browser tabs using WebContentsView.
108 > */
109 > export const vscodeBrowser = 'vscode-browser';
110 >
111 > /**
112 > * Scheme used for extension pages
113 > */
114 > export const extension = 'extension';
115 >
116 > /**
117 > * Scheme used as a replacement of `file` scheme to load
118 > * files with our custom protocol handler (desktop only).
119 > */
120 > export const vscodeFileResource = 'vscode-file';
121 >
122 > /**
123 > * Scheme used for temporary resources
124 > */
125 > export const tmp = 'tmp';
126 >
127 > /**
128 > * Scheme used vs live share
129 > */
130 > export const vsls = 'vsls';
131 >
132 > /**
133 > * Scheme used for the Source Control commit input's text document
134 > */
135 > export const vscodeSourceControl = 'vscode-scm';
136 >
137 > /**
138 > * Scheme used for input box for creating comments.
139 > */
140 > export const commentsInput = 'comment';
141 >
142 > /**
143 > * Scheme used for special rendering of settings in the release notes
144 > */
145 > export const codeSetting = 'code-setting';
146 >
147 > /**
148 > * Scheme used for output panel resources
149 > */
150 > export const outputChannel = 'output';
151 >
152 > /**
153 > * Scheme used for the accessible view
154 > */
155 > export const accessibleView = 'accessible-view';
156 >
157 > /**
158 > * Used for snapshots of chat edits
159 > */
160 > export const chatEditingSnapshotScheme = 'chat-editing-snapshot-text-model';
161 > export const chatEditingModel = 'chat-editing-text-model';
162 >
163 > /**
164 > * Used for rendering multidiffs in copilot agent sessions
165 > */
166 > export const copilotPr = 'copilot-pr';
167 > }
168 >
169 > export function matchesScheme(target: URI | string, scheme: string): boolean {
170 if (URI.isUri(target)) {
171 return equalsIgnoreCase(target.scheme, scheme);
174 }
175 }
176 > network.ts
177 > export function matchesSomeScheme(target: URI | string, ...schemes: string[]): boolean {
178 return schemes.some(scheme => matchesScheme(target, scheme));
179 }
180 > network.ts
181 > export const connectionTokenCookieName = 'vscode-tkn';
182 > export const connectionTokenQueryName = 'tkn';
183 >
184 > class RemoteAuthoritiesImpl {
185 > private readonly _hosts: { [authority: string]: string | undefined } = Object.create(null);
186 > private readonly _ports: { [authority: string]: number | undefined } = Object.create(null);
187 > private readonly _connectionTokens: { [authority: string]: string | undefined } = Object.create(null);
188 > private _preferredWebSchema: 'http' | 'https' = 'http';
189 > private _delegate: ((uri: URI) => URI) | null = null;
190 > private _serverRootPath: string = '/';
191 >
192 > setPreferredWebSchema(schema: 'http' | 'https') {
193 this._preferredWebSchema = schema;
194 }
195 > network.ts
196 > setDelegate(delegate: (uri: URI) => URI): void {
197 this._delegate = delegate;
198 }
199 > network.ts
200 > setServerRootPath(product: { quality?: string; commit?: string }, serverBasePath: string | undefined): void {
201 this._serverRootPath = paths.posix.join(serverBasePath ?? '/', getServerProductSegment(product));
202 }
203 > network.ts
204 > getServerRootPath(): string {
205 return this._serverRootPath;
206 }
207 > network.ts
208 > private get _remoteResourcesPath(): string {
209 return paths.posix.join(this._serverRootPath, Schemas.vscodeRemoteResource);
210 }
211 > network.ts
212 > set(authority: string, host: string, port: number): void {
213 this._hosts[authority] = host;
214 this._ports[authority] = port;
215 }
216 > network.ts
217 > setConnectionToken(authority: string, connectionToken: string): void {
218 this._connectionTokens[authority] = connectionToken;
219 }
220 > network.ts
221 > getPreferredWebSchema(): 'http' | 'https' {
222 return this._preferredWebSchema;
223 }
224 > network.ts
225 > rewrite(uri: URI): URI {
226 if (this._delegate) {
227 try {
250 });
251 }
252 > } network.ts
253 >
254 > export const RemoteAuthorities = new RemoteAuthoritiesImpl();
255 >
256 > export function getServerProductSegment(product: { quality?: string; commit?: string }) {
257 return `${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`;
258 }
259 > network.ts
260 > /**
261 > * A string pointing to a path inside the app. It should not begin with ./ or ../
262 > */
263 > export type AppResourcePath = (
264 > `a${string}` | `b${string}` | `c${string}` | `d${string}` | `e${string}` | `f${string}`
265 > | `g${string}` | `h${string}` | `i${string}` | `j${string}` | `k${string}` | `l${string}`
266 > | `m${string}` | `n${string}` | `o${string}` | `p${string}` | `q${string}` | `r${string}`
267 > | `s${string}` | `t${string}` | `u${string}` | `v${string}` | `w${string}` | `x${string}`
268 > | `y${string}` | `z${string}`
269 > );
270 >
271 > export const builtinExtensionsPath: AppResourcePath = 'vs/../../extensions';
272 > export const nodeModulesPath: AppResourcePath = 'vs/../../node_modules';
273 > export const nodeModulesAsarPath: AppResourcePath = 'vs/../../node_modules.asar';
274 > export const nodeModulesAsarUnpackedPath: AppResourcePath = 'vs/../../node_modules.asar.unpacked';
275 >
276 > export const VSCODE_AUTHORITY = 'vscode-app';
277 >
278 > class FileAccessImpl {
279 >
280 > private static readonly FALLBACK_AUTHORITY = VSCODE_AUTHORITY;
281 >
282 > /**
283 > * Returns a URI to use in contexts where the browser is responsible
284 > * for loading (e.g. fetch()) or when used within the DOM.
285 > *
286 > * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
287 > */
288 > asBrowserUri(resourcePath: AppResourcePath | ''): URI {
289 const uri = this.toUri(resourcePath);
290 return this.uriToBrowserUri(uri);
291 }
292 > network.ts
293 > /**
294 > * Returns a URI to use in contexts where the browser is responsible
295 > * for loading (e.g. fetch()) or when used within the DOM.
296 > *
297 > * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
298 > */
299 > uriToBrowserUri(uri: URI): URI {
300 // Handle remote URIs via `RemoteAuthorities`
301 if (uri.scheme === Schemas.vscodeRemote) {
328 return uri;
329 }
330 > network.ts
331 > /**
332 > * Returns the `file` URI to use in contexts where node.js
333 > * is responsible for loading.
334 > */
335 > asFileUri(resourcePath: AppResourcePath | ''): URI {
336 const uri = this.toUri(resourcePath);
337 return this.uriToFileUri(uri);
338 }
339 > network.ts
340 > /**
341 > * Returns the `file` URI to use in contexts where node.js
342 > * is responsible for loading.
343 > */
344 > uriToFileUri(uri: URI): URI {
345 // Only convert the URI if it is `vscode-file:` scheme
346 if (uri.scheme === Schemas.vscodeFileResource) {
358 return uri;
359 }
360 > network.ts
361 > private toUri(uriOrModule: URI | string): URI {
362 if (URI.isUri(uriOrModule)) {
363 return uriOrModule;
379 throw new Error('Cannot determine URI for module id!');
380 }
381 > } network.ts
382 >
383 > export const FileAccess = new FileAccessImpl();
384 >
385 > export const CacheControlheaders: Record<string, string> = Object.freeze({
386 > 'Cache-Control': 'no-cache, no-store'
387 > });
388 >
389 > export const DocumentPolicyheaders: Record<string, string> = Object.freeze({
390 > 'Document-Policy': 'include-js-call-stacks-in-crash-reports'
391 > });
392 >
393 > export namespace COI {
394 >
395 > const coiHeaders = new Map<'3' | '2' | '1' | string, Record<string, string>>([
396 > ['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }],
397 > ['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }],
398 > ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }],
399 > ]);
400 >
401 > export const CoopAndCoep = Object.freeze(coiHeaders.get('3'));
402 >
403 > const coiSearchParamName = 'vscode-coi';
404 >
405 > /**
406 > * Extract desired headers from `vscode-coi` invocation
407 > */
408 > export function getHeadersFromQuery(url: string | URI | URL): Record<string, string> | undefined {
409 let params: URLSearchParams | undefined;
410 if (typeof url === 'string') {
421 return coiHeaders.get(value);
422 }
423 > network.ts
424 > /**
425 > * Add the `vscode-coi` query attribute based on wanting `COOP` and `COEP`. Will be a noop when `crossOriginIsolated`
426 > * isn't enabled the current context
427 > */
428 > export function addSearchParam(urlOrSearch: URLSearchParams | Record<string, string>, coop: boolean, coep: boolean): void {
429 if (!(globalThis as typeof globalThis & { crossOriginIsolated?: boolean }).crossOriginIsolated) {
430 // depends on the current context being COI
src/vs/base/common/types.ts 284 covered LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- types.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { assert } from './assert.js';
7 >
8 > /**
9 > * @returns whether the provided parameter is a JavaScript String or not.
10 > */
11 > export function isString(str: unknown): str is string {
12 return (typeof str === 'string');
13 }
14 > types.ts
15 > /**
16 > * @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
17 > */
18 > export function isStringArray(value: unknown): value is string[] {
19 return isArrayOf(value, isString);
20 }
21 > types.ts
22 > /**
23 > * @returns whether the provided parameter is a JavaScript Array and each element in the array satisfies the provided type guard.
24 > */
25 > export function isArrayOf<T>(value: unknown, check: (item: unknown) => item is T): value is T[] {
26 return Array.isArray(value) && value.every(check);
27 }
28 > types.ts
29 > /**
30 > * @returns whether the provided parameter is of type `object` but **not**
31 > * `null`, an `array`, a `regexp`, nor a `date`.
32 > */
33 > export function isObject(obj: unknown): obj is Object {
34 // The method can't do a type cast since there are type (like strings) which
35 // are subclasses of any put not positvely matched by the function. Hence type
41 && !(obj instanceof Date);
42 }
43 > types.ts
44 > /**
45 > * @returns whether the provided parameter is of type `Buffer` or Uint8Array dervived type
46 > */
47 > export function isTypedArray(obj: unknown): obj is Object {
48 const TypedArray = Object.getPrototypeOf(Uint8Array);
49 return typeof obj === 'object'
50 && obj instanceof TypedArray;
51 }
52 > types.ts
53 > /**
54 > * In **contrast** to just checking `typeof` this will return `false` for `NaN`.
55 > * @returns whether the provided parameter is a JavaScript Number or not.
56 > */
57 > export function isNumber(obj: unknown): obj is number {
58 return (typeof obj === 'number' && !isNaN(obj));
59 }
60 > types.ts
61 > /**
62 > * @returns whether the provided parameter is an Iterable, casting to the given generic
63 > */
64 > export function isIterable<T>(obj: unknown): obj is Iterable<T> {
65 // eslint-disable-next-line local/code-no-any-casts
66 return !!obj && typeof (obj as any)[Symbol.iterator] === 'function';
67 }
68 > types.ts
69 > /**
70 > * @returns whether the provided parameter is an Iterable, casting to the given generic
71 > */
72 > export function isAsyncIterable<T>(obj: unknown): obj is AsyncIterable<T> {
73 // eslint-disable-next-line local/code-no-any-casts
74 return !!obj && typeof (obj as any)[Symbol.asyncIterator] === 'function';
75 }
76 > types.ts
77 > /**
78 > * @returns whether the provided parameter is a JavaScript Boolean or not.
79 > */
80 > export function isBoolean(obj: unknown): obj is boolean {
81 return (obj === true || obj === false);
82 }
83 > types.ts
84 > /**
85 > * @returns whether the provided parameter is undefined.
86 > */
87 > export function isUndefined(obj: unknown): obj is undefined {
88 return (typeof obj === 'undefined');
89 }
90 > types.ts
91 > /**
92 > * @returns whether the provided parameter is defined.
93 > */
94 > export function isDefined<T>(arg: T | null | undefined): arg is T {
95 return !isUndefinedOrNull(arg);
96 }
97 > types.ts
98 > /**
99 > * @returns whether the provided parameter is undefined or null.
100 > */
101 > export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
102 return (isUndefined(obj) || obj === null);
103 }
104 > types.ts
105 >
106 > export function assertType(condition: unknown, type?: string): asserts condition {
107 if (!condition) {
108 throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
109 }
110 }
111 > types.ts
112 > /**
113 > * Asserts that the argument passed in is neither undefined nor null.
114 > *
115 > * @see {@link assertDefined} for a similar utility that leverages TS assertion functions to narrow down the type of `arg` to be non-nullable.
116 > */
117 > export function assertReturnsDefined<T>(arg: T | null | undefined): NonNullable<T> {
118 assert(
119 arg !== null && arg !== undefined,
123 return arg;
124 }
125 > types.ts
126 > /**
127 > * Asserts that a provided `value` is `defined` - not `null` or `undefined`,
128 > * throwing an error with the provided error or error message, while also
129 > * narrowing down the type of the `value` to be `NonNullable` using TS
130 > * assertion functions.
131 > *
132 > * @throws if the provided `value` is `null` or `undefined`.
133 > *
134 > * ## Examples
135 > *
136 > * ```typescript
137 > * // an assert with an error message
138 > * assertDefined('some value', 'String constant is not defined o_O.');
139 > *
140 > * // `throws!` the provided error
141 > * assertDefined(null, new Error('Should throw this error.'));
142 > *
143 > * // narrows down the type of `someValue` to be non-nullable
144 > * const someValue: string | undefined | null = blackbox();
145 > * assertDefined(someValue, 'Some value must be defined.');
146 > * console.log(someValue.length); // now type of `someValue` is `string`
147 > * ```
148 > *
149 > * @see {@link assertReturnsDefined} for a similar utility but without assertion.
150 > * @see {@link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions typescript-3-7.html#assertion-functions}
151 > */
152 > export function assertDefined<T>(value: T, error: string | NonNullable<Error>): asserts value is NonNullable<T> {
153 if (value === null || value === undefined) {
154 const errorToThrow = typeof error === 'string' ? new Error(error) : error;
157 }
158 }
159 > types.ts
160 > /**
161 > * Asserts that each argument passed in is neither undefined nor null.
162 > */
163 > export function assertReturnsAllDefined<T1, T2>(t1: T1 | null | undefined, t2: T2 | null | undefined): [T1, T2];
164 > export function assertReturnsAllDefined<T1, T2, T3>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined): [T1, T2, T3];
165 > export function assertReturnsAllDefined<T1, T2, T3, T4>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined, t4: T4 | null | undefined): [T1, T2, T3, T4];
166 > export function assertReturnsAllDefined(...args: (unknown | null | undefined)[]): unknown[] {
167 const result = [];
168
179 return result;
180 }
181 > types.ts
182 > /**
183 > * Checks if the provided value is one of the vales in the provided list.
184 > *
185 > * ## Examples
186 > *
187 > * ```typescript
188 > * // note! item type is a `subset of string`
189 > * type TItem = ':' | '.' | '/';
190 > *
191 > * // note! item is type of `string` here
192 > * const item: string = ':';
193 > * // list of the items to check against
194 > * const list: TItem[] = [':', '.'];
195 > *
196 > * // ok
197 > * assert(
198 > * isOneOf(item, list),
199 > * 'Must succeed.',
200 > * );
201 > *
202 > * // `item` is of `TItem` type now
203 > * ```
204 > */
205 > export const isOneOf = <TType, TSubtype extends TType>(
206 value: TType,
207 validValues: readonly TSubtype[],
211 return validValues.includes(<TSubtype>value);
212 };
213 > types.ts
214 > /**
215 > * Compile-time type check of a variable.
216 > */
217 > export function typeCheck<T = never>(_thing: NoInfer<T>): void { }
218 >
219 > const hasOwnProperty = Object.prototype.hasOwnProperty;
220 >
221 > /**
222 > * @returns whether the provided parameter is an empty JavaScript Object or not.
223 > */
224 > export function isEmptyObject(obj: unknown): obj is object {
225 if (!isObject(obj)) {
226 return false;
235 return true;
236 }
237 > types.ts
238 > /**
239 > * @returns whether the provided parameter is a JavaScript Function or not.
240 > */
241 > export function isFunction(obj: unknown): obj is Function {
242 return (typeof obj === 'function');
243 }
244 > types.ts
245 > /**
246 > * @returns whether the provided parameters is are JavaScript Function or not.
247 > */
248 > export function areFunctions(...objects: unknown[]): boolean {
249 return objects.length > 0 && objects.every(isFunction);
250 }
251 > types.ts
252 > export type TypeConstraint = string | Function;
253 >
254 > export function validateConstraints(args: unknown[], constraints: Array<TypeConstraint | undefined>): void {
255 const len = Math.min(args.length, constraints.length);
256 for (let i = 0; i < len; i++) {
258 }
259 }
260 > types.ts
261 > export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
262
263 if (isString(constraint)) {
283 }
284 }
285 > types.ts
286 > /**
287 > * Helper type assertion that safely upcasts a type to a supertype.
288 > *
289 > * This can be used to make sure the argument correctly conforms to the subtype while still being able to pass it
290 > * to contexts that expects the supertype.
291 > */
292 > export function upcast<Base, Sub extends Base = Base>(x: Sub): Base {
293 return x;
294 }
295 > types.ts
296 > type AddFirstParameterToFunction<T, TargetFunctionsReturnType, FirstParameter> = T extends (...args: any[]) => TargetFunctionsReturnType ?
297 > // Function: add param to function
298 > (firstArg: FirstParameter, ...args: Parameters<T>) => ReturnType<T> :
299 >
300 > // Else: just leave as is
301 > T;
302 >
303 > /**
304 > * Allows to add a first parameter to functions of a type.
305 > */
306 > export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {
307 > // For every property
308 > [K in keyof Target]: AddFirstParameterToFunction<Target[K], TargetFunctionsReturnType, FirstParameter>;
309 > };
310 >
311 > /**
312 > * Given an object with all optional properties, requires at least one to be defined.
313 > * i.e. AtLeastOne<MyObject>;
314 > */
315 > export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
316 >
317 > /**
318 > * Only picks the non-optional properties of a type.
319 > */
320 > export type OmitOptional<T> = { [K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K] };
321 >
322 > /**
323 > * A type that removed readonly-less from all properties of `T`
324 > */
325 > export type Mutable<T> = {
326 > -readonly [P in keyof T]: T[P]
327 > };
328 >
329 > /**
330 > * A type that adds readonly to all properties of T, recursively.
331 > */
332 > export type DeepImmutable<T> = T extends (infer U)[]
333 > ? ReadonlyArray<DeepImmutable<U>>
334 > : T extends ReadonlyArray<infer U>
335 > ? ReadonlyArray<DeepImmutable<U>>
336 > : T extends Map<infer K, infer V>
337 > ? ReadonlyMap<K, DeepImmutable<V>>
338 > : T extends Set<infer U>
339 > ? ReadonlySet<DeepImmutable<U>>
340 > : T extends object
341 > ? {
342 > readonly [K in keyof T]: DeepImmutable<T[K]>;
343 > }
344 > : T;
345 >
346 > /**
347 > * A single object or an array of the objects.
348 > */
349 > export type SingleOrMany<T> = T | T[];
350 >
351 > /**
352 > * Given a `type X = { foo?: string }` checking that an object `satisfies X`
353 > * will ensure each property was explicitly defined, ensuring no properties
354 > * are omitted or forgotten.
355 > */
356 > export type WithDefinedProps<T> = { [K in keyof Required<T>]: T[K] };
357 >
358 >
359 > /**
360 > * A type that recursively makes all properties of `T` required
361 > */
362 > export type DeepRequiredNonNullable<T> = {
363 > [P in keyof T]-?: T[P] extends object ? DeepRequiredNonNullable<T[P]> : Required<NonNullable<T[P]>>;
364 > };
365 >
366 >
367 > /**
368 > * Represents a type that is a partial version of a given type `T`, where all properties are optional and can be deeply nested.
369 > */
370 > export type DeepPartial<T> = {
371 > [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : Partial<T[P]>;
372 > };
373 >
374 > /**
375 > * Represents a type that is a partial version of a given type `T`, except a subset.
376 > */
377 > export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>;
378 >
379 >
380 > type KeysOfUnionType<T> = T extends T ? keyof T : never;
381 > type FilterType<T, TTest> = T extends TTest ? T : never;
382 > type MakeOptionalAndTrue<T extends object> = { [K in keyof T]?: true };
383 >
384 > /**
385 > * Type guard that checks if an object has specific keys and narrows the type accordingly.
386 > *
387 > * @param x - The object to check
388 > * @param key - An object with boolean values indicating which keys to check for
389 > * @returns true if all specified keys exist in the object, false otherwise
390 > *
391 > * @example
392 > * ```typescript
393 > * type A = { a: string };
394 > * type B = { b: number };
395 > * const obj: A | B = getObject();
396 > *
397 > * if (hasKey(obj, { a: true })) {
398 > * // obj is now narrowed to type A
399 > * console.log(obj.a);
400 > * }
401 > * ```
402 > */
403 > export function hasKey<T extends object, TKeys extends MakeOptionalAndTrue<T>>(x: T, key: TKeys): x is FilterType<T, { [K in KeysOfUnionType<T> & keyof TKeys]: unknown }> {
404 for (const k in key) {
405 if (!(k in x)) {
src/vs/base/common/uri.ts 283 covered LOC · 27 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uri.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from './charCode.js';
7 > import { MarshalledId } from './marshallingIds.js';
8 > import * as paths from './path.js';
9 > import { isWindows } from './platform.js';
10 >
11 > const _schemePattern = /^\w[\w\d+.-]*$/;
12 > const _singleSlashStart = /^\//;
13 > const _doubleSlashStart = /^\/\//;
14 >
15 function _validateUri(ret: URI, _strict?: boolean): void {
16
47 }
48 }
49 > uri.ts
50 > // for a while we allowed uris *without* schemes and this is the migration
51 > // for them, e.g. an uri without scheme and without strict-mode warns and falls
52 > // back to the file-scheme. that should cause the least carnage and still be a
53 > // clear warning
54 function _schemeFix(scheme: string, _strict: boolean): string {
55 if (!scheme && !_strict) {
58 return scheme;
59 }
60 > uri.ts
61 > // implements a bit of https://tools.ietf.org/html/rfc3986#section-5
62 function _referenceResolution(scheme: string, path: string): string {
63
79 return path;
80 }
81 > uri.ts
82 > const _empty = '';
83 > const _slash = '/';
84 > const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
85 >
86 > /**
87 > * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
88 > * This class is a simple parser which creates the basic component parts
89 > * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
90 > * and encoding.
91 > *
92 > * ```txt
93 > * foo://example.com:8042/over/there?name=ferret#nose
94 > * \_/ \______________/\_________/ \_________/ \__/
95 > * | | | | |
96 > * scheme authority path query fragment
97 > * | _____________________|__
98 > * / \ / \
99 > * urn:example:animal:ferret:nose
100 > * ```
101 > */
102 > export class URI implements UriComponents {
103 >
104 > static isUri(thing: unknown): thing is URI {
105 if (thing instanceof URI) {
106 return true;
118 && typeof (<URI>thing).toString === 'function';
119 }
120 > uri.ts
121 > /**
122 > * scheme is the 'http' part of 'http://www.example.com/some/path?query#fragment'.
123 > * The part before the first colon.
124 > */
125 > readonly scheme: string;
126 >
127 > /**
128 > * authority is the 'www.example.com' part of 'http://www.example.com/some/path?query#fragment'.
129 > * The part between the first double slashes and the next slash.
130 > */
131 > readonly authority: string;
132 >
133 > /**
134 > * path is the '/some/path' part of 'http://www.example.com/some/path?query#fragment'.
135 > */
136 > readonly path: string;
137 >
138 > /**
139 > * query is the 'query' part of 'http://www.example.com/some/path?query#fragment'.
140 > */
141 > readonly query: string;
142 >
143 > /**
144 > * fragment is the 'fragment' part of 'http://www.example.com/some/path?query#fragment'.
145 > */
146 > readonly fragment: string;
147 >
148 > /**
149 > * @internal
150 > */
151 > protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
152 >
153 > /**
154 > * @internal
155 > */
156 > protected constructor(components: UriComponents);
157 >
158 > /**
159 > * @internal
160 > */
161 > protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) {
162
163 if (typeof schemeOrData === 'object') {
180 }
181 }
182 > uri.ts
183 > // ---- filesystem path -----------------------
184 >
185 > /**
186 > * Returns a string representing the corresponding file system path of this URI.
187 > * Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
188 > * platform specific path separator.
189 > *
190 > * * Will *not* validate the path for invalid characters and semantics.
191 > * * Will *not* look at the scheme of this URI.
192 > * * The result shall *not* be used for display purposes but for accessing a file on disk.
193 > *
194 > *
195 > * The *difference* to `URI#path` is the use of the platform specific separator and the handling
196 > * of UNC paths. See the below sample of a file-uri with an authority (UNC path).
197 > *
198 > * ```ts
199 > const u = URI.parse('file://server/c$/folder/file.txt')
200 > u.authority === 'server'
201 > u.path === '/shares/c$/file.txt'
202 > u.fsPath === '\\server\c$\folder\file.txt'
203 > ```
204 > *
205 > * Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
206 > * namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
207 > * with URIs that represent files on disk (`file` scheme).
208 > */
209 > get fsPath(): string {
210 // if (this.scheme !== 'file') {
211 // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
213 return uriToFsPath(this, false);
214 }
215 > uri.ts
216 > // ---- modify to new -------------------------
217 >
218 > with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
219
220 if (!change) {
260 return new Uri(scheme, authority, path, query, fragment);
261 }
262 > uri.ts
263 > // ---- parse & validate ------------------------
264 >
265 > /**
266 > * Creates a new URI from a string, e.g. `http://www.example.com/some/path`,
267 > * `file:///usr/home`, or `scheme:with/path`.
268 > *
269 > * @param value A string which represents an URI (see `URI#toString`).
270 > */
271 > static parse(value: string, _strict: boolean = false): URI {
272 const match = _regexp.exec(value);
273 if (!match) {
283 );
284 }
285 > uri.ts
286 > /**
287 > * Creates a new URI from a file system path, e.g. `c:\my\files`,
288 > * `/usr/home`, or `\\server\share\some\path`.
289 > *
290 > * The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
291 > * as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
292 > * `URI.parse('file://' + path)` because the path might contain characters that are
293 > * interpreted (# and ?). See the following sample:
294 > * ```ts
295 > const good = URI.file('/coding/c#/project1');
296 > good.scheme === 'file';
297 > good.path === '/coding/c#/project1';
298 > good.fragment === '';
299 > const bad = URI.parse('file://' + '/coding/c#/project1');
300 > bad.scheme === 'file';
301 > bad.path === '/coding/c'; // path is now broken
302 > bad.fragment === '/project1';
303 > ```
304 > *
305 > * @param path A file system path (see `URI#fsPath`)
306 > */
307 > static file(path: string): URI {
308
309 let authority = _empty;
331 return new Uri('file', authority, path, _empty, _empty);
332 }
333 > uri.ts
334 > /**
335 > * Creates new URI from uri components.
336 > *
337 > * Unless `strict` is `true` the scheme is defaults to be `file`. This function performs
338 > * validation and should be used for untrusted uri components retrieved from storage,
339 > * user input, command arguments etc
340 > */
341 > static from(components: UriComponents, strict?: boolean): URI {
342 const result = new Uri(
343 components.scheme,
350 return result;
351 }
352 > uri.ts
353 > /**
354 > * Join a URI path with path fragments and normalizes the resulting path.
355 > *
356 > * @param uri The input URI.
357 > * @param pathFragment The path fragment to add to the URI path.
358 > * @returns The resulting URI.
359 > */
360 > static joinPath(uri: URI, ...pathFragment: string[]): URI {
361 if (!uri.path) {
362 throw new Error(`[UriError]: cannot call joinPath on URI without path: ${uri.toString()}`);
370 return uri.with({ path: newPath });
371 }
372 > uri.ts
373 > // ---- printing/externalize ---------------------------
374 >
375 > /**
376 > * Creates a string representation for this URI. It's guaranteed that calling
377 > * `URI.parse` with the result of this function creates an URI which is equal
378 > * to this URI.
379 > *
380 > * * The result shall *not* be used for display purposes but for externalization or transport.
381 > * * The result will be encoded using the percentage encoding and encoding happens mostly
382 > * ignore the scheme-specific encoding rules.
383 > *
384 > * @param skipEncoding Do not encode the result, default is `false`
385 > */
386 > toString(skipEncoding: boolean = false): string {
387 return _asFormatted(this, skipEncoding);
388 }
389 > uri.ts
390 > toJSON(): UriComponents {
391 return this;
392 }
393 > uri.ts
394 > /**
395 > * A helper function to revive URIs.
396 > *
397 > * **Note** that this function should only be used when receiving URI#toJSON generated data
398 > * and that it doesn't do any validation. Use {@link URI.from} when received "untrusted"
399 > * uri components such as command arguments or data from storage.
400 > *
401 > * @param data The URI components or URI to revive.
402 > * @returns The revived URI or undefined or null.
403 > */
404 > static revive(data: UriComponents | URI): URI;
405 > static revive(data: UriComponents | URI | undefined): URI | undefined;
406 > static revive(data: UriComponents | URI | null): URI | null;
407 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
408 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null {
409 if (!data) {
410 return data;
418 }
419 }
420 > uri.ts
421 > [Symbol.for('debug.description')]() {
422 return `URI(${this.toString()})`;
423 }
424 > } uri.ts
425 >
426 > export interface UriComponents {
427 > scheme: string;
428 > authority?: string;
429 > path?: string;
430 > query?: string;
431 > fragment?: string;
432 > }
433 >
434 > export function isUriComponents(thing: unknown): thing is UriComponents {
435 if (!thing || typeof thing !== 'object') {
436 return false;
442 && (typeof (<UriComponents>thing).fragment === 'string' || typeof (<UriComponents>thing).fragment === 'undefined');
443 }
444 > uri.ts
445 > interface UriState extends UriComponents {
446 > $mid: MarshalledId.Uri;
447 > external?: string;
448 > fsPath?: string;
449 > _sep?: 1;
450 > }
451 >
452 > const _pathSepMarker = isWindows ? 1 : undefined;
453 >
454 > // This class exists so that URI is compatible with vscode.Uri (API).
455 class Uri extends URI {
456
457 _formatted: string | null = null;
458 _fsPath: string | null = null;
459 > uri.ts
460 > override get fsPath(): string {
461 if (!this._fsPath) {
462 this._fsPath = uriToFsPath(this, false);
464 return this._fsPath;
465 }
466 > uri.ts
467 > override toString(skipEncoding: boolean = false): string {
468 if (!skipEncoding) {
469 if (!this._formatted) {
476 }
477 }
478 > uri.ts
479 > override toJSON(): UriComponents {
480 // eslint-disable-next-line local/code-no-dangerous-type-assertions
481 const res = <UriState>{
512 return res;
513 }
514 > } uri.ts
515 >
516 > // reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
517 > const encodeTable: { [ch: number]: string } = {
518 > [CharCode.Colon]: '%3A', // gen-delims
519 > [CharCode.Slash]: '%2F',
520 > [CharCode.QuestionMark]: '%3F',
521 > [CharCode.Hash]: '%23',
522 > [CharCode.OpenSquareBracket]: '%5B',
523 > [CharCode.CloseSquareBracket]: '%5D',
524 > [CharCode.AtSign]: '%40',
525 >
526 > [CharCode.ExclamationMark]: '%21', // sub-delims
527 > [CharCode.DollarSign]: '%24',
528 > [CharCode.Ampersand]: '%26',
529 > [CharCode.SingleQuote]: '%27',
530 > [CharCode.OpenParen]: '%28',
531 > [CharCode.CloseParen]: '%29',
532 > [CharCode.Asterisk]: '%2A',
533 > [CharCode.Plus]: '%2B',
534 > [CharCode.Comma]: '%2C',
535 > [CharCode.Semicolon]: '%3B',
536 > [CharCode.Equals]: '%3D',
537 >
538 > [CharCode.Space]: '%20',
539 > };
540 >
541 function encodeURIComponentFast(uriComponent: string, isPath: boolean, isAuthority: boolean): string {
542 let res: string | undefined = undefined;
602 return res !== undefined ? res : uriComponent;
603 }
604 > uri.ts
605 function encodeURIComponentMinimal(path: string): string {
606 let res: string | undefined = undefined;
620 return res !== undefined ? res : path;
621 }
622 > uri.ts
623 > /**
624 > * Compute `fsPath` for the given uri
625 > */
626 > export function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string {
627
628 let value: string;
650 return value;
651 }
652 > uri.ts
653 > /**
654 > * Create the external version of a uri
655 > */
656 function _asFormatted(uri: URI, skipEncoding: boolean): string {
657
723 return res;
724 }
725 > uri.ts
726 > // --- decode
727 >
728 function decodeURIComponentGraceful(str: string): string {
729 try {
737 }
738 }
739 > uri.ts
740 > const _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
741 >
742 function percentDecode(str: string): string {
743 if (!str.match(_rEncodedAsHex)) {
746 return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match));
747 }
748 > uri.ts
749 > /**
750 > * Mapped-type that replaces all occurrences of URI with UriComponents
751 > */
752 > export type UriDto<T> = { [K in keyof T]: T[K] extends URI
753 > ? UriComponents
754 > : UriDto<T[K]> };
src/vs/base/common/resources.ts 253 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- resources.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from './charCode.js';
7 > import * as extpath from './extpath.js';
8 > import { Schemas } from './network.js';
9 > import * as paths from './path.js';
10 > import { isLinux, isWindows } from './platform.js';
11 > import { compare as strCompare, equalsIgnoreCase } from './strings.js';
12 > import { URI, uriToFsPath } from './uri.js';
13 >
14 > export function originalFSPath(uri: URI): string {
15 return uriToFsPath(uri, true);
16 }
18 > //#region IExtUri
19 >
20 > export interface IExtUri {
21 >
22 > // --- identity
23 >
24 > /**
25 > * Compares two uris.
26 > *
27 > * @param uri1 Uri
28 > * @param uri2 Uri
29 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
30 > */
31 > compare(uri1: URI, uri2: URI, ignoreFragment?: boolean): number;
32 >
33 > /**
34 > * Tests whether two uris are equal
35 > *
36 > * @param uri1 Uri
37 > * @param uri2 Uri
38 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
39 > */
40 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment?: boolean): boolean;
41 >
42 > /**
43 > * Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
44 > *
45 > * @param base A uri which is "longer" or at least same length as `parentCandidate`
46 > * @param parentCandidate A uri which is "shorter" or up to same length as `base`
47 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
48 > */
49 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean;
50 >
51 > /**
52 > * Creates a key from a resource URI to be used to resource comparison and for resource maps.
53 > * @see {@link ResourceMap}
54 > * @param uri Uri
55 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
56 > */
57 > getComparisonKey(uri: URI, ignoreFragment?: boolean): string;
58 >
59 > /**
60 > * Whether the casing of the path-component of the uri should be ignored.
61 > */
62 > ignorePathCasing(uri: URI): boolean;
63 >
64 > // --- path math
65 >
66 > basenameOrAuthority(resource: URI): string;
67 >
68 > /**
69 > * Returns the basename of the path component of an uri.
70 > * @param resource
71 > */
72 > basename(resource: URI): string;
73 >
74 > /**
75 > * Returns the extension of the path component of an uri.
76 > * @param resource
77 > */
78 > extname(resource: URI): string;
79 > /**
80 > * Return a URI representing the directory of a URI path.
81 > *
82 > * @param resource The input URI.
83 > * @returns The URI representing the directory of the input URI.
84 > */
85 > dirname(resource: URI): URI;
86 > /**
87 > * Join a URI path with path fragments and normalizes the resulting path.
88 > *
89 > * @param resource The input URI.
90 > * @param pathFragment The path fragment to add to the URI path.
91 > * @returns The resulting URI.
92 > */
93 > joinPath(resource: URI, ...pathFragment: string[]): URI;
94 > /**
95 > * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
96 > *
97 > * @param resource The URI to normalize the path.
98 > * @returns The URI with the normalized path.
99 > */
100 > normalizePath(resource: URI): URI;
101 > /**
102 > *
103 > * @param from
104 > * @param to
105 > */
106 > relativePath(from: URI, to: URI): string | undefined;
107 > /**
108 > * Resolves an absolute or relative path against a base URI.
109 > * The path can be relative or absolute posix or a Windows path
110 > */
111 > resolvePath(base: URI, path: string): URI;
112 >
113 > // --- misc
114 >
115 > /**
116 > * Returns true if the URI path is absolute.
117 > */
118 > isAbsolutePath(resource: URI): boolean;
119 > /**
120 > * Tests whether the two authorities are the same
121 > */
122 > isEqualAuthority(a1: string, a2: string): boolean;
123 > /**
124 > * Returns true if the URI path has a trailing path separator
125 > */
126 > hasTrailingPathSeparator(resource: URI, sep?: string): boolean;
127 > /**
128 > * Removes a trailing path separator, if there's one.
129 > * Important: Doesn't remove the first slash, it would make the URI invalid
130 > */
131 > removeTrailingPathSeparator(resource: URI, sep?: string): URI;
132 > /**
133 > * Adds a trailing path separator to the URI if there isn't one already.
134 > * For example, c:\ would be unchanged, but c:\users would become c:\users\
135 > */
136 > addTrailingPathSeparator(resource: URI, sep?: string): URI;
137 > }
138 >
139 > export class ExtUri implements IExtUri {
140 >
141 > constructor(private _ignorePathCasing: (uri: URI) => boolean) { }
142 >
143 > compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number {
144 if (uri1 === uri2) {
145 return 0;
147 return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
148 }
149 > resources.ts
150 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean {
151 if (uri1 === uri2) {
152 return true;
157 return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment);
158 }
159 > resources.ts
160 > getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
161 return uri.with({
162 path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
164 }).toString();
165 }
166 > resources.ts
167 > ignorePathCasing(uri: URI): boolean {
168 return this._ignorePathCasing(uri);
169 }
170 > resources.ts
171 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean {
172 if (base.scheme === parentCandidate.scheme) {
173 if (base.scheme === Schemas.file) {
180 return false;
181 }
182 > resources.ts
183 > // --- path math
184 >
185 > joinPath(resource: URI, ...pathFragment: string[]): URI {
186 return URI.joinPath(resource, ...pathFragment);
187 }
188 > resources.ts
189 > basenameOrAuthority(resource: URI): string {
190 return basename(resource) || resource.authority;
191 }
192 > resources.ts
193 > basename(resource: URI, suffix?: string): string {
194 return paths.posix.basename(resource.path, suffix);
195 }
196 > resources.ts
197 > extname(resource: URI): string {
198 return paths.posix.extname(resource.path);
199 }
200 > resources.ts
201 > dirname(resource: URI): URI {
202 if (resource.path.length === 0) {
203 return resource;
217 });
218 }
219 > resources.ts
220 > normalizePath(resource: URI): URI {
221 if (!resource.path.length) {
222 return resource;
232 });
233 }
234 > resources.ts
235 > relativePath(from: URI, to: URI): string | undefined {
236 if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
237 return undefined;
257 return paths.posix.relative(fromPath, toPath);
258 }
259 > resources.ts
260 > resolvePath(base: URI, path: string): URI {
261 if (base.scheme === Schemas.file) {
262 const newURI = URI.file(paths.resolve(originalFSPath(base), path));
271 });
272 }
273 > resources.ts
274 > // --- misc
275 >
276 > isAbsolutePath(resource: URI): boolean {
277 return !!resource.path && resource.path[0] === '/';
278 }
279 > resources.ts
280 > isEqualAuthority(a1: string | undefined, a2: string | undefined) {
281 return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2));
282 }
283 > resources.ts
284 > hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean {
285 if (resource.scheme === Schemas.file) {
286 const fsp = originalFSPath(resource);
291 }
292 }
293 > resources.ts
294 > removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
295 // Make sure that the path isn't a drive letter. A trailing separator there is not removable.
296 if (hasTrailingPathSeparator(resource, sep)) {
299 return resource;
300 }
301 > resources.ts
302 > addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
303 let isRootSep: boolean = false;
304 if (resource.scheme === Schemas.file) {
315 return resource;
316 }
317 > } resources.ts
318 >
319 >
320 > /**
321 > * Unbiased utility that takes uris "as they are". This means it can be interchanged with
322 > * uri#toString() usages. The following is true
323 > * ```
324 > * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
325 > * ```
326 > */
327 > export const extUri = new ExtUri(() => false);
328 >
329 > /**
330 > * BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you
331 > * understand what you are doing.
332 > *
333 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
334 > *
335 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
336 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
337 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
338 > * casing matters.
339 > */
340 > export const extUriBiasedIgnorePathCase = new ExtUri(uri => {
341 // A file scheme resource is in the same platform as code, so ignore case for non linux platforms
342 // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
343 return uri.scheme === Schemas.file ? !isLinux : true;
344 });
345 > resources.ts
346 >
347 > /**
348 > * BIASED utility that always ignores the casing of uris paths. ONLY use this util if you
349 > * understand what you are doing.
350 > *
351 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
352 > *
353 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
354 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
355 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
356 > * casing matters.
357 > */
358 > export const extUriIgnorePathCase = new ExtUri(_ => true);
359 >
360 > export const isEqual = extUri.isEqual.bind(extUri);
361 > export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri);
362 > export const getComparisonKey = extUri.getComparisonKey.bind(extUri);
363 > export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
364 > export const basename = extUri.basename.bind(extUri);
365 > export const extname = extUri.extname.bind(extUri);
366 > export const dirname = extUri.dirname.bind(extUri);
367 > export const joinPath = extUri.joinPath.bind(extUri);
368 > export const normalizePath = extUri.normalizePath.bind(extUri);
369 > export const relativePath = extUri.relativePath.bind(extUri);
370 > export const resolvePath = extUri.resolvePath.bind(extUri);
371 > export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri);
372 > export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
373 > export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
374 > export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri);
375 > export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri);
376 >
377 > //#endregion
378 >
379 > export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] {
380 const distinctParents: T[] = [];
381 for (let i = 0; i < items.length; i++) {
396 return distinctParents;
397 }
398 > resources.ts
399 > /**
400 > * Data URI related helpers.
401 > */
402 > export namespace DataUri {
403 >
404 > export const META_DATA_LABEL = 'label';
405 > export const META_DATA_DESCRIPTION = 'description';
406 > export const META_DATA_SIZE = 'size';
407 > export const META_DATA_MIME = 'mime';
408 >
409 > export function parseMetaData(dataUri: URI): Map<string, string> {
410 const metadata = new Map<string, string>();
411
429 return metadata;
430 }
431 > } resources.ts
432 >
433 > export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI {
434 if (authority) {
435 let path = resource.path;
src/vs/base/test/common/virtualScheduling/processor.ts 215 covered LOC · 19 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- processor.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../common/cancellation.js';
7 > import { Disposable, DisposableStore, IDisposable } from '../../../common/lifecycle.js';
8 > import { Embedding, nextMacrotask } from './embedding.js';
9 > import { TimeApi } from './timeApi.js';
10 > import { ROOT_TRACE, TraceContext } from './trace.js';
11 > import { EventSource, VirtualClock, VirtualEvent, VirtualTime } from './virtualClock.js';
12 >
13 > // ============================================================================
14 > // Termination policy
15 > // ============================================================================
16 >
17 > /**
18 > * When a {@link Run} should terminate.
19 > *
20 > * Greenfield design choice: termination is *always* explicit. There is no
21 > * "bare run()" that terminates on first empty queue, because that creates a
22 > * race with the caller's microtask chain (the run can resolve before the
23 > * caller's `.then` has had a chance to schedule).
24 > */
25 > export type TerminationPolicy =
26 > /** Resolve as soon as the virtual queue is empty. */
27 > | { readonly kind: 'idle' }
28 > /** Resolve when the token is cancelled AND the queue is empty. */
29 > | { readonly kind: 'token'; readonly token: CancellationToken }
30 > /** Resolve when virtual time has reached `time` and all events scheduled
31 > * at or before `time` have been processed. A sentinel event at `time`
32 > * is scheduled by the processor so virtual time always reaches it. */
33 > | { readonly kind: 'time'; readonly time: VirtualTime };
34 >
35 > export const untilIdle: TerminationPolicy = { kind: 'idle' };
36 > export function untilToken(token: CancellationToken): TerminationPolicy { return { kind: 'token', token }; }
37 > export function untilTime(time: VirtualTime): TerminationPolicy { return { kind: 'time', time }; }
38 >
39 > export interface RunOptions {
40 > readonly until: TerminationPolicy;
41 > /** Maximum number of virtual events this run will execute. Default: 100. */
42 > readonly maxEvents?: number;
43 > /** Maximum causal-trace depth this run will tolerate. Useful for catching
44 > * runaway self-rescheduling timers. */
45 > readonly maxTraceDepth?: number;
46 > }
47 >
48 > // ============================================================================
49 > // Run — internal state for a single processor.run() invocation
50 > // ============================================================================
51 >
52 > type RunStatus = 'continue' | 'done' | { readonly error: Error };
53 >
54 > class Run {
55 > private static _idCounter = 0;
56 > public readonly id = ++Run._idCounter;
57 >
58 > public readonly promise: Promise<void>;
59 > private _resolve!: () => void;
60 > private _reject!: (e: Error) => void;
61 > private _settled = false;
62 > public get settled(): boolean { return this._settled; }
63 >
64 > constructor(
65 public readonly options: RunOptions,
66 public readonly executedAtStart: number,
69 this.promise = new Promise<void>((res, rej) => { this._resolve = res; this._reject = rej; });
70 }
72 > settle(error?: Error): void {
73 if (this._settled) { return; }
74 this._settled = true;
75 if (error) { this._reject(error); } else { this._resolve(); }
76 }
78 > evaluate(clock: VirtualClock, executedTotal: number, makeOverflow: () => Error): RunStatus {
79 const local = executedTotal - this.executedAtStart;
80 if (local >= this.maxEvents && clock.hasEvents) {
98 }
99 }
100 > } processor.ts
101 >
102 > // ============================================================================
103 > // Step outcome — what the pure state machine tells the trampoline
104 > // ============================================================================
105 >
106 > type StepOutcome =
107 > /** Either a virtual event was executed, or a run was rejected for a
108 > * bookkeeping reason (depth/event overflow). The trampoline should let
109 > * the embedding decide how to reach the next step. */
110 > | 'progress'
111 > /** No actionable event under any active deadline. The trampoline should
112 > * park until something wakes the processor. */
113 > | 'park'
114 > /** No active runs. The trampoline should stop driving. */
115 > | 'quiesce';
116 >
117 > // ============================================================================
118 > // VirtualTimeProcessor
119 > // ============================================================================
120 >
121 > export interface VirtualTimeProcessorOptions {
122 > readonly defaultMaxEvents?: number;
123 > }
124 >
125 > /**
126 > * # VirtualTimeProcessor
127 > *
128 > * Drives a {@link VirtualClock} from the host event loop. This is the
129 > * **embedding** of a small virtual event loop into the host event loop.
130 > *
131 > * ## Responsibilities, separated
132 > *
133 > * - {@link _step} is a *pure* state-machine advance. It reads the clock,
134 > * decides what to do, optionally executes one virtual event, and returns
135 > * a {@link StepOutcome}. It never touches host time.
136 > *
137 > * - {@link _drive} is the *trampoline*. It calls `_step` and lets the
138 > * {@link Embedding} decide whether to loop in place (`'continueSync'`)
139 > * or schedule the next iteration on the host (`'cbScheduled'`). It is
140 > * the only code that touches host time.
141 > *
142 > * - {@link Run} carries the user's termination predicate. Runs are pure
143 > * over `_step`'s observations; they never schedule.
144 > *
145 > * ## Invariants
146 > *
147 > * 1. **Single driver.** At any moment at most one `_drive` invocation is
148 > * active per processor (the `_inDrive` guard).
149 > *
150 > * 2. **Step is pure w.r.t. host time.** `_step` only reads the clock,
151 > * mutates the run set via settling, and synchronously runs at most one
152 > * virtual event. It never calls into a host time API.
153 > *
154 > * 3. **Embedding chooses the host primitive.** Whether the next step runs
155 > * inline, after a microtask drain, or on a paint frame is entirely the
156 > * embedding's decision — *per event*.
157 > *
158 > * 4. **Park is breakable.** While parked, the processor wakes on
159 > * {@link VirtualClock.onEventScheduled}, on a token cancellation, and
160 > * on a new run being added.
161 > *
162 > * 5. **Disposal is terminal.** After dispose, all runs are rejected and
163 > * `_step`/`_drive` short-circuit to `'quiesce'`.
164 > *
165 > * ## On the trace-reset sink
166 > *
167 > * The trace context's deferred reset (see {@link TraceContext.runAsHandler})
168 > * needs a "fire after the microtask closure" primitive. The processor passes
169 > * its *own* {@link nextMacrotask} as that sink, so the reset goes through
170 > * the same primitive the embedding uses for its own host hops. This removes
171 > * any race between the processor's hops and the trace-reset timer.
172 > */
173 > export class VirtualTimeProcessor extends Disposable {
174 >
175 > private readonly _runs = new Map<Run, IDisposable>();
176 > private readonly _history: VirtualEvent[] = [];
177 > private _executedTotal = 0;
178 > private _disposed = false;
179 >
180 > private _inDrive = false;
181 > private _parkCleanup: IDisposable | undefined;
182 >
183 > private readonly _defaultMaxEvents: number;
184 >
185 > public get history(): readonly VirtualEvent[] { return this._history; }
186 > public get executedTotal(): number { return this._executedTotal; }
187 >
188 > constructor(
189 private readonly _clock: VirtualClock,
190 private readonly _embedding: Embedding,
196 this._register({ dispose: () => this._onDispose() });
197 }
198 > processor.ts
199 > // ---- Public API -----------------------------------------------------
200 >
201 > /** Start a run with the given termination policy. */
202 > run(options: RunOptions): Promise<void> {
203 const run = new Run(options, this._executedTotal, options.maxEvents ?? this._defaultMaxEvents);
204 const cleanup = new DisposableStore();
226 return run.promise;
227 }
228 > processor.ts
229 > // ---- The pure step --------------------------------------------------
230 >
231 > private _step(): StepOutcome {
232 if (this._disposed) { return 'quiesce'; }
233
254 return 'progress';
255 }
256 > processor.ts
257 > private _executeOne(event: VirtualEvent): void {
258 try {
259 TraceContext.instance.runAsHandler(
280 }
281 }
282 > processor.ts
283 > // ---- The trampoline -------------------------------------------------
284 >
285 > private readonly _drive = (): void => {
286 > if (this._inDrive) { return; }
287 > this._inDrive = true;
288 > try {
289 > while (true) {
290 > const outcome = this._step();
291 > if (outcome === 'quiesce') { return; }
292 > if (outcome === 'park') { this._park(); return; } processor.ts
293 >
294 > // 'progress': read the next event so the embedding can pick a
295 > // per-event primitive. If there is none, loop and let the next
296 > // `_step` decide between 'park' and 'quiesce'.
297 > const next = this._clock.peekNext();
298 > if (next === undefined) { continue; }
299 > processor.ts
300 > const choice = this._embedding(next, this._drive);
301 > if (choice === 'cbScheduled') { return; }
302 > // 'continueSync': loop in place. processor.ts
303 > }
304 > } finally {
305 > this._inDrive = false;
306 > }
307 > };
308 >
309 > // ---- Park & wake ----------------------------------------------------
310 >
311 > private _park(): void {
312 this._unpark();
313 const store = new DisposableStore();
315 this._parkCleanup = store;
316 }
317 > processor.ts
318 > private _unpark(): void {
319 this._parkCleanup?.dispose();
320 this._parkCleanup = undefined;
321 }
322 > processor.ts
323 > private _wake(): void {
324 if (this._disposed) { return; }
325 this._unpark();
337 nextMacrotask(this._realApi, this._drive);
338 }
339 > processor.ts
340 > // ---- Run lifecycle --------------------------------------------------
341 >
342 > private _settleFinishedRuns(): void {
343 for (const run of [...this._runs.keys()]) {
344 if (run.settled) { continue; }
351 }
352 }
353 > processor.ts
354 > private _settleRun(run: Run, error?: Error): void {
355 const cleanup = this._runs.get(run);
356 if (!cleanup) { return; }
359 run.settle(error);
360 }
361 > processor.ts
362 > private _buildOverflow(run: Run): Error {
363 const local = this._executedTotal - run.executedAtStart;
364 return new Error(
367 );
368 }
369 > processor.ts
370 > private _buildDepthOverflow(run: Run, depth: number): Error {
371 return new Error(
372 `[VirtualTimeProcessor] Run #${run.id} exceeded maxTraceDepth (${run.options.maxTraceDepth}) — ` +
375 );
376 }
377 > processor.ts
378 > private _onDispose(): void {
379 this._disposed = true;
380 this._unpark();
382 for (const run of [...this._runs.keys()]) { this._settleRun(run, err); }
383 }
384 > } processor.ts
src/vs/base/common/observableInternal/base.ts 206 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- base.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { DisposableStore, onUnexpectedError } from './commonFacade/deps.js';
7 >
8 > /**
9 > * Represents an observable value.
10 > *
11 > * @template T The type of the values the observable can hold.
12 > */
13 > // This interface exists so that, for example for string observables,
14 > // typescript renders the type as `IObservable<string>` instead of `IObservable<string, unknown>`.
15 > export interface IObservable<T> extends IObservableWithChange<T, unknown> { }
16 >
17 > /**
18 > * Represents an observable value.
19 > *
20 > * @template T The type of the values the observable can hold.
21 > * @template TChange The type used to describe value changes
22 > * (usually `void` and only used in advanced scenarios).
23 > * While observers can miss temporary values of an observable,
24 > * they will receive all change values (as long as they are subscribed)!
25 > */
26 > export interface IObservableWithChange<T, TChange = unknown> {
27 > /**
28 > * Returns the current value.
29 > *
30 > * Calls {@link IObserver.handleChange} if the observable notices that the value changed.
31 > * Must not be called from {@link IObserver.handleChange}!
32 > */
33 > get(): T;
34 >
35 > /**
36 > * Forces the observable to check for changes and report them.
37 > *
38 > * Has the same effect as calling {@link IObservable.get}, but does not force the observable
39 > * to actually construct the value, e.g. if change deltas are used.
40 > * Calls {@link IObserver.handleChange} if the observable notices that the value changed.
41 > * Must not be called from {@link IObserver.handleChange}!
42 > */
43 > reportChanges(): void;
44 >
45 > /**
46 > * Adds the observer to the set of subscribed observers.
47 > * This method is idempotent.
48 > */
49 > addObserver(observer: IObserver): void;
50 >
51 > /**
52 > * Removes the observer from the set of subscribed observers.
53 > * This method is idempotent.
54 > */
55 > removeObserver(observer: IObserver): void;
56 >
57 > // #region These members have a standard implementation and are only part of the interface for convenience.
58 >
59 > /**
60 > * Reads the current value and subscribes the reader to this observable.
61 > *
62 > * Calls {@link IReader.readObservable} if a reader is given, otherwise {@link IObservable.get}
63 > * (see {@link ConvenientObservable.read} for the implementation).
64 > */
65 > read(reader: IReader | undefined): T;
66 >
67 > /**
68 > * Makes sure this value is computed eagerly.
69 > */
70 > recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
71 >
72 > /**
73 > * Makes sure this value is cached.
74 > */
75 > keepObserved(store: DisposableStore): IObservable<T>;
76 >
77 > /**
78 > * Creates a derived observable that depends on this observable.
79 > * Use the reader to read other observables
80 > * (see {@link ConvenientObservable.map} for the implementation).
81 > */
82 > map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
83 > map<TNew>(owner: object, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
84 >
85 > flatten<TNew>(this: IObservable<IObservable<TNew>>): IObservable<TNew>;
86 >
87 > /**
88 > * ONLY FOR DEBUGGING!
89 > * Logs computations of this derived.
90 > */
91 > log(): IObservableWithChange<T, TChange>;
92 >
93 > /**
94 > * A human-readable name for debugging purposes.
95 > */
96 > readonly debugName: string;
97 >
98 > /**
99 > * This property captures the type of the change object. Do not use it at runtime!
100 > */
101 > readonly TChange: TChange;
102 >
103 > // #endregion
104 > }
105 >
106 > /**
107 > * Represents an observer that can be subscribed to an observable.
108 > *
109 > * If an observer is subscribed to an observable and that observable didn't signal
110 > * a change through one of the observer methods, the observer can assume that the
111 > * observable didn't change.
112 > * If an observable reported a possible change, {@link IObservable.reportChanges} forces
113 > * the observable to report an actual change if there was one.
114 > */
115 > export interface IObserver {
116 > /**
117 > * Signals that the given observable might have changed and a transaction potentially modifying that observable started.
118 > * Before the given observable can call this method again, is must call {@link IObserver.endUpdate}.
119 > *
120 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
121 > * The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
122 > */
123 > beginUpdate<T>(observable: IObservable<T>): void;
124 >
125 > /**
126 > * Signals that the transaction that potentially modified the given observable ended.
127 > * This is a good place to react to (potential) changes.
128 > */
129 > endUpdate<T>(observable: IObservable<T>): void;
130 >
131 > /**
132 > * Signals that the given observable might have changed.
133 > * The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
134 > *
135 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
136 > * The change should be processed lazily or in {@link IObserver.endUpdate}.
137 > */
138 > handlePossibleChange<T>(observable: IObservable<T>): void;
139 >
140 > /**
141 > * Signals that the given {@link observable} changed.
142 > *
143 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
144 > * The change should be processed lazily or in {@link IObserver.endUpdate}.
145 > *
146 > * @param change Indicates how or why the value changed.
147 > */
148 > handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void;
149 > }
150 >
151 > /**
152 > * A reader allows code to track what it depends on, so the caller knows when the computed value or produced side-effect is no longer valid.
153 > * Use `derived(reader => ...)` to turn code that needs a reader into an observable value.
154 > */
155 > export interface IReader {
156 > /**
157 > * Reads the value of an observable and subscribes to it.
158 > */
159 > readObservable<T>(observable: IObservableWithChange<T, any>): T;
160 > }
161 >
162 > export interface ISettable<T, TChange = void> {
163 > /**
164 > * Sets the value of the observable.
165 > * Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction).
166 > *
167 > * @param transaction When given, value changes are handled on demand or when the transaction ends.
168 > * @param change Describes how or why the value changed.
169 > */
170 > set(value: T, transaction: ITransaction | undefined, change: TChange): void;
171 > }
172 >
173 > export interface ITransaction {
174 > /**
175 > * Calls {@link Observer.beginUpdate} immediately
176 > * and {@link Observer.endUpdate} when the transaction ends.
177 > */
178 > updateObserver(observer: IObserver, observable: IObservableWithChange<any, any>): void;
179 > }
180 >
181 > /**
182 > * This function is used to indicate that the caller recovered from an error that indicates a bug.
183 > */
184 > export function handleBugIndicatingErrorRecovery(message: string) {
185 const err = new Error('BugIndicatingErrorRecovery: ' + message);
186 onUnexpectedError(err);
187 console.error('recovered from an error that indicates a bug', err);
188 }
189 > base.ts
190 > /**
191 > * A settable observable.
192 > */
193 > export interface ISettableObservable<T, TChange = void> extends IObservableWithChange<T, TChange>, ISettable<T, TChange> {
194 > }
195 >
196 > export interface IReaderWithStore extends IReader {
197 > /**
198 > * Items in this store get disposed just before the observable recomputes/reruns or when it becomes unobserved.
199 > */
200 > get store(): DisposableStore;
201 >
202 > /**
203 > * Items in this store get disposed just after the observable recomputes/reruns or when it becomes unobserved.
204 > * This is important if the current run needs the undisposed result from the last run.
205 > *
206 > * Warning: Items in this store might still get disposed before dependents (that read the now disposed value in the past) are recomputed with the new (undisposed) value!
207 > * A clean solution for this is ref counting.
208 > */
209 > get delayedStore(): DisposableStore;
210 > }
src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts 204 covered LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- devToolsLogger.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { AutorunObserver, AutorunState } from '../../reactions/autorunImpl.js';
7 > import { TransactionImpl } from '../../transaction.js';
8 > import { IChangeInformation, IObservableLogger } from '../logging.js';
9 > import { formatValue } from '../consoleObservableLogger.js';
10 > import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransactionState, ObserverInstanceState } from './debuggerApi.js';
11 > import { registerDebugChannel } from './debuggerRpc.js';
12 > import { deepAssign, deepAssignDeleteNulls, Throttler } from './utils.js';
13 > import { isDefined } from '../../../types.js';
14 > import { FromEventObservable } from '../../observables/observableFromEvent.js';
15 > import { BugIndicatingError, onUnexpectedError } from '../../../errors.js';
16 > import { IObservable, IObserver } from '../../base.js';
17 > import { BaseObservable } from '../../observables/baseObservable.js';
18 > import { Derived, DerivedState } from '../../observables/derivedImpl.js';
19 > import { ObservableValue } from '../../observables/observableValue.js';
20 > import { DebugLocation } from '../../debugLocation.js';
21 >
22 > interface IInstanceInfo {
23 > declarationId: number;
24 > instanceId: number;
25 > }
26 >
27 > interface IObservableInfo extends IInstanceInfo {
28 > listenerCount: number;
29 > lastValue: string | undefined;
30 > updateCount: number;
31 > changedObservables: Set<IObservable<any>>;
32 > }
33 >
34 > interface IAutorunInfo extends IInstanceInfo {
35 > updateCount: number;
36 > changedObservables: Set<IObservable<any>>;
37 > }
38 >
39 > export class DevToolsLogger implements IObservableLogger {
40 > private static _instance: DevToolsLogger | undefined = undefined;
41 > public static getInstance(): DevToolsLogger {
42 if (DevToolsLogger._instance === undefined) {
43 DevToolsLogger._instance = new DevToolsLogger();
45 return DevToolsLogger._instance;
46 }
48 > private _declarationId = 0;
49 > private _instanceId = 0;
50 >
51 > private readonly _declarations = new Map</* declarationId + type */string, IObsDeclaration>();
52 > private readonly _instanceInfos = new WeakMap<object, IObservableInfo | IAutorunInfo>();
53 > private readonly _aliveInstances = new Map<ObsInstanceId, IObservable<any> | AutorunObserver>();
54 > private readonly _activeTransactions = new Set<TransactionImpl>();
55 >
56 > private readonly _channel = registerDebugChannel<ObsDebuggerApi>('observableDevTools', () => {
57 > return {
58 > notifications: {
59 > setDeclarationIdFilter: declarationIds => {
60 >
61 > },
62 > logObservableValue: (observableId) => {
63 > console.log('logObservableValue', observableId);
64 > },
65 > flushUpdates: () => {
66 > this._flushUpdates();
67 > },
68 > resetUpdates: () => {
69 > this._pendingChanges = null;
70 > this._channel.api.notifications.handleChange(this._fullState, true);
71 > },
72 > },
73 > requests: {
74 > getDeclarations: () => {
75 > const result: Record<string, IObsDeclaration> = {};
76 > for (const decl of this._declarations.values()) {
77 > result[decl.id] = decl;
78 > }
79 > return { decls: result };
80 > },
81 > getSummarizedInstances: () => {
82 > return null!;
83 > },
84 > getObservableValueInfo: instanceId => {
85 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
86 > return {
87 > observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
88 > };
89 > },
90 > getDerivedInfo: instanceId => {
91 > const d = this._aliveInstances.get(instanceId) as Derived<any>;
92 > return {
93 > dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
94 > observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
95 > };
96 > },
97 > getAutorunInfo: instanceId => {
98 > const obs = this._aliveInstances.get(instanceId) as AutorunObserver;
99 > return {
100 > dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
101 > };
102 > },
103 > getTransactionState: () => {
104 > return this.getTransactionState();
105 > },
106 > setValue: (instanceId, jsonValue) => {
107 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
108 >
109 > if (obs instanceof Derived) {
110 > obs.debugSetValue(jsonValue);
111 > } else if (obs instanceof ObservableValue) {
112 > obs.debugSetValue(jsonValue);
113 > } else if (obs instanceof FromEventObservable) {
114 > obs.debugSetValue(jsonValue);
115 > } else {
116 > throw new BugIndicatingError('Observable is not supported');
117 > }
118 >
119 > const observers = [...obs.debugGetObservers()];
120 > for (const d of observers) {
121 > d.beginUpdate(obs);
122 > }
123 > for (const d of observers) {
124 > d.handleChange(obs, undefined);
125 > }
126 > for (const d of observers) {
127 > d.endUpdate(obs);
128 > }
129 > },
130 > getValue: instanceId => {
131 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
132 > if (obs instanceof Derived) {
133 > return formatValue(obs.debugGetState().value, 200);
134 > } else if (obs instanceof ObservableValue) {
135 > return formatValue(obs.debugGetState().value, 200);
136 > }
137 >
138 > return undefined;
139 > },
140 > logValue: (instanceId) => {
141 > const obs = this._aliveInstances.get(instanceId);
142 > if (obs && 'get' in obs) {
143 > console.log('Logged Value:', obs.get());
144 > } else {
145 > throw new BugIndicatingError('Observable is not supported');
146 > }
147 > },
148 > rerun: (instanceId) => {
149 > const obs = this._aliveInstances.get(instanceId);
150 > if (obs instanceof Derived) {
151 > obs.debugRecompute();
152 > } else if (obs instanceof AutorunObserver) {
153 > obs.debugRerun();
154 > } else {
155 > throw new BugIndicatingError('Observable is not supported');
156 > }
157 > },
158 > }
159 > };
160 > });
161 >
162 > private getTransactionState(): ITransactionState | undefined {
163 const affected: ObserverInstanceState[] = [];
164 const txs = [...this._activeTransactions];
188 return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected };
189 }
191 > private _getObservableInfo(observable: IObservable<any>): IObservableInfo | undefined {
192 const info = this._instanceInfos.get(observable);
193 if (!info) {
197 return info as IObservableInfo;
198 }
200 > private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo | undefined {
201 const info = this._instanceInfos.get(autorun);
202 if (!info) {
206 return info as IAutorunInfo;
207 }
209 > private _getInfo(observer: IObserver, queue: (observer: IObserver) => void): ObserverInstanceState | undefined {
210 if (observer instanceof Derived) {
211 const observersToUpdate = [...observer.debugGetObservers()];
255 return undefined;
256 }
258 > private _formatObservable(obs: IObservable<any>): { name: string; instanceId: ObsInstanceId } | undefined {
259 const info = this._getObservableInfo(obs);
260 if (!info) { return undefined; }
261 return { name: obs.debugName, instanceId: info.instanceId };
262 }
264 > private _formatObserver(obs: IObserver): { name: string; instanceId: ObsInstanceId } | undefined {
265 if (obs instanceof Derived) {
266 return { name: obs.toString(), instanceId: this._getObservableInfo(obs)?.instanceId! };
273 return undefined;
274 }
276 > private constructor() {
277 DebugLocation.enable();
278 }
280 > private _pendingChanges: ObsStateUpdate | null = null;
281 > private readonly _changeThrottler = new Throttler();
282 >
283 > private readonly _fullState = {};
284 >
285 > private _handleChange(update: ObsStateUpdate): void {
286 deepAssignDeleteNulls(this._fullState, update);
287
294 this._changeThrottler.throttle(this._flushUpdates, 10);
295 }
297 > private readonly _flushUpdates = () => {
298 > if (this._pendingChanges !== null) {
299 > this._channel.api.notifications.handleChange(this._pendingChanges, false);
300 > this._pendingChanges = null;
301 > }
302 > };
303 >
304 > private _getDeclarationId(type: IObsDeclaration['type'], location: DebugLocation): number {
305 if (!location) {
306 return -1;
322 return decInfo.id;
323 }
325 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
326 const declarationId = this._getDeclarationId('observable/value', location);
327
336 this._instanceInfos.set(observable, info);
337 }
339 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void {
340 const info = this._getObservableInfo(observable);
341 if (!info) { return; }
364 info.listenerCount = newCount;
365 }
367 > handleObservableUpdated(observable: IObservable<any>, changeInfo: IChangeInformation): void {
368 if (observable instanceof Derived) {
369 this._handleDerivedRecomputed(observable, changeInfo);
383 }
384 }
386 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void {
387 const declarationId = this._getDeclarationId('autorun', location);
388 const info: IAutorunInfo = {
408 }
409 }
410 > handleAutorunDisposed(autorun: AutorunObserver): void { devToolsLogger.ts
411 const info = this._getAutorunInfo(autorun);
412 if (!info) { return; }
418 this._aliveInstances.delete(info.instanceId);
419 }
420 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { devToolsLogger.ts
421 const info = this._getAutorunInfo(autorun);
422 if (!info) { return; }
424 info.changedObservables.add(observable);
425 }
426 > handleAutorunStarted(autorun: AutorunObserver): void { devToolsLogger.ts
427
428 }
429 > handleAutorunFinished(autorun: AutorunObserver): void { devToolsLogger.ts
430 const info = this._getAutorunInfo(autorun);
431 if (!info) { return; }
437 });
438 }
440 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
441 const info = this._getObservableInfo(derived);
442 if (info) {
444 }
445 }
446 > _handleDerivedRecomputed(observable: Derived<any>, changeInfo: IChangeInformation): void { devToolsLogger.ts
447 const info = this._getObservableInfo(observable);
448 if (!info) { return; }
459 }
460 }
461 > handleDerivedCleared(observable: Derived<any>): void { devToolsLogger.ts
462 const info = this._getObservableInfo(observable);
463 if (!info) { return; }
475 }
476 }
477 > handleBeginTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
478 this._activeTransactions.add(transaction);
479 }
480 > handleEndTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
481 this._activeTransactions.delete(transaction);
482 }
src/vs/base/common/platform.ts 189 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- platform.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as nls from '../../nls.js';
7 >
8 > export const LANGUAGE_DEFAULT = 'en';
9 >
10 > let _isWindows = false;
11 > let _isMacintosh = false;
12 > let _isLinux = false;
13 > let _isLinuxSnap = false;
14 > let _isNative = false;
15 > let _isWeb = false;
16 > let _isElectron = false;
17 > let _isIOS = false;
18 > let _isCI = false;
19 > let _isMobile = false;
20 > let _locale: string | undefined = undefined;
21 > let _language: string = LANGUAGE_DEFAULT;
22 > let _platformLocale: string = LANGUAGE_DEFAULT;
23 > let _translationsConfigFile: string | undefined = undefined;
24 > let _userAgent: string | undefined = undefined;
25 >
26 > export interface IProcessEnvironment {
27 > [key: string]: string | undefined;
28 > }
29 >
30 > /**
31 > * This interface is intentionally not identical to node.js
32 > * process because it also works in sandboxed environments
33 > * where the process object is implemented differently. We
34 > * define the properties here that we need for `platform`
35 > * to work and nothing else.
36 > */
37 > export interface INodeProcess {
38 > platform: string;
39 > arch: string;
40 > env: IProcessEnvironment;
41 > versions?: {
42 > node?: string;
43 > electron?: string;
44 > chrome?: string;
45 > };
46 > type?: string;
47 > cwd: () => string;
48 > }
49 >
50 > declare const process: INodeProcess;
51 >
52 > const $globalThis: any = globalThis;
53 >
54 > let nodeProcess: INodeProcess | undefined = undefined;
55 > if (typeof $globalThis.vscode !== 'undefined' && typeof $globalThis.vscode.process !== 'undefined') {
56 // Native environment (sandboxed)
57 nodeProcess = $globalThis.vscode.process;
58 > } else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') { platform.ts
59 > // Native environment (non-sandboxed)
60 > nodeProcess = process;
61 > }
62 >
63 > const isElectronProcess = typeof nodeProcess?.versions?.electron === 'string';
64 > const isElectronRenderer = isElectronProcess && nodeProcess?.type === 'renderer';
65 >
66 > interface INavigator {
67 > userAgent: string;
68 > maxTouchPoints?: number;
69 > language: string;
70 > }
71 > declare const navigator: INavigator;
72 >
73 > // Native environment
74 > if (typeof nodeProcess === 'object') {
75 > _isWindows = (nodeProcess.platform === 'win32');
76 > _isMacintosh = (nodeProcess.platform === 'darwin');
77 > _isLinux = (nodeProcess.platform === 'linux');
78 > _isLinuxSnap = _isLinux && !!nodeProcess.env['SNAP'] && !!nodeProcess.env['SNAP_REVISION'];
79 > _isElectron = isElectronProcess;
80 > _isCI = !!nodeProcess.env['CI'] || !!nodeProcess.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] || !!nodeProcess.env['GITHUB_WORKSPACE'];
81 > _locale = LANGUAGE_DEFAULT;
82 > _language = LANGUAGE_DEFAULT;
83 > const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG'];
84 > if (rawNlsConfig) {
85 try {
86 const nlsConfig: nls.INLSConfiguration = JSON.parse(rawNlsConfig);
113 console.error('Unable to resolve platform.');
114 }
115 > platform.ts
116 > export const enum Platform {
117 > Web,
118 > Mac,
119 > Linux,
120 > Windows
121 > }
122 > export type PlatformName = 'Web' | 'Windows' | 'Mac' | 'Linux';
123 >
124 > export function PlatformToString(platform: Platform): PlatformName {
125 switch (platform) {
126 case Platform.Web: return 'Web';
130 }
131 }
132 > platform.ts
133 > let _platform: Platform = Platform.Web;
134 > if (_isMacintosh) {
135 _platform = Platform.Mac;
136 > } else if (_isWindows) { platform.ts
137 _platform = Platform.Windows;
138 > } else if (_isLinux) { platform.ts
139 > _platform = Platform.Linux;
140 > }
141 >
142 > export const isWindows = _isWindows;
143 > export const isMacintosh = _isMacintosh;
144 > export const isLinux = _isLinux;
145 > export const isLinuxSnap = _isLinuxSnap;
146 > export const isNative = _isNative;
147 > export const isElectron = _isElectron;
148 > export const isWeb = _isWeb;
149 > export const isWebWorker = (_isWeb && typeof $globalThis.importScripts === 'function');
150 > export const webWorkerOrigin = isWebWorker ? $globalThis.origin : undefined;
151 > export const isIOS = _isIOS;
152 > export const isMobile = _isMobile;
153 > /**
154 > * Whether we run inside a CI environment, such as
155 > * GH actions or Azure Pipelines.
156 > */
157 > export const isCI = _isCI;
158 > export const platform = _platform;
159 > export const userAgent = _userAgent;
160 >
161 > /**
162 > * The language used for the user interface. The format of
163 > * the string is all lower case (e.g. zh-tw for Traditional
164 > * Chinese or de for German)
165 > */
166 > export const language = _language;
167 >
168 > export namespace Language {
169 >
170 > export function value(): string {
171 return language;
172 }
173 > platform.ts
174 > export function isDefaultVariant(): boolean {
175 if (language.length === 2) {
176 return language === 'en';
181 }
182 }
183 > platform.ts
184 > export function isDefault(): boolean {
185 return language === 'en';
186 }
187 > } platform.ts
188 >
189 > /**
190 > * Desktop: The OS locale or the locale specified by --locale or `argv.json`.
191 > * Web: matches `platformLocale`.
192 > *
193 > * The UI is not necessarily shown in the provided locale.
194 > */
195 > export const locale = _locale;
196 >
197 > /**
198 > * This will always be set to the OS/browser's locale regardless of
199 > * what was specified otherwise. The format of the string is all
200 > * lower case (e.g. zh-tw for Traditional Chinese). The UI is not
201 > * necessarily shown in the provided locale.
202 > */
203 > export const platformLocale = _platformLocale;
204 >
205 > /**
206 > * The translations that are available through language packs.
207 > */
208 > export const translationsConfigFile = _translationsConfigFile;
209 >
210 > export const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);
211 >
212 > /**
213 > * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
214 > *
215 > * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
216 > * that browsers set when the nesting level is > 5.
217 > */
218 > export const setTimeout0 = (() => {
219 > if (setTimeout0IsFaster) {
220 interface IQueueElement {
221 id: number;
246 };
247 }
248 > return (callback: () => void) => setTimeout(callback); platform.ts
249 > })();
250 >
251 > export const enum OperatingSystem {
252 > Windows = 1,
253 > Macintosh = 2,
254 > Linux = 3
255 > }
256 > export const OS = (_isMacintosh || _isIOS ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux));
257 >
258 > let _isLittleEndian = true;
259 > let _isLittleEndianComputed = false;
260 > export function isLittleEndian(): boolean {
261 if (!_isLittleEndianComputed) {
262 _isLittleEndianComputed = true;
269 return _isLittleEndian;
270 }
271 > platform.ts
272 > export const isChrome = !!(userAgent && userAgent.indexOf('Chrome') >= 0);
273 > export const isFirefox = !!(userAgent && userAgent.indexOf('Firefox') >= 0);
274 > export const isSafari = !!(!isChrome && (userAgent && userAgent.indexOf('Safari') >= 0));
275 > export const isEdge = !!(userAgent && userAgent.indexOf('Edg/') >= 0);
276 > export const isAndroid = !!(userAgent && userAgent.indexOf('Android') >= 0);
277 > export const hasElectronUserAgent = !!(userAgent && userAgent.indexOf('Electron') >= 0);
278 >
279 > export function isTahoeOrNewer(osVersion: string): boolean {
280 return parseFloat(osVersion) >= 25;
281 }
src/vs/nls.ts 187 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- nls.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export function getNLSMessages(): string[] {
7 return globalThis._VSCODE_NLS_MESSAGES;
8 }
9 > nls.ts
10 > export function getNLSLanguage(): string | undefined {
11 > return globalThis._VSCODE_NLS_LANGUAGE;
12 > }
13 >
14 > declare const document: { location?: { hash?: string } } | undefined;
15 > const isPseudo = getNLSLanguage() === 'pseudo' || (typeof document !== 'undefined' && document.location && typeof document.location.hash === 'string' && document.location.hash.indexOf('pseudo=true') >= 0);
16 >
17 > export interface ILocalizeInfo {
18 > key: string;
19 > comment: string[];
20 > }
21 >
22 > export interface ILocalizedString {
23 > original: string;
24 > value: string;
25 > }
26 >
27 function _format(message: string, args: (string | number | boolean | undefined | null)[]): string {
28 let result: string;
51 return result;
52 }
53 > nls.ts
54 > /**
55 > * Marks a string to be localized. Returns the localized string.
56 > *
57 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
58 > * @param message The string to localize
59 > * @param args The arguments to the string
60 > *
61 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
62 > * @example `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
63 > *
64 > * @returns string The localized string.
65 > */
66 > export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
67 >
68 > /**
69 > * Marks a string to be localized. Returns the localized string.
70 > *
71 > * @param key The key to use for localizing the string
72 > * @param message The string to localize
73 > * @param args The arguments to the string
74 > *
75 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
76 > * @example For example, `localize('sayHello', 'hello {0}', name)`
77 > *
78 > * @returns string The localized string.
79 > */
80 > export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
81 >
82 > /**
83 > * @skipMangle
84 > */
85 > export function localize(data: ILocalizeInfo | string /* | number when built */, message: string /* | null when built */, ...args: (string | number | boolean | undefined | null)[]): string {
86 if (typeof data === 'number') {
87 return _format(lookupMessage(data, message), args);
89 return _format(message, args);
90 }
91 > nls.ts
92 > /**
93 > * Only used when built: Looks up the message in the global NLS table.
94 > * This table is being made available as a global through bootstrapping
95 > * depending on the target context.
96 > */
97 function lookupMessage(index: number, fallback: string | null): string {
98 const message = getNLSMessages()?.[index];
105 return message;
106 }
107 > nls.ts
108 > /**
109 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
110 > * which contains the localized string and the original string.
111 > *
112 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
113 > * @param message The string to localize
114 > * @param args The arguments to the string
115 > *
116 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
117 > * @example `localize2({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
118 > *
119 > * @returns ILocalizedString which contains the localized string and the original string.
120 > */
121 > export function localize2(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
122 >
123 > /**
124 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
125 > * which contains the localized string and the original string.
126 > *
127 > * @param key The key to use for localizing the string
128 > * @param message The string to localize
129 > * @param args The arguments to the string
130 > *
131 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
132 > * @example `localize('sayHello', 'hello {0}', name)`
133 > *
134 > * @returns ILocalizedString which contains the localized string and the original string.
135 > */
136 > export function localize2(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
137 >
138 > /**
139 > * @skipMangle
140 > */
141 > export function localize2(data: ILocalizeInfo | string /* | number when built */, originalMessage: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
142 let message: string;
143 if (typeof data === 'number') {
154 };
155 }
156 > nls.ts
157 > export interface INLSLanguagePackConfiguration {
158 >
159 > /**
160 > * The path to the translations config file that contains pointers to
161 > * all message bundles for `main` and extensions.
162 > */
163 > readonly translationsConfigFile: string;
164 >
165 > /**
166 > * The path to the file containing the translations for this language
167 > * pack as flat string array.
168 > */
169 > readonly messagesFile: string;
170 >
171 > /**
172 > * The path to the file that can be used to signal a corrupt language
173 > * pack, for example when reading the `messagesFile` fails. This will
174 > * instruct the application to re-create the cache on next startup.
175 > */
176 > readonly corruptMarkerFile: string;
177 > }
178 >
179 > export interface INLSConfiguration {
180 >
181 > /**
182 > * Locale as defined in `argv.json` or `app.getLocale()`.
183 > */
184 > readonly userLocale: string;
185 >
186 > /**
187 > * Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
188 > */
189 > readonly osLocale: string;
190 >
191 > /**
192 > * The actual language of the UI that ends up being used considering `userLocale`
193 > * and `osLocale`.
194 > */
195 > readonly resolvedLanguage: string;
196 >
197 > /**
198 > * Defined if a language pack is used that is not the
199 > * default english language pack. This requires a language
200 > * pack to be installed as extension.
201 > */
202 > readonly languagePack?: INLSLanguagePackConfiguration;
203 >
204 > /**
205 > * The path to the file containing the default english messages
206 > * as flat string array. The file is only present in built
207 > * versions of the application.
208 > */
209 > readonly defaultMessagesFile: string;
210 >
211 > /**
212 > * Below properties are deprecated and only there to continue support
213 > * for `vscode-nls` module that depends on them.
214 > * Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46
215 > */
216 > /** @deprecated */
217 > readonly locale: string;
218 > /** @deprecated */
219 > readonly availableLanguages: Record<string, string>;
220 > /** @deprecated */
221 > readonly _languagePackSupport?: boolean;
222 > /** @deprecated */
223 > readonly _languagePackId?: string;
224 > /** @deprecated */
225 > readonly _translationsConfigFile?: string;
226 > /** @deprecated */
227 > readonly _cacheRoot?: string;
228 > /** @deprecated */
229 > readonly _resolvedLanguagePackCoreLocation?: string;
230 > /** @deprecated */
231 > readonly _corruptedFile?: string;
232 > }
233 >
234 > export interface ILanguagePack {
235 > readonly hash: string;
236 > readonly label: string | undefined;
237 > readonly extensions: {
238 > readonly extensionIdentifier: { readonly id: string; readonly uuid?: string };
239 > readonly version: string;
240 > }[];
241 > readonly translations: Record<string, string | undefined>;
242 > }
243 >
244 > export type ILanguagePacks = Record<string, ILanguagePack | undefined>;
src/vs/base/common/errors.ts 183 covered LOC · 32 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- errors.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export interface ErrorListenerCallback {
7 > (error: any): void;
8 > }
9 >
10 > export interface ErrorListenerUnbind {
11 > (): void;
12 > }
13 >
14 > // Avoid circular dependency on EventEmitter by implementing a subset of the interface.
15 > export class ErrorHandler {
16 > private unexpectedErrorHandler: (e: any) => void;
17 > private listeners: ErrorListenerCallback[];
18 >
19 > constructor() {
20 >
21 > this.listeners = [];
22 >
23 > this.unexpectedErrorHandler = function (e: any) {
24 setTimeout(() => {
25 if (e.stack) {
34 }, 0);
35 };
36 > } errors.ts
37 >
38 > addListener(listener: ErrorListenerCallback): ErrorListenerUnbind {
39 this.listeners.push(listener);
40
43 };
44 }
45 > errors.ts
46 > private emit(e: any): void {
47 this.listeners.forEach((listener) => {
48 listener(e);
49 });
50 }
51 > errors.ts
52 > private _removeListener(listener: ErrorListenerCallback): void {
53 this.listeners.splice(this.listeners.indexOf(listener), 1);
54 }
55 > errors.ts
56 > setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
57 > this.unexpectedErrorHandler = newUnexpectedErrorHandler;
58 > }
59 >
60 > getUnexpectedErrorHandler(): (e: any) => void {
61 return this.unexpectedErrorHandler;
62 }
63 > errors.ts
64 > onUnexpectedError(e: any): void {
65 this.unexpectedErrorHandler(e);
66 this.emit(e);
67 }
68 > errors.ts
69 > // For external errors, we don't want the listeners to be called
70 > onUnexpectedExternalError(e: any): void {
71 this.unexpectedErrorHandler(e);
72 }
73 > } errors.ts
74 >
75 > export const errorHandler = new ErrorHandler();
76 >
77 > /** @skipMangle */
78 > export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
79 > errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);
80 > }
81 >
82 > /**
83 > * Returns if the error is a SIGPIPE error. SIGPIPE errors should generally be
84 > * logged at most once, to avoid a loop.
85 > *
86 > * @see https://github.com/microsoft/vscode-remote-release/issues/6481
87 > */
88 > export function isSigPipeError(e: unknown): e is Error {
89 if (!e || typeof e !== 'object') {
90 return false;
94 return cast.code === 'EPIPE' && cast.syscall?.toUpperCase() === 'WRITE';
95 }
96 > errors.ts
97 > /**
98 > * This function should only be called with errors that indicate a bug in the product.
99 > * E.g. buggy extensions/invalid user-input/network issues should not be able to trigger this code path.
100 > * If they are, this indicates there is also a bug in the product.
101 > */
102 > export function onBugIndicatingError(e: any): undefined {
103 errorHandler.onUnexpectedError(e);
104 return undefined;
105 }
106 > errors.ts
107 > export function onUnexpectedError(e: any): undefined {
108 // ignore errors from cancelled promises
109 if (!isCancellationError(e)) {
112 return undefined;
113 }
114 > errors.ts
115 > export function onUnexpectedExternalError(e: any): undefined {
116 // ignore errors from cancelled promises
117 if (!isCancellationError(e)) {
120 return undefined;
121 }
122 > errors.ts
123 > type ObjectWithCode = {
124 > readonly code: unknown;
125 > };
126 >
127 function hasErrorCode(error: object): error is ObjectWithCode {
128 return Object.hasOwn(error, 'code');
129 }
130 > errors.ts
131 > export function getErrorCode(error: unknown): string | undefined {
132 if (!error || typeof error !== 'object' || !hasErrorCode(error)) {
133 return undefined;
136 return typeof code === 'string' || typeof code === 'number' ? String(code) : undefined;
137 }
138 > errors.ts
139 > export interface SerializedError {
140 > readonly $isError: true;
141 > readonly name: string;
142 > readonly message: string;
143 > readonly stack: string;
144 > readonly noTelemetry: boolean;
145 > readonly code?: string;
146 > readonly cause?: SerializedError;
147 > }
148 >
149 > type ErrorWithCode = Error & {
150 > code: string | undefined;
151 > };
152 >
153 > export function transformErrorForSerialization(error: Error): SerializedError;
154 > export function transformErrorForSerialization(error: any): any;
155 > export function transformErrorForSerialization(error: any): any {
156 if (error instanceof Error) {
157 const { name, message, cause } = error;
172 return error;
173 }
174 > errors.ts
175 > export function transformErrorFromSerialization(data: SerializedError): Error {
176 let error: Error;
177 if (data.noTelemetry) {
191 return error;
192 }
193 > errors.ts
194 > // see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces
195 > export interface V8CallSite {
196 > getThis(): unknown;
197 > getTypeName(): string | null;
198 > getFunction(): Function | undefined;
199 > getFunctionName(): string | null;
200 > getMethodName(): string | null;
201 > getFileName(): string | null;
202 > getLineNumber(): number | null;
203 > getColumnNumber(): number | null;
204 > getEvalOrigin(): string | undefined;
205 > isToplevel(): boolean;
206 > isEval(): boolean;
207 > isNative(): boolean;
208 > isConstructor(): boolean;
209 > toString(): string;
210 > }
211 >
212 > export const canceledName = 'Canceled';
213 >
214 > /**
215 > * Checks if the given error is a promise in canceled state
216 > */
217 > export function isCancellationError(error: any): boolean {
218 if (error instanceof CancellationError) {
219 return true;
221 return error instanceof Error && error.name === canceledName && error.message === canceledName;
222 }
223 > errors.ts
224 > // !!!IMPORTANT!!!
225 > // Do NOT change this class because it is also used as an API-type.
226 > export class CancellationError extends Error {
227 > constructor() {
228 super(canceledName);
229 this.name = this.message;
230 }
231 > } errors.ts
232 >
233 > export class PendingMigrationError extends Error {
234 >
235 > private static readonly _name = 'PendingMigrationError';
236 >
237 > static is(error: unknown): error is PendingMigrationError {
238 return error instanceof PendingMigrationError || (error instanceof Error && error.name === PendingMigrationError._name);
239 }
240 > errors.ts
241 > constructor(message: string) {
242 super(message);
243 this.name = PendingMigrationError._name;
244 }
245 > } errors.ts
246 >
247 > /**
248 > * @deprecated use {@link CancellationError `new CancellationError()`} instead
249 > */
250 > export function canceled(): Error {
251 const error = new Error(canceledName);
252 error.name = error.message;
253 return error;
254 }
255 > errors.ts
256 > export function illegalArgument(name?: string): Error {
257 if (name) {
258 return new Error(`Illegal argument: ${name}`);
261 }
262 }
263 > errors.ts
264 > export function illegalState(name?: string): Error {
265 if (name) {
266 return new Error(`Illegal state: ${name}`);
269 }
270 }
271 > errors.ts
272 > export class ReadonlyError extends TypeError {
273 > constructor(name?: string) {
274 super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');
275 }
276 > } errors.ts
277 >
278 > export function getErrorMessage(err: any): string {
279 if (!err) {
280 return 'Error';
291 return String(err);
292 }
293 > errors.ts
294 > export class NotImplementedError extends Error {
295 > constructor(message?: string) {
296 super('NotImplemented');
297 if (message) {
299 }
300 }
301 > } errors.ts
302 >
303 > export class NotSupportedError extends Error {
304 > constructor(message?: string) {
305 super('NotSupported');
306 if (message) {
308 }
309 }
310 > } errors.ts
311 >
312 > export class ExpectedError extends Error {
313 readonly isExpected = true;
314 > } errors.ts
315 >
316 > /**
317 > * Error that when thrown won't be logged in telemetry as an unhandled error.
318 > */
319 > export class ErrorNoTelemetry extends Error {
320 > override readonly name: string;
321 >
322 > constructor(msg?: string) {
323 super(msg);
324 this.name = 'CodeExpectedError';
325 }
326 > errors.ts
327 > public static fromError(err: Error): ErrorNoTelemetry {
328 if (err instanceof ErrorNoTelemetry) {
329 return err;
335 return result;
336 }
337 > errors.ts
338 > public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry {
339 return err.name === 'CodeExpectedError';
340 }
341 > } errors.ts
342 >
343 > /**
344 > * This error indicates a bug.
345 > * Do not throw this for invalid user input.
346 > * Only catch this error to recover gracefully from bugs.
347 > */
348 > export class BugIndicatingError extends Error {
349 > constructor(message?: string) {
350 super(message || 'An unexpected bug occurred.');
351 Object.setPrototypeOf(this, BugIndicatingError.prototype);
src/vs/base/common/path.ts 177 covered LOC · 31 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- path.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace
7 > // Copied from: https://github.com/nodejs/node/commits/v22.15.0/lib/path.js
8 > // Excluding: the change that adds primordials
9 > // (https://github.com/nodejs/node/commit/187a862d221dec42fa9a5c4214e7034d9092792f and others)
10 > // Excluding: the change that adds glob matching
11 > // (https://github.com/nodejs/node/commit/57b8b8e18e5e2007114c63b71bf0baedc01936a6)
12 >
13 > /**
14 > * Copyright Joyent, Inc. and other Node contributors.
15 > *
16 > * Permission is hereby granted, free of charge, to any person obtaining a
17 > * copy of this software and associated documentation files (the
18 > * "Software"), to deal in the Software without restriction, including
19 > * without limitation the rights to use, copy, modify, merge, publish,
20 > * distribute, sublicense, and/or sell copies of the Software, and to permit
21 > * persons to whom the Software is furnished to do so, subject to the
22 > * following conditions:
23 > *
24 > * The above copyright notice and this permission notice shall be included
25 > * in all copies or substantial portions of the Software.
26 > *
27 > * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 > * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 > * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
30 > * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
31 > * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32 > * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
33 > * USE OR OTHER DEALINGS IN THE SOFTWARE.
34 > */
35 >
36 > import * as process from './process.js';
37 >
38 > const CHAR_UPPERCASE_A = 65;/* A */
39 > const CHAR_LOWERCASE_A = 97; /* a */
40 > const CHAR_UPPERCASE_Z = 90; /* Z */
41 > const CHAR_LOWERCASE_Z = 122; /* z */
42 > const CHAR_DOT = 46; /* . */
43 > const CHAR_FORWARD_SLASH = 47; /* / */
44 > const CHAR_BACKWARD_SLASH = 92; /* \ */
45 > const CHAR_COLON = 58; /* : */
46 > const CHAR_QUESTION_MARK = 63; /* ? */
47 >
48 > class ErrorInvalidArgType extends Error {
49 > code: 'ERR_INVALID_ARG_TYPE';
50 > constructor(name: string, expected: string, actual: unknown) {
51 // determiner: 'must be' or 'must not be'
52 let determiner;
66 this.code = 'ERR_INVALID_ARG_TYPE';
67 }
68 > } path.ts
69 >
70 function validateObject(pathObject: object, name: string) {
71 if (pathObject === null || typeof pathObject !== 'object') {
73 }
74 }
75 > path.ts
76 function validateString(value: string, name: string) {
77 if (typeof value !== 'string') {
79 }
80 }
81 > path.ts
82 > const platformIsWin32 = (process.platform === 'win32');
83 >
84 function isPathSeparator(code: number | undefined) {
85 return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
86 }
87 > path.ts
88 function isPosixPathSeparator(code: number | undefined) {
89 return code === CHAR_FORWARD_SLASH;
90 }
91 > path.ts
92 function isWindowsDeviceRoot(code: number) {
93 return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
94 (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z);
95 }
96 > path.ts
97 > // Resolves . and .. elements in a path with directory names
98 function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code?: number) => boolean) {
99 let res = '';
163 return res;
164 }
165 > path.ts
166 function formatExt(ext: string): string {
167 return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
168 }
169 > path.ts
170 function _format(sep: string, pathObject: ParsedPath) {
171 validateObject(pathObject, 'pathObject');
178 return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
179 }
180 > path.ts
181 > export interface ParsedPath {
182 > root: string;
183 > dir: string;
184 > base: string;
185 > ext: string;
186 > name: string;
187 > }
188 >
189 > export interface IPath {
190 > normalize(path: string): string;
191 > isAbsolute(path: string): boolean;
192 > join(...paths: string[]): string;
193 > resolve(...pathSegments: string[]): string;
194 > relative(from: string, to: string): string;
195 > dirname(path: string): string;
196 > basename(path: string, suffix?: string): string;
197 > extname(path: string): string;
198 > format(pathObject: ParsedPath): string;
199 > parse(path: string): ParsedPath;
200 > toNamespacedPath(path: string): string;
201 > sep: '\\' | '/';
202 > delimiter: string;
203 > win32: IPath | null;
204 > posix: IPath | null;
205 > }
206 >
207 > export const win32: IPath = {
208 > // path.resolve([from ...], to)
209 > resolve(...pathSegments: string[]): string {
210 let resolvedDevice = '';
211 let resolvedTail = '';
343 `${resolvedDevice}${resolvedTail}` || '.';
344 },
345 > path.ts
346 > normalize(path: string): string {
347 validateString(path, 'path');
348 const len = path.length;
450 return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`;
451 },
452 > path.ts
453 > isAbsolute(path: string): boolean {
454 validateString(path, 'path');
455 const len = path.length;
466 isPathSeparator(path.charCodeAt(2)));
467 },
468 > path.ts
469 > join(...paths: string[]): string {
470 if (paths.length === 0) {
471 return '.';
536 return win32.normalize(joined);
537 },
538 > path.ts
539 >
540 > // It will solve the relative path from `from` to `to`, for instance:
541 > // from = 'C:\\orandea\\test\\aaa'
542 > // to = 'C:\\orandea\\impl\\bbb'
543 > // The output of the function should be: '..\\..\\impl\\bbb'
544 > relative(from: string, to: string): string {
545 validateString(from, 'from');
546 validateString(to, 'to');
699 return toOrig.slice(toStart, toEnd);
700 },
701 > path.ts
702 > toNamespacedPath(path: string): string {
703 // Note: this will *probably* throw somewhere.
704 if (typeof path !== 'string' || path.length === 0) {
730 return resolvedPath;
731 },
732 > path.ts
733 > dirname(path: string): string {
734 validateString(path, 'path');
735 const len = path.length;
818 return path.slice(0, end);
819 },
820 > path.ts
821 > basename(path: string, suffix?: string): string {
822 if (suffix !== undefined) {
823 validateString(suffix, 'suffix');
906 return path.slice(start, end);
907 },
908 > path.ts
909 > extname(path: string): string {
910 validateString(path, 'path');
911 let start = 0;
972 return path.slice(startDot, end);
973 },
974 > path.ts
975 > format: _format.bind(null, '\\'),
976 >
977 > parse(path) {
978 validateString(path, 'path');
979
1126 return ret;
1127 },
1128 > path.ts
1129 > sep: '\\',
1130 > delimiter: ';',
1131 > win32: null,
1132 > posix: null
1133 > };
1134 >
1135 > const posixCwd = (() => {
1136 > if (platformIsWin32) {
1137 // Converts Windows' backslash path separators to POSIX forward slashes
1138 // and truncates any drive indicator
1143 };
1144 }
1145 > path.ts
1146 > // We're already on POSIX, no need for any transformations
1147 > return () => process.cwd();
1148 > })();
1149 >
1150 > export const posix: IPath = {
1151 > // path.resolve([from ...], to)
1152 > resolve(...pathSegments: string[]): string {
1153 let resolvedPath = '';
1154 let resolvedAbsolute = false;
1186 return resolvedPath.length > 0 ? resolvedPath : '.';
1187 },
1188 > path.ts
1189 > normalize(path: string): string {
1190 validateString(path, 'path');
1191
1213 return isAbsolute ? `/${path}` : path;
1214 },
1215 > path.ts
1216 > isAbsolute(path: string): boolean {
1217 validateString(path, 'path');
1218 return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1219 },
1220 > path.ts
1221 > join(...paths: string[]): string {
1222 if (paths.length === 0) {
1223 return '.';
1239 return posix.normalize(path.join('/'));
1240 },
1241 > path.ts
1242 > relative(from: string, to: string): string {
1243 validateString(from, 'from');
1244 validateString(to, 'to');
1312 return `${out}${to.slice(toStart + lastCommonSep)}`;
1313 },
1314 > path.ts
1315 > toNamespacedPath(path: string): string {
1316 // Non-op on posix systems
1317 return path;
1318 },
1319 > path.ts
1320 > dirname(path: string): string {
1321 validateString(path, 'path');
1322 if (path.length === 0) {
1346 return path.slice(0, end);
1347 },
1348 > path.ts
1349 > basename(path: string, suffix?: string): string {
1350 if (suffix !== undefined) {
1351 validateString(suffix, 'suffix');
1426 return path.slice(start, end);
1427 },
1428 > path.ts
1429 > extname(path: string): string {
1430 validateString(path, 'path');
1431 let startDot = -1;
1480 return path.slice(startDot, end);
1481 },
1482 > path.ts
1483 > format: _format.bind(null, '/'),
1484 >
1485 > parse(path: string): ParsedPath {
1486 validateString(path, 'path');
1487
1565 return ret;
1566 },
1567 > path.ts
1568 > sep: '/',
1569 > delimiter: ':',
1570 > win32: null,
1571 > posix: null
1572 > };
1573 >
1574 > posix.win32 = win32.win32 = win32;
1575 > posix.posix = win32.posix = posix;
1576 >
1577 > export const normalize = (platformIsWin32 ? win32.normalize : posix.normalize);
1578 > export const isAbsolute = (platformIsWin32 ? win32.isAbsolute : posix.isAbsolute);
1579 > export const join = (platformIsWin32 ? win32.join : posix.join);
1580 > export const resolve = (platformIsWin32 ? win32.resolve : posix.resolve);
1581 > export const relative = (platformIsWin32 ? win32.relative : posix.relative);
1582 > export const dirname = (platformIsWin32 ? win32.dirname : posix.dirname);
1583 > export const basename = (platformIsWin32 ? win32.basename : posix.basename);
1584 > export const extname = (platformIsWin32 ? win32.extname : posix.extname);
1585 > export const format = (platformIsWin32 ? win32.format : posix.format);
1586 > export const parse = (platformIsWin32 ? win32.parse : posix.parse);
1587 > export const toNamespacedPath = (platformIsWin32 ? win32.toNamespacedPath : posix.toNamespacedPath);
1588 > export const sep = (platformIsWin32 ? win32.sep : posix.sep);
1589 > export const delimiter = (platformIsWin32 ? win32.delimiter : posix.delimiter);
src/vs/base/test/common/virtualScheduling/trace.ts 122 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- trace.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { BugIndicatingError } from '../../../common/errors.js';
7 >
8 > /**
9 > * # Trace — causal-chain attribution for scheduled work
10 > *
11 > * A {@link Trace} is an immutable value identifying a causal chain. Every
12 > * non-root trace carries a `parent`; the head of the chain has no parent.
13 > * Use {@link child} to extend a chain when scheduling follow-up work.
14 > *
15 > * Traces are used to answer "who caused this?" for any virtual event:
16 > * useful for debugging, for per-owner termination, and for attribution in
17 > * error messages.
18 > */
19 > export class Trace {
20 > private static _idCounter = 0;
21 > public readonly id: number = ++Trace._idCounter;
22 > public readonly root: Trace;
23 > public readonly depth: number;
24 >
25 > constructor(
26 > public readonly parent: Trace | undefined,
27 > public readonly label: string,
28 > public readonly stack: string | undefined = undefined,
29 > ) {
30 > this.root = parent?.root ?? this;
31 > this.depth = (parent?.depth ?? -1) + 1;
32 > }
33 >
34 > child(label: string, stack?: string): Trace {
35 return new Trace(this, label, stack);
36 }
37 > trace.ts
38 > /** "#id label ← #id label ← … ← #id label" */
39 > describe(): string {
40 const parts: string[] = [];
41 for (let t: Trace | undefined = this; t; t = t.parent) {
44 return parts.join(' ← ');
45 }
46 > trace.ts
47 > toString(): string { return this.describe(); }
48 > }
49 >
50 > /** Sentinel for "no known causal predecessor". */
51 > export const ROOT_TRACE: Trace = new Trace(undefined, '<root>');
52 >
53 > export function createTraceRoot(label: string, stack?: string): Trace {
54 return new Trace(undefined, label, stack);
55 }
56 > trace.ts
57 > interface Frame {
58 > readonly trace: Trace;
59 > readonly prev: Frame | undefined;
60 > }
61 >
62 > const ROOT_FRAME: Frame = { trace: ROOT_TRACE, prev: undefined };
63 >
64 > /**
65 > * Options for {@link TraceContext.runAsHandler}.
66 > *
67 > * # Why this is a per-call option
68 > *
69 > * `runAsHandler` cannot restore the previous trace synchronously: microtasks
70 > * enqueued by `fn` (including awaited continuations) must observe the new
71 > * trace. So the reset is deferred — but it must fire after the *closure* of
72 > * the microtask queue (the current microtask plus every microtask it
73 > * recursively enqueues), not just one drain.
74 > *
75 > * Per spec, the host doesn't run a macrotask until the microtask queue is
76 > * empty, so any macrotask primitive (`setTimeout(0)`, `setImmediate`, the
77 > * `setTimeout0` shim) achieves this. Letting the *caller* supply the sink
78 > * means:
79 > *
80 > * - the {@link VirtualTimeProcessor} can route the reset through the same
81 > * primitive its embedding uses for its own host hops, eliminating any
82 > * race between the processor's hops and the trace-reset timer;
83 > *
84 > * - production code without a processor can still use a real
85 > * `setTimeout(0)`-based sink and get the same semantics;
86 > *
87 > * - tests can install a deterministic sink (e.g. a hand-driven queue) for
88 > * fully synchronous assertions.
89 > */
90 > export interface RunAsHandlerOptions {
91 > /**
92 > * Sink for the deferred trace-reset.
93 > *
94 > * Must invoke `reset` after the microtask closure that follows the
95 > * `runAsHandler` call returns — i.e. on the next host macrotask.
96 > */
97 > readonly afterMicrotaskClosure: (reset: () => void) => void;
98 > }
99 >
100 > /**
101 > * Holds the mutable "current trace frame" slot. Construct fresh instances
102 > * for test isolation, or use {@link TraceContext.instance} for shared state.
103 > */
104 > export class TraceContext {
105 > public static readonly instance = new TraceContext();
106 >
107 > private _current: Frame = ROOT_FRAME;
108 > private _isHandlerRunning = false;
109 >
110 > currentTrace(): Trace { return this._current.trace; }
111 >
112 > /**
113 > * Install `t` as current for the synchronous duration of `fn`, then
114 > * restore. Nestable. Microtasks enqueued by fn that run after fn returns
115 > * see the *restored* trace — use {@link runAsHandler} when continuation
116 > * inheritance is wanted.
117 > */
118 > runWithTrace<T>(t: Trace, fn: () => T): T {
119 const prev = this._current;
120 const next: Frame = { trace: t, prev };
132 }
133 }
134 > trace.ts
135 > /**
136 > * Install `t` as current and run `fn`. The trace stays current through
137 > * the microtask closure that follows `fn`, so awaited continuations
138 > * inside fn observe `t`. The reset is dispatched via
139 > * `opts.afterMicrotaskClosure`.
140 > *
141 > * Throws on synchronous re-entry: timer callbacks never nest on the
142 > * same JS stack frame, so this only fires for misuse.
143 > */
144 > runAsHandler<T>(t: Trace, fn: () => T, opts: RunAsHandlerOptions): T {
145 if (this._isHandlerRunning) {
146 throw new Error(
165 }
166 }
167 > trace.ts
168 > _resetForTesting(): void {
169 this._current = ROOT_FRAME;
170 this._isHandlerRunning = false;
171 }
172 > } trace.ts
src/vs/base/common/observableInternal/observables/derivedImpl.ts 112 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derivedImpl.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IObservableWithChange, IObserver, IReaderWithStore, ISettableObservable, ITransaction, } from '../base.js';
7 > import { BaseObservable } from './baseObservable.js';
8 > import { DebugNameData } from '../debugName.js';
9 > import { BugIndicatingError, DisposableStore, EqualityComparer, assertFn, onBugIndicatingError } from '../commonFacade/deps.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { IChangeTracker } from '../changeTracker.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > export interface IDerivedReader<TChange = void> extends IReaderWithStore {
15 > /**
16 > * Call this to report a change delta or to force report a change, even if the new value is the same as the old value.
17 > */
18 > reportChange(change: TChange): void;
19 > }
20 >
21 > export const enum DerivedState {
22 > /** Initial state, no previous value, recomputation needed */
23 > initial = 0,
24 >
25 > /**
26 > * A dependency could have changed.
27 > * We need to explicitly ask them if at least one dependency changed.
28 > */
29 > dependenciesMightHaveChanged = 1,
30 >
31 > /**
32 > * A dependency changed and we need to recompute.
33 > * After recomputation, we need to check the previous value to see if we changed as well.
34 > */
35 > stale = 2,
36 >
37 > /**
38 > * No change reported, our cached value is up to date.
39 > */
40 > upToDate = 3,
41 > }
42 >
43 function derivedStateToString(state: DerivedState): string {
44 switch (state) {
50 }
51 }
53 > export class Derived<T, TChangeSummary = any, TChange = void> extends BaseObservable<T, TChange> implements IDerivedReader<TChange>, IObserver {
54 > private _state = DerivedState.initial;
55 > private _value: T | undefined = undefined;
56 > private _updateCount = 0;
57 > private _dependencies = new Set<IObservable<any>>();
58 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
59 > private _changeSummary: TChangeSummary | undefined = undefined;
60 > private _isUpdating = false;
61 > private _isComputing = false;
62 > private _didReportChange = false;
63 > private _isInBeforeUpdate = false;
64 > private _isReaderValid = false;
65 > private _store: DisposableStore | undefined = undefined;
66 > private _delayedStore: DisposableStore | undefined = undefined;
67 > private _removedObserverToCallEndUpdateOn: Set<IObserver> | null = null;
68 >
69 > public override get debugName(): string {
70 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
71 > }
72 >
73 > constructor(
74 public readonly _debugNameData: DebugNameData,
75 public readonly _computeFn: (reader: IDerivedReader<TChange>, changeSummary: TChangeSummary) => T,
82 this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
83 }
85 > protected override onLastObserverRemoved(): void {
86 /**
87 * We are not tracking changes anymore, thus we have to assume
107 this._handleLastObserverRemoved?.();
108 }
110 > public override get(): T {
111 const checkEnabled = false; // TODO set to true
112 if (this._isComputing && checkEnabled) {
164 }
165 }
167 > private _recompute() {
168 let didChange = false;
169 this._isComputing = true;
238 }
239 }
241 > public override toString(): string {
242 return `LazyDerived<${this.debugName}>`;
243 }
245 > // IObserver Implementation
246 >
247 > public beginUpdate<T>(_observable: IObservable<T>): void {
248 if (this._isUpdating) {
249 throw new BugIndicatingError('Cyclic deriveds are not supported yet!');
272 }
273 }
275 > public endUpdate<T>(_observable: IObservable<T>): void {
276 this._updateCount--;
277 if (this._updateCount === 0) {
291 assertFn(() => this._updateCount >= 0);
292 }
294 > public handlePossibleChange<T>(observable: IObservable<T>): void {
295 // In all other states, observers already know that we might have changed.
296 if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
301 }
302 }
304 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
305 if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable) || this._isInBeforeUpdate) {
306 getLogger()?.handleDerivedDependencyChanged(this, observable, change);
329 }
330 }
332 > // IReader Implementation
333 >
334 > private _ensureReaderValid(): void {
335 if (!this._isReaderValid) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
336 }
338 > public readObservable<T>(observable: IObservable<T>): T {
339 this._ensureReaderValid();
340
348 return value;
349 }
351 > public reportChange(change: TChange): void {
352 this._ensureReaderValid();
353
358 }
359 }
361 > get store(): DisposableStore {
362 this._ensureReaderValid();
363
367 return this._store;
368 }
370 > get delayedStore(): DisposableStore {
371 this._ensureReaderValid();
372
376 return this._delayedStore;
377 }
379 > public override addObserver(observer: IObserver): void {
380 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCount > 0;
381 super.addObserver(observer);
387 }
388 }
390 > public override removeObserver(observer: IObserver): void {
391 if (this._observers.has(observer) && this._updateCount > 0) {
392 if (!this._removedObserverToCallEndUpdateOn) {
397 super.removeObserver(observer);
398 }
400 > public debugGetState() {
401 return {
402 state: this._state,
408 };
409 }
411 > public debugSetValue(newValue: unknown) {
412 // eslint-disable-next-line local/code-no-any-casts
413 this._value = newValue as any;
414 }
416 > public debugRecompute(): void {
417 this.beginUpdate(this);
418 try {
426 }
427 }
429 > public setValue(newValue: T, tx: ITransaction, change: TChange): void {
430 this._value = newValue;
431 const observers = this._observers;
435 }
436 }
437 > } derivedImpl.ts
438 >
439 >
440 > export class DerivedWithSetter<T, TChangeSummary = any, TOutChanges = any> extends Derived<T, TChangeSummary, TOutChanges> implements ISettableObservable<T, TOutChanges> {
441 > constructor(
442 debugNameData: DebugNameData,
443 computeFn: (reader: IDerivedReader<TOutChanges>, changeSummary: TChangeSummary) => T,
src/vs/base/common/equals.ts 108 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- equals.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as arrays from './arrays.js';
7 >
8 > /*
9 > * Each function in this file which offers an equality comparison, has an accompanying
10 > * `*C` variant which returns an EqualityComparer function.
11 > *
12 > * The `*C` variant allows for easier composition of equality comparers and improved type-inference.
13 > */
14 >
15 >
16 > /** Represents a function that decides if two values are equal. */
17 > export type EqualityComparer<T> = (a: T, b: T) => boolean;
18 >
19 > export interface IEquatable<T> {
20 > equals(other: T): boolean;
21 > }
22 >
23 > /**
24 > * Compares two items for equality using strict equality.
25 > */
26 > export function strictEquals<T>(a: T, b: T): boolean {
27 return a === b;
28 }
29 > equals.ts
30 > export function strictEqualsC<T>(): EqualityComparer<T> {
31 return (a, b) => a === b;
32 }
33 > equals.ts
34 > /**
35 > * Checks if the items of two arrays are equal.
36 > * By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
37 > */
38 > export function arrayEquals<T>(a: readonly T[], b: readonly T[], itemEquals?: EqualityComparer<T>): boolean {
39 return arrays.equals(a, b, itemEquals ?? strictEquals);
40 }
41 > equals.ts
42 > /**
43 > * Checks if the items of two arrays are equal.
44 > * By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
45 > */
46 > export function arrayEqualsC<T>(itemEquals?: EqualityComparer<T>): EqualityComparer<readonly T[]> {
47 return (a, b) => arrays.equals(a, b, itemEquals ?? strictEquals);
48 }
49 > equals.ts
50 > /**
51 > * Drills into arrays (items ordered) and objects (keys unordered) and uses strict equality on everything else.
52 > */
53 > export function structuralEquals<T>(a: T, b: T): boolean {
54 if (a === b) {
55 return true;
95 return false;
96 }
97 > equals.ts
98 > export function structuralEqualsC<T>(): EqualityComparer<T> {
99 return (a, b) => structuralEquals(a, b);
100 }
101 > equals.ts
102 > /**
103 > * `getStructuralKey(a) === getStructuralKey(b) <=> structuralEquals(a, b)`
104 > * (assuming that a and b are not cyclic structures and nothing extends globalThis Array).
105 > */
106 > export function getStructuralKey(t: unknown): string {
107 return JSON.stringify(toNormalizedJsonStructure(t));
108 }
109 > equals.ts
110 > let objectId = 0;
111 > const objIds = new WeakMap<object, number>();
112 >
113 function toNormalizedJsonStructure(t: unknown): unknown {
114 if (Array.isArray(t)) {
136 return t;
137 }
138 > equals.ts
139 >
140 > /**
141 > * Two items are considered equal, if their stringified representations are equal.
142 > */
143 > export function jsonStringifyEquals<T>(a: T, b: T): boolean {
144 return JSON.stringify(a) === JSON.stringify(b);
145 }
146 > equals.ts
147 > /**
148 > * Two items are considered equal, if their stringified representations are equal.
149 > */
150 > export function jsonStringifyEqualsC<T>(): EqualityComparer<T> {
151 return (a, b) => JSON.stringify(a) === JSON.stringify(b);
152 }
153 > equals.ts
154 > /**
155 > * Uses `item.equals(other)` to determine equality.
156 > */
157 > export function thisEqualsC<T extends IEquatable<T>>(): EqualityComparer<T> {
158 return (a, b) => a.equals(b);
159 }
160 > equals.ts
161 > /**
162 > * Checks if two items are both null or undefined, or are equal according to the provided equality comparer.
163 > */
164 > export function equalsIfDefined<T>(v1: T | undefined | null, v2: T | undefined | null, equals: EqualityComparer<T>): boolean {
165 if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
166 return v2 === v1;
168 return equals(v1, v2);
169 }
170 > equals.ts
171 > /**
172 > * Returns an equality comparer that checks if two items are both null or undefined, or are equal according to the provided equality comparer.
173 > */
174 > export function equalsIfDefinedC<T>(equals: EqualityComparer<T>): EqualityComparer<T | undefined | null> {
175 return (v1, v2) => {
176 if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
180 };
181 }
182 > equals.ts
183 > /**
184 > * Each function in this file which offers an equality comparison, has an accompanying
185 > * `*C` variant which returns an EqualityComparer function.
186 > *
187 > * The `*C` variant allows for easier composition of equality comparers and improved type-inference.
188 > */
189 > export namespace equals {
190 > export const strict = strictEquals;
191 > export const strictC = strictEqualsC;
192 >
193 > export const array = arrayEquals;
194 > export const arrayC = arrayEqualsC;
195 >
196 > export const structural = structuralEquals;
197 > export const structuralC = structuralEqualsC;
198 >
199 > export const jsonStringify = jsonStringifyEquals;
200 > export const jsonStringifyC = jsonStringifyEqualsC;
201 >
202 > export const thisC = thisEqualsC;
203 >
204 > export const ifDefined = equalsIfDefined;
205 > export const ifDefinedC = equalsIfDefinedC;
206 > }
src/vs/base/common/observableInternal/observables/baseObservable.ts 104 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- baseObservable.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservableWithChange, IObserver, IReader, IObservable } from '../base.js';
7 > import { DisposableStore } from '../commonFacade/deps.js';
8 > import { DebugLocation } from '../debugLocation.js';
9 > import { DebugOwner, getFunctionName } from '../debugName.js';
10 > import { debugGetObservableGraph } from '../logging/debugGetDependencyGraph.js';
11 > import { getLogger, logObservable } from '../logging/logging.js';
12 > import type { keepObserved, recomputeInitiallyAndOnChange } from '../utils/utils.js';
13 > import { derivedOpts } from './derived.js';
14 >
15 > let _derived: typeof derivedOpts;
16 > /**
17 > * @internal
18 > * This is to allow splitting files.
19 > */
20 > export function _setDerivedOpts(derived: typeof _derived) {
21 > _derived = derived;
22 > }
23 >
24 > let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
25 > export function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange: typeof _recomputeInitiallyAndOnChange) {
26 > _recomputeInitiallyAndOnChange = recomputeInitiallyAndOnChange;
27 > }
28 >
29 > let _keepObserved: typeof keepObserved;
30 > export function _setKeepObserved(keepObserved: typeof _keepObserved) {
31 > _keepObserved = keepObserved;
32 > }
33 >
34 > let _debugGetObservableGraph: typeof debugGetObservableGraph;
35 > export function _setDebugGetObservableGraph(debugGetObservableGraph: typeof _debugGetObservableGraph) {
36 > _debugGetObservableGraph = debugGetObservableGraph;
37 > }
38 >
39 > export abstract class ConvenientObservable<T, TChange> implements IObservableWithChange<T, TChange> {
40 > get TChange(): TChange { return null!; }
41 >
42 > public abstract get(): T;
43 >
44 > public reportChanges(): void {
45 this.get();
46 }
48 > public abstract addObserver(observer: IObserver): void;
49 > public abstract removeObserver(observer: IObserver): void;
50 >
51 > /** @sealed */
52 > public read(reader: IReader | undefined): T {
53 if (reader) {
54 return reader.readObservable(this);
57 }
58 }
60 > /** @sealed */
61 > public map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
62 > public map<TNew>(owner: DebugOwner, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
63 > public map<TNew>(fnOrOwner: DebugOwner | ((value: T, reader: IReader) => TNew), fnOrUndefined?: (value: T, reader: IReader) => TNew, debugLocation: DebugLocation = DebugLocation.ofCaller()): IObservable<TNew> {
64 const owner = fnOrUndefined === undefined ? undefined : fnOrOwner as DebugOwner;
65 const fn = fnOrUndefined === undefined ? fnOrOwner as (value: T, reader: IReader) => TNew : fnOrUndefined;
91 );
92 }
94 > public abstract log(): IObservableWithChange<T, TChange>;
95 >
96 > /**
97 > * @sealed
98 > * Converts an observable of an observable value into a direct observable of the value.
99 > */
100 > public flatten<TNew>(this: IObservable<IObservableWithChange<TNew, any>>): IObservable<TNew> {
101 return _derived(
102 {
107 );
108 }
110 > public recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T> {
111 store.add(_recomputeInitiallyAndOnChange!(this, handleValue));
112 return this;
113 }
115 > /**
116 > * Ensures that this observable is observed. This keeps the cache alive.
117 > * However, in case of deriveds, it does not force eager evaluation (only when the value is read/get).
118 > * Use `recomputeInitiallyAndOnChange` for eager evaluation.
119 > */
120 > public keepObserved(store: DisposableStore): IObservable<T> {
121 store.add(_keepObserved!(this));
122 return this;
123 }
125 > public abstract get debugName(): string;
126 >
127 > protected get debugValue() {
128 return this.get();
129 }
131 > get debug(): DebugHelper {
132 return new DebugHelper(this);
133 }
135 >
136 > class DebugHelper {
137 > constructor(public readonly observable: IObservableWithChange<any, any>) {
138 }
140 > getDependencyGraph(): string {
141 return _debugGetObservableGraph(this.observable, { type: 'dependencies' });
142 }
144 > getObserverGraph(): string {
145 return _debugGetObservableGraph(this.observable, { type: 'observers' });
146 }
148 >
149 > export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
150 > protected readonly _observers = new Set<IObserver>();
151 >
152 > constructor(debugLocation: DebugLocation) {
153 super();
154 getLogger()?.handleObservableCreated(this, debugLocation);
155 }
157 > public addObserver(observer: IObserver): void {
158 const len = this._observers.size;
159 this._observers.add(observer);
165 }
166 }
168 > public removeObserver(observer: IObserver): void {
169 const deleted = this._observers.delete(observer);
170 if (deleted && this._observers.size === 0) {
175 }
176 }
178 > protected onFirstObserverAdded(): void { }
179 > protected onLastObserverRemoved(): void { }
180 >
181 > public override log(): IObservableWithChange<T, TChange> {
182 const hadLogger = !!getLogger();
183 logObservable(this);
src/vs/base/common/observableInternal/utils/promise.ts 104 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- promise.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 > import { DisposableStore } from '../../lifecycle.js';
6 > import { IObservable, ISettableObservable } from '../base.js';
7 > import { autorun } from '../reactions/autorun.js';
8 > import { transaction } from '../transaction.js';
9 > import { derived } from '../observables/derived.js';
10 > import { observableValue } from '../observables/observableValue.js';
11 >
12 > export class ObservableLazy<T> {
13 > private readonly _value = observableValue<T | undefined>(this, undefined);
14 >
15 > /**
16 > * The cached value.
17 > * Does not force a computation of the value.
18 > */
19 > public get cachedValue(): IObservable<T | undefined> { return this._value; }
20 >
21 > constructor(private readonly _computeValue: () => T) {
22 }
23 > promise.ts
24 > /**
25 > * Returns the cached value.
26 > * Computes the value if the value has not been cached yet.
27 > */
28 > public getValue(): T {
29 let v = this._value.get();
30 if (!v) {
34 return v;
35 }
36 > } promise.ts
37 >
38 > /**
39 > * A promise whose state is observable.
40 > */
41 > export class ObservablePromise<T> {
42 > public static fromFn<T>(fn: () => Promise<T>): ObservablePromise<T> {
43 > return new ObservablePromise(fn());
44 > }
45 >
46 > public static resolved<T>(value: T): ObservablePromise<T> {
47 return new ObservablePromise(Promise.resolve(value));
48 }
49 > promise.ts
50 > private readonly _value = observableValue<PromiseResult<T> | undefined>(this, undefined);
51 >
52 > /**
53 > * The promise that this object wraps.
54 > */
55 > public readonly promise: Promise<T>;
56 >
57 > /**
58 > * The current state of the promise.
59 > * Is `undefined` if the promise didn't resolve yet.
60 > */
61 > public readonly promiseResult: IObservable<PromiseResult<T> | undefined> = this._value;
62 >
63 > constructor(promise: Promise<T>) {
64 this.promise = promise.then(value => {
65 transaction(tx => {
76 });
77 }
78 > promise.ts
79 > public readonly resolvedValue = derived(this, reader => {
80 > const result = this.promiseResult.read(reader); promise.ts
81 > if (!result) {
82 > return undefined;
83 > }
84 > return result.getDataOrThrow();
85 > }); promise.ts
86 > }
87 >
88 > export class PromiseResult<T> {
89 > constructor(
90 /**
91 * The value of the resolved promise.
101 ) {
102 }
103 > promise.ts
104 > /**
105 > * Returns the value if the promise resolved, otherwise throws the error.
106 > */
107 > public getDataOrThrow(): T {
108 if (this.error) {
109 throw this.error;
111 return this.data!;
112 }
113 > } promise.ts
114 >
115 > /**
116 > * Tracks a changing {@link ObservablePromise}, exposing the last resolved value
117 > * and whether a newer promise is still pending.
118 > */
119 > export class ObservableResolvedPromise<T> {
120 > private readonly _lastResolved: ISettableObservable<T>;
121 > public readonly lastResolved: IObservable<T>;
122 >
123 > private readonly _isResolving = observableValue<boolean>(this, false);
124 > public readonly isResolving: IObservable<boolean> = this._isResolving;
125 >
126 > private _runningPromise: ObservablePromise<T> | undefined;
127 >
128 > constructor(
129 source: IObservable<ObservablePromise<T>>,
130 initialValue: T,
149 }));
150 }
151 > } promise.ts
152 >
153 > /**
154 > * A lazy promise whose state is observable.
155 > */
156 > export class ObservableLazyPromise<T> {
157 > private readonly _lazyValue = new ObservableLazy(() => new ObservablePromise(this._computePromise()));
158 >
159 > /**
160 > * Does not enforce evaluation of the promise compute function.
161 > * Is undefined if the promise has not been computed yet.
162 > */
163 > public readonly cachedPromiseResult = derived(this, reader => this._lazyValue.cachedValue.read(reader)?.promiseResult.read(reader));
164 >
165 > constructor(private readonly _computePromise: () => Promise<T>) {
166 }
167 > promise.ts
168 > public getPromise(): Promise<T> {
169 return this._lazyValue.getValue().promise;
170 }
171 > } promise.ts
src/vs/base/common/arraysFind.ts 95 covered LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- arraysFind.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Comparator } from './arrays.js';
7 >
8 > export function findLast<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;
9 > export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;
10 > export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): T | undefined {
11 const idx = findLastIdx(array, predicate, fromIndex);
12 if (idx === -1) {
15 return array[idx];
16 }
18 > export function findLastIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): number {
19 for (let i = fromIndex; i >= 0; i--) {
20 const element = array[i];
27 return -1;
28 }
30 > export function findFirst<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;
31 > export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;
32 > export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): T | undefined {
33 const idx = findFirstIdx(array, predicate, fromIndex);
34 if (idx === -1) {
37 return array[idx];
38 }
40 > export function findFirstIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): number {
41 for (let i = fromIndex; i < array.length; i++) {
42 const element = array[i];
49 return -1;
50 }
52 > /**
53 > * Finds the last item where predicate is true using binary search.
54 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
55 > *
56 > * @returns `undefined` if no item matches, otherwise the last item that matches the predicate.
57 > */
58 > export function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
59 const idx = findLastIdxMonotonous(array, predicate);
60 return idx === -1 ? undefined : array[idx];
61 }
63 > /**
64 > * Finds the last item where predicate is true using binary search.
65 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
66 > *
67 > * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
68 > */
69 > export function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
70 let i = startIdx;
71 let j = endIdxEx;
80 return i - 1;
81 }
83 > /**
84 > * Finds the first item where predicate is true using binary search.
85 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
86 > *
87 > * @returns `undefined` if no item matches, otherwise the first item that matches the predicate.
88 > */
89 > export function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
90 const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
91 return idx === array.length ? undefined : array[idx];
92 }
94 > /**
95 > * Finds the first item where predicate is true using binary search.
96 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
97 > *
98 > * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
99 > */
100 > export function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
101 let i = startIdx;
102 let j = endIdxEx;
111 return i;
112 }
114 > export function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
115 const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);
116 return idx === array.length ? -1 : idx;
117 }
119 > /**
120 > * Use this when
121 > * * You have a sorted array
122 > * * You query this array with a monotonous predicate to find the last item that has a certain property.
123 > * * You query this array multiple times with monotonous predicates that get weaker and weaker.
124 > */
125 > export class MonotonousArray<T> {
126 > public static assertInvariants = false;
127 >
128 > private _findLastMonotonousLastIdx = 0;
129 > private _prevFindLastPredicate: ((item: T) => boolean) | undefined;
130 >
131 > constructor(private readonly _array: readonly T[]) {
132 }
134 > /**
135 > * The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
136 > * For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.
137 > */
138 > findLastMonotonous(predicate: (item: T) => boolean): T | undefined {
139 if (MonotonousArray.assertInvariants) {
140 if (this._prevFindLastPredicate) {
152 return idx === -1 ? undefined : this._array[idx];
153 }
154 > } arraysFind.ts
155 >
156 > /**
157 > * Returns the first item that is equal to or greater than every other item.
158 > */
159 > export function findFirstMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
160 if (array.length === 0) {
161 return undefined;
171 return max;
172 }
174 > /**
175 > * Returns the last item that is equal to or greater than every other item.
176 > */
177 > export function findLastMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
178 if (array.length === 0) {
179 return undefined;
189 return max;
190 }
192 > /**
193 > * Returns the first item that is equal to or less than every other item.
194 > */
195 > export function findFirstMin<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
196 return findFirstMax(array, (a, b) => -comparator(a, b));
197 }
199 > export function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): number {
200 if (array.length === 0) {
201 return -1;
211 return maxIdx;
212 }
214 > /**
215 > * Returns the first mapped value of the array which is not undefined.
216 > */
217 > export function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
218 for (const value of items) {
219 const mapped = mapFn(value);
src/vs/base/test/common/virtualScheduling/virtualClock.ts 94 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- virtualClock.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { compareBy, numberComparator, tieBreakComparators } from '../../../common/arrays.js';
7 > import { Emitter } from '../../../common/event.js';
8 > import { IDisposable } from '../../../common/lifecycle.js';
9 > import { Trace } from './trace.js';
10 >
11 > export type VirtualTime = number;
12 >
13 > /** Debug source description for an event. */
14 > export interface EventSource {
15 > toString(): string;
16 > readonly stackTrace?: string;
17 > }
18 >
19 > /**
20 > * A unit of work scheduled at a point in virtual time.
21 > *
22 > * Timer callbacks are events. External completions (e.g. fake fs reads) can
23 > * also be modelled as events whose virtual completion time is chosen by a
24 > * scheduling policy — to the {@link VirtualClock} they are indistinguishable.
25 > */
26 > export interface VirtualEvent {
27 > readonly time: VirtualTime;
28 > readonly source: EventSource;
29 > readonly trace?: Trace;
30 > /**
31 > * Hint for the {@link Embedding}: this event prefers to run on a real
32 > * animation frame (e.g. so DOM measurements after it observe a real
33 > * reflow). Pure-time tests can ignore the hint.
34 > */
35 > readonly preferRealAnimationFrame?: boolean;
36 > run(): void;
37 > }
38 >
39 > interface QueuedEvent extends VirtualEvent { readonly id: number }
40 >
41 > const eventComparator = tieBreakComparators<QueuedEvent>(
42 > compareBy(e => e.time, numberComparator),
43 > compareBy(e => e.id, numberComparator),
44 > );
45 >
46 > /**
47 > * A pure data structure: a virtual clock + a priority queue of events.
48 > *
49 > * The clock has no concept of "real time". It is advanced exclusively by
50 > * {@link runNext}, which sets `now` to the next event's `time` before running
51 > * it. The {@link VirtualTimeProcessor} is the only intended driver, but the
52 > * clock is useful in isolation (e.g. for unit-testing a scheduler or for
53 > * stepping a scenario manually).
54 > */
55 > export class VirtualClock {
56 > private _now: VirtualTime;
57 > private _idCounter = 0;
58 > private readonly _queue = new SimplePriorityQueue<QueuedEvent>(eventComparator);
59 > private readonly _onEventScheduled = new Emitter<VirtualEvent>();
60 >
61 > public readonly onEventScheduled = this._onEventScheduled.event;
62 >
63 > constructor(startTime: VirtualTime = 0) {
64 this._now = startTime;
65 }
67 > get now(): VirtualTime { return this._now; }
68 > get hasEvents(): boolean { return this._queue.length > 0; }
69 >
70 > schedule(event: VirtualEvent): IDisposable {
71 if (event.time < this._now) {
72 throw new Error(`Scheduled time (${event.time}) must be >= now (${this._now}).`);
77 return { dispose: () => this._queue.remove(queued) };
78 }
80 > peekNext(): VirtualEvent | undefined { return this._queue.getMin(); }
81 >
82 > runNext(): VirtualEvent | undefined {
83 const e = this._queue.removeMin();
84 if (e) {
88 return e;
89 }
91 > getEvents(): readonly VirtualEvent[] { return this._queue.toSortedArray(); }
92 > }
93 >
94 > class SimplePriorityQueue<T> {
95 > private _items: T[] = [];
96 > private _sorted = true;
97 >
98 > constructor(private readonly _compare: (a: T, b: T) => number) { }
99 >
100 > get length(): number { return this._items.length; }
101 >
102 > add(value: T): void {
103 this._items.push(value);
104 this._sorted = false;
105 }
107 > remove(value: T): void {
108 const i = this._items.indexOf(value);
109 if (i !== -1) { this._items.splice(i, 1); }
110 }
112 > getMin(): T | undefined { this._ensureSorted(); return this._items[0]; }
113 > removeMin(): T | undefined { this._ensureSorted(); return this._items.shift(); }
114 > toSortedArray(): T[] { this._ensureSorted(); return [...this._items]; }
115 >
116 > private _ensureSorted(): void {
117 if (this._sorted) { return; }
118 this._items.sort(this._compare);
119 this._sorted = true;
120 }
121 > } virtualClock.ts
src/vs/base/common/extpath.ts 91 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extpath.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from './charCode.js';
7 > import { isAbsolute, join, normalize, posix, sep } from './path.js';
8 > import { isWindows } from './platform.js';
9 > import { equalsIgnoreCase, rtrim, startsWithIgnoreCase } from './strings.js';
10 > import { isNumber } from './types.js';
11 >
12 > export function isPathSeparator(code: number) {
13 return code === CharCode.Slash || code === CharCode.Backslash;
14 }
15 > extpath.ts
16 > /**
17 > * Takes a Windows OS path and changes backward slashes to forward slashes.
18 > * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
19 > * Using it on a Linux or MaxOS path might change it.
20 > */
21 > export function toSlashes(osPath: string) {
22 return osPath.replace(/[\\/]/g, posix.sep);
23 }
24 > extpath.ts
25 > /**
26 > * Takes a Windows OS path (using backward or forward slashes) and turns it into a posix path:
27 > * - turns backward slashes into forward slashes
28 > * - makes it absolute if it starts with a drive letter
29 > * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
30 > * Using it on a Linux or MaxOS path might change it.
31 > */
32 > export function toPosixPath(osPath: string) {
33 if (osPath.indexOf('/') === -1) {
34 osPath = toSlashes(osPath);
39 return osPath;
40 }
41 > extpath.ts
42 > /**
43 > * Computes the _root_ this path, like `getRoot('c:\files') === c:\`,
44 > * `getRoot('files:///files/path') === files:///`,
45 > * or `getRoot('\\server\shares\path') === \\server\shares\`
46 > */
47 > export function getRoot(path: string, sep: string = posix.sep): string {
48 if (!path) {
49 return '';
111 return '';
112 }
113 > extpath.ts
114 > /**
115 > * Check if the path follows this pattern: `\\hostname\sharename`.
116 > *
117 > * @see https://msdn.microsoft.com/en-us/library/gg465305.aspx
118 > * @return A boolean indication if the path is a UNC path, on none-windows
119 > * always false.
120 > */
121 > export function isUNC(path: string): boolean {
122 if (!isWindows) {
123 // UNC is a windows concept
162 return true;
163 }
164 > extpath.ts
165 > // Reference: https://en.wikipedia.org/wiki/Filename
166 > const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
167 > const UNIX_INVALID_FILE_CHARS = /[/]/g;
168 > const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i;
169 > export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
170 const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
171
201 return true;
202 }
203 > extpath.ts
204 > /**
205 > * @deprecated please use `IUriIdentityService.extUri.isEqual` instead. If you are
206 > * in a context without services, consider to pass down the `extUri` from the outside
207 > * or use `extUriBiasedIgnorePathCase` if you know what you are doing.
208 > */
209 > export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean {
210 const identityEquals = (pathA === pathB);
211 if (!ignoreCase || identityEquals) {
219 return equalsIgnoreCase(pathA, pathB);
220 }
221 > extpath.ts
222 > /**
223 > * @deprecated please use `IUriIdentityService.extUri.isEqualOrParent` instead. If
224 > * you are in a context without services, consider to pass down the `extUri` from the
225 > * outside, or use `extUriBiasedIgnorePathCase` if you know what you are doing.
226 > */
227 > export function isEqualOrParent(base: string, parentCandidate: string, ignoreCase?: boolean, forcePosixSemantics = false): boolean {
228 const separator = forcePosixSemantics ? posix.sep : sep;
229
272 return base.indexOf(parentCandidate) === 0;
273 }
274 > extpath.ts
275 > export function isWindowsDriveLetter(char0: number): boolean {
276 return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z;
277 }
278 > extpath.ts
279 > export function sanitizeFilePath(candidate: string, cwd: string): string {
280
281 // Special case: allow to open a drive letter without trailing backslash
295 return removeTrailingPathSeparator(candidate);
296 }
297 > extpath.ts
298 > export function removeTrailingPathSeparator(candidate: string): string {
299 if (isWindows) {
300 candidate = rtrim(candidate, sep);
316 return candidate;
317 }
318 > extpath.ts
319 > export function isRootOrDriveLetter(path: string): boolean {
320 const pathNormalized = normalize(path);
321
331 return pathNormalized === posix.sep;
332 }
333 > extpath.ts
334 > export function hasDriveLetter(path: string, isWindowsOS: boolean = isWindows): boolean {
335 if (isWindowsOS) {
336 return isWindowsDriveLetter(path.charCodeAt(0)) && path.charCodeAt(1) === CharCode.Colon;
339 return false;
340 }
341 > extpath.ts
342 > export function getDriveLetter(path: string, isWindowsOS: boolean = isWindows): string | undefined {
343 return hasDriveLetter(path, isWindowsOS) ? path[0] : undefined;
344 }
345 > extpath.ts
346 > export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number {
347 if (candidate.length > path.length) {
348 return -1;
360 return path.indexOf(candidate);
361 }
362 > extpath.ts
363 > export interface IPathWithLineAndColumn {
364 > path: string;
365 > line?: number;
366 > column?: number;
367 > }
368 >
369 > export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn {
370 const segments = rawPath.split(':'); // C:\file.txt:<line>:<column>
371
395 };
396 }
397 > extpath.ts
398 > const pathChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
399 > const windowsSafePathFirstChars = 'BDEFGHIJKMOQRSTUVWXYZbdefghijkmoqrstuvwxyz0123456789';
400 >
401 > export function randomPath(parent?: string, prefix?: string, randomLength = 8): string {
402 let suffix = '';
403 for (let i = 0; i < randomLength; i++) {
src/vs/base/common/observableInternal/reactions/autorun.ts 91 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorun.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IReaderWithStore, IReader, IObservable, ISettableObservable } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, IDisposable, toDisposable } from '../commonFacade/deps.js';
9 > import { DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { AutorunObserver } from './autorunImpl.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 > import { observableValue } from '../observables/observableValue.js';
13 > import { transaction } from '../transaction.js';
14 >
15 > /**
16 > * Runs immediately and whenever a transaction ends and an observed observable changed.
17 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
18 > */
19 > export function autorun(fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
20 return new AutorunObserver(
21 new DebugNameData(undefined, undefined, fn),
25 );
26 }
27 > autorun.ts
28 > /**
29 > * Runs immediately and whenever a transaction ends and an observed observable changed.
30 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
31 > */
32 > export function autorunOpts(options: IDebugNameData & {}, fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
33 return new AutorunObserver(
34 new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? fn),
38 );
39 }
40 > autorun.ts
41 > /**
42 > * Runs immediately and whenever a transaction ends and an observed observable changed.
43 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
44 > *
45 > * Use `changeTracker.createChangeSummary` to create a "change summary" that can collect the changes.
46 > * Use `changeTracker.handleChange` to add a reported change to the change summary.
47 > * The run function is given the last change summary.
48 > * The change summary is discarded after the run function was called.
49 > *
50 > * @see autorun
51 > */
52 > export function autorunHandleChanges<TChangeSummary>(
53 options: IDebugNameData & {
54 changeTracker: IChangeTracker<TChangeSummary>;
64 );
65 }
66 > autorun.ts
67 > /**
68 > * @see autorunHandleChanges (but with a disposable store that is cleared before the next run or on dispose)
69 > */
70 > export function autorunWithStoreHandleChanges<TChangeSummary>(
71 options: IDebugNameData & {
72 changeTracker: IChangeTracker<TChangeSummary>;
92 });
93 }
94 > autorun.ts
95 > /**
96 > * @see autorun (but with a disposable store that is cleared before the next run or on dispose)
97 > *
98 > * @deprecated Use `autorun(reader => { reader.store.add(...) })` instead!
99 > */
100 > export function autorunWithStore(fn: (reader: IReader, store: DisposableStore) => void): IDisposable {
101 const store = new DisposableStore();
102 const disposable = autorunOpts(
116 });
117 }
118 > autorun.ts
119 > export function autorunDelta<T>(
120 observable: IObservable<T>,
121 handler: (args: { lastValue: T | undefined; newValue: T }) => void
129 });
130 }
131 > autorun.ts
132 > export function autorunIterableDelta<T>(
133 getValue: (reader: IReader) => Iterable<T>,
134 handler: (args: { addedValues: T[]; removedValues: T[] }) => void,
157 });
158 }
159 > autorun.ts
160 > /**
161 > * For each key-stable item in {@link items}, runs {@link setup} once when the
162 > * key is first observed and disposes the per-key {@link DisposableStore} when
163 > * the key is no longer present in the array (or when the returned disposable
164 > * is disposed).
165 > *
166 > * The {@link IObservable} handed to {@link setup} fires whenever the array
167 > * still contains an item with the same key but the item value itself has
168 > * changed (e.g. because the upstream state is immutable and produced a new
169 > * object with the same id). All per-key value updates triggered by a single
170 > * change to {@link items} are batched into one transaction, so dependent
171 > * autoruns observe a consistent snapshot.
172 > *
173 > * Per-key state should be stored in closures or in disposables registered
174 > * against the per-key {@link DisposableStore}. {@link setup} should not call
175 > * `.read()` on the outer {@link items} observable from its body (use the
176 > * provided per-key value observable, or create inner autoruns).
177 > */
178 > export function autorunPerKeyedItem<TIn, TKey>(
179 items: IObservable<readonly TIn[]>,
180 keyFn: (input: TIn) => TKey,
227 });
228 }
229 > autorun.ts
230 > export interface IReaderWithDispose extends IReaderWithStore, IDisposable { }
231 >
232 > /**
233 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
234 > * It it safe to call `dispose()` synchronously.
235 > * @deprecated Use autorunSelfDisposable2
236 > */
237 > export function autorunSelfDisposable(fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
238 let ar: IDisposable | undefined;
239 let disposed = false;
258 return ar;
259 }
260 > autorun.ts
261 >
262 > /**
263 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
264 > * It it safe to call `dispose()` synchronously.
265 > * TODO@hediet/copilot: rename to delete autorunSelfDisposable, and rename autorunSelfDisposable2 to autorunSelfDisposable.
266 > */
267 > export function registerAutorunSelfDisposable(store: DisposableStore, fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): void {
268 let ar: IDisposable | undefined;
269 let disposeSync = false;
src/vs/base/common/cancellation.ts 90 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- cancellation.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Emitter, Event } from './event.js';
7 > import { DisposableStore, IDisposable } from './lifecycle.js';
8 >
9 > export interface CancellationToken {
10 >
11 > /**
12 > * A flag signalling is cancellation has been requested.
13 > */
14 > readonly isCancellationRequested: boolean;
15 >
16 > /**
17 > * An event which fires when cancellation is requested. This event
18 > * only ever fires `once` as cancellation can only happen once. Listeners
19 > * that are registered after cancellation will be called (next event loop run),
20 > * but also only once.
21 > *
22 > * @event
23 > */
24 > readonly onCancellationRequested: (listener: (e: void) => unknown, thisArgs?: unknown, disposables?: IDisposable[]) => IDisposable;
25 > }
26 >
27 > const shortcutEvent: Event<void> = Object.freeze(function (callback, context?): IDisposable {
28 const handle = setTimeout(callback.bind(context), 0);
29 return { dispose() { clearTimeout(handle); } };
30 });
32 > export namespace CancellationToken {
33 >
34 > export function isCancellationToken(thing: unknown): thing is CancellationToken {
35 if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
36 return true;
45 && typeof (thing as CancellationToken).onCancellationRequested === 'function';
46 }
48 >
49 > export const None = Object.freeze<CancellationToken>({
50 > isCancellationRequested: false,
51 > onCancellationRequested: Event.None
52 > });
53 >
54 > export const Cancelled = Object.freeze<CancellationToken>({
55 > isCancellationRequested: true,
56 > onCancellationRequested: shortcutEvent
57 > });
58 > }
59 >
60 class MutableToken implements CancellationToken {
61
62 private _isCancelled: boolean = false;
63 private _emitter: Emitter<void> | null = null;
65 > public cancel() {
66 if (!this._isCancelled) {
67 this._isCancelled = true;
72 }
73 }
75 > get isCancellationRequested(): boolean {
76 return this._isCancelled;
77 }
79 > get onCancellationRequested(): Event<void> {
80 if (this._isCancelled) {
81 return shortcutEvent;
86 return this._emitter.event;
87 }
89 > public dispose(): void {
90 if (this._emitter) {
91 this._emitter.dispose();
93 }
94 }
96 >
97 > export class CancellationTokenSource {
98 >
99 > private _token?: CancellationToken = undefined;
100 > private _parentListener?: IDisposable = undefined;
101 >
102 > constructor(parent?: CancellationToken) {
103 this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
104 }
106 > get token(): CancellationToken {
107 if (!this._token) {
108 // be lazy and create the token only when
112 return this._token;
113 }
115 > cancel(): void {
116 if (!this._token) {
117 // save an object by returning the default
125 }
126 }
128 > dispose(cancel: boolean = false): void {
129 if (cancel) {
130 this.cancel();
140 }
141 }
142 > } cancellation.ts
143 >
144 > export function cancelOnDispose(store: DisposableStore): CancellationToken {
145 const source = new CancellationTokenSource();
146 store.add({ dispose() { source.cancel(); } });
147 return source.token;
148 }
150 > /**
151 > * A pool that aggregates multiple cancellation tokens. The pool's own token
152 > * (accessible via `pool.token`) is cancelled only after every token added
153 > * to the pool has been cancelled. Adding tokens after the pool token has
154 > * been cancelled has no effect.
155 > */
156 > export class CancellationTokenPool {
157
158 private readonly _source = new CancellationTokenSource();
162 private _cancelled: number = 0;
163 private _isDone: boolean = false;
165 > get token(): CancellationToken {
166 return this._source.token;
167 }
169 > /**
170 > * Add a token to the pool. If the token is already cancelled it is counted
171 > * immediately. Tokens added after the pool token has been cancelled are ignored.
172 > */
173 > add(token: CancellationToken): void {
174 if (this._isDone) {
175 return;
191 this._listeners.add(d);
192 }
194 > private _check(): void {
195 if (!this._isDone && this._total > 0 && this._total === this._cancelled) {
196 this._isDone = true;
src/vs/base/common/observableInternal/utils/utils.ts 89 covered LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { autorun } from '../reactions/autorun.js';
7 > import { IObservable, IObservableWithChange, IObserver, IReader, ITransaction } from '../base.js';
8 > import { observableValue } from '../observables/observableValue.js';
9 > import { DebugOwner } from '../debugName.js';
10 > import { DisposableStore, Event, IDisposable, toDisposable } from '../commonFacade/deps.js';
11 > import { derived, derivedOpts } from '../observables/derived.js';
12 > import { observableFromEvent } from '../observables/observableFromEvent.js';
13 > import { observableSignal } from '../observables/observableSignal.js';
14 > import { _setKeepObserved, _setRecomputeInitiallyAndOnChange } from '../observables/baseObservable.js';
15 > import { DebugLocation } from '../debugLocation.js';
16 >
17 > export function observableFromPromise<T>(promise: Promise<T>): IObservable<{ value?: T }> {
18 const observable = observableValue<{ value?: T }>('promiseValue', {});
19 promise.then((value) => {
22 return observable;
23 }
24 > utils.ts
25 > export function signalFromObservable<T>(owner: DebugOwner | undefined, observable: IObservable<T>): IObservable<void> {
26 return derivedOpts({
27 owner,
31 });
32 }
33 > utils.ts
34 > /**
35 > * Creates an observable that debounces the input observable.
36 > */
37 > export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number | ((lastValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
38 let hasValue = false;
39 let lastValue: T | undefined;
79 }, debugLocation);
80 }
81 > utils.ts
82 > /**
83 > * Creates an observable that throttles the input observable.
84 > * Unlike {@link debouncedObservable}, the timer starts on the first change
85 > * and is not reset by subsequent changes, preventing starvation.
86 > */
87 > export function throttledObservable<T>(observable: IObservable<T>, throttleMs: number, debugLocation = DebugLocation.ofCaller()): IObservable<T> {
88 let hasValue = false;
89 let lastValue: T | undefined;
126 }, debugLocation);
127 }
128 > utils.ts
129 > /**
130 > * Creates an observable that debounces the input observable.
131 > */
132 > export function debouncedObservable2<T>(observable: IObservable<T>, debounceMs: number | ((currentValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
133 const s = observableSignal('handleTimeout');
134
167 return d;
168 }
169 > utils.ts
170 > export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
171 const observable = observableValue('triggeredRecently', false);
172
186 return observable;
187 }
188 > utils.ts
189 > /**
190 > * This makes sure the observable is being observed and keeps its cache alive.
191 > */
192 > export function keepObserved<T>(observable: IObservable<T>): IDisposable {
193 const o = new KeepAliveObserver(false, undefined);
194 observable.addObserver(o);
197 });
198 }
199 > utils.ts
200 > _setKeepObserved(keepObserved);
201 >
202 > /**
203 > * This converts the given observable into an autorun.
204 > */
205 > export function recomputeInitiallyAndOnChange<T>(observable: IObservable<T>, handleValue?: (value: T) => void): IDisposable {
206 const o = new KeepAliveObserver(true, handleValue);
207 observable.addObserver(o);
216 });
217 }
218 > utils.ts
219 > _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
220 >
221 > export class KeepAliveObserver implements IObserver {
222 > private _counter = 0;
223 >
224 > constructor(
225 private readonly _forceRecompute: boolean,
226 private readonly _handleValue: ((value: any) => void) | undefined,
227 ) { }
228 > utils.ts
229 > beginUpdate<T>(observable: IObservable<T>): void {
230 this._counter++;
231 }
232 > utils.ts
233 > endUpdate<T>(observable: IObservable<T>): void {
234 if (this._counter === 1 && this._forceRecompute) {
235 if (this._handleValue) {
241 this._counter--;
242 }
243 > utils.ts
244 > handlePossibleChange<T>(observable: IObservable<T>): void {
245 // NO OP
246 }
247 > utils.ts
248 > handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
249 // NO OP
250 }
251 > } utils.ts
252 >
253 > export function derivedObservableWithCache<T>(owner: DebugOwner, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T> {
254 let lastValue: T | undefined = undefined;
255 const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
259 return observable;
260 }
261 > utils.ts
262 > export function derivedObservableWithWritableCache<T>(owner: object, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T>
263 & { clearCache(transaction: ITransaction): void; setCache(newValue: T | undefined, tx: ITransaction | undefined): void } {
264 let lastValue: T | undefined = undefined;
280 });
281 }
282 > utils.ts
283 > /**
284 > * When the items array changes, referential equal items are not mapped again.
285 > */
286 > export function mapObservableArrayCached<TIn, TOut, TKey = TIn>(owner: DebugOwner, items: IObservable<readonly TIn[]>, map: (input: TIn, store: DisposableStore) => TOut, keySelector?: (input: TIn) => TKey): IObservable<readonly TOut[]> {
287 let m = new ArrayMap(map, keySelector);
288 const self = derivedOpts({
300 return self;
301 }
302 > utils.ts
303 > class ArrayMap<TIn, TOut, TKey> implements IDisposable {
304 > private readonly _cache = new Map<TKey, { out: TOut; store: DisposableStore }>();
305 > private _items: TOut[] = [];
306 > constructor(
307 private readonly _map: (input: TIn, store: DisposableStore) => TOut,
308 private readonly _keySelector?: (input: TIn) => TKey,
309 ) {
310 }
311 > utils.ts
312 > public dispose(): void {
313 this._cache.forEach(entry => entry.store.dispose());
314 this._cache.clear();
315 }
316 > utils.ts
317 > public setItems(items: readonly TIn[]): void {
318 const newItems: TOut[] = [];
319 const itemsToRemove = new Set(this._cache.keys());
342 this._items = newItems;
343 }
344 > utils.ts
345 > public getItems(): TOut[] {
346 return this._items;
347 }
348 > } utils.ts
349 >
350 > export function isObservable<T>(obj: unknown): obj is IObservable<T> {
351 return !!obj && (<IObservable<T>>obj).read !== undefined && (<IObservable<T>>obj).reportChanges !== undefined;
352 }
src/vs/base/common/cache.ts 88 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- cache.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken, CancellationTokenSource } from './cancellation.js';
7 > import { IDisposable } from './lifecycle.js';
8 >
9 > export interface CacheResult<T> extends IDisposable {
10 > promise: Promise<T>;
11 > }
12 >
13 > export class Cache<T> {
14 >
15 > private result: CacheResult<T> | null = null;
16 > constructor(private task: (ct: CancellationToken) => Promise<T>) { }
17 >
18 > get(): CacheResult<T> {
19 if (this.result) {
20 return this.result;
35 return this.result;
36 }
37 > } cache.ts
38 >
39 > export function identity<T>(t: T): T {
40 return t;
41 }
42 > cache.ts
43 > interface ICacheOptions<TArg> {
44 > /**
45 > * The cache key is used to identify the cache entry.
46 > * Strict equality is used to compare cache keys.
47 > */
48 > getCacheKey: (arg: TArg) => unknown;
49 > }
50 >
51 > /**
52 > * Uses a LRU cache to make a given parametrized function cached.
53 > * Caches just the last key/value.
54 > */
55 > export class LRUCachedFunction<TArg, TComputed> {
56 > private lastCache: TComputed | undefined = undefined;
57 > private lastArgKey: unknown | undefined = undefined;
58 >
59 > private readonly _fn: (arg: TArg) => TComputed;
60 > private readonly _computeKey: (arg: TArg) => unknown;
61 >
62 > constructor(fn: (arg: TArg) => TComputed);
63 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
64 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
65 > if (typeof arg1 === 'function') {
66 > this._fn = arg1;
67 > this._computeKey = identity;
68 > } else {
69 this._fn = arg2!;
70 this._computeKey = arg1.getCacheKey;
71 }
72 > } cache.ts
73 >
74 > public get(arg: TArg): TComputed {
75 const key = this._computeKey(arg);
76 if (this.lastArgKey !== key) {
80 return this.lastCache!;
81 }
82 > } cache.ts
83 >
84 > /**
85 > * Uses an unbounded cache to memoize the results of the given function.
86 > */
87 > export class CachedFunction<TArg, TComputed> {
88 > private readonly _map = new Map<TArg, TComputed>();
89 > private readonly _map2 = new Map<unknown, TComputed>();
90 > public get cachedValues(): ReadonlyMap<TArg, TComputed> {
91 > return this._map;
92 > }
93 >
94 > private readonly _fn: (arg: TArg) => TComputed;
95 > private readonly _computeKey: (arg: TArg) => unknown;
96 >
97 > constructor(fn: (arg: TArg) => TComputed);
98 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
99 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
100 if (typeof arg1 === 'function') {
101 this._fn = arg1;
106 }
107 }
108 > cache.ts
109 > public get(arg: TArg): TComputed {
110 const key = this._computeKey(arg);
111 if (this._map2.has(key)) {
118 return value;
119 }
120 > } cache.ts
121 >
122 > /**
123 > * Uses an unbounded cache to memoize the results of the given function.
124 > */
125 > export class WeakCachedFunction<TArg, TComputed> {
126 > private readonly _map = new WeakMap<WeakKey, TComputed>();
127 >
128 > private readonly _fn: (arg: TArg) => TComputed;
129 > private readonly _computeKey: (arg: TArg) => unknown;
130 >
131 > constructor(fn: (arg: TArg) => TComputed);
132 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
133 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
134 if (typeof arg1 === 'function') {
135 this._fn = arg1;
140 }
141 }
142 > cache.ts
143 > public get(arg: TArg): TComputed {
144 const key = this._computeKey(arg) as WeakKey;
145 if (this._map.has(key)) {
151 return value;
152 }
153 > } cache.ts
src/vs/base/common/observableInternal/reactions/autorunImpl.ts 78 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorunImpl.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IObservableWithChange, IObserver, IReaderWithStore } from '../base.js';
7 > import { DebugNameData } from '../debugName.js';
8 > import { assertFn, BugIndicatingError, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, trackDisposable } from '../commonFacade/deps.js';
9 > import { getLogger } from '../logging/logging.js';
10 > import { IChangeTracker } from '../changeTracker.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export const enum AutorunState {
14 > /**
15 > * A dependency could have changed.
16 > * We need to explicitly ask them if at least one dependency changed.
17 > */
18 > dependenciesMightHaveChanged = 1,
19 >
20 > /**
21 > * A dependency changed and we need to recompute.
22 > */
23 > stale = 2,
24 > upToDate = 3,
25 > }
26 >
27 function autorunStateToString(state: AutorunState): string {
28 switch (state) {
33 }
34 }
36 > export class AutorunObserver<TChangeSummary = any> implements IObserver, IReaderWithStore, IDisposable {
37 > private _state = AutorunState.stale;
38 > private _updateCount = 0;
39 > private _disposed = false;
40 > private _dependencies = new Set<IObservable<any>>();
41 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
42 > private _changeSummary: TChangeSummary | undefined;
43 > private _isRunning = false;
44 > private _iteration = 0;
45 >
46 > public get debugName(): string {
47 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
48 > }
49 >
50 > constructor(
51 public readonly _debugNameData: DebugNameData,
52 public readonly _runFn: (reader: IReaderWithStore, changeSummary: TChangeSummary) => void,
60 trackDisposable(this);
61 }
63 > public dispose(): void {
64 if (this._disposed) {
65 return;
81 markAsDisposed(this);
82 }
84 > private _run() {
85 const emptySet = this._dependenciesToBeRemoved;
86 this._dependenciesToBeRemoved = this._dependencies;
130 }
131 }
133 > public toString(): string {
134 return `Autorun<${this.debugName}>`;
135 }
137 > // IObserver implementation
138 > public beginUpdate(_observable: IObservable<any>): void {
139 if (this._state === AutorunState.upToDate) {
140 this._checkIterations();
143 this._updateCount++;
144 }
146 > public endUpdate(_observable: IObservable<any>): void {
147 try {
148 if (this._updateCount === 1) {
175 assertFn(() => this._updateCount >= 0);
176 }
178 > public handlePossibleChange(observable: IObservable<any>): void {
179 if (this._state === AutorunState.upToDate && this._isDependency(observable)) {
180 this._checkIterations();
182 }
183 }
185 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
186 if (this._isDependency(observable)) {
187 getLogger()?.handleAutorunDependencyChanged(this, observable, change);
203 }
204 }
206 > private _isDependency(observable: IObservableWithChange<any, any>): boolean {
207 return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable);
208 }
210 > // IReader implementation
211 >
212 > private _ensureNoRunning(): void {
213 if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
214 }
216 > public readObservable<T>(observable: IObservable<T>): T {
217 this._ensureNoRunning();
218
228 return value;
229 }
231 > private _store: DisposableStore | undefined = undefined;
232 > get store(): DisposableStore {
233 this._ensureNoRunning();
234 if (this._disposed) {
241 return this._store;
242 }
244 > private _delayedStore: DisposableStore | undefined = undefined;
245 > get delayedStore(): DisposableStore {
246 this._ensureNoRunning();
247 if (this._disposed) {
254 return this._delayedStore;
255 }
257 > public debugGetState() {
258 return {
259 isRunning: this._isRunning,
264 };
265 }
267 > public debugRerun(): void {
268 if (!this._isRunning) {
269 this._run();
272 }
273 }
275 > private _checkIterations(): boolean {
276 if (this._iteration > 100) {
277 onBugIndicatingError(new BugIndicatingError(`Autorun '${this.debugName}' is stuck in an infinite update loop.`));
src/vs/base/test/common/virtualScheduling/embedding.ts 76 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- embedding.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { setTimeout0, setTimeout0IsFaster } from '../../../common/platform.js';
7 > import { TimeApi } from './timeApi.js';
8 > import { VirtualEvent } from './virtualClock.js';
9 >
10 > /**
11 > * # The processor/host embedding
12 > *
13 > * An {@link Embedding} is the contract between the processor's pure state
14 > * machine and the host event loop. It is invoked once per virtual step that
15 > * produced progress, and decides *how* the processor reaches the host before
16 > * the next step.
17 > *
18 > * ## Contract
19 > *
20 > * On each invocation the embedding MUST do exactly one of:
21 > *
22 > * 1. Return `'continueSync'` **without** calling `then`. The processor will
23 > * loop in place on the same host stack frame.
24 > *
25 > * 2. Schedule `then` on a host primitive (microtask, macrotask, paint frame)
26 > * and return `'cbScheduled'`. The processor will return and wait for the
27 > * callback to re-enter the trampoline.
28 > *
29 > * The embedding MUST NOT call `then` synchronously and also return
30 > * `'cbScheduled'` (that would re-enter the trampoline before this call
31 > * completed). Likewise, returning `'continueSync'` while having scheduled
32 > * `then` async would cause `then` to fire after the trampoline already
33 > * looped — also a bug.
34 > *
35 > * ## Why a callback contract instead of async/await
36 > *
37 > * Every `await` is an implicit microtask hop. For code whose job is to
38 > * decide host hops, that's the wrong abstraction: the reader has to mentally
39 > * compile the `await` to a boundary. With this contract, every host hop is
40 > * a named call to a single primitive (`api.setTimeout`, `setTimeout0`,
41 > * `api.requestAnimationFrame`, …) at exactly one site in this file.
42 > */
43 > export type Embedding = (
44 > nextEvent: VirtualEvent,
45 > then: () => void,
46 > ) => 'continueSync' | 'cbScheduled';
47 >
48 > /**
49 > * Tasks never schedule via promise chains. The processor runs virtual events
50 > * back-to-back on a single host stack frame — fastest possible, but starves
51 > * the host event loop for the duration of the run.
52 > *
53 > * Use only for tests where no `await` / `.then` chains are involved between
54 > * scheduling and execution of virtual events.
55 > */
56 > export const syncEmbedding: Embedding = () => 'continueSync';
57 >
58 > /**
59 > * Tasks may schedule via `await` / `.then`. Between virtual events, yield to
60 > * the host so the *microtask closure* — the current microtask plus every
61 > * microtask it transitively enqueues — drains before the next event runs.
62 > *
63 > * This is the embedding to use for almost all integration-style tests.
64 > */
65 > export function drainMicrotasksEmbedding(realApi: TimeApi): Embedding {
66 return (next, then) => {
67 if (next.preferRealAnimationFrame && realApi.requestAnimationFrame) {
73 };
74 }
76 > /**
77 > * Schedule `cb` after the closure of the current microtask queue: `cb`
78 > * fires only after the current microtask AND every microtask it
79 > * (recursively, transitively) enqueues has settled.
80 > *
81 > * Per the HTML spec, a macrotask runs only when the microtask queue is
82 > * empty, so any macrotask primitive achieves this. We pick the fastest
83 > * one available on the host.
84 > */
85 > export function nextMacrotask(api: TimeApi, cb: () => void): void {
86 if (setTimeout0IsFaster) { setTimeout0(cb); return; }
87 if (api.setImmediate) { api.setImmediate(cb); return; }
src/vs/base/common/observableInternal/logging/debugGetDependencyGraph.ts 75 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugGetDependencyGraph.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IObserver } from '../base.js';
7 > import { Derived } from '../observables/derivedImpl.js';
8 > import { FromEventObservable } from '../observables/observableFromEvent.js';
9 > import { ObservableValue } from '../observables/observableValue.js';
10 > import { AutorunObserver } from '../reactions/autorunImpl.js';
11 > import { formatValue } from './consoleObservableLogger.js';
12 >
13 > interface IOptions {
14 > type: 'dependencies' | 'observers';
15 > debugNamePostProcessor?: (name: string) => string;
16 > }
17 >
18 > export function debugGetObservableGraph(obs: IObservable<any> | IObserver, options: IOptions): string {
19 const debugNamePostProcessor = options?.debugNamePostProcessor ?? ((str: string) => str);
20 const info = Info.from(obs, debugNamePostProcessor);
31 }
32 }
34 function formatObservableInfoWithDependencies(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
35 const indent = '\t\t'.repeat(indentLevel);
58 return lines.join('\n');
59 }
61 function formatObservableInfoWithObservers(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
62 const indent = '\t\t'.repeat(indentLevel);
85 return lines.join('\n');
86 }
88 > class Info {
89 > public static from(obs: IObservable<any> | IObserver, debugNamePostProcessor: (name: string) => string): Info | undefined {
90 > if (obs instanceof AutorunObserver) {
91 > const state = obs.debugGetState(); debugGetDependencyGraph.ts
92 > return new Info(
93 > obs,
94 > debugNamePostProcessor(obs.debugName),
95 > 'autorun',
96 > undefined,
97 > state.stateStr,
98 > Array.from(state.dependencies),
99 > []
100 > );
101 > } else if (obs instanceof Derived) { debugGetDependencyGraph.ts
102 > const state = obs.debugGetState();
103 > return new Info(
104 > obs,
105 > debugNamePostProcessor(obs.debugName),
106 > 'derived',
107 > state.value,
108 > state.stateStr,
109 > Array.from(state.dependencies),
110 > Array.from(obs.debugGetObservers())
111 > );
112 > } else if (obs instanceof ObservableValue) {
113 > const state = obs.debugGetState();
114 > return new Info(
115 > obs,
116 > debugNamePostProcessor(obs.debugName),
117 > 'observableValue',
118 > state.value,
119 > 'upToDate',
120 > [],
121 > Array.from(obs.debugGetObservers())
122 > );
123 > } else if (obs instanceof FromEventObservable) {
124 > const state = obs.debugGetState(); debugGetDependencyGraph.ts
125 > return new Info(
126 > obs,
127 > debugNamePostProcessor(obs.debugName),
128 > 'fromEvent',
129 > state.value,
130 > state.hasValue ? 'upToDate' : 'initial',
131 > [],
132 > Array.from(obs.debugGetObservers())
133 > );
134 > }
135 > return undefined;
137 >
138 > public static unknown(obs: IObservable<any> | IObserver): Info {
139 return new Info(
140 obs,
147 );
148 }
150 > constructor(
151 public readonly sourceObj: IObservable<any> | IObserver,
152 public readonly name: string,
src/vs/base/common/collections.ts 74 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- collections.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /**
7 > * An interface for a JavaScript object that
8 > * acts a dictionary. The keys are strings.
9 > */
10 > export type IStringDictionary<V> = Record<string, V>;
11 >
12 > /**
13 > * An interface for a JavaScript object that
14 > * acts a dictionary. The keys are numbers.
15 > */
16 > export type INumberDictionary<V> = Record<number, V>;
17 >
18 > /**
19 > * Groups the collection into a dictionary based on the provided
20 > * group function.
21 > */
22 > export function groupBy<K extends string | number | symbol, V>(data: readonly V[], groupFn: (element: V) => K): Partial<Record<K, V[]>> {
23 const result: Partial<Record<K, V[]>> = Object.create(null);
24 for (const element of data) {
32 return result;
33 }
35 > export function groupByMap<K, V>(data: V[], groupFn: (element: V) => K): Map<K, V[]> {
36 const result = new Map<K, V[]>();
37 for (const element of data) {
46 return result;
47 }
49 > export function diffSets<T>(before: ReadonlySet<T>, after: ReadonlySet<T>): { removed: T[]; added: T[] } {
50 const removed: T[] = [];
51 const added: T[] = [];
62 return { removed, added };
63 }
65 > /**
66 > * Checks whether two sets contain exactly the same elements.
67 > *
68 > * @param a - The first set.
69 > * @param b - The second set.
70 > * @returns `true` if both sets have the same size and every element of `a` is also in `b`.
71 > */
72 > export function equalSets<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean {
73 if (a === b) {
74 return true;
84 return true;
85 }
87 > export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[]; added: V[] } {
88 const removed: V[] = [];
89 const added: V[] = [];
100 return { removed, added };
101 }
103 > /**
104 > * Computes the intersection of two sets.
105 > *
106 > * @param setA - The first set.
107 > * @param setB - The second iterable.
108 > * @returns A new set containing the elements that are in both `setA` and `setB`.
109 > */
110 > export function intersection<T>(setA: Set<T>, setB: Iterable<T>): Set<T> {
111 const result = new Set<T>();
112 for (const elem of setB) {
117 return result;
118 }
120 > export class SetWithKey<T> implements Set<T> {
121 > private _map = new Map<unknown, T>();
122 >
123 > constructor(values: T[], private toKey: (t: T) => unknown) {
124 for (const value of values) {
125 this.add(value);
126 }
127 }
129 > get size(): number {
130 return this._map.size;
131 }
133 > add(value: T): this {
134 const key = this.toKey(value);
135 this._map.set(key, value);
136 return this;
137 }
139 > delete(value: T): boolean {
140 return this._map.delete(this.toKey(value));
141 }
143 > has(value: T): boolean {
144 return this._map.has(this.toKey(value));
145 }
147 > *entries(): SetIterator<[T, T]> {
148 for (const entry of this._map.values()) {
149 yield [entry, entry];
150 }
151 }
153 > keys(): SetIterator<T> {
154 return this.values();
155 }
157 > *values(): SetIterator<T> {
158 for (const entry of this._map.values()) {
159 yield entry;
160 }
161 }
163 > clear(): void {
164 this._map.clear();
165 }
167 > forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: unknown): void {
168 this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
169 }
171 > [Symbol.iterator](): SetIterator<T> {
172 return this.values();
173 }
175 > [Symbol.toStringTag]: string = 'SetWithKey';
176 > }
src/vs/base/common/observableInternal/logging/logging.ts 67 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- logging.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { AutorunObserver } from '../reactions/autorunImpl.js';
7 > import { IObservable } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import type { Derived } from '../observables/derivedImpl.js';
10 > import { DebugLocation } from '../debugLocation.js';
11 >
12 > let globalObservableLogger: IObservableLogger | undefined;
13 >
14 > export function addLogger(logger: IObservableLogger): void {
15 if (!globalObservableLogger) {
16 globalObservableLogger = logger;
21 }
22 }
23 > logging.ts
24 > export function getLogger(): IObservableLogger | undefined {
25 return globalObservableLogger;
26 }
27 > logging.ts
28 > let globalObservableLoggerFn: ((obs: IObservable<any>) => void) | undefined = undefined;
29 > export function setLogObservableFn(fn: (obs: IObservable<any>) => void): void {
30 > globalObservableLoggerFn = fn;
31 > }
32 >
33 > export function logObservable(obs: IObservable<any>): void {
34 if (globalObservableLoggerFn) {
35 globalObservableLoggerFn(obs);
36 }
37 }
38 > logging.ts
39 > export interface IChangeInformation {
40 > oldValue: unknown;
41 > newValue: unknown;
42 > change: unknown;
43 > didChange: boolean;
44 > hadValue: boolean;
45 > }
46 >
47 > export interface IObservableLogger {
48 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void;
49 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void;
50 >
51 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void;
52 >
53 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void;
54 > handleAutorunDisposed(autorun: AutorunObserver): void;
55 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void;
56 > handleAutorunStarted(autorun: AutorunObserver): void;
57 > handleAutorunFinished(autorun: AutorunObserver): void;
58 >
59 > handleDerivedDependencyChanged(derived: Derived<any, any, any>, observable: IObservable<any>, change: unknown): void;
60 > handleDerivedCleared(observable: Derived<any, any, any>): void;
61 >
62 > handleBeginTransaction(transaction: TransactionImpl): void;
63 > handleEndTransaction(transaction: TransactionImpl): void;
64 > }
65 >
66 > class ComposedLogger implements IObservableLogger {
67 > constructor(
68 public readonly loggers: IObservableLogger[],
69 ) { }
70 > logging.ts
71 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
72 for (const logger of this.loggers) {
73 logger.handleObservableCreated(observable, location);
74 }
75 }
76 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void { logging.ts
77 for (const logger of this.loggers) {
78 logger.handleOnListenerCountChanged(observable, newCount);
79 }
80 }
81 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void { logging.ts
82 for (const logger of this.loggers) {
83 logger.handleObservableUpdated(observable, info);
84 }
85 }
86 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void { logging.ts
87 for (const logger of this.loggers) {
88 logger.handleAutorunCreated(autorun, location);
89 }
90 }
91 > handleAutorunDisposed(autorun: AutorunObserver): void { logging.ts
92 for (const logger of this.loggers) {
93 logger.handleAutorunDisposed(autorun);
94 }
95 }
96 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { logging.ts
97 for (const logger of this.loggers) {
98 logger.handleAutorunDependencyChanged(autorun, observable, change);
99 }
100 }
101 > handleAutorunStarted(autorun: AutorunObserver): void { logging.ts
102 for (const logger of this.loggers) {
103 logger.handleAutorunStarted(autorun);
104 }
105 }
106 > handleAutorunFinished(autorun: AutorunObserver): void { logging.ts
107 for (const logger of this.loggers) {
108 logger.handleAutorunFinished(autorun);
109 }
110 }
111 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void { logging.ts
112 for (const logger of this.loggers) {
113 logger.handleDerivedDependencyChanged(derived, observable, change);
114 }
115 }
116 > handleDerivedCleared(observable: Derived<any>): void { logging.ts
117 for (const logger of this.loggers) {
118 logger.handleDerivedCleared(observable);
119 }
120 }
121 > handleBeginTransaction(transaction: TransactionImpl): void { logging.ts
122 for (const logger of this.loggers) {
123 logger.handleBeginTransaction(transaction);
124 }
125 }
126 > handleEndTransaction(transaction: TransactionImpl): void { logging.ts
127 for (const logger of this.loggers) {
128 logger.handleEndTransaction(transaction);
129 }
130 }
131 > } logging.ts
src/vs/base/common/iterator.ts 65 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- iterator.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { isIterable } from './types.js';
7 >
8 > export namespace Iterable {
9 >
10 > export function is<T = unknown>(thing: unknown): thing is Iterable<T> {
11 > return !!thing && typeof thing === 'object' && typeof (thing as Iterable<T>)[Symbol.iterator] === 'function'; iterator.ts
12 > }
14 > const _empty: Iterable<never> = Object.freeze([]);
15 > export function empty<T = never>(): readonly never[] {
16 return _empty as readonly never[];
17 }
19 > export function* single<T>(element: T): Iterable<T> {
20 yield element;
21 }
23 > export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
24 if (is(iterableOrElement)) {
25 return iterableOrElement;
28 }
29 }
31 > export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
32 return iterable ?? (_empty as Iterable<T>);
33 }
35 > export function* reverse<T>(array: ReadonlyArray<T>): Iterable<T> {
36 for (let i = array.length - 1; i >= 0; i--) {
37 yield array[i];
38 }
39 }
41 > export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
42 return !iterable || iterable[Symbol.iterator]().next().done === true;
43 }
45 > export function first<T>(iterable: Iterable<T>): T | undefined {
46 return iterable[Symbol.iterator]().next().value;
47 }
49 > export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
50 let i = 0;
51 for (const element of iterable) {
56 return false;
57 }
59 > export function every<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
60 let i = 0;
61 for (const element of iterable) {
66 return true;
67 }
69 > export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
70 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
71 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
72 for (const element of iterable) {
73 if (predicate(element)) {
78 return undefined;
79 }
81 > export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
82 > export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
83 > export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
84 for (const element of iterable) {
85 if (predicate(element)) {
88 }
89 }
91 > export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
92 let index = 0;
93 for (const element of iterable) {
95 }
96 }
98 > export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
99 let index = 0;
100 for (const element of iterable) {
102 }
103 }
104 > iterator.ts
105 > export function* concat<T>(...iterables: (Iterable<T> | T)[]): Iterable<T> {
106 for (const item of iterables) {
107 if (isIterable(item)) {
112 }
113 }
114 > iterator.ts
115 > export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
116 let value = initialValue;
117 for (const element of iterable) {
120 return value;
121 }
122 > iterator.ts
123 > export function length<T>(iterable: Iterable<T>): number {
124 let count = 0;
125 for (const _ of iterable) {
128 return count;
129 }
130 > iterator.ts
131 > /**
132 > * Returns an iterable slice of the array, with the same semantics as `array.slice()`.
133 > */
134 > export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
135 if (from < -arr.length) {
136 from = 0;
150 }
151 }
152 > iterator.ts
153 > /**
154 > * Consumes `atMost` elements from iterable and returns the consumed elements,
155 > * and an iterable for the rest of the elements.
156 > */
157 > export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
158 const consumed: T[] = [];
159
176 return [consumed, { [Symbol.iterator]() { return iterator; } }];
177 }
178 > iterator.ts
179 > export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
180 const result: T[] = [];
181 for await (const item of iterable) {
184 return result;
185 }
186 > iterator.ts
187 > export async function asyncToArrayFlat<T>(iterable: AsyncIterable<T[]>): Promise<T[]> {
188 let result: T[] = [];
189 for await (const item of iterable) {
src/vs/base/common/observableInternal/observables/observableValue.ts 58 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableValue.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ISettableObservable, ITransaction } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { BaseObservable } from './baseObservable.js';
9 > import { EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
10 > import { DebugNameData } from '../debugName.js';
11 > import { getLogger } from '../logging/logging.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Creates an observable value.
16 > * Observers get informed when the value changes.
17 > * @template TChange An arbitrary type to describe how or why the value changed. Defaults to `void`.
18 > * Observers will receive every single change value.
19 > */
20 >
21 > export function observableValue<T, TChange = void>(name: string, initialValue: T): ISettableObservable<T, TChange>;
22 > export function observableValue<T, TChange = void>(owner: object, initialValue: T): ISettableObservable<T, TChange>;
23 > export function observableValue<T, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> {
24 let debugNameData: DebugNameData;
25 if (typeof nameOrOwner === 'string') {
30 return new ObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
31 }
33 > export class ObservableValue<T, TChange = void>
34 > extends BaseObservable<T, TChange>
35 > implements ISettableObservable<T, TChange> {
36 > protected _value: T;
37 >
38 > get debugName() {
39 > return this._debugNameData.getDebugName(this) ?? 'ObservableValue';
40 > }
41 >
42 > constructor(
43 private readonly _debugNameData: DebugNameData,
44 initialValue: T,
51 getLogger()?.handleObservableUpdated(this, { hadValue: false, newValue: initialValue, change: undefined, didChange: true, oldValue: undefined });
52 }
53 > public override get(): T { observableValue.ts
54 return this._value;
55 }
57 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
58 if (change === undefined && this._equalityComparator(this._value, value)) {
59 return;
79 }
80 }
82 > override toString(): string {
83 return `${this.debugName}: ${this._value}`;
84 }
86 > protected _setValue(newValue: T): void {
87 this._value = newValue;
88 }
90 > public debugGetState() {
91 return {
92 value: this._value,
93 };
94 }
96 > public debugSetValue(value: unknown) {
97 this._value = value as T;
98 }
100 > /**
101 > * A disposable observable. When disposed, its value is also disposed.
102 > * When a new value is set, the previous value is disposed.
103 > */
104 >
105 > export function disposableObservableValue<T extends IDisposable | undefined, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> & IDisposable {
106 let debugNameData: DebugNameData;
107 if (typeof nameOrOwner === 'string') {
112 return new DisposableObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
113 }
115 > export class DisposableObservableValue<T extends IDisposable | undefined, TChange = void> extends ObservableValue<T, TChange> implements IDisposable {
116 > protected override _setValue(newValue: T): void {
117 if (this._value === newValue) {
118 return;
src/vs/base/test/common/utils.ts 58 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { DisposableStore, DisposableTracker, IDisposable, setDisposableTracker } from '../../common/lifecycle.js';
7 > import { join } from '../../common/path.js';
8 > import { isWindows } from '../../common/platform.js';
9 > import { URI } from '../../common/uri.js';
10 >
11 > export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
12 >
13 > export function toResource(this: any, path: string): URI {
14 if (isWindows) {
15 return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
18 return URI.file(join('/', btoa(this.test.fullTitle()), path));
19 }
20 > utils.ts
21 > export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void {
22 for (let i = 0; i < n; i++) {
23 suite(`${description} (iteration ${i})`, callback);
24 }
25 }
26 > utils.ts
27 > export function testRepeat(n: number, description: string, callback: (this: any) => any): void {
28 for (let i = 0; i < n; i++) {
29 test(`${description} (iteration ${i})`, callback);
30 }
31 }
32 > utils.ts
33 export async function assertThrowsAsync(block: () => any, message: string | Error = 'Missing expected exception'): Promise<void> {
34 try {
41 throw err;
42 }
43 > utils.ts
44 > /**
45 > * Use this function to ensure that all disposables are cleaned up at the end of each test in the current suite.
46 > *
47 > * Use `markAsSingleton` if disposable singletons are created lazily that are allowed to outlive the test.
48 > * Make sure that the singleton properly registers all child disposables so that they are excluded too.
49 > *
50 > * @returns A {@link DisposableStore} that can optionally be used to track disposables in the test.
51 > * This will be automatically disposed on test teardown.
52 > */
53 > export function ensureNoDisposablesAreLeakedInTestSuite(): Pick<DisposableStore, 'add'> {
54 > let tracker: DisposableTracker | undefined;
55 > let store: DisposableStore;
56 > setup(() => {
57 > store = new DisposableStore(); utils.ts
58 > tracker = new DisposableTracker();
59 > setDisposableTracker(tracker);
60 > }); utils.ts
61 >
62 > teardown(function (this: import('mocha').Context) {
63 > store.dispose(); utils.ts
64 > setDisposableTracker(null);
65 > if (this.currentTest?.state !== 'failed') {
66 > const result = tracker!.computeLeakingDisposables();
67 > if (result) {
68 console.error(result.details);
69 throw new Error(`There are ${result.leaks.length} undisposed disposables!${result.details}`);
70 }
71 > } utils.ts
72 > }); utils.ts
73 >
74 > // Wrap store as the suite function is called before it's initialized
75 > const testContext = {
76 > add<T extends IDisposable>(o: T): T {
77 > return store.add(o); utils.ts
78 > }
79 > }; utils.ts
80 > return testContext;
81 > }
82 >
83 > export function throwIfDisposablesAreLeaked(body: () => void, logToConsole = true): void {
84 const tracker = new DisposableTracker();
85 setDisposableTracker(tracker);
88 computeLeakingDisposables(tracker, logToConsole);
89 }
90 > utils.ts
91 export async function throwIfDisposablesAreLeakedAsync(body: () => Promise<void>): Promise<void> {
92 const tracker = new DisposableTracker();
96 computeLeakingDisposables(tracker);
97 }
98 > utils.ts
99 function computeLeakingDisposables(tracker: DisposableTracker, logToConsole = true) {
100 const result = tracker.computeLeakingDisposables();
src/vs/base/common/observableInternal/index.ts 57 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- index.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // This is a facade for the observable implementation. Only import from here!
7 >
8 > export { observableValueOpts } from './observables/observableValueOpts.js';
9 > export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges, autorunIterableDelta, autorunPerKeyedItem, autorunSelfDisposable, registerAutorunSelfDisposable } from './reactions/autorun.js';
10 > export { type IObservable, type IObservableWithChange, type IObserver, type IReader, type ISettable, type IReaderWithStore, type ISettableObservable, type ITransaction } from './base.js';
11 > export { disposableObservableValue } from './observables/observableValue.js';
12 > export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './observables/derived.js';
13 > export { type IDerivedReader } from './observables/derivedImpl.js';
14 > export { ObservableLazy, ObservableLazyPromise, ObservablePromise, ObservableResolvedPromise, PromiseResult, } from './utils/promise.js';
15 > export { derivedWithCancellationToken, waitForState } from './utils/utilsCancellation.js';
16 > export {
17 > debouncedObservable, debouncedObservable2, derivedObservableWithCache,
18 > derivedObservableWithWritableCache, keepObserved, mapObservableArrayCached, observableFromPromise,
19 > recomputeInitiallyAndOnChange,
20 > signalFromObservable, throttledObservable, wasEventTriggeredRecently,
21 > isObservable,
22 > } from './utils/utils.js';
23 > export { type DebugOwner } from './debugName.js';
24 > export { type IChangeContext, type IChangeTracker, recordChanges, recordChangesLazy } from './changeTracker.js';
25 > export { constObservable } from './observables/constObservable.js';
26 > export { type IObservableSignal, observableSignal } from './observables/observableSignal.js';
27 > export { observableFromEventOpts } from './observables/observableFromEvent.js';
28 > export { observableSignalFromEvent } from './observables/observableSignalFromEvent.js';
29 > export { asyncTransaction, globalTransaction, subtransaction, transaction, TransactionImpl } from './transaction.js';
30 > export { observableFromValueWithChangeEvent, ValueWithChangeEventFromObservable } from './utils/valueWithChangeEvent.js';
31 > export { runOnChange, runOnChangeWithCancellationToken, runOnChangeWithStore, type RemoveUndefined } from './utils/runOnChange.js';
32 > export { derivedConstOnceDefined, latestChangedValue } from './experimental/utils.js';
33 > export { observableFromEvent } from './observables/observableFromEvent.js';
34 > export { observableValue } from './observables/observableValue.js';
35 >
36 > export { ObservableSet } from './set.js';
37 > export { ObservableMap } from './map.js';
38 > export { DebugLocation } from './debugLocation.js';
39 >
40 > import { addLogger, setLogObservableFn } from './logging/logging.js';
41 > import { ConsoleObservableLogger, logObservableToConsole } from './logging/consoleObservableLogger.js';
42 > import { DevToolsLogger } from './logging/debugger/devToolsLogger.js';
43 > import { env } from '../process.js';
44 > import { _setDebugGetObservableGraph } from './observables/baseObservable.js';
45 > import { debugGetObservableGraph } from './logging/debugGetDependencyGraph.js';
46 >
47 > _setDebugGetObservableGraph(debugGetObservableGraph);
48 > setLogObservableFn(logObservableToConsole);
49 >
50 > // Remove "//" in the next line to enable logging
51 > const enableLogging = false
52 > // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this
53 > ;
54 >
55 > if (enableLogging) {
56 addLogger(new ConsoleObservableLogger());
57 }
58 > index.ts
59 > if (env && env['VSCODE_DEV_DEBUG_OBSERVABLES']) {
60 // To debug observables you also need the extension "ms-vscode.debug-value-editor"
61 addLogger(DevToolsLogger.getInstance());
src/vs/base/common/observableInternal/observables/derived.ts 57 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derived.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IReader, ITransaction, ISettableObservable, IObservableWithChange } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugLocation } from '../debugLocation.js';
10 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
11 > import { _setDerivedOpts } from './baseObservable.js';
12 > import { IDerivedReader, Derived, DerivedWithSetter } from './derivedImpl.js';
13 >
14 > /**
15 > * Creates an observable that is derived from other observables.
16 > * The value is only recomputed when absolutely needed.
17 > *
18 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
19 > */
20 > export function derived<T, TChange = void>(computeFn: (reader: IDerivedReader<TChange>, debugLocation?: DebugLocation) => T): IObservableWithChange<T, TChange>;
21 > export function derived<T, TChange = void>(owner: DebugOwner, computeFn: (reader: IDerivedReader<TChange>) => T, debugLocation?: DebugLocation): IObservableWithChange<T, TChange>;
22 > export function derived<T, TChange = void>(
23 computeFnOrOwner: ((reader: IDerivedReader<TChange>) => T) | DebugOwner,
24 computeFn?: ((reader: IDerivedReader<TChange>) => T) | undefined,
46 );
47 }
48 > derived.ts
49 > export function derivedWithSetter<T>(owner: DebugOwner | undefined, computeFn: (reader: IReader) => T, setter: (value: T, transaction: ITransaction | undefined) => void, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T> {
50 return new DerivedWithSetter(
51 new DebugNameData(owner, undefined, computeFn),
58 );
59 }
60 > derived.ts
61 > export function derivedOpts<T>(
62 options: IDebugNameData & {
63 equalsFn?: EqualityComparer<T>;
76 );
77 }
78 > _setDerivedOpts(derivedOpts); derived.ts
79 >
80 > /**
81 > * Represents an observable that is derived from other observables.
82 > * The value is only recomputed when absolutely needed.
83 > *
84 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
85 > *
86 > * Use `createEmptyChangeSummary` to create a "change summary" that can collect the changes.
87 > * Use `handleChange` to add a reported change to the change summary.
88 > * The compute function is given the last change summary.
89 > * The change summary is discarded after the compute function was called.
90 > *
91 > * @see derived
92 > */
93 > export function derivedHandleChanges<T, TDelta, TChangeSummary>(
94 options: IDebugNameData & {
95 changeTracker: IChangeTracker<TChangeSummary>;
108 );
109 }
110 > derived.ts
111 > /**
112 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
113 > */
114 > export function derivedWithStore<T>(computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
115 >
116 > /**
117 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
118 > */
119 > export function derivedWithStore<T>(owner: DebugOwner, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
120 > export function derivedWithStore<T>(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
121 let computeFn: (reader: IReader, store: DisposableStore) => T;
122 let owner: DebugOwner;
151 );
152 }
153 > derived.ts
154 > export function derivedDisposable<T extends IDisposable | undefined>(computeFn: (reader: IReader) => T): IObservable<T>;
155 > export function derivedDisposable<T extends IDisposable | undefined>(owner: DebugOwner, computeFn: (reader: IReader) => T): IObservable<T>;
156 > export function derivedDisposable<T extends IDisposable | undefined>(computeFnOrOwner: ((reader: IReader) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
157 let computeFn: (reader: IReader) => T;
158 let owner: DebugOwner;
src/vs/base/common/observableInternal/logging/debugger/rpc.ts 53 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- rpc.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export type ChannelFactory = (handler: IChannelHandler) => IChannel;
7 >
8 > export interface IChannel {
9 > sendNotification(data: unknown): void;
10 > sendRequest(data: unknown): Promise<RpcRequestResult>;
11 > }
12 >
13 > export interface IChannelHandler {
14 > handleNotification(notificationData: unknown): void;
15 > handleRequest(requestData: unknown): Promise<RpcRequestResult> | RpcRequestResult;
16 > }
17 >
18 > export type RpcRequestResult = { type: 'result'; value: unknown } | { type: 'error'; value: unknown };
19 >
20 > export type API = {
21 > host: Side;
22 > client: Side;
23 > };
24 >
25 > export type Side = {
26 > notifications: Record<string, (...args: any[]) => void>;
27 > requests: Record<string, (...args: any[]) => Promise<unknown> | unknown>;
28 > };
29 >
30 > type MakeAsyncIfNot<TFn> = TFn extends (...args: infer TArgs) => infer TResult ? TResult extends Promise<unknown> ? TFn : (...args: TArgs) => Promise<TResult> : never;
31 >
32 > export type MakeSideAsync<T extends Side> = {
33 > notifications: T['notifications'];
34 > requests: { [K in keyof T['requests']]: MakeAsyncIfNot<T['requests'][K]> };
35 > };
36 >
37 > export class SimpleTypedRpcConnection<T extends Side> {
38 > public static createHost<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['host']): SimpleTypedRpcConnection<MakeSideAsync<T['client']>> {
39 > return new SimpleTypedRpcConnection(channelFactory, getHandler);
40 > }
41 >
42 > public static createClient<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['client']): SimpleTypedRpcConnection<MakeSideAsync<T['host']>> {
43 return new SimpleTypedRpcConnection(channelFactory, getHandler);
44 }
45 > rpc.ts
46 > public readonly api: T;
47 > private readonly _channel: IChannel;
48 >
49 > private constructor(
50 private readonly _channelFactory: ChannelFactory,
51 private readonly _getHandler: () => Side,
95 this.api = { notifications: notifications, requests: requests } as any;
96 }
97 > } rpc.ts
98 >
99 > type OutgoingMessage = [
100 > method: string,
101 > args: unknown[],
102 > ];
src/vs/base/common/process.ts 53 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- process.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { INodeProcess, isMacintosh, isWindows } from './platform.js';
7 >
8 > let safeProcess: Omit<INodeProcess, 'arch'> & { arch: string | undefined };
9 > declare const process: INodeProcess;
10 >
11 > // Native sandbox environment
12 > const vscodeGlobal = (globalThis as { vscode?: { process?: INodeProcess } }).vscode;
13 > if (typeof vscodeGlobal !== 'undefined' && typeof vscodeGlobal.process !== 'undefined') {
14 const sandboxProcess: INodeProcess = vscodeGlobal.process;
15 safeProcess = {
20 };
21 }
22 > process.ts
23 > // Native node.js environment
24 > else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') {
25 > safeProcess = {
26 > get platform() { return process.platform; },
27 > get arch() { return process.arch; },
28 > get env() { return process.env; },
29 > cwd() { return process.env['VSCODE_CWD'] || process.cwd(); }
30 > };
31 }
32
44 };
45 }
46 > process.ts
47 > /**
48 > * Provides safe access to the `cwd` property in node.js, sandboxed or web
49 > * environments.
50 > *
51 > * Note: in web, this property is hardcoded to be `/`.
52 > *
53 > * @skipMangle
54 > */
55 > export const cwd = safeProcess.cwd;
56 >
57 > /**
58 > * Provides safe access to the `env` property in node.js, sandboxed or web
59 > * environments.
60 > *
61 > * Note: in web, this property is hardcoded to be `{}`.
62 > */
63 > export const env = safeProcess.env;
64 >
65 > /**
66 > * Provides safe access to the `platform` property in node.js, sandboxed or web
67 > * environments.
68 > */
69 > export const platform = safeProcess.platform;
70 >
71 > /**
72 > * Provides safe access to the `arch` method in node.js, sandboxed or web
73 > * environments.
74 > * Note: `arch` is `undefined` in web
75 > */
76 > export const arch = safeProcess.arch;
src/vs/base/common/observableInternal/debugName.ts 52 covered LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugName.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export interface IDebugNameData {
7 > /**
8 > * The owner object of an observable.
9 > * Used for debugging only, such as computing a name for the observable by iterating over the fields of the owner.
10 > */
11 > readonly owner?: DebugOwner | undefined;
12 >
13 > /**
14 > * A string or function that returns a string that represents the name of the observable.
15 > * Used for debugging only.
16 > */
17 > readonly debugName?: DebugNameSource | undefined;
18 >
19 > /**
20 > * A function that points to the defining function of the object.
21 > * Used for debugging only.
22 > */
23 > readonly debugReferenceFn?: Function | undefined;
24 > }
25 >
26 > export class DebugNameData {
27 > constructor(
28 public readonly owner: DebugOwner | undefined,
29 public readonly debugNameSource: DebugNameSource | undefined,
30 public readonly referenceFn: Function | undefined,
31 ) { }
33 > public getDebugName(target: object): string | undefined {
34 return getDebugName(target, this);
35 }
36 > } debugName.ts
37 >
38 > /**
39 > * The owning object of an observable.
40 > * Is only used for debugging purposes, such as computing a name for the observable by iterating over the fields of the owner.
41 > */
42 > export type DebugOwner = object | undefined;
43 > export type DebugNameSource = string | (() => string | undefined);
44 >
45 > const countPerName = new Map<string, number>();
46 > const cachedDebugName = new WeakMap<object, string>();
47 >
48 > export function getDebugName(target: object, data: DebugNameData): string | undefined {
49 const cached = cachedDebugName.get(target);
50 if (cached) {
63 return undefined;
64 }
66 function computeDebugName(self: object, data: DebugNameData): string | undefined {
67 const cached = cachedDebugName.get(self);
101 return undefined;
102 }
103 > debugName.ts
104 function findKey(obj: object, value: object): string | undefined {
105 for (const key in obj) {
110 return undefined;
111 }
112 > debugName.ts
113 > const countPerClassName = new Map<string, number>();
114 > const ownerId = new WeakMap<object, string>();
115 >
116 function formatOwner(owner: object): string {
117 const id = ownerId.get(owner);
127 return result;
128 }
129 > debugName.ts
130 > export function getClassName(obj: object): string | undefined {
131 const ctor = obj.constructor;
132 if (ctor) {
138 return undefined;
139 }
140 > debugName.ts
141 > export function getFunctionName(fn: Function): string | undefined {
142 const fnSrc = fn.toString();
143 // Pattern: /** @description ... */
src/vs/base/common/observableInternal/logging/consoleObservableLogger.ts 52 covered LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- consoleObservableLogger.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { IObservableLogger, IChangeInformation, addLogger } from './logging.js';
9 > import { FromEventObservable } from '../observables/observableFromEvent.js';
10 > import { getClassName } from '../debugName.js';
11 > import { Derived } from '../observables/derivedImpl.js';
12 > import { AutorunObserver } from '../reactions/autorunImpl.js';
13 >
14 > let consoleObservableLogger: ConsoleObservableLogger | undefined;
15 >
16 > export function logObservableToConsole(obs: IObservable<any>): void {
17 if (!consoleObservableLogger) {
18 consoleObservableLogger = new ConsoleObservableLogger();
21 consoleObservableLogger.addFilteredObj(obs);
22 }
24 > export class ConsoleObservableLogger implements IObservableLogger {
25 private indentation = 0;
26
118
119 private readonly changedObservablesSets = new WeakMap<object, Set<IObservable<any>>>();
121 > formatChanges(changes: Set<IObservable<any>>): ConsoleText | undefined {
122 if (changes.size === 0) {
123 return undefined;
130 );
131 }
133 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
134 if (!this._isIncluded(derived)) { return; }
135
136 this.changedObservablesSets.get(derived)?.add(observable);
137 }
139 > _handleDerivedRecomputed(derived: Derived<unknown>, info: IChangeInformation): void {
140 if (!this._isIncluded(derived)) { return; }
141
151 changedObservables.clear();
152 }
154 > handleDerivedCleared(derived: Derived<unknown>): void {
155 if (!this._isIncluded(derived)) { return; }
156
160 ]));
161 }
163 > handleFromEventObservableTriggered(observable: FromEventObservable<any, any>, info: IChangeInformation): void {
164 if (!this._isIncluded(observable)) { return; }
165
171 ]));
172 }
174 > handleAutorunCreated(autorun: AutorunObserver): void {
175 if (!this._isIncluded(autorun)) { return; }
176
177 this.changedObservablesSets.set(autorun, new Set());
178 }
180 > handleAutorunDisposed(autorun: AutorunObserver): void {
181 }
183 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void {
184 if (!this._isIncluded(autorun)) { return; }
185
186 this.changedObservablesSets.get(autorun)!.add(observable);
187 }
189 > handleAutorunStarted(autorun: AutorunObserver): void {
190 const changedObservables = this.changedObservablesSets.get(autorun);
191 if (!changedObservables) { return; }
202 this.indentation++;
203 }
205 > handleAutorunFinished(autorun: AutorunObserver): void {
206 this.indentation--;
207 }
209 > handleBeginTransaction(transaction: TransactionImpl): void {
210 let transactionName = transaction.getDebugName();
211 if (transactionName === undefined) {
221 this.indentation++;
222 }
224 > handleEndTransaction(): void {
225 this.indentation--;
226 }
228 > type ConsoleText = (ConsoleText | undefined)[] |
229 > { text: string; style: string; data?: unknown[] } |
230 > { data: unknown[] };
231 function consoleTextToArgs(text: ConsoleText): unknown[] {
232 const styles = new Array<any>();
294 };
295 }
297 > export function formatValue(value: unknown, availableLen: number): string {
298 switch (typeof value) {
299 case 'number':
325 }
326 }
328 function formatArray(value: unknown[], availableLen: number): string {
329 let result = '[ ';
343 return result;
344 }
346 function formatObject(value: object, availableLen: number): string {
347 if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
371 return result;
372 }
374 function repeat(str: string, count: number): string {
375 let result = '';
379 return result;
380 }
382 function padStr(str: string, length: number): string {
383 while (str.length < length) {
src/vs/base/common/observableInternal/observables/observableFromEvent.ts 51 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableFromEvent.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, ITransaction } from '../base.js';
7 > import { subtransaction } from '../transaction.js';
8 > import { EqualityComparer, Event, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 >
15 > export function observableFromEvent<T, TArgs = unknown>(
16 > owner: DebugOwner,
17 > event: Event<TArgs>,
18 > getValue: (args: TArgs | undefined) => T,
19 > debugLocation?: DebugLocation,
20 > ): IObservable<T>;
21 > export function observableFromEvent<T, TArgs = unknown>(
22 > event: Event<TArgs>,
23 > getValue: (args: TArgs | undefined) => T,
24 > ): IObservable<T>;
25 > export function observableFromEvent(...args:
26 [owner: DebugOwner, event: Event<any>, getValue: (args: any | undefined) => any, debugLocation?: DebugLocation] |
27 [event: Event<any>, getValue: (args: any | undefined) => any]
45 );
46 }
48 > export function observableFromEventOpts<T, TArgs = unknown>(
49 options: IDebugNameData & {
50 equalsFn?: EqualityComparer<T>;
64 );
65 }
67 > export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
68 > public static globalTransaction: ITransaction | undefined;
69 >
70 > private _value: T | undefined;
71 > private _hasValue = false;
72 > private _subscription: IDisposable | undefined;
73 >
74 > constructor(
75 private readonly _debugNameData: DebugNameData,
76 private readonly event: Event<TArgs>,
131 }
132 };
134 > protected override onLastObserverRemoved(): void {
135 this._subscription!.dispose();
136 this._subscription = undefined;
138 this._value = undefined;
139 }
141 > public get(): T {
142 if (this._subscription) {
143 if (!this._hasValue) {
151 }
152 }
154 > public debugSetValue(value: unknown): void {
155 // eslint-disable-next-line local/code-no-any-casts
156 this._value = value as any;
157 }
159 > public debugGetState() {
160 return { value: this._value, hasValue: this._hasValue };
161 }
163 >
164 > export namespace observableFromEvent {
165 > export const Observer = FromEventObservable;
166 >
167 > export function batchEventsGlobally(tx: ITransaction, fn: () => void): void {
168 let didSet = false;
169 if (FromEventObservable.globalTransaction === undefined) {
src/vs/base/common/observableInternal/observables/lazyObservableValue.ts 49 covered LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lazyObservableValue.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { EqualityComparer } from '../commonFacade/deps.js';
7 > import { IObserver, ISettableObservable, ITransaction } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import { DebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Holds off updating observers until the value is actually read.
16 > */
17 > export class LazyObservableValue<T, TChange = void>
18 > extends BaseObservable<T, TChange>
19 > implements ISettableObservable<T, TChange> {
20 > protected _value: T;
21 > private _isUpToDate = true;
22 > private readonly _deltas: TChange[] = [];
23 >
24 > get debugName() {
25 > return this._debugNameData.getDebugName(this) ?? 'LazyObservableValue';
26 > }
27 >
28 > constructor(
29 private readonly _debugNameData: DebugNameData,
30 initialValue: T,
35 this._value = initialValue;
36 }
38 > public override get(): T {
39 this._update();
40 return this._value;
41 }
43 > private _update(): void {
44 if (this._isUpToDate) {
45 return;
62 }
63 }
65 > private _updateCounter = 0;
66 >
67 > private _beginUpdate(): void {
68 this._updateCounter++;
69 if (this._updateCounter === 1) {
73 }
74 }
76 > private _endUpdate(): void {
77 this._updateCounter--;
78 if (this._updateCounter === 0) {
86 }
87 }
89 > public override addObserver(observer: IObserver): void {
90 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCounter > 0;
91 super.addObserver(observer);
95 }
96 }
98 > public override removeObserver(observer: IObserver): void {
99 const shouldCallEndUpdate = this._observers.has(observer) && this._updateCounter > 0;
100 super.removeObserver(observer);
105 }
106 }
108 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
109 if (change === undefined && this._equalityComparator(this._value, value)) {
110 return;
src/vs/base/common/assert.ts 48 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- assert.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { BugIndicatingError, onUnexpectedError } from './errors.js';
7 >
8 > /**
9 > * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value.
10 > *
11 > * @deprecated Use `assert(...)` instead.
12 > * This method is usually used like this:
13 > * ```ts
14 > * import * as assert from 'vs/base/common/assert';
15 > * assert.ok(...);
16 > * ```
17 > *
18 > * However, `assert` in that example is a user chosen name.
19 > * There is no tooling for generating such an import statement.
20 > * Thus, the `assert(...)` function should be used instead.
21 > */
22 > export function ok(value?: unknown, message?: string) {
23 if (!value) {
24 throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed');
25 }
26 }
27 > assert.ts
28 > export function assertNever(value: never, message = 'Unreachable'): never {
29 throw new Error(message);
30 }
31 > assert.ts
32 > export function softAssertNever(value: never): void {
33 // no-op
34 }
35 > assert.ts
36 > /**
37 > * Asserts that a condition is `truthy`.
38 > *
39 > * @throws provided {@linkcode messageOrError} if the {@linkcode condition} is `falsy`.
40 > *
41 > * @param condition The condition to assert.
42 > * @param messageOrError An error message or error object to throw if condition is `falsy`.
43 > */
44 > export function assert(
45 condition: boolean,
46 messageOrError: string | Error = 'unexpected state',
55 }
56 }
57 > assert.ts
58 > /**
59 > * Like assert, but doesn't throw.
60 > */
61 > export function softAssert(condition: boolean, message = 'Soft Assertion Failed'): void {
62 if (!condition) {
63 onUnexpectedError(new BugIndicatingError(message));
64 }
65 }
66 > assert.ts
67 > /**
68 > * condition must be side-effect free!
69 > */
70 > export function assertFn(condition: () => boolean): void {
71 if (!condition()) {
72 // eslint-disable-next-line no-debugger
77 }
78 }
79 > assert.ts
80 > export function checkAdjacentItems<T>(items: readonly T[], predicate: (item1: T, item2: T) => boolean): boolean {
81 let i = 0;
82 while (i < items.length - 1) {
src/vs/base/test/common/virtualScheduling/timeApi.ts 48 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- timeApi.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export interface TimeoutId { readonly _timeoutIdBrand: void }
7 > export interface IntervalId { readonly _intervalIdBrand: void }
8 > export interface ImmediateId { readonly _immediateIdBrand: void }
9 > export type AnimationFrameId = number & { readonly _animationFrameIdBrand: void };
10 >
11 > /**
12 > * The subset of host time APIs the processor and embeddings need.
13 > *
14 > * Used both for the real host API (captured via {@link captureGlobalTimeApi})
15 > * and for the virtual replacement that runs through a {@link VirtualClock}.
16 > *
17 > * Keeping this as a plain interface means the processor never reaches into
18 > * `globalThis` directly: the boundary between "real time" and "virtual time"
19 > * is exactly which `TimeApi` instance is in use.
20 > */
21 > export interface TimeApi {
22 > setTimeout(handler: () => void, timeout?: number): TimeoutId;
23 > clearTimeout(id: TimeoutId): void;
24 > setInterval(handler: () => void, interval: number): IntervalId;
25 > clearInterval(id: IntervalId): void;
26 > setImmediate?: ((handler: () => void) => ImmediateId);
27 > clearImmediate?: ((id: ImmediateId) => void);
28 > requestAnimationFrame?: ((cb: (time: number) => void) => AnimationFrameId);
29 > cancelAnimationFrame?: ((id: AnimationFrameId) => void);
30 > Date: DateConstructor;
31 > }
32 >
33 > export function captureGlobalTimeApi(): TimeApi {
34 > return {
35 > setTimeout: globalThis.setTimeout.bind(globalThis) as unknown as TimeApi['setTimeout'],
36 > clearTimeout: globalThis.clearTimeout.bind(globalThis) as unknown as TimeApi['clearTimeout'],
37 > setInterval: globalThis.setInterval.bind(globalThis) as unknown as TimeApi['setInterval'],
38 > clearInterval: globalThis.clearInterval.bind(globalThis) as unknown as TimeApi['clearInterval'],
39 > setImmediate: globalThis.setImmediate?.bind(globalThis) as unknown as TimeApi['setImmediate'],
40 > clearImmediate: globalThis.clearImmediate?.bind(globalThis) as unknown as TimeApi['clearImmediate'],
41 > requestAnimationFrame: globalThis.requestAnimationFrame?.bind(globalThis) as unknown as TimeApi['requestAnimationFrame'],
42 > cancelAnimationFrame: globalThis.cancelAnimationFrame?.bind(globalThis) as unknown as TimeApi['cancelAnimationFrame'],
43 > Date: globalThis.Date,
44 > };
45 > }
46 >
47 > /** A snapshot of the real host time API at module-load time. */
48 > export const realTimeApi: TimeApi = captureGlobalTimeApi();
src/vs/base/test/common/virtualScheduling/traceLogger.ts 47 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- traceLogger.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { LogEntryLike } from '../executionGraph.js';
7 > import { Trace, TraceContext } from './trace.js';
8 >
9 > /**
10 > * A minimal logger for tests that captures the active {@link Trace} at log
11 > * time so messages can later be woven into a swimlane diagram next to the
12 > * timer events that produced them.
13 > */
14 > export interface ITraceLogger {
15 > log(message: string): void;
16 > warn(message: string): void;
17 > error(message: string): void;
18 > /**
19 > * Run `fn` synchronously and log it as a marker in the trace. The
20 > * marker text is `fn.toString()` so call sites read naturally as e.g.
21 > * `logger.logRun(() => model.trigger())`. Returns whatever `fn`
22 > * returns.
23 > */
24 > logRun<T>(fn: () => T): T;
25 > }
26 >
27 > /**
28 > * One log entry produced by an {@link ITraceLogger}. Extends
29 > * {@link LogEntryLike} (consumed by `buildHistoryFromTasks`) with a level so
30 > * renderers can differentiate `log` / `warn` / `error`.
31 > */
32 > export interface ITraceLogEntry extends LogEntryLike {
33 > readonly trace: Trace;
34 > readonly level: 'log' | 'warn' | 'error';
35 > }
36 >
37 > /**
38 > * Build an {@link ITraceLogger} that pushes every call into `buffer`,
39 > * tagging each entry with the trace that's current at call time.
40 > *
41 > * Pass the same `buffer` to `buildHistoryFromTasks(history, startTime,
42 > * buffer)` to interleave log lines with the timer swimlane.
43 > */
44 > export function createTraceLogger(buffer: ITraceLogEntry[]): ITraceLogger {
45 const make = (level: 'log' | 'warn' | 'error') => (message: string) => {
46 buffer.push({
61 };
62 }
64 > /** Best-effort one-line description of `fn` for trace log markers. */
65 function _describeFn(fn: () => unknown): string {
66 const src = fn.toString();
72 return _collapseWhitespace(src);
73 }
75 function _collapseWhitespace(s: string): string {
76 return s.replace(/\s+/g, ' ').trim();
src/vs/base/test/common/virtualScheduling/virtualTimeApi.ts 45 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- virtualTimeApi.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IDisposable } from '../../../common/lifecycle.js';
7 > import { realTimeApi, TimeApi } from './timeApi.js';
8 > import { ROOT_TRACE, TraceContext } from './trace.js';
9 > import { VirtualClock } from './virtualClock.js';
10 >
11 > // V8 default `Error.stackTraceLimit` of 10 swallows everything past the
12 > // first async boundary in the stacks we capture for trace diagnostics.
13 > // Bump it so swimlane callers actually see the user code that scheduled a
14 > // timer rather than just the Promise wrapper.
15 > if (typeof Error.stackTraceLimit === 'number' && Error.stackTraceLimit < 50) {
16 > Error.stackTraceLimit = 50;
17 > }
18 >
19 > /** Virtual timer IDs are `IDisposable`s. Recover one from an opaque id. */
20 function asDisposable(id: unknown): IDisposable | undefined {
21 if (id === null || typeof id !== 'object') { return undefined; }
23 return typeof maybe.dispose === 'function' ? id as IDisposable : undefined;
24 }
26 > export interface CreateVirtualTimeApiOptions {
27 > /**
28 > * If `true`, `requestAnimationFrame` is faked: callbacks are scheduled
29 > * onto the virtual queue at `now + 16ms` and the resulting event hints
30 > * the embedding to use a real `requestAnimationFrame` so the host can
31 > * reflow before the callback runs. Useful for fixtures that need DOM
32 > * measurements after rAF callbacks.
33 > *
34 > * If `false` (default), `requestAnimationFrame` is left to the host.
35 > */
36 > readonly fakeRequestAnimationFrame?: boolean;
37 > }
38 >
39 > /**
40 > * Build a {@link TimeApi} that schedules every timer call into `clock`'s
41 > * virtual queue, capturing the current trace at schedule time so that
42 > * causal chains (`setTimeout` → `setTimeout`, etc.) are preserved.
43 > *
44 > * The returned API is suitable to install with {@link pushGlobalTimeApi},
45 > * which is what {@link runWithFakedTimers} does internally.
46 > */
47 > export function createVirtualTimeApi(
48 clock: VirtualClock,
49 options?: CreateVirtualTimeApiOptions,
190 return api;
191 }
193 > // Re-exported for convenience: many tests want to install both at once.
194 > export { pushGlobalTimeApi } from './globalTimeApi.js';
src/vs/base/common/linkedList.ts 44 covered LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- linkedList.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > class Node<E> {
7 >
8 > static readonly Undefined = new Node<unknown>(undefined);
9 >
10 > element: E;
11 > next: Node<E> | typeof Node.Undefined;
12 > prev: Node<E> | typeof Node.Undefined;
13 >
14 > constructor(element: E) {
15 > this.element = element;
16 > this.next = Node.Undefined;
17 > this.prev = Node.Undefined;
18 > }
19 > }
20 >
21 > export class LinkedList<E> {
22
23 private _first: Node<E> | typeof Node.Undefined = Node.Undefined;
24 private _last: Node<E> | typeof Node.Undefined = Node.Undefined;
25 private _size: number = 0;
27 > get size(): number {
28 return this._size;
29 }
31 > isEmpty(): boolean {
32 return this._first === Node.Undefined;
33 }
35 > clear(): void {
36 let node = this._first;
37 while (node !== Node.Undefined) {
46 this._size = 0;
47 }
49 > unshift(element: E): () => void {
50 return this._insert(element, false);
51 }
53 > push(element: E): () => void {
54 return this._insert(element, true);
55 }
57 > private _insert(element: E, atTheEnd: boolean): () => void {
58 const newNode = new Node(element);
59 if (this._first === Node.Undefined) {
85 };
86 }
88 > shift(): E | undefined {
89 if (this._first === Node.Undefined) {
90 return undefined;
95 }
96 }
98 > pop(): E | undefined {
99 if (this._last === Node.Undefined) {
100 return undefined;
105 }
106 }
108 > peek(): E | undefined {
109 if (this._last === Node.Undefined) {
110 return undefined;
114 }
115 }
117 > private _remove(node: Node<E> | typeof Node.Undefined): void {
118 if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
119 // middle
141 this._size -= 1;
142 }
144 > *[Symbol.iterator](): Iterator<E> {
145 let node = this._first;
146 while (node !== Node.Undefined) {
src/vs/base/test/common/virtualScheduling/runWithFakedTimers.ts 44 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- runWithFakedTimers.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationTokenSource } from '../../../common/cancellation.js';
7 > import { drainMicrotasksEmbedding } from './embedding.js';
8 > import { pushGlobalTimeApi } from './globalTimeApi.js';
9 > import { realTimeApi } from './timeApi.js';
10 > import { untilToken, VirtualTimeProcessor } from './processor.js';
11 > import { createRecordingRealTimeApi, RecordedTimerEvent } from './recordingTimeApi.js';
12 > import { VirtualClock } from './virtualClock.js';
13 > import { createVirtualTimeApi } from './virtualTimeApi.js';
14 >
15 > export interface RunWithFakedTimersOptions {
16 > readonly startTime?: number;
17 > /** Default `true`. Set `false` to bypass virtual time entirely (for
18 > * cases where the same test is parameterised over real/virtual time). */
19 > readonly useFakeTimers?: boolean;
20 > /** No effect in the new processor; accepted for legacy compatibility.
21 > * The drain-microtasks embedding picks the fastest available macrotask
22 > * primitive automatically. */
23 > readonly useSetImmediate?: boolean;
24 > /** Maximum number of virtual events the run is allowed to execute
25 > * before being rejected. Default 100. */
26 > readonly maxTaskCount?: number;
27 > /**
28 > * If set, called once `fn` resolves with the recorded timer events.
29 > * In virtual mode the events come from the {@link VirtualTimeProcessor}'s
30 > * own history; in real mode a recording wrapper around the host time
31 > * API is installed for the duration of `fn`. Useful for swimlane
32 > * diagnostics.
33 > */
34 > readonly onHistory?: (history: readonly RecordedTimerEvent[]) => void;
35 > }
36 >
37 > /**
38 > * Run `fn` with a virtual clock installed as the global time API.
39 > *
40 > * After `fn` resolves, the virtual queue is drained (so any timers `fn`
41 > * scheduled and `await`ed for, transitively, complete deterministically).
42 > * If `fn` throws, the queue is *not* drained — the original error is
43 > * re-thrown immediately.
44 > */
45 export async function runWithFakedTimers<T>(
46 options: RunWithFakedTimersOptions,
src/vs/base/common/uint.ts 43 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uint.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export const enum Constants {
7 > /**
8 > * MAX SMI (SMall Integer) as defined in v8.
9 > * one bit is lost for boxing/unboxing flag.
10 > * one bit is lost for sign flag.
11 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
12 > */
13 > MAX_SAFE_SMALL_INTEGER = 1 << 30,
14 >
15 > /**
16 > * MIN SMI (SMall Integer) as defined in v8.
17 > * one bit is lost for boxing/unboxing flag.
18 > * one bit is lost for sign flag.
19 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
20 > */
21 > MIN_SAFE_SMALL_INTEGER = -(1 << 30),
22 >
23 > /**
24 > * Max unsigned integer that fits on 8 bits.
25 > */
26 > MAX_UINT_8 = 255, // 2^8 - 1
27 >
28 > /**
29 > * Max unsigned integer that fits on 16 bits.
30 > */
31 > MAX_UINT_16 = 65535, // 2^16 - 1
32 >
33 > /**
34 > * Max unsigned integer that fits on 32 bits.
35 > */
36 > MAX_UINT_32 = 4294967295, // 2^32 - 1
37 >
38 > UNICODE_SUPPLEMENTARY_PLANE_BEGIN = 0x010000
39 > }
40 >
41 > export function toUint8(v: number): number {
42 if (v < 0) {
43 return 0;
48 return v | 0;
49 }
50 > uint.ts
51 > export function toUint32(v: number): number {
52 if (v < 0) {
53 return 0;
src/vs/base/common/observableInternal/debugLocation.ts 42 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugLocation.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export type DebugLocation = DebugLocationImpl | undefined;
7 >
8 > export namespace DebugLocation {
9 > let enabled = false;
10 >
11 > export function enable(): void {
12 enabled = true;
13 }
15 > export function ofCaller(): DebugLocation {
16 if (!enabled) {
17 return undefined;
26 return DebugLocationImpl.fromStack(stack, 2);
27 }
29 >
30 > class DebugLocationImpl implements ILocation {
31 > public static fromStack(stack: string, parentIdx: number): DebugLocationImpl | undefined {
32 > const lines = stack.split('\n');
33 > const location = parseLine(lines[parentIdx + 1]);
34 > if (location) {
35 > return new DebugLocationImpl(
36 > location.fileName,
37 > location.line,
38 > location.column,
39 > location.id
40 > );
41 > } else {
42 > return undefined;
43 > }
44 > }
45 >
46 > constructor(
47 public readonly fileName: string,
48 public readonly line: number,
51 ) {
52 }
54 >
55 >
56 > export interface ILocation {
57 > fileName: string;
58 > line: number;
59 > column: number;
60 > id: string;
61 > }
62 >
63 function parseLine(stackLine: string): ILocation | undefined {
64 const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
src/vs/base/test/common/virtualScheduling/globalTimeApi.ts 42 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- globalTimeApi.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IDisposable } from '../../../common/lifecycle.js';
7 > import { captureGlobalTimeApi, realTimeApi, TimeApi } from './timeApi.js';
8 >
9 > /** Cast through `unknown` so we don't widen our typed `TimeApi` shapes to `any`. */
10 > type AsGlobal<K extends keyof typeof globalThis> = (typeof globalThis)[K];
11 >
12 > /**
13 > * Ensure `fn` carries an `originalFn` back-door pointing at the real
14 > * (non-virtual) `setTimeout`. We prefer the existing tag on `fn`, then a tag
15 > * inherited from `previousFn` (which may itself be a wrapper that already
16 > * carried the back-door), and finally fall back to `realTimeApi.setTimeout`
17 > * — which has its own `originalFn` set at module load.
18 > */
19 function ensureSetTimeoutOriginalFn(fn: TimeApi['setTimeout'], previousFn: TimeApi['setTimeout']): TimeApi['setTimeout'] {
20 const tagged = fn as TimeApi['setTimeout'] & { originalFn?: TimeApi['setTimeout'] };
26 return tagged;
27 }
29 > /**
30 > * Replace the global time APIs (`setTimeout`, `setInterval`, …, `Date`,
31 > * optionally `requestAnimationFrame`) with the ones from `api`. Returns a
32 > * disposable that restores the previous globals.
33 > *
34 > * The previous globals are captured *at install time*, so nested installs
35 > * compose correctly (the disposable restores to whatever was current when
36 > * this call was made, not to the original real values).
37 > *
38 > * `setTimeout.originalFn` is preserved on the installed function so callers
39 > * like the component-explorer host can escape virtual time when polling.
40 > * If `api.setTimeout` does not already carry `originalFn`, it is copied from
41 > * the previous global (or defaulted to the real `setTimeout`) so wrapping
42 > * APIs such as a logging wrapper don't drop the back-door.
43 > */
44 > export function pushGlobalTimeApi(api: TimeApi): IDisposable {
45 const previous = captureGlobalTimeApi();
46
74 };
75 }
77 > // One-shot tag on the *real* setTimeout: lets callers (e.g. the
78 > // component-explorer host's polling loop) escape virtual time even after
79 > // pushGlobalTimeApi has installed a virtual version on top. The `originalFn`
80 > // property is not on the `setTimeout` signature by design — it's a back-door
81 > // convention shared with the polling code.
82 > (realTimeApi.setTimeout as unknown as { originalFn: TimeApi['setTimeout'] }).originalFn = realTimeApi.setTimeout;
src/vs/base/common/lazy.ts 39 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lazy.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > enum LazyValueState {
7 > Uninitialized,
8 > Running,
9 > Completed,
10 > }
11 >
12 > export class Lazy<T> {
13 >
14 > private _state = LazyValueState.Uninitialized;
15 > private _value?: T;
16 > private _error: Error | undefined;
17 >
18 > constructor(
19 > private readonly executor: () => T, lazy.ts
20 > ) { }
21 > lazy.ts
22 > /**
23 > * True if the lazy value has been resolved.
24 > */
25 > get hasValue(): boolean { return this._state === LazyValueState.Completed; }
26 >
27 > /**
28 > * Get the wrapped value.
29 > *
30 > * This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only
31 > * resolved once. `getValue` will re-throw exceptions that are hit while resolving the value
32 > */
33 > get value(): T {
34 if (this._state === LazyValueState.Uninitialized) {
35 this._state = LazyValueState.Running;
50 return this._value!;
51 }
52 > lazy.ts
53 > /**
54 > * Get the wrapped value without forcing evaluation.
55 > */
56 > get rawValue(): T | undefined { return this._value; }
57 > }
src/vs/base/common/observableInternal/transaction.ts 39 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- transaction.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { handleBugIndicatingErrorRecovery, IObservable, IObserver, ITransaction } from './base.js';
7 > import { getFunctionName } from './debugName.js';
8 > import { getLogger } from './logging/logging.js';
9 >
10 > /**
11 > * Starts a transaction in which many observables can be changed at once.
12 > * {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
13 > * Reaction run on demand or when the transaction ends.
14 > */
15 >
16 > export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
17 const tx = new TransactionImpl(fn, getDebugName);
18 try {
22 }
23 }
24 > let _globalTransaction: ITransaction | undefined = undefined; transaction.ts
25 >
26 > export function globalTransaction(fn: (tx: ITransaction) => void) {
27 if (_globalTransaction) {
28 fn(_globalTransaction);
40 }
41 }
42 > /** @deprecated */ transaction.ts
43 >
44 export async function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void> {
45 const tx = new TransactionImpl(fn, getDebugName);
50 }
51 }
52 > /** transaction.ts
53 > * Allows to chain transactions.
54 > */
55 >
56 > export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
57 if (!tx) {
58 transaction(fn, getDebugName);
60 fn(tx);
61 }
62 > } export class TransactionImpl implements ITransaction { transaction.ts
63 > private _updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
64 >
65 > constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) {
66 getLogger()?.handleBeginTransaction(this);
67 }
69 > public getDebugName(): string | undefined {
70 if (this._getDebugName) {
71 return this._getDebugName();
73 return getFunctionName(this._fn);
74 }
76 > public updateObserver(observer: IObserver, observable: IObservable<any>): void {
77 if (!this._updatingObservers) {
78 // This happens when a transaction is used in a callback or async function.
90 observer.beginUpdate(observable);
91 }
93 > public finish(): void {
94 const updatingObservers = this._updatingObservers;
95 if (!updatingObservers) {
106 getLogger()?.handleEndTransaction(this);
107 }
109 > public debugGetUpdatingObservers() {
110 return this._updatingObservers;
111 }
112 > } transaction.ts
113
src/vs/base/common/observableInternal/observables/observableSignalFromEvent.ts 38 covered LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableSignalFromEvent.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable } from '../base.js';
7 > import { transaction } from '../transaction.js';
8 > import { Event, IDisposable } from '../commonFacade/deps.js';
9 > import { DebugOwner, DebugNameData } from '../debugName.js';
10 > import { BaseObservable } from './baseObservable.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export function observableSignalFromEvent(
14 owner: DebugOwner | string,
15 event: Event<any>,
18 return new FromEventObservableSignal(typeof owner === 'string' ? owner : new DebugNameData(owner, undefined, undefined), event, debugLocation);
19 }
21 > class FromEventObservableSignal extends BaseObservable<void> {
22 > private subscription: IDisposable | undefined;
23 >
24 > public readonly debugName: string;
25 > constructor(
26 debugNameDataOrName: DebugNameData | string,
27 private readonly event: Event<any>,
33 : debugNameDataOrName.getDebugName(this) ?? 'Observable Signal From Event';
34 }
36 > protected override onFirstObserverAdded(): void {
37 this.subscription = this.event(this.handleEvent);
38 }
40 > private readonly handleEvent = () => {
41 > transaction( observableSignalFromEvent.ts
42 > (tx) => {
43 > for (const o of this._observers) {
44 > tx.updateObserver(o, this);
45 > o.handleChange(this, undefined);
46 > }
47 > },
48 > () => this.debugName
49 > );
50 > };
52 > protected override onLastObserverRemoved(): void {
53 this.subscription!.dispose();
54 this.subscription = undefined;
55 }
57 > public override get(): void {
58 // NO OP
59 }
src/vs/base/common/observableInternal/observables/observableSignal.ts 37 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableSignal.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservableWithChange, ITransaction } from '../base.js';
7 > import { transaction } from '../transaction.js';
8 > import { DebugNameData } from '../debugName.js';
9 > import { BaseObservable } from './baseObservable.js';
10 > import { DebugLocation } from '../debugLocation.js';
11 >
12 > /**
13 > * Creates a signal that can be triggered to invalidate observers.
14 > * Signals don't have a value - when they are triggered they indicate a change.
15 > * However, signals can carry a delta that is passed to observers.
16 > */
17 > export function observableSignal<TDelta = void>(debugName: string): IObservableSignal<TDelta>;
18 > export function observableSignal<TDelta = void>(owner: object): IObservableSignal<TDelta>;
19 > export function observableSignal<TDelta = void>(debugNameOrOwner: string | object, debugLocation = DebugLocation.ofCaller()): IObservableSignal<TDelta> {
20 if (typeof debugNameOrOwner === 'string') {
21 return new ObservableSignal<TDelta>(debugNameOrOwner, undefined, debugLocation);
24 }
25 }
27 > export interface IObservableSignal<TChange> extends IObservableWithChange<void, TChange> {
28 > trigger(tx: ITransaction | undefined, change: TChange): void;
29 > }
30 >
31 > class ObservableSignal<TChange> extends BaseObservable<void, TChange> implements IObservableSignal<TChange> {
32 > public get debugName() {
33 > return new DebugNameData(this._owner, this._debugName, undefined).getDebugName(this) ?? 'Observable Signal';
34 > }
35 >
36 > public override toString(): string {
37 return this.debugName;
38 }
40 > constructor(
41 private readonly _debugName: string | undefined,
42 private readonly _owner: object | undefined,
45 super(debugLocation);
46 }
48 > public trigger(tx: ITransaction | undefined, change: TChange): void {
49 if (!tx) {
50 transaction(tx => {
src/vs/base/test/common/virtualScheduling/index.ts 36 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- index.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // Greenfield virtual scheduling primitives.
7 > //
8 > // This folder is the new home for virtual-time scheduling. It supersedes
9 > // `timeTravelScheduler.ts` and `traceableTimeApi.ts`, both of which are
10 > // retained as @deprecated re-export shims.
11 >
12 > export type { TimeApi } from './timeApi.js';
13 > export { captureGlobalTimeApi, realTimeApi } from './timeApi.js';
14 >
15 > export type { EventSource, VirtualEvent, VirtualTime } from './virtualClock.js';
16 > export { VirtualClock } from './virtualClock.js';
17 >
18 > export type { RunAsHandlerOptions } from './trace.js';
19 > export { ROOT_TRACE, Trace, TraceContext, createTraceRoot } from './trace.js';
20 >
21 > export type { Embedding } from './embedding.js';
22 > export { drainMicrotasksEmbedding, nextMacrotask, syncEmbedding } from './embedding.js';
23 >
24 > export type { RunOptions, TerminationPolicy, VirtualTimeProcessorOptions } from './processor.js';
25 > export { VirtualTimeProcessor, untilIdle, untilTime, untilToken } from './processor.js';
26 >
27 > export { pushGlobalTimeApi } from './globalTimeApi.js';
28 > export type { CreateVirtualTimeApiOptions } from './virtualTimeApi.js';
29 > export { createVirtualTimeApi } from './virtualTimeApi.js';
30 > export { createLoggingTimeApi } from './loggingTimeApi.js';
31 > export type { RecordedTimerEvent } from './recordingTimeApi.js';
32 > export { createRecordingRealTimeApi } from './recordingTimeApi.js';
33 > export type { ITraceLogEntry, ITraceLogger } from './traceLogger.js';
34 > export { createTraceLogger } from './traceLogger.js';
35 > export type { RunWithFakedTimersOptions } from './runWithFakedTimers.js';
36 > export { runWithFakedTimers } from './runWithFakedTimers.js';
src/vs/base/common/observableInternal/changeTracker.ts 35 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- changeTracker.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { BugIndicatingError } from './commonFacade/deps.js';
7 > import { IObservableWithChange, IReader } from './base.js';
8 >
9 > export interface IChangeTracker<TChangeSummary> {
10 > createChangeSummary(previousChangeSummary: TChangeSummary | undefined): TChangeSummary;
11 > handleChange(ctx: IChangeContext, change: TChangeSummary): boolean;
12 > beforeUpdate?(reader: IReader, change: TChangeSummary): void;
13 > }
14 >
15 > export interface IChangeContext {
16 > readonly changedObservable: IObservableWithChange<any, any>;
17 > readonly change: unknown;
18 >
19 > /**
20 > * Returns if the given observable caused the change.
21 > */
22 > didChange<T, TChange>(observable: IObservableWithChange<T, TChange>): this is { change: TChange };
23 > }
24 >
25 > /**
26 > * Subscribes to and records changes and the last value of the given observables.
27 > * Don't use the key "changes", as it is reserved for the changes array!
28 > */
29 > export function recordChanges<TObs extends Record<any, IObservableWithChange<any, any>>>(obs: TObs):
30 IChangeTracker<{ [TKey in keyof TObs]: ReturnType<TObs[TKey]['get']> }
31 & { changes: readonly ({ [TKey in keyof TObs]: { key: TKey; change: TObs[TKey]['TChange'] } }[keyof TObs])[] }> {
56 };
57 }
59 > /**
60 > * Subscribes to and records changes and the last value of the given observables.
61 > * Don't use the key "changes", as it is reserved for the changes array!
62 > */
63 > export function recordChangesLazy<TObs extends Record<any, IObservableWithChange<any, any>>>(getObs: () => TObs):
64 IChangeTracker<{ [TKey in keyof TObs]: ReturnType<TObs[TKey]['get']> }
65 & { changes: readonly ({ [TKey in keyof TObs]: { key: TKey; change: TObs[TKey]['TChange'] } }[keyof TObs])[] }> {
src/vs/base/common/observableInternal/map.ts 35 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- map.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, ITransaction } from '../observable.js';
7 > import { observableValueOpts } from './observables/observableValueOpts.js';
8 >
9 >
10 > export class ObservableMap<K, V> implements Map<K, V> {
11 private readonly _data = new Map<K, V>();
12
14
15 readonly observable: IObservable<Map<K, V>> = this._obs;
16 > map.ts
17 > get size(): number {
18 return this._data.size;
19 }
20 > map.ts
21 > has(key: K): boolean {
22 return this._data.has(key);
23 }
24 > map.ts
25 > get(key: K): V | undefined {
26 return this._data.get(key);
27 }
28 > map.ts
29 > set(key: K, value: V, tx?: ITransaction): this {
30 const hadKey = this._data.has(key);
31 const oldValue = this._data.get(key);
36 return this;
37 }
38 > map.ts
39 > delete(key: K, tx?: ITransaction): boolean {
40 const result = this._data.delete(key);
41 if (result) {
44 return result;
45 }
46 > map.ts
47 > clear(tx?: ITransaction): void {
48 if (this._data.size > 0) {
49 this._data.clear();
51 }
52 }
53 > map.ts
54 > forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
55 this._data.forEach((value, key, _map) => {
56 callbackfn.call(thisArg, value, key, this);
57 });
58 }
59 > map.ts
60 > *entries(): MapIterator<[K, V]> {
61 yield* this._data.entries();
62 }
63 > map.ts
64 > *keys(): MapIterator<K> {
65 yield* this._data.keys();
66 }
67 > map.ts
68 > *values(): MapIterator<V> {
69 yield* this._data.values();
70 }
71 > map.ts
72 > [Symbol.iterator](): MapIterator<[K, V]> {
73 return this.entries();
74 }
75 > map.ts
76 > get [Symbol.toStringTag](): string {
77 return 'ObservableMap';
78 }
79 > } map.ts
src/vs/base/test/common/timeTravelScheduler.ts 35 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- timeTravelScheduler.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /**
7 > * @deprecated The contents of this file have moved to
8 > * `./virtualScheduling/index.js`. This re-export is kept for backwards
9 > * compatibility and will be removed once all callers have migrated.
10 > *
11 > * Notes for migration:
12 > * - `TimeTravelScheduler` is now {@link VirtualClock} (same constructor).
13 > * - `AsyncSchedulerProcessor` is now {@link VirtualTimeProcessor}; its
14 > * constructor takes an explicit {@link Embedding}, and {@link Run.options}
15 > * use `until` (a {@link TerminationPolicy}) instead of the implicit
16 > * "drain queue" behaviour. See {@link runWithFakedTimers} for a
17 > * drop-in helper.
18 > * - `originalGlobalValues` is now {@link realTimeApi}.
19 > */
20 >
21 > export {
22 > captureGlobalTimeApi,
23 > createLoggingTimeApi,
24 > createVirtualTimeApi,
25 > pushGlobalTimeApi,
26 > realTimeApi as originalGlobalValues,
27 > runWithFakedTimers,
28 > VirtualClock as TimeTravelScheduler,
29 > } from './virtualScheduling/index.js';
30 >
31 > export type {
32 > CreateVirtualTimeApiOptions,
33 > RunWithFakedTimersOptions,
34 > TimeApi,
35 > } from './virtualScheduling/index.js';
src/vs/base/common/marshallingIds.ts 33 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- marshallingIds.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export const enum MarshalledId {
7 > Uri = 1,
8 > Regexp,
9 > ScmResource,
10 > ScmResourceGroup,
11 > ScmProvider,
12 > CommentController,
13 > CommentThread,
14 > CommentThreadInstance,
15 > CommentThreadReply,
16 > CommentNode,
17 > CommentThreadNode,
18 > TimelineActionContext,
19 > NotebookCellActionContext,
20 > NotebookActionContext,
21 > TerminalContext,
22 > TestItemContext,
23 > Date,
24 > TestMessageMenuArgs,
25 > ChatViewContext,
26 > LanguageModelToolResult,
27 > LanguageModelTextPart,
28 > LanguageModelThinkingPart,
29 > LanguageModelPromptTsxPart,
30 > LanguageModelDataPart,
31 > AgentSessionContext,
32 > ChatResponsePullRequestPart,
33 > }
src/vs/base/test/common/virtualScheduling/recordingTimeApi.ts 33 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- recordingTimeApi.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { realTimeApi, TimeApi } from './timeApi.js';
7 > import { Trace, TraceContext } from './trace.js';
8 > import { EventSource } from './virtualClock.js';
9 >
10 > /**
11 > * One entry in a real-time trace recording. Structurally compatible with
12 > * `VirtualEvent` (and `ScheduledTaskLike` consumed by
13 > * `buildHistoryFromTasks`), so the same swimlane renderer can plot both.
14 > */
15 > export interface RecordedTimerEvent {
16 > readonly time: number;
17 > readonly source: EventSource;
18 > readonly trace?: Trace;
19 > }
20 >
21 > /**
22 > * Wrap the real host time API so every `setTimeout` / `setInterval` /
23 > * `requestAnimationFrame` call is tagged with a child {@link Trace} and
24 > * pushes a {@link RecordedTimerEvent} into `history` when the handler
25 > * actually runs.
26 > *
27 > * Handlers are invoked through {@link TraceContext.runAsHandler} so causal
28 > * chains carry across awaits inside a handler. Note: because each handler's
29 > * deferred trace-reset fires as its own real macrotask, attribution can
30 > * drift slightly when many handlers fire in quick succession — accurate
31 > * enough for diagnostics, not for assertions.
32 > */
33 > export function createRecordingRealTimeApi(history: RecordedTimerEvent[]): TimeApi {
34 const realSetTimeout = realTimeApi.setTimeout;
35
src/vs/base/common/observableInternal/set.ts 32 covered LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- set.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, ITransaction } from '../observable.js';
7 > import { observableValueOpts } from './observables/observableValueOpts.js';
8 >
9 > export class ObservableSet<T> implements Set<T> {
10
11 private readonly _data = new Set<T>();
14
15 readonly observable: IObservable<Set<T>> = this._obs;
16 > set.ts
17 > get size(): number {
18 return this._data.size;
19 }
20 > set.ts
21 > has(value: T): boolean {
22 return this._data.has(value);
23 }
24 > set.ts
25 > add(value: T, tx?: ITransaction): this {
26 const hadValue = this._data.has(value);
27 if (!hadValue) {
31 return this;
32 }
33 > set.ts
34 > delete(value: T, tx?: ITransaction): boolean {
35 const result = this._data.delete(value);
36 if (result) {
39 return result;
40 }
41 > set.ts
42 > clear(tx?: ITransaction): void {
43 if (this._data.size > 0) {
44 this._data.clear();
46 }
47 }
48 > set.ts
49 > forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: unknown): void {
50 this._data.forEach((value, value2, _set) => {
51 callbackfn.call(thisArg, value, value2, this);
52 });
53 }
54 > set.ts
55 > *entries(): SetIterator<[T, T]> {
56 for (const value of this._data) {
57 yield [value, value];
58 }
59 }
60 > set.ts
61 > *keys(): SetIterator<T> {
62 yield* this._data.keys();
63 }
64 > set.ts
65 > *values(): SetIterator<T> {
66 yield* this._data.values();
67 }
68 > set.ts
69 > [Symbol.iterator](): SetIterator<T> {
70 return this.values();
71 }
72 > set.ts
73 > get [Symbol.toStringTag](): string {
74 return 'ObservableSet';
75 }
76 > } set.ts
src/vs/base/common/observableInternal/experimental/utils.ts 26 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IReader } from '../base.js';
7 > import { BugIndicatingError, DisposableStore } from '../commonFacade/deps.js';
8 > import { DebugOwner, getDebugName, DebugNameData } from '../debugName.js';
9 > import { observableFromEvent } from '../observables/observableFromEvent.js';
10 > import { autorunOpts } from '../reactions/autorun.js';
11 > import { derivedObservableWithCache } from '../utils/utils.js';
12 >
13 > /**
14 > * Creates an observable that has the latest changed value of the given observables.
15 > * Initially (and when not observed), it has the value of the last observable.
16 > * When observed and any of the observables change, it has the value of the last changed observable.
17 > * If multiple observables change in the same transaction, the last observable wins.
18 > */
19 > export function latestChangedValue<T extends IObservable<any>[]>(owner: DebugOwner, observables: T): IObservable<ReturnType<T[number]['get']>> {
20 if (observables.length === 0) {
21 throw new BugIndicatingError();
50 return result;
51 }
52 > utils.ts
53 > /**
54 > * Works like a derived.
55 > * However, if the value is not undefined, it is cached and will not be recomputed anymore.
56 > * In that case, the derived will unsubscribe from its dependencies.
57 > */
58 > export function derivedConstOnceDefined<T>(owner: DebugOwner, fn: (reader: IReader) => T): IObservable<T | undefined> {
59 return derivedObservableWithCache<T | undefined>(owner, (reader, lastValue) => lastValue ?? fn(reader));
60 }
src/vs/base/common/observableInternal/observables/constObservable.ts 26 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- constObservable.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable, IObserver, IObservableWithChange } from '../base.js';
7 > import { ConvenientObservable } from './baseObservable.js';
8 >
9 > /**
10 > * Represents an efficient observable whose value never changes.
11 > */
12 >
13 > export function constObservable<T>(value: T): IObservable<T> {
14 return new ConstObservable(value);
15 }
16 > class ConstObservable<T> extends ConvenientObservable<T, void> { constObservable.ts
17 > constructor(private readonly value: T) {
18 super();
19 }
21 > public override get debugName(): string {
22 return this.toString();
23 }
25 > public get(): T {
26 return this.value;
27 }
28 > public addObserver(observer: IObserver): void { constObservable.ts
29 // NO OP
30 }
31 > public removeObserver(observer: IObserver): void { constObservable.ts
32 // NO OP
33 }
35 > override log(): IObservableWithChange<T, void> {
36 return this;
37 }
39 > override toString(): string {
40 return `Const: ${this.value}`;
41 }
src/vs/base/common/functional.ts 25 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- functional.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /**
7 > * Given a function, returns a function that is only calling that function once.
8 > */
9 > export function createSingleCallFunction<T extends Function>(this: unknown, fn: T, fnDidRunCallback?: () => void): T {
10 > const _this = this; functional.ts
11 > let didCall = false;
12 > let result: unknown;
13 >
14 > return function () {
15 > if (didCall) { functional.ts
16 return result;
17 }
19 > didCall = true;
20 > if (fnDidRunCallback) {
21 try {
22 result = fn.apply(_this, arguments);
24 fnDidRunCallback();
25 }
26 > } else { functional.ts
27 > result = fn.apply(_this, arguments);
28 > }
29 >
30 > return result;
31 > } as unknown as T;
32 > } functional.ts
src/vs/base/common/stopwatch.ts 25 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- stopwatch.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > declare const globalThis: { performance: { now(): number } };
7 > const performanceNow = globalThis.performance.now.bind(globalThis.performance);
8 >
9 > export class StopWatch {
10 >
11 > private _startTime: number;
12 > private _stopTime: number;
13 >
14 > private readonly _now: () => number;
15 >
16 > public static create(highResolution?: boolean): StopWatch {
17 return new StopWatch(highResolution);
18 }
20 > constructor(highResolution?: boolean) {
21 this._now = highResolution === false ? Date.now : performanceNow;
22 this._startTime = this._now();
23 this._stopTime = -1;
24 }
26 > public stop(): void {
27 this._stopTime = this._now();
28 }
30 > public reset(): void {
31 this._startTime = this._now();
32 this._stopTime = -1;
33 }
35 > public elapsed(): number {
36 if (this._stopTime !== -1) {
37 return this._stopTime - this._startTime;
39 return this._now() - this._startTime;
40 }
41 > } stopwatch.ts
src/vs/base/common/observableInternal/logging/debugger/utils.ts 24 covered LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IDisposable } from '../../../lifecycle.js';
7 >
8 > export class Debouncer implements IDisposable {
9 private _timeout: Timeout | undefined = undefined;
10 > utils.ts
11 > public debounce(fn: () => void, timeoutMs: number): void {
12 if (this._timeout !== undefined) {
13 clearTimeout(this._timeout);
18 }, timeoutMs);
19 }
20 > utils.ts
21 > dispose(): void {
22 if (this._timeout !== undefined) {
23 clearTimeout(this._timeout);
24 }
25 }
26 > } utils.ts
27 >
28 > export class Throttler implements IDisposable {
29 private _timeout: Timeout | undefined = undefined;
30 > utils.ts
31 > public throttle(fn: () => void, timeoutMs: number): void {
32 if (this._timeout === undefined) {
33 this._timeout = setTimeout(() => {
37 }
38 }
39 > utils.ts
40 > dispose(): void {
41 if (this._timeout !== undefined) {
42 clearTimeout(this._timeout);
43 }
44 }
45 > } utils.ts
46 >
47 > export function deepAssign<T>(target: T, source: T): void {
48 for (const key in source) {
49 if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') {
54 }
55 }
56 > utils.ts
57 > export function deepAssignDeleteNulls<T>(target: T, source: T): void {
58 for (const key in source) {
59 if (source[key] === null) {
src/vs/base/common/observableInternal/utils/utilsCancellation.ts 24 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utilsCancellation.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IReader, IObservable } from '../base.js';
7 > import { DebugOwner, DebugNameData } from '../debugName.js';
8 > import { CancellationError, CancellationToken, CancellationTokenSource } from '../commonFacade/cancellation.js';
9 > import { strictEquals } from '../commonFacade/deps.js';
10 > import { autorun } from '../reactions/autorun.js';
11 > import { Derived } from '../observables/derivedImpl.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Resolves the promise when the observables state matches the predicate.
16 > */
17 > export function waitForState<T>(observable: IObservable<T | null | undefined>): Promise<T>;
18 > export function waitForState<T, TState extends T>(observable: IObservable<T>, predicate: (state: T) => state is TState, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<TState>;
19 > export function waitForState<T>(observable: IObservable<T>, predicate: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<T>;
20 > export function waitForState<T>(observable: IObservable<T>, predicate?: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<T> {
21 if (!predicate) {
22 predicate = state => state !== null && state !== undefined;
69 });
70 }
72 > export function derivedWithCancellationToken<T>(computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable<T>;
73 > export function derivedWithCancellationToken<T>(owner: object, computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable<T>;
74 > export function derivedWithCancellationToken<T>(computeFnOrOwner: ((reader: IReader, cancellationToken: CancellationToken) => T) | object, computeFnOrUndefined?: ((reader: IReader, cancellationToken: CancellationToken) => T)): IObservable<T> {
75 let computeFn: (reader: IReader, store: CancellationToken) => T;
76 let owner: DebugOwner;
src/vs/base/common/observableInternal/utils/valueWithChangeEvent.ts 19 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- valueWithChangeEvent.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservable } from '../base.js';
7 > import { Event, IValueWithChangeEvent } from '../commonFacade/deps.js';
8 > import { DebugOwner } from '../debugName.js';
9 > import { observableFromEvent } from '../observables/observableFromEvent.js';
10 >
11 > export class ValueWithChangeEventFromObservable<T> implements IValueWithChangeEvent<T> {
12 > constructor(public readonly observable: IObservable<T>) {
13 }
15 > get onDidChange(): Event<void> {
16 return Event.fromObservableLight(this.observable);
17 }
19 > get value(): T {
20 return this.observable.get();
21 }
23 >
24 > export function observableFromValueWithChangeEvent<T>(owner: DebugOwner, value: IValueWithChangeEvent<T>): IObservable<T> {
25 if (value instanceof ValueWithChangeEventFromObservable) {
26 return value.observable;
src/vs/base/common/observableInternal/logging/debugger/debuggerRpc.ts 17 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debuggerRpc.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ChannelFactory, IChannelHandler, API, SimpleTypedRpcConnection, MakeSideAsync } from './rpc.js';
7 >
8 > export function registerDebugChannel<T extends { channelId: string } & API>(
9 channelId: T['channelId'],
10 createClient: () => T['client'],
43 });
44 }
46 > interface GlobalObj {
47 > $$debugValueEditor_debugChannels: Record<string, (host: IHost) => { handleRequest: (data: unknown) => unknown }>;
48 > }
49 >
50 > interface IHost {
51 > sendNotification: (data: unknown) => void;
52 > }
53 >
54 function createChannelFactoryFromDebugChannel(host: IHost): { channel: ChannelFactory; handler: { handleRequest: (data: unknown) => unknown } } {
55 let h: IChannelHandler | undefined;
src/vs/base/common/observableInternal/utils/runOnChange.ts 17 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- runOnChange.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IObservableWithChange } from '../base.js';
7 > import { CancellationToken, cancelOnDispose } from '../commonFacade/cancellation.js';
8 > import { DisposableStore, IDisposable } from '../commonFacade/deps.js';
9 > import { autorunWithStoreHandleChanges } from '../reactions/autorun.js';
10 >
11 > export type RemoveUndefined<T> = T extends undefined ? never : T;
12 >
13 > export function runOnChange<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[]) => void): IDisposable {
14 let _previousValue: T | undefined;
15 let _firstRun = true;
42 });
43 }
45 > export function runOnChangeWithStore<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[], store: DisposableStore) => void): IDisposable {
46 const store = new DisposableStore();
47 const disposable = runOnChange(observable, (value, previousValue: T, deltas) => {
56 };
57 }
59 > export function runOnChangeWithCancellationToken<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[], token: CancellationToken) => Promise<void>): IDisposable {
60 return runOnChangeWithStore(observable, (value, previousValue, deltas, store) => {
61 cb(value, previousValue, deltas, cancelOnDispose(store));
src/vs/base/test/common/virtualScheduling/loggingTimeApi.ts 15 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- loggingTimeApi.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { TimeApi } from './timeApi.js';
7 >
8 > /**
9 > * Wrap `underlying` so that every call to `setTimeout`, `setInterval`,
10 > * `setImmediate` or `requestAnimationFrame` invokes `onCall` first.
11 > *
12 > * Useful for diagnostics — e.g. logging timer registrations made outside of
13 > * virtual time, to find leaks of real-time scheduling into a fixture.
14 > */
15 > export function createLoggingTimeApi(
16 underlying: TimeApi,
17 onCall: (name: string, stack: string | undefined, handler?: () => void) => void,
src/vs/base/common/observableInternal/observables/observableValueOpts.ts 13 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableValueOpts.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { ISettableObservable } from '../base.js';
7 > import { DebugNameData, IDebugNameData } from '../debugName.js';
8 > import { EqualityComparer, strictEquals } from '../commonFacade/deps.js';
9 > import { ObservableValue } from './observableValue.js';
10 > import { LazyObservableValue } from './lazyObservableValue.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export function observableValueOpts<T, TChange = void>(
14 options: IDebugNameData & {
15 equalsFn?: EqualityComparer<T>;
src/vs/base/common/observableInternal/commonFacade/deps.ts 10 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- deps.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export { assertFn } from '../../assert.js';
7 > export { type EqualityComparer, strictEquals } from '../../equals.js';
8 > export { BugIndicatingError, onBugIndicatingError, onUnexpectedError } from '../../errors.js';
9 > export { Event, type IValueWithChangeEvent } from '../../event.js';
10 > export { DisposableStore, type IDisposable, markAsDisposed, toDisposable, trackDisposable } from '../../lifecycle.js';
src/vs/base/common/symbols.ts 9 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- symbols.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /**
7 > * Can be passed into the Delayed to defer using a microtask
8 > * */
9 > export const MicrotaskDelay = Symbol('MicrotaskDelay');
src/vs/base/common/observable.ts 8 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observable.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // This is a facade for the observable implementation. Only import from here!
7 >
8 > export * from './observableInternal/index.js';
src/vs/base/common/observableInternal/commonFacade/cancellation.ts 7 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- cancellation.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > export { CancellationError } from '../../errors.js';
7 > export { CancellationToken, CancellationTokenSource, cancelOnDispose } from '../../cancellation.js';