Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/vs/base/common/map.ts
13405 views
1
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'
2
3
/*---------------------------------------------------------------------------------------------
4
* Copyright (c) Microsoft Corporation. All rights reserved.
5
* Licensed under the MIT License. See License.txt in the project root for license information.
6
*--------------------------------------------------------------------------------------------*/
7
8
import { URI } from './uri';
9
10
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
11
let result = map.get(key);
12
if (result === undefined) {
13
result = value;
14
map.set(key, result);
15
}
16
17
return result;
18
}
19
20
export function mapToString<K, V>(map: Map<K, V>): string {
21
const entries: string[] = [];
22
map.forEach((value, key) => {
23
entries.push(`${key} => ${value}`);
24
});
25
26
return `Map(${map.size}) {${entries.join(', ')}}`;
27
}
28
29
export function setToString<K>(set: Set<K>): string {
30
const entries: K[] = [];
31
set.forEach(value => {
32
entries.push(value);
33
});
34
35
return `Set(${set.size}) {${entries.join(', ')}}`;
36
}
37
38
interface ResourceMapKeyFn {
39
(resource: URI): string;
40
}
41
42
class ResourceMapEntry<T> {
43
constructor(readonly uri: URI, readonly value: T) { }
44
}
45
46
function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {
47
return Array.isArray(arg);
48
}
49
50
export class ResourceMap<T> implements Map<URI, T> {
51
52
private static readonly defaultToKey = (resource: URI) => resource.toString();
53
54
readonly [Symbol.toStringTag] = 'ResourceMap';
55
56
private readonly map: Map<string, ResourceMapEntry<T>>;
57
private readonly toKey: ResourceMapKeyFn;
58
59
/**
60
*
61
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
62
*/
63
constructor(toKey?: ResourceMapKeyFn);
64
65
/**
66
*
67
* @param other Another resource which this maps is created from
68
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
69
*/
70
constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
71
72
/**
73
*
74
* @param other Another resource which this maps is created from
75
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
76
*/
77
constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
78
79
constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
80
if (arg instanceof ResourceMap) {
81
this.map = new Map(arg.map);
82
this.toKey = toKey ?? ResourceMap.defaultToKey;
83
} else if (isEntries(arg)) {
84
this.map = new Map();
85
this.toKey = toKey ?? ResourceMap.defaultToKey;
86
87
for (const [resource, value] of arg) {
88
this.set(resource, value);
89
}
90
} else {
91
this.map = new Map();
92
this.toKey = arg ?? ResourceMap.defaultToKey;
93
}
94
}
95
96
set(resource: URI, value: T): this {
97
this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));
98
return this;
99
}
100
101
get(resource: URI): T | undefined {
102
return this.map.get(this.toKey(resource))?.value;
103
}
104
105
has(resource: URI): boolean {
106
return this.map.has(this.toKey(resource));
107
}
108
109
get size(): number {
110
return this.map.size;
111
}
112
113
clear(): void {
114
this.map.clear();
115
}
116
117
delete(resource: URI): boolean {
118
return this.map.delete(this.toKey(resource));
119
}
120
121
forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {
122
if (typeof thisArg !== 'undefined') {
123
clb = clb.bind(thisArg);
124
}
125
for (const [_, entry] of this.map) {
126
clb(entry.value, entry.uri, this);
127
}
128
}
129
130
*values(): IterableIterator<T> {
131
for (const entry of this.map.values()) {
132
yield entry.value;
133
}
134
}
135
136
*keys(): IterableIterator<URI> {
137
for (const entry of this.map.values()) {
138
yield entry.uri;
139
}
140
}
141
142
*entries(): IterableIterator<[URI, T]> {
143
for (const entry of this.map.values()) {
144
yield [entry.uri, entry.value];
145
}
146
}
147
148
*[Symbol.iterator](): IterableIterator<[URI, T]> {
149
for (const [, entry] of this.map) {
150
yield [entry.uri, entry.value];
151
}
152
}
153
}
154
155
export class ResourceSet implements Set<URI> {
156
157
readonly [Symbol.toStringTag]: string = 'ResourceSet';
158
159
private readonly _map: ResourceMap<URI>;
160
161
constructor(toKey?: ResourceMapKeyFn);
162
constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);
163
constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
164
if (!entriesOrKey || typeof entriesOrKey === 'function') {
165
this._map = new ResourceMap(entriesOrKey);
166
} else {
167
this._map = new ResourceMap(toKey);
168
entriesOrKey.forEach(this.add, this);
169
}
170
}
171
172
173
get size(): number {
174
return this._map.size;
175
}
176
177
add(value: URI): this {
178
this._map.set(value, value);
179
return this;
180
}
181
182
clear(): void {
183
this._map.clear();
184
}
185
186
delete(value: URI): boolean {
187
return this._map.delete(value);
188
}
189
190
forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {
191
this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
192
}
193
194
has(value: URI): boolean {
195
return this._map.has(value);
196
}
197
198
entries(): IterableIterator<[URI, URI]> {
199
return this._map.entries();
200
}
201
202
keys(): IterableIterator<URI> {
203
return this._map.keys();
204
}
205
206
values(): IterableIterator<URI> {
207
return this._map.keys();
208
}
209
210
[Symbol.iterator](): IterableIterator<URI> {
211
return this.keys();
212
}
213
}
214
215
216
interface Item<K, V> {
217
previous: Item<K, V> | undefined;
218
next: Item<K, V> | undefined;
219
key: K;
220
value: V;
221
}
222
223
export const enum Touch {
224
None = 0,
225
AsOld = 1,
226
AsNew = 2
227
}
228
229
export class LinkedMap<K, V> implements Map<K, V> {
230
231
readonly [Symbol.toStringTag] = 'LinkedMap';
232
233
private _map: Map<K, Item<K, V>>;
234
private _head: Item<K, V> | undefined;
235
private _tail: Item<K, V> | undefined;
236
private _size: number;
237
238
private _state: number;
239
240
constructor() {
241
this._map = new Map<K, Item<K, V>>();
242
this._head = undefined;
243
this._tail = undefined;
244
this._size = 0;
245
this._state = 0;
246
}
247
248
clear(): void {
249
this._map.clear();
250
this._head = undefined;
251
this._tail = undefined;
252
this._size = 0;
253
this._state++;
254
}
255
256
isEmpty(): boolean {
257
return !this._head && !this._tail;
258
}
259
260
get size(): number {
261
return this._size;
262
}
263
264
get first(): V | undefined {
265
return this._head?.value;
266
}
267
268
get last(): V | undefined {
269
return this._tail?.value;
270
}
271
272
has(key: K): boolean {
273
return this._map.has(key);
274
}
275
276
get(key: K, touch: Touch = Touch.None): V | undefined {
277
const item = this._map.get(key);
278
if (!item) {
279
return undefined;
280
}
281
if (touch !== Touch.None) {
282
this.touch(item, touch);
283
}
284
return item.value;
285
}
286
287
set(key: K, value: V, touch: Touch = Touch.None): this {
288
let item = this._map.get(key);
289
if (item) {
290
item.value = value;
291
if (touch !== Touch.None) {
292
this.touch(item, touch);
293
}
294
} else {
295
item = { key, value, next: undefined, previous: undefined };
296
switch (touch) {
297
case Touch.None:
298
this.addItemLast(item);
299
break;
300
case Touch.AsOld:
301
this.addItemFirst(item);
302
break;
303
case Touch.AsNew:
304
this.addItemLast(item);
305
break;
306
default:
307
this.addItemLast(item);
308
break;
309
}
310
this._map.set(key, item);
311
this._size++;
312
}
313
return this;
314
}
315
316
delete(key: K): boolean {
317
return !!this.remove(key);
318
}
319
320
remove(key: K): V | undefined {
321
const item = this._map.get(key);
322
if (!item) {
323
return undefined;
324
}
325
this._map.delete(key);
326
this.removeItem(item);
327
this._size--;
328
return item.value;
329
}
330
331
shift(): V | undefined {
332
if (!this._head && !this._tail) {
333
return undefined;
334
}
335
if (!this._head || !this._tail) {
336
throw new Error('Invalid list');
337
}
338
const item = this._head;
339
this._map.delete(item.key);
340
this.removeItem(item);
341
this._size--;
342
return item.value;
343
}
344
345
forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: unknown): void {
346
const state = this._state;
347
let current = this._head;
348
while (current) {
349
if (thisArg) {
350
callbackfn.bind(thisArg)(current.value, current.key, this);
351
} else {
352
callbackfn(current.value, current.key, this);
353
}
354
if (this._state !== state) {
355
throw new Error(`LinkedMap got modified during iteration.`);
356
}
357
current = current.next;
358
}
359
}
360
361
keys(): IterableIterator<K> {
362
const map = this;
363
const state = this._state;
364
let current = this._head;
365
const iterator: IterableIterator<K> = {
366
[Symbol.iterator]() {
367
return iterator;
368
},
369
next(): IteratorResult<K> {
370
if (map._state !== state) {
371
throw new Error(`LinkedMap got modified during iteration.`);
372
}
373
if (current) {
374
const result = { value: current.key, done: false };
375
current = current.next;
376
return result;
377
} else {
378
return { value: undefined, done: true };
379
}
380
}
381
};
382
return iterator;
383
}
384
385
values(): IterableIterator<V> {
386
const map = this;
387
const state = this._state;
388
let current = this._head;
389
const iterator: IterableIterator<V> = {
390
[Symbol.iterator]() {
391
return iterator;
392
},
393
next(): IteratorResult<V> {
394
if (map._state !== state) {
395
throw new Error(`LinkedMap got modified during iteration.`);
396
}
397
if (current) {
398
const result = { value: current.value, done: false };
399
current = current.next;
400
return result;
401
} else {
402
return { value: undefined, done: true };
403
}
404
}
405
};
406
return iterator;
407
}
408
409
entries(): IterableIterator<[K, V]> {
410
const map = this;
411
const state = this._state;
412
let current = this._head;
413
const iterator: IterableIterator<[K, V]> = {
414
[Symbol.iterator]() {
415
return iterator;
416
},
417
next(): IteratorResult<[K, V]> {
418
if (map._state !== state) {
419
throw new Error(`LinkedMap got modified during iteration.`);
420
}
421
if (current) {
422
const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
423
current = current.next;
424
return result;
425
} else {
426
return { value: undefined, done: true };
427
}
428
}
429
};
430
return iterator;
431
}
432
433
[Symbol.iterator](): IterableIterator<[K, V]> {
434
return this.entries();
435
}
436
437
protected trimOld(newSize: number) {
438
if (newSize >= this.size) {
439
return;
440
}
441
if (newSize === 0) {
442
this.clear();
443
return;
444
}
445
let current = this._head;
446
let currentSize = this.size;
447
while (current && currentSize > newSize) {
448
this._map.delete(current.key);
449
current = current.next;
450
currentSize--;
451
}
452
this._head = current;
453
this._size = currentSize;
454
if (current) {
455
current.previous = undefined;
456
}
457
this._state++;
458
}
459
460
protected trimNew(newSize: number) {
461
if (newSize >= this.size) {
462
return;
463
}
464
if (newSize === 0) {
465
this.clear();
466
return;
467
}
468
let current = this._tail;
469
let currentSize = this.size;
470
while (current && currentSize > newSize) {
471
this._map.delete(current.key);
472
current = current.previous;
473
currentSize--;
474
}
475
this._tail = current;
476
this._size = currentSize;
477
if (current) {
478
current.next = undefined;
479
}
480
this._state++;
481
}
482
483
private addItemFirst(item: Item<K, V>): void {
484
// First time Insert
485
if (!this._head && !this._tail) {
486
this._tail = item;
487
} else if (!this._head) {
488
throw new Error('Invalid list');
489
} else {
490
item.next = this._head;
491
this._head.previous = item;
492
}
493
this._head = item;
494
this._state++;
495
}
496
497
private addItemLast(item: Item<K, V>): void {
498
// First time Insert
499
if (!this._head && !this._tail) {
500
this._head = item;
501
} else if (!this._tail) {
502
throw new Error('Invalid list');
503
} else {
504
item.previous = this._tail;
505
this._tail.next = item;
506
}
507
this._tail = item;
508
this._state++;
509
}
510
511
private removeItem(item: Item<K, V>): void {
512
if (item === this._head && item === this._tail) {
513
this._head = undefined;
514
this._tail = undefined;
515
}
516
else if (item === this._head) {
517
// This can only happen if size === 1 which is handled
518
// by the case above.
519
if (!item.next) {
520
throw new Error('Invalid list');
521
}
522
item.next.previous = undefined;
523
this._head = item.next;
524
}
525
else if (item === this._tail) {
526
// This can only happen if size === 1 which is handled
527
// by the case above.
528
if (!item.previous) {
529
throw new Error('Invalid list');
530
}
531
item.previous.next = undefined;
532
this._tail = item.previous;
533
}
534
else {
535
const next = item.next;
536
const previous = item.previous;
537
if (!next || !previous) {
538
throw new Error('Invalid list');
539
}
540
next.previous = previous;
541
previous.next = next;
542
}
543
item.next = undefined;
544
item.previous = undefined;
545
this._state++;
546
}
547
548
private touch(item: Item<K, V>, touch: Touch): void {
549
if (!this._head || !this._tail) {
550
throw new Error('Invalid list');
551
}
552
if ((touch !== Touch.AsOld && touch !== Touch.AsNew)) {
553
return;
554
}
555
556
if (touch === Touch.AsOld) {
557
if (item === this._head) {
558
return;
559
}
560
561
const next = item.next;
562
const previous = item.previous;
563
564
// Unlink the item
565
if (item === this._tail) {
566
// previous must be defined since item was not head but is tail
567
// So there are more than on item in the map
568
previous!.next = undefined;
569
this._tail = previous;
570
}
571
else {
572
// Both next and previous are not undefined since item was neither head nor tail.
573
next!.previous = previous;
574
previous!.next = next;
575
}
576
577
// Insert the node at head
578
item.previous = undefined;
579
item.next = this._head;
580
this._head.previous = item;
581
this._head = item;
582
this._state++;
583
} else if (touch === Touch.AsNew) {
584
if (item === this._tail) {
585
return;
586
}
587
588
const next = item.next;
589
const previous = item.previous;
590
591
// Unlink the item.
592
if (item === this._head) {
593
// next must be defined since item was not tail but is head
594
// So there are more than on item in the map
595
next!.previous = undefined;
596
this._head = next;
597
} else {
598
// Both next and previous are not undefined since item was neither head nor tail.
599
next!.previous = previous;
600
previous!.next = next;
601
}
602
item.next = undefined;
603
item.previous = this._tail;
604
this._tail.next = item;
605
this._tail = item;
606
this._state++;
607
}
608
}
609
610
toJSON(): [K, V][] {
611
const data: [K, V][] = [];
612
613
this.forEach((value, key) => {
614
data.push([key, value]);
615
});
616
617
return data;
618
}
619
620
fromJSON(data: [K, V][]): void {
621
this.clear();
622
623
for (const [key, value] of data) {
624
this.set(key, value);
625
}
626
}
627
}
628
629
abstract class Cache<K, V> extends LinkedMap<K, V> {
630
631
protected _limit: number;
632
protected _ratio: number;
633
634
constructor(limit: number, ratio: number = 1) {
635
super();
636
this._limit = limit;
637
this._ratio = Math.min(Math.max(0, ratio), 1);
638
}
639
640
get limit(): number {
641
return this._limit;
642
}
643
644
set limit(limit: number) {
645
this._limit = limit;
646
this.checkTrim();
647
}
648
649
get ratio(): number {
650
return this._ratio;
651
}
652
653
set ratio(ratio: number) {
654
this._ratio = Math.min(Math.max(0, ratio), 1);
655
this.checkTrim();
656
}
657
658
override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
659
return super.get(key, touch);
660
}
661
662
peek(key: K): V | undefined {
663
return super.get(key, Touch.None);
664
}
665
666
override set(key: K, value: V): this {
667
super.set(key, value, Touch.AsNew);
668
return this;
669
}
670
671
protected checkTrim() {
672
if (this.size > this._limit) {
673
this.trim(Math.round(this._limit * this._ratio));
674
}
675
}
676
677
protected abstract trim(newSize: number): void;
678
}
679
680
export class LRUCache<K, V> extends Cache<K, V> {
681
682
constructor(limit: number, ratio: number = 1) {
683
super(limit, ratio);
684
}
685
686
protected override trim(newSize: number) {
687
this.trimOld(newSize);
688
}
689
690
override set(key: K, value: V): this {
691
super.set(key, value);
692
this.checkTrim();
693
return this;
694
}
695
}
696
697
export class MRUCache<K, V> extends Cache<K, V> {
698
699
constructor(limit: number, ratio: number = 1) {
700
super(limit, ratio);
701
}
702
703
protected override trim(newSize: number) {
704
this.trimNew(newSize);
705
}
706
707
override set(key: K, value: V): this {
708
if (this._limit <= this.size && !this.has(key)) {
709
this.trim(Math.round(this._limit * this._ratio) - 1);
710
}
711
712
super.set(key, value);
713
return this;
714
}
715
}
716
717
export class CounterSet<T> {
718
719
private map = new Map<T, number>();
720
721
add(value: T): CounterSet<T> {
722
this.map.set(value, (this.map.get(value) || 0) + 1);
723
return this;
724
}
725
726
delete(value: T): boolean {
727
let counter = this.map.get(value) || 0;
728
729
if (counter === 0) {
730
return false;
731
}
732
733
counter--;
734
735
if (counter === 0) {
736
this.map.delete(value);
737
} else {
738
this.map.set(value, counter);
739
}
740
741
return true;
742
}
743
744
has(value: T): boolean {
745
return this.map.has(value);
746
}
747
}
748
749
/**
750
* A map that allows access both by keys and values.
751
* **NOTE**: values need to be unique.
752
*/
753
export class BidirectionalMap<K, V> {
754
755
private readonly _m1 = new Map<K, V>();
756
private readonly _m2 = new Map<V, K>();
757
758
constructor(entries?: readonly (readonly [K, V])[]) {
759
if (entries) {
760
for (const [key, value] of entries) {
761
this.set(key, value);
762
}
763
}
764
}
765
766
clear(): void {
767
this._m1.clear();
768
this._m2.clear();
769
}
770
771
set(key: K, value: V): void {
772
this._m1.set(key, value);
773
this._m2.set(value, key);
774
}
775
776
get(key: K): V | undefined {
777
return this._m1.get(key);
778
}
779
780
getKey(value: V): K | undefined {
781
return this._m2.get(value);
782
}
783
784
delete(key: K): boolean {
785
const value = this._m1.get(key);
786
if (value === undefined) {
787
return false;
788
}
789
this._m1.delete(key);
790
this._m2.delete(value);
791
return true;
792
}
793
794
forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {
795
this._m1.forEach((value, key) => {
796
callbackfn.call(thisArg, value, key, this);
797
});
798
}
799
800
keys(): IterableIterator<K> {
801
return this._m1.keys();
802
}
803
804
values(): IterableIterator<V> {
805
return this._m1.values();
806
}
807
}
808
809
export class SetMap<K, V> {
810
811
private map = new Map<K, Set<V>>();
812
813
add(key: K, value: V): void {
814
let values = this.map.get(key);
815
816
if (!values) {
817
values = new Set<V>();
818
this.map.set(key, values);
819
}
820
821
values.add(value);
822
}
823
824
delete(key: K, value: V): void {
825
const values = this.map.get(key);
826
827
if (!values) {
828
return;
829
}
830
831
values.delete(value);
832
833
if (values.size === 0) {
834
this.map.delete(key);
835
}
836
}
837
838
forEach(key: K, fn: (value: V) => void): void {
839
const values = this.map.get(key);
840
841
if (!values) {
842
return;
843
}
844
845
values.forEach(fn);
846
}
847
848
get(key: K): ReadonlySet<V> {
849
const values = this.map.get(key);
850
if (!values) {
851
return new Set<V>();
852
}
853
return values;
854
}
855
}
856
857
export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
858
if (a === b) {
859
return true;
860
}
861
862
if (a.size !== b.size) {
863
return false;
864
}
865
866
for (const [key, value] of a) {
867
if (!b.has(key) || b.get(key) !== value) {
868
return false;
869
}
870
}
871
872
for (const [key] of b) {
873
if (!a.has(key)) {
874
return false;
875
}
876
}
877
878
return true;
879
}
880
881
/**
882
* A map that is addressable with an arbitrary number of keys. This is useful in high performance
883
* scenarios where creating a composite key whenever the data is accessed is too expensive. For
884
* example for a very hot function, constructing a string like `first-second-third` for every call
885
* will cause a significant hit to performance.
886
*/
887
export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {
888
private _data: Map<any, any> = new Map();
889
890
/**
891
* Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
892
* This is because the spread operator is used for the keys and must be last..
893
* @param value The value to set.
894
* @param keys The keys for the value.
895
*/
896
public set(value: TValue, ...keys: [...TKeys]): void {
897
let currentMap = this._data;
898
for (let i = 0; i < keys.length - 1; i++) {
899
let nextMap = currentMap.get(keys[i]);
900
if (nextMap === undefined) {
901
nextMap = new Map();
902
currentMap.set(keys[i], nextMap);
903
}
904
currentMap = nextMap;
905
}
906
currentMap.set(keys[keys.length - 1], value);
907
}
908
909
public get(...keys: [...TKeys]): TValue | undefined {
910
let currentMap = this._data;
911
for (let i = 0; i < keys.length - 1; i++) {
912
const nextMap = currentMap.get(keys[i]);
913
if (nextMap === undefined) {
914
return undefined;
915
}
916
currentMap = nextMap;
917
}
918
return currentMap.get(keys[keys.length - 1]);
919
}
920
921
public clear(): void {
922
this._data.clear();
923
}
924
925
public *values(): IterableIterator<TValue> {
926
function* iterate(map: Map<any, any>): IterableIterator<TValue> {
927
for (const value of map.values()) {
928
if (value instanceof Map) {
929
yield* iterate(value);
930
} else {
931
yield value;
932
}
933
}
934
}
935
yield* iterate(this._data);
936
}
937
938
/**
939
* Get a textual representation of the map for debugging purposes.
940
*/
941
public toString(): string {
942
const printMap = (map: Map<any, any>, depth: number): string => {
943
let result = '';
944
for (const [key, value] of map) {
945
result += `${' '.repeat(depth)}${key}: `;
946
if (value instanceof Map) {
947
result += '\n' + printMap(value, depth + 1);
948
} else {
949
result += `${value}\n`;
950
}
951
}
952
return result;
953
};
954
955
return printMap(this._data, 0);
956
}
957
}
958
959