Path: blob/main/extensions/copilot/src/util/vs/base/common/map.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import { URI } from './uri';89export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {10let result = map.get(key);11if (result === undefined) {12result = value;13map.set(key, result);14}1516return result;17}1819export function mapToString<K, V>(map: Map<K, V>): string {20const entries: string[] = [];21map.forEach((value, key) => {22entries.push(`${key} => ${value}`);23});2425return `Map(${map.size}) {${entries.join(', ')}}`;26}2728export function setToString<K>(set: Set<K>): string {29const entries: K[] = [];30set.forEach(value => {31entries.push(value);32});3334return `Set(${set.size}) {${entries.join(', ')}}`;35}3637interface ResourceMapKeyFn {38(resource: URI): string;39}4041class ResourceMapEntry<T> {42constructor(readonly uri: URI, readonly value: T) { }43}4445function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {46return Array.isArray(arg);47}4849export class ResourceMap<T> implements Map<URI, T> {5051private static readonly defaultToKey = (resource: URI) => resource.toString();5253readonly [Symbol.toStringTag] = 'ResourceMap';5455private readonly map: Map<string, ResourceMapEntry<T>>;56private readonly toKey: ResourceMapKeyFn;5758/**59*60* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util61*/62constructor(toKey?: ResourceMapKeyFn);6364/**65*66* @param other Another resource which this maps is created from67* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util68*/69constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);7071/**72*73* @param other Another resource which this maps is created from74* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util75*/76constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);7778constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {79if (arg instanceof ResourceMap) {80this.map = new Map(arg.map);81this.toKey = toKey ?? ResourceMap.defaultToKey;82} else if (isEntries(arg)) {83this.map = new Map();84this.toKey = toKey ?? ResourceMap.defaultToKey;8586for (const [resource, value] of arg) {87this.set(resource, value);88}89} else {90this.map = new Map();91this.toKey = arg ?? ResourceMap.defaultToKey;92}93}9495set(resource: URI, value: T): this {96this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));97return this;98}99100get(resource: URI): T | undefined {101return this.map.get(this.toKey(resource))?.value;102}103104has(resource: URI): boolean {105return this.map.has(this.toKey(resource));106}107108get size(): number {109return this.map.size;110}111112clear(): void {113this.map.clear();114}115116delete(resource: URI): boolean {117return this.map.delete(this.toKey(resource));118}119120forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {121if (typeof thisArg !== 'undefined') {122clb = clb.bind(thisArg);123}124for (const [_, entry] of this.map) {125clb(entry.value, entry.uri, this);126}127}128129*values(): IterableIterator<T> {130for (const entry of this.map.values()) {131yield entry.value;132}133}134135*keys(): IterableIterator<URI> {136for (const entry of this.map.values()) {137yield entry.uri;138}139}140141*entries(): IterableIterator<[URI, T]> {142for (const entry of this.map.values()) {143yield [entry.uri, entry.value];144}145}146147*[Symbol.iterator](): IterableIterator<[URI, T]> {148for (const [, entry] of this.map) {149yield [entry.uri, entry.value];150}151}152}153154export class ResourceSet implements Set<URI> {155156readonly [Symbol.toStringTag]: string = 'ResourceSet';157158private readonly _map: ResourceMap<URI>;159160constructor(toKey?: ResourceMapKeyFn);161constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);162constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {163if (!entriesOrKey || typeof entriesOrKey === 'function') {164this._map = new ResourceMap(entriesOrKey);165} else {166this._map = new ResourceMap(toKey);167entriesOrKey.forEach(this.add, this);168}169}170171172get size(): number {173return this._map.size;174}175176add(value: URI): this {177this._map.set(value, value);178return this;179}180181clear(): void {182this._map.clear();183}184185delete(value: URI): boolean {186return this._map.delete(value);187}188189forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {190this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));191}192193has(value: URI): boolean {194return this._map.has(value);195}196197entries(): IterableIterator<[URI, URI]> {198return this._map.entries();199}200201keys(): IterableIterator<URI> {202return this._map.keys();203}204205values(): IterableIterator<URI> {206return this._map.keys();207}208209[Symbol.iterator](): IterableIterator<URI> {210return this.keys();211}212}213214215interface Item<K, V> {216previous: Item<K, V> | undefined;217next: Item<K, V> | undefined;218key: K;219value: V;220}221222export const enum Touch {223None = 0,224AsOld = 1,225AsNew = 2226}227228export class LinkedMap<K, V> implements Map<K, V> {229230readonly [Symbol.toStringTag] = 'LinkedMap';231232private _map: Map<K, Item<K, V>>;233private _head: Item<K, V> | undefined;234private _tail: Item<K, V> | undefined;235private _size: number;236237private _state: number;238239constructor() {240this._map = new Map<K, Item<K, V>>();241this._head = undefined;242this._tail = undefined;243this._size = 0;244this._state = 0;245}246247clear(): void {248this._map.clear();249this._head = undefined;250this._tail = undefined;251this._size = 0;252this._state++;253}254255isEmpty(): boolean {256return !this._head && !this._tail;257}258259get size(): number {260return this._size;261}262263get first(): V | undefined {264return this._head?.value;265}266267get last(): V | undefined {268return this._tail?.value;269}270271has(key: K): boolean {272return this._map.has(key);273}274275get(key: K, touch: Touch = Touch.None): V | undefined {276const item = this._map.get(key);277if (!item) {278return undefined;279}280if (touch !== Touch.None) {281this.touch(item, touch);282}283return item.value;284}285286set(key: K, value: V, touch: Touch = Touch.None): this {287let item = this._map.get(key);288if (item) {289item.value = value;290if (touch !== Touch.None) {291this.touch(item, touch);292}293} else {294item = { key, value, next: undefined, previous: undefined };295switch (touch) {296case Touch.None:297this.addItemLast(item);298break;299case Touch.AsOld:300this.addItemFirst(item);301break;302case Touch.AsNew:303this.addItemLast(item);304break;305default:306this.addItemLast(item);307break;308}309this._map.set(key, item);310this._size++;311}312return this;313}314315delete(key: K): boolean {316return !!this.remove(key);317}318319remove(key: K): V | undefined {320const item = this._map.get(key);321if (!item) {322return undefined;323}324this._map.delete(key);325this.removeItem(item);326this._size--;327return item.value;328}329330shift(): V | undefined {331if (!this._head && !this._tail) {332return undefined;333}334if (!this._head || !this._tail) {335throw new Error('Invalid list');336}337const item = this._head;338this._map.delete(item.key);339this.removeItem(item);340this._size--;341return item.value;342}343344forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: unknown): void {345const state = this._state;346let current = this._head;347while (current) {348if (thisArg) {349callbackfn.bind(thisArg)(current.value, current.key, this);350} else {351callbackfn(current.value, current.key, this);352}353if (this._state !== state) {354throw new Error(`LinkedMap got modified during iteration.`);355}356current = current.next;357}358}359360keys(): IterableIterator<K> {361const map = this;362const state = this._state;363let current = this._head;364const iterator: IterableIterator<K> = {365[Symbol.iterator]() {366return iterator;367},368next(): IteratorResult<K> {369if (map._state !== state) {370throw new Error(`LinkedMap got modified during iteration.`);371}372if (current) {373const result = { value: current.key, done: false };374current = current.next;375return result;376} else {377return { value: undefined, done: true };378}379}380};381return iterator;382}383384values(): IterableIterator<V> {385const map = this;386const state = this._state;387let current = this._head;388const iterator: IterableIterator<V> = {389[Symbol.iterator]() {390return iterator;391},392next(): IteratorResult<V> {393if (map._state !== state) {394throw new Error(`LinkedMap got modified during iteration.`);395}396if (current) {397const result = { value: current.value, done: false };398current = current.next;399return result;400} else {401return { value: undefined, done: true };402}403}404};405return iterator;406}407408entries(): IterableIterator<[K, V]> {409const map = this;410const state = this._state;411let current = this._head;412const iterator: IterableIterator<[K, V]> = {413[Symbol.iterator]() {414return iterator;415},416next(): IteratorResult<[K, V]> {417if (map._state !== state) {418throw new Error(`LinkedMap got modified during iteration.`);419}420if (current) {421const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };422current = current.next;423return result;424} else {425return { value: undefined, done: true };426}427}428};429return iterator;430}431432[Symbol.iterator](): IterableIterator<[K, V]> {433return this.entries();434}435436protected trimOld(newSize: number) {437if (newSize >= this.size) {438return;439}440if (newSize === 0) {441this.clear();442return;443}444let current = this._head;445let currentSize = this.size;446while (current && currentSize > newSize) {447this._map.delete(current.key);448current = current.next;449currentSize--;450}451this._head = current;452this._size = currentSize;453if (current) {454current.previous = undefined;455}456this._state++;457}458459protected trimNew(newSize: number) {460if (newSize >= this.size) {461return;462}463if (newSize === 0) {464this.clear();465return;466}467let current = this._tail;468let currentSize = this.size;469while (current && currentSize > newSize) {470this._map.delete(current.key);471current = current.previous;472currentSize--;473}474this._tail = current;475this._size = currentSize;476if (current) {477current.next = undefined;478}479this._state++;480}481482private addItemFirst(item: Item<K, V>): void {483// First time Insert484if (!this._head && !this._tail) {485this._tail = item;486} else if (!this._head) {487throw new Error('Invalid list');488} else {489item.next = this._head;490this._head.previous = item;491}492this._head = item;493this._state++;494}495496private addItemLast(item: Item<K, V>): void {497// First time Insert498if (!this._head && !this._tail) {499this._head = item;500} else if (!this._tail) {501throw new Error('Invalid list');502} else {503item.previous = this._tail;504this._tail.next = item;505}506this._tail = item;507this._state++;508}509510private removeItem(item: Item<K, V>): void {511if (item === this._head && item === this._tail) {512this._head = undefined;513this._tail = undefined;514}515else if (item === this._head) {516// This can only happen if size === 1 which is handled517// by the case above.518if (!item.next) {519throw new Error('Invalid list');520}521item.next.previous = undefined;522this._head = item.next;523}524else if (item === this._tail) {525// This can only happen if size === 1 which is handled526// by the case above.527if (!item.previous) {528throw new Error('Invalid list');529}530item.previous.next = undefined;531this._tail = item.previous;532}533else {534const next = item.next;535const previous = item.previous;536if (!next || !previous) {537throw new Error('Invalid list');538}539next.previous = previous;540previous.next = next;541}542item.next = undefined;543item.previous = undefined;544this._state++;545}546547private touch(item: Item<K, V>, touch: Touch): void {548if (!this._head || !this._tail) {549throw new Error('Invalid list');550}551if ((touch !== Touch.AsOld && touch !== Touch.AsNew)) {552return;553}554555if (touch === Touch.AsOld) {556if (item === this._head) {557return;558}559560const next = item.next;561const previous = item.previous;562563// Unlink the item564if (item === this._tail) {565// previous must be defined since item was not head but is tail566// So there are more than on item in the map567previous!.next = undefined;568this._tail = previous;569}570else {571// Both next and previous are not undefined since item was neither head nor tail.572next!.previous = previous;573previous!.next = next;574}575576// Insert the node at head577item.previous = undefined;578item.next = this._head;579this._head.previous = item;580this._head = item;581this._state++;582} else if (touch === Touch.AsNew) {583if (item === this._tail) {584return;585}586587const next = item.next;588const previous = item.previous;589590// Unlink the item.591if (item === this._head) {592// next must be defined since item was not tail but is head593// So there are more than on item in the map594next!.previous = undefined;595this._head = next;596} else {597// Both next and previous are not undefined since item was neither head nor tail.598next!.previous = previous;599previous!.next = next;600}601item.next = undefined;602item.previous = this._tail;603this._tail.next = item;604this._tail = item;605this._state++;606}607}608609toJSON(): [K, V][] {610const data: [K, V][] = [];611612this.forEach((value, key) => {613data.push([key, value]);614});615616return data;617}618619fromJSON(data: [K, V][]): void {620this.clear();621622for (const [key, value] of data) {623this.set(key, value);624}625}626}627628abstract class Cache<K, V> extends LinkedMap<K, V> {629630protected _limit: number;631protected _ratio: number;632633constructor(limit: number, ratio: number = 1) {634super();635this._limit = limit;636this._ratio = Math.min(Math.max(0, ratio), 1);637}638639get limit(): number {640return this._limit;641}642643set limit(limit: number) {644this._limit = limit;645this.checkTrim();646}647648get ratio(): number {649return this._ratio;650}651652set ratio(ratio: number) {653this._ratio = Math.min(Math.max(0, ratio), 1);654this.checkTrim();655}656657override get(key: K, touch: Touch = Touch.AsNew): V | undefined {658return super.get(key, touch);659}660661peek(key: K): V | undefined {662return super.get(key, Touch.None);663}664665override set(key: K, value: V): this {666super.set(key, value, Touch.AsNew);667return this;668}669670protected checkTrim() {671if (this.size > this._limit) {672this.trim(Math.round(this._limit * this._ratio));673}674}675676protected abstract trim(newSize: number): void;677}678679export class LRUCache<K, V> extends Cache<K, V> {680681constructor(limit: number, ratio: number = 1) {682super(limit, ratio);683}684685protected override trim(newSize: number) {686this.trimOld(newSize);687}688689override set(key: K, value: V): this {690super.set(key, value);691this.checkTrim();692return this;693}694}695696export class MRUCache<K, V> extends Cache<K, V> {697698constructor(limit: number, ratio: number = 1) {699super(limit, ratio);700}701702protected override trim(newSize: number) {703this.trimNew(newSize);704}705706override set(key: K, value: V): this {707if (this._limit <= this.size && !this.has(key)) {708this.trim(Math.round(this._limit * this._ratio) - 1);709}710711super.set(key, value);712return this;713}714}715716export class CounterSet<T> {717718private map = new Map<T, number>();719720add(value: T): CounterSet<T> {721this.map.set(value, (this.map.get(value) || 0) + 1);722return this;723}724725delete(value: T): boolean {726let counter = this.map.get(value) || 0;727728if (counter === 0) {729return false;730}731732counter--;733734if (counter === 0) {735this.map.delete(value);736} else {737this.map.set(value, counter);738}739740return true;741}742743has(value: T): boolean {744return this.map.has(value);745}746}747748/**749* A map that allows access both by keys and values.750* **NOTE**: values need to be unique.751*/752export class BidirectionalMap<K, V> {753754private readonly _m1 = new Map<K, V>();755private readonly _m2 = new Map<V, K>();756757constructor(entries?: readonly (readonly [K, V])[]) {758if (entries) {759for (const [key, value] of entries) {760this.set(key, value);761}762}763}764765clear(): void {766this._m1.clear();767this._m2.clear();768}769770set(key: K, value: V): void {771this._m1.set(key, value);772this._m2.set(value, key);773}774775get(key: K): V | undefined {776return this._m1.get(key);777}778779getKey(value: V): K | undefined {780return this._m2.get(value);781}782783delete(key: K): boolean {784const value = this._m1.get(key);785if (value === undefined) {786return false;787}788this._m1.delete(key);789this._m2.delete(value);790return true;791}792793forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {794this._m1.forEach((value, key) => {795callbackfn.call(thisArg, value, key, this);796});797}798799keys(): IterableIterator<K> {800return this._m1.keys();801}802803values(): IterableIterator<V> {804return this._m1.values();805}806}807808export class SetMap<K, V> {809810private map = new Map<K, Set<V>>();811812add(key: K, value: V): void {813let values = this.map.get(key);814815if (!values) {816values = new Set<V>();817this.map.set(key, values);818}819820values.add(value);821}822823delete(key: K, value: V): void {824const values = this.map.get(key);825826if (!values) {827return;828}829830values.delete(value);831832if (values.size === 0) {833this.map.delete(key);834}835}836837forEach(key: K, fn: (value: V) => void): void {838const values = this.map.get(key);839840if (!values) {841return;842}843844values.forEach(fn);845}846847get(key: K): ReadonlySet<V> {848const values = this.map.get(key);849if (!values) {850return new Set<V>();851}852return values;853}854}855856export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {857if (a === b) {858return true;859}860861if (a.size !== b.size) {862return false;863}864865for (const [key, value] of a) {866if (!b.has(key) || b.get(key) !== value) {867return false;868}869}870871for (const [key] of b) {872if (!a.has(key)) {873return false;874}875}876877return true;878}879880/**881* A map that is addressable with an arbitrary number of keys. This is useful in high performance882* scenarios where creating a composite key whenever the data is accessed is too expensive. For883* example for a very hot function, constructing a string like `first-second-third` for every call884* will cause a significant hit to performance.885*/886export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {887private _data: Map<any, any> = new Map();888889/**890* Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.891* This is because the spread operator is used for the keys and must be last..892* @param value The value to set.893* @param keys The keys for the value.894*/895public set(value: TValue, ...keys: [...TKeys]): void {896let currentMap = this._data;897for (let i = 0; i < keys.length - 1; i++) {898let nextMap = currentMap.get(keys[i]);899if (nextMap === undefined) {900nextMap = new Map();901currentMap.set(keys[i], nextMap);902}903currentMap = nextMap;904}905currentMap.set(keys[keys.length - 1], value);906}907908public get(...keys: [...TKeys]): TValue | undefined {909let currentMap = this._data;910for (let i = 0; i < keys.length - 1; i++) {911const nextMap = currentMap.get(keys[i]);912if (nextMap === undefined) {913return undefined;914}915currentMap = nextMap;916}917return currentMap.get(keys[keys.length - 1]);918}919920public clear(): void {921this._data.clear();922}923924public *values(): IterableIterator<TValue> {925function* iterate(map: Map<any, any>): IterableIterator<TValue> {926for (const value of map.values()) {927if (value instanceof Map) {928yield* iterate(value);929} else {930yield value;931}932}933}934yield* iterate(this._data);935}936937/**938* Get a textual representation of the map for debugging purposes.939*/940public toString(): string {941const printMap = (map: Map<any, any>, depth: number): string => {942let result = '';943for (const [key, value] of map) {944result += `${' '.repeat(depth)}${key}: `;945if (value instanceof Map) {946result += '\n' + printMap(value, depth + 1);947} else {948result += `${value}\n`;949}950}951return result;952};953954return printMap(this._data, 0);955}956}957958959