src/vs/base/common/map.ts
1016 LOC · 873 covered · 143 uncovered · 293 ranges · 20961 concepts · 118 introducers · 12741 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
/*---------------------------------------------------------------------------------------------
map.ts ×97
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from './uri.js';
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
if (result === undefined) {
result = value;
map.set(key, result);
}
return result;
}
export function mapToString<K, V>(map: Map<K, V>): string {
const entries: string[] = [];
map.forEach((value, key) => {
entries.push(`${key} => ${value}`);
});
return `Map(${map.size}) {${entries.join(', ')}}`;
}
export function setToString<K>(set: Set<K>): string {
const entries: K[] = [];
set.forEach(value => {
entries.push(value);
});
return `Set(${set.size}) {${entries.join(', ')}}`;
}
interface ResourceMapKeyFn {
(resource: URI): string;
}
class ResourceMapEntry<T> {
constructor(readonly uri: URI, readonly value: T) { }
}
function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {
map.ts ×4
return Array.isArray(arg);
}
export class ResourceMap<T> implements Map<URI, T> {
private static readonly defaultToKey = (resource: URI) => resource.toString();
readonly [Symbol.toStringTag] = 'ResourceMap';
private readonly map: Map<string, ResourceMapEntry<T>>;
private readonly toKey: ResourceMapKeyFn;
/**
*
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
*/
constructor(toKey?: ResourceMapKeyFn);
/**
*
* @param other Another resource which this maps is created from
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
*/
constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
/**
*
* @param other Another resource which this maps is created from
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
*/
constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
this.map = new Map(arg.map);
this.toKey = toKey ?? ResourceMap.defaultToKey;
this.map = new Map();
this.toKey = toKey ?? ResourceMap.defaultToKey;
for (const [resource, value] of arg) {
this.set(resource, value);
}
this.map = new Map();
this.toKey = arg ?? ResourceMap.defaultToKey;
}
}
set(resource: URI, value: T): this {
return this;
}
get(resource: URI): T | undefined {
}
has(resource: URI): boolean {
}
get size(): number {
}
clear(): void {
}
delete(resource: URI): boolean {
}
forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {
clb = clb.bind(thisArg);
}
}
*values(): MapIterator<T> {
}
*keys(): MapIterator<URI> {
*entries(): MapIterator<[URI, T]> {
}
*[Symbol.iterator](): MapIterator<[URI, T]> {
}
export class ResourceSet implements Set<URI> {
readonly [Symbol.toStringTag]: string = 'ResourceSet';
private readonly _map: ResourceMap<URI>;
constructor(toKey?: ResourceMapKeyFn);
constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);
constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
entriesOrKey.forEach(this.add, this);
}
get size(): number {
}
add(value: URI): this {
return this;
}
clear(): void {
}
delete(value: URI): boolean {
return this._map.delete(value);
}
forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {
this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
}
has(value: URI): boolean {
}
entries(): SetIterator<[URI, URI]> {
return this._map.entries() as unknown as SetIterator<[URI, URI]>;
}
keys(): SetIterator<URI> {
}
values(): SetIterator<URI> {
return this._map.keys() as unknown as SetIterator<URI>;
}
[Symbol.iterator](): SetIterator<URI> {
}
interface Item<K, V> {
previous: Item<K, V> | undefined;
next: Item<K, V> | undefined;
key: K;
value: V;
}
export const enum Touch {
None = 0,
AsOld = 1,
AsNew = 2
}
export class LinkedMap<K, V> implements Map<K, V> {
readonly [Symbol.toStringTag] = 'LinkedMap';
private _map: Map<K, Item<K, V>>;
private _head: Item<K, V> | undefined;
private _tail: Item<K, V> | undefined;
private _size: number;
private _state: number;
constructor() {
this._head = undefined;
this._tail = undefined;
this._size = 0;
this._state = 0;
}
clear(): void {
this._head = undefined;
this._tail = undefined;
this._size = 0;
this._state++;
}
isEmpty(): boolean {
return !this._head && !this._tail;
}
get size(): number {
}
get first(): V | undefined {
}
get last(): V | undefined {
}
has(key: K): boolean {
}
get(key: K, touch: Touch = Touch.None): V | undefined {
if (!item) {
}
}
set(key: K, value: V, touch: Touch = Touch.None): this {
if (item) {
if (touch !== Touch.None) {
this.touch(item, touch);
}
item = { key, value, next: undefined, previous: undefined };
switch (touch) {
case Touch.None:
break;
this.addItemFirst(item);
break;
break;
this.addItemLast(item);
break;
this._map.set(key, item);
this._size++;
}
return this;
}
delete(key: K): boolean {
}
remove(key: K): V | undefined {
if (!item) {
}
this.removeItem(item);
this._size--;
return item.value;
}
shift(): V | undefined {
if (!this._head && !this._tail) {
return undefined;
}
if (!this._head || !this._tail) {
throw new Error('Invalid list');
}
const item = this._head;
this._map.delete(item.key);
this.removeItem(item);
this._size--;
return item.value;
}
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
let current = this._head;
while (current) {
if (thisArg) {
callbackfn.bind(thisArg)(current.value, current.key, this);
callbackfn(current.value, current.key, this);
}
if (this._state !== state) {
throw new Error(`LinkedMap got modified during iteration.`);
}
}
}
keys(): MapIterator<K> {
const state = this._state;
let current = this._head;
const iterator: MapIterator<K> = {
[Symbol.iterator]() {
return iterator;
},
[Symbol.dispose]() { /* no-op */ },
next(): IteratorResult<K> {
if (map._state !== state) {
}
current = current.next;
return result;
return { value: undefined, done: true };
}
}
};
return iterator;
}
values(): MapIterator<V> {
const state = this._state;
let current = this._head;
const iterator: MapIterator<V> = {
[Symbol.iterator]() {
return iterator;
},
[Symbol.dispose]() { /* no-op */ },
next(): IteratorResult<V> {
if (map._state !== state) {
}
const result = { value: current.value, done: false };
current = current.next;
return result;
} else {
return { value: undefined, done: true };
}
}
};
return iterator;
}
entries(): MapIterator<[K, V]> {
const state = this._state;
let current = this._head;
const iterator: MapIterator<[K, V]> = {
[Symbol.iterator]() {
return iterator;
},
[Symbol.dispose]() { /* no-op */ },
next(): IteratorResult<[K, V]> {
if (map._state !== state) {
}
const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
current = current.next;
return result;
} else {
return { value: undefined, done: true };
}
}
};
return iterator;
}
[Symbol.iterator](): MapIterator<[K, V]> {
return this.entries();
}
protected trimOld(newSize: number) {
return;
}
this.clear();
return;
}
let currentSize = this.size;
while (current && currentSize > newSize) {
this._map.delete(current.key);
current = current.next;
currentSize--;
}
this._head = current;
this._size = currentSize;
if (current) {
current.previous = undefined;
}
this._state++;
}
protected trimNew(newSize: number) {
return;
}
this.clear();
return;
}
let currentSize = this.size;
while (current && currentSize > newSize) {
this._map.delete(current.key);
current = current.previous;
currentSize--;
}
this._tail = current;
this._size = currentSize;
if (current) {
current.next = undefined;
}
this._state++;
}
private addItemFirst(item: Item<K, V>): void {
// First time Insert
if (!this._head && !this._tail) {
this._tail = item;
} else if (!this._head) {
throw new Error('Invalid list');
} else {
item.next = this._head;
this._head.previous = item;
}
this._head = item;
this._state++;
}
private addItemLast(item: Item<K, V>): void {
if (!this._head && !this._tail) {
this._head = item;
} else if (!this._tail) {
throw new Error('Invalid list');
item.previous = this._tail;
this._tail.next = item;
}
this._state++;
}
private removeItem(item: Item<K, V>): void {
this._tail = undefined;
}
// by the case above.
if (!item.next) {
throw new Error('Invalid list');
}
this._head = item.next;
}
// This can only happen if size === 1 which is handled
// by the case above.
if (!item.previous) {
throw new Error('Invalid list');
}
this._tail = item.previous;
}
else {
const next = item.next;
const previous = item.previous;
if (!next || !previous) {
throw new Error('Invalid list');
}
next.previous = previous;
previous.next = next;
}
item.previous = undefined;
this._state++;
}
private touch(item: Item<K, V>, touch: Touch): void {
throw new Error('Invalid list');
}
return;
}
if (touch === Touch.AsOld) {
}
const next = item.next;
const previous = item.previous;
// Unlink the item
if (item === this._tail) {
// So there are more than on item in the map
previous!.next = undefined;
this._tail = previous;
}
// Both next and previous are not undefined since item was neither head nor tail.
next!.previous = previous;
previous!.next = next;
}
// Insert the node at head
item.previous = undefined;
item.next = this._head;
this._head.previous = item;
this._head = item;
this._state++;
}
const next = item.next;
const previous = item.previous;
// Unlink the item.
if (item === this._head) {
// So there are more than on item in the map
next!.previous = undefined;
this._head = next;
next!.previous = previous;
previous!.next = next;
}
item.previous = this._tail;
this._tail.next = item;
this._tail = item;
this._state++;
}
toJSON(): [K, V][] {
this.forEach((value, key) => {
data.push([key, value]);
});
return data;
}
fromJSON(data: [K, V][]): void {
for (const [key, value] of data) {
this.set(key, value);
}
}
abstract class Cache<K, V> extends LinkedMap<K, V> {
protected _limit: number;
protected _ratio: number;
constructor(limit: number, ratio: number = 1) {
this._limit = limit;
this._ratio = Math.min(Math.max(0, ratio), 1);
}
get limit(): number {
return this._limit;
}
set limit(limit: number) {
this.checkTrim();
}
get ratio(): number {
return this._ratio;
}
set ratio(ratio: number) {
this._ratio = Math.min(Math.max(0, ratio), 1);
this.checkTrim();
}
override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
}
peek(key: K): V | undefined {
}
override set(key: K, value: V): this {
return this;
}
protected checkTrim() {
}
protected abstract trim(newSize: number): void;
}
export class LRUCache<K, V> extends Cache<K, V> {
constructor(limit: number, ratio: number = 1) {
}
protected override trim(newSize: number) {
}
override set(key: K, value: V): this {
this.checkTrim();
return this;
}
export class MRUCache<K, V> extends Cache<K, V> {
constructor(limit: number, ratio: number = 1) {
}
protected override trim(newSize: number) {
}
override set(key: K, value: V): this {
}
super.set(key, value);
return this;
}
export class CounterSet<T> {
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
/**
* A map that allows access both by keys and values.
* **NOTE**: values need to be unique.
*/
export class BidirectionalMap<K, V> {
private readonly _m1 = new Map<K, V>();
private readonly _m2 = new Map<V, K>();
constructor(entries?: readonly (readonly [K, V])[]) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
clear(): void {
this._m2.clear();
}
set(key: K, value: V): void {
this._m2.set(value, key);
}
get(key: K): V | undefined {
}
getKey(value: V): K | undefined {
}
delete(key: K): boolean {
if (value === undefined) {
}
this._m2.delete(value);
return true;
forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {
callbackfn.call(thisArg, value, key, this);
});
}
keys(): IterableIterator<K> {
return this._m1.keys();
}
values(): IterableIterator<V> {
return this._m1.values();
}
export class SetMap<K, V> {
private map = new Map<K, Set<V>>();
add(key: K, value: V): void {
if (!values) {
values = new Set<V>();
this.map.set(key, values);
}
values.add(value);
}
delete(key: K, value: V): void {
if (!values) {
return;
}
values.delete(value);
if (values.size === 0) {
this.map.delete(key);
}
}
forEach(key: K, fn: (value: V) => void): void {
if (!values) {
}
values.forEach(fn);
get(key: K): ReadonlySet<V> {
if (!values) {
}
export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
return true;
}
if (a.size !== b.size) {
return false;
}
for (const [key, value] of a) {
if (!b.has(key) || b.get(key) !== value) {
return false;
}
for (const [key] of b) {
if (!a.has(key)) {
return false;
}
return true;
}
/**
* A map that is addressable with an arbitrary number of keys. This is useful in high performance
* scenarios where creating a composite key whenever the data is accessed is too expensive. For
* example for a very hot function, constructing a string like `first-second-third` for every call
* will cause a significant hit to performance.
*/
export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {
/**
* Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
* This is because the spread operator is used for the keys and must be last..
* @param value The value to set.
* @param keys The keys for the value.
*/
public set(value: TValue, ...keys: [...TKeys]): void {
for (let i = 0; i < keys.length - 1; i++) {
let nextMap = currentMap.get(keys[i]);
if (nextMap === undefined) {
nextMap = new Map();
currentMap.set(keys[i], nextMap);
}
currentMap = nextMap;
}
currentMap.set(keys[keys.length - 1], value);
}
public get(...keys: [...TKeys]): TValue | undefined {
for (let i = 0; i < keys.length - 1; i++) {
const nextMap = currentMap.get(keys[i]);
if (nextMap === undefined) {
}
}
return currentMap.get(keys[keys.length - 1]);
public delete(...keys: [...TKeys]): boolean {
let currentMap = this._data;
for (let i = 0; i < keys.length - 1; i++) {
const nextMap = currentMap.get(keys[i]);
if (nextMap === undefined) {
}
maps.push(currentMap);
}
const deleted = currentMap.delete(keys[keys.length - 1]);
}
return deleted;
public deleteAll(...keys: Partial<TKeys>): boolean {
this._data.clear();
return hadData;
}
let currentMap = this._data;
for (let i = 0; i < keys.length - 1; i++) {
if (nextMap === undefined) {
return false;
}
maps.push(currentMap);
}
for (let i = keys.length - 2; deleted && i >= 0; i--) {
}
}
public clear(): void {
}
public *getAll(...keys: Partial<TKeys>): IterableIterator<TValue> {
for (const key of keys) {
const nextMap = currentMap.get(key);
if (nextMap === undefined) {
}
}
yield* this._values(currentMap);
public *values(): IterableIterator<TValue> {
}
private *_values(map: Map<any, any>): IterableIterator<TValue> {
yield value;
/**
* Get a textual representation of the map for debugging purposes.
*/
public toString(): string {
let result = '';
for (const [key, value] of map) {
result += `${' '.repeat(depth)}${key}: `;
if (value instanceof Map) {
result += '\n' + printMap(value, depth + 1);
} else {
result += `${value}\n`;
}
}
return result;
};
return printMap(this._data, 0);
}