• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.server {
3    export class ThrottledOperations {
4        private readonly pendingTimeouts = new Map<string, any>();
5        private readonly logger?: Logger | undefined;
6        constructor(private readonly host: ServerHost, logger: Logger) {
7            this.logger = logger.hasLevel(LogLevel.verbose) ? logger : undefined;
8        }
9
10        /**
11         * Wait `number` milliseconds and then invoke `cb`.  If, while waiting, schedule
12         * is called again with the same `operationId`, cancel this operation in favor
13         * of the new one.  (Note that the amount of time the canceled operation had been
14         * waiting does not affect the amount of time that the new operation waits.)
15         */
16        public schedule(operationId: string, delay: number, cb: () => void) {
17            const pendingTimeout = this.pendingTimeouts.get(operationId);
18            if (pendingTimeout) {
19                // another operation was already scheduled for this id - cancel it
20                this.host.clearTimeout(pendingTimeout);
21            }
22            // schedule new operation, pass arguments
23            this.pendingTimeouts.set(operationId, this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb));
24            if (this.logger) {
25                this.logger.info(`Scheduled: ${operationId}${pendingTimeout ? ", Cancelled earlier one" : ""}`);
26            }
27        }
28
29        public cancel(operationId: string) {
30            const pendingTimeout = this.pendingTimeouts.get(operationId);
31            if (!pendingTimeout) return false;
32            this.host.clearTimeout(pendingTimeout);
33            return this.pendingTimeouts.delete(operationId);
34        }
35
36        private static run(self: ThrottledOperations, operationId: string, cb: () => void) {
37            perfLogger.logStartScheduledOperation(operationId);
38            self.pendingTimeouts.delete(operationId);
39            if (self.logger) {
40                self.logger.info(`Running: ${operationId}`);
41            }
42            cb();
43            perfLogger.logStopScheduledOperation();
44        }
45    }
46
47    export class GcTimer {
48        private timerId: any;
49        constructor(private readonly host: ServerHost, private readonly delay: number, private readonly logger: Logger) {
50        }
51
52        public scheduleCollect() {
53            if (!this.host.gc || this.timerId !== undefined) {
54                // no global.gc or collection was already scheduled - skip this request
55                return;
56            }
57            this.timerId = this.host.setTimeout(GcTimer.run, this.delay, this);
58        }
59
60        private static run(self: GcTimer) {
61            self.timerId = undefined;
62
63            perfLogger.logStartScheduledOperation("GC collect");
64            const log = self.logger.hasLevel(LogLevel.requestTime);
65            const before = log && self.host.getMemoryUsage!(); // TODO: GH#18217
66
67            self.host.gc!(); // TODO: GH#18217
68            if (log) {
69                const after = self.host.getMemoryUsage!(); // TODO: GH#18217
70                self.logger.perftrc(`GC::before ${before}, after ${after}`);
71            }
72            perfLogger.logStopScheduledOperation();
73        }
74    }
75
76    export function getBaseConfigFileName(configFilePath: NormalizedPath): "tsconfig.json" | "jsconfig.json" | undefined {
77        const base = getBaseFileName(configFilePath);
78        return base === "tsconfig.json" || base === "jsconfig.json" ? base : undefined;
79    }
80
81    export function removeSorted<T>(array: SortedArray<T>, remove: T, compare: Comparer<T>): void {
82        if (!array || array.length === 0) {
83            return;
84        }
85
86        if (array[0] === remove) {
87            array.splice(0, 1);
88            return;
89        }
90
91        const removeIndex = binarySearch(array, remove, identity, compare);
92        if (removeIndex >= 0) {
93            array.splice(removeIndex, 1);
94        }
95    }
96
97    const indentStr = "\n    ";
98
99    export function indent(str: string): string {
100        return indentStr + str.replace(/\n/g, indentStr);
101    }
102
103    /** Put stringified JSON on the next line, indented. */
104    export function stringifyIndented(json: {}): string {
105        return indentStr + JSON.stringify(json);
106    }
107}
108