mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
Remove BoundedMap
This commit is contained in:
@@ -13,7 +13,7 @@ import paths = require('vs/base/common/paths');
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
@@ -82,7 +82,7 @@ export interface IResourceDescriptor {
|
||||
// we need to bypass the cache or not. We could always bypass the cache everytime we show the image
|
||||
// however that has very bad impact on memory consumption because each time the image gets shown,
|
||||
// memory grows (see also https://github.com/electron/electron/issues/6275)
|
||||
const IMAGE_RESOURCE_ETAG_CACHE = new BoundedMap<{ etag: string, src: string }>(100);
|
||||
const IMAGE_RESOURCE_ETAG_CACHE = new LRUCache<string, { etag: string, src: string }>(100);
|
||||
function imageSrc(descriptor: IResourceDescriptor): string {
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
return descriptor.resource.toString(true /* skip encoding */);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
import strings = require('vs/base/common/strings');
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export interface IFilter {
|
||||
@@ -317,7 +317,7 @@ function nextWord(word: string, start: number): number {
|
||||
|
||||
export const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString);
|
||||
const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString);
|
||||
const fuzzyRegExpCache = new BoundedMap<RegExp>(10000); // bounded to 10000 elements
|
||||
const fuzzyRegExpCache = new LRUCache<string, RegExp>(10000); // bounded to 10000 elements
|
||||
|
||||
export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
|
||||
if (typeof word !== 'string' || typeof wordToMatchAgainst !== 'string') {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import arrays = require('vs/base/common/arrays');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
@@ -267,7 +267,7 @@ interface ParsedExpressionPattern {
|
||||
allPaths?: string[];
|
||||
}
|
||||
|
||||
const CACHE = new BoundedMap<ParsedStringPattern>(10000); // bounded to 10000 elements
|
||||
const CACHE = new LRUCache<string, ParsedStringPattern>(10000); // bounded to 10000 elements
|
||||
|
||||
const FALSE = function () {
|
||||
return false;
|
||||
|
||||
+4
-186
@@ -7,11 +7,6 @@
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
export interface Entry<K, T> {
|
||||
key: K;
|
||||
value: T;
|
||||
}
|
||||
|
||||
export function values<K, V>(map: Map<K, V>): V[] {
|
||||
const result: V[] = [];
|
||||
map.forEach(value => result.push(value));
|
||||
@@ -36,187 +31,6 @@ export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface ISerializedBoundedLinkedMap<T> {
|
||||
version?: string;
|
||||
entries: { key: string; value: T }[];
|
||||
}
|
||||
|
||||
interface LinkedEntry<K, T> extends Entry<K, T> {
|
||||
next?: LinkedEntry<K, T>;
|
||||
prev?: LinkedEntry<K, T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Map<T> that optionally allows to set a limit of entries to store. Once the limit is hit,
|
||||
* the cache will remove the entry that was last recently added. Or, if a ratio is provided below 1,
|
||||
* all elements will be removed until the ratio is full filled (e.g. 0.75 to remove 25% of old elements).
|
||||
*/
|
||||
export class BoundedMap<T> {
|
||||
private map: Map<string, LinkedEntry<string, T>>;
|
||||
|
||||
private head: LinkedEntry<string, T>;
|
||||
private tail: LinkedEntry<string, T>;
|
||||
private ratio: number;
|
||||
|
||||
constructor(private limit = Number.MAX_VALUE, ratio = 1, value?: ISerializedBoundedLinkedMap<T>) {
|
||||
this.map = new Map<string, LinkedEntry<string, T>>();
|
||||
this.ratio = limit * ratio;
|
||||
|
||||
if (value) {
|
||||
value.entries.forEach(entry => {
|
||||
this.set(entry.key, entry.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public setLimit(limit: number): void {
|
||||
if (limit < 0) {
|
||||
return; // invalid limit
|
||||
}
|
||||
|
||||
this.limit = limit;
|
||||
while (this.map.size > this.limit) {
|
||||
this.trim();
|
||||
}
|
||||
}
|
||||
|
||||
public serialize(): ISerializedBoundedLinkedMap<T> {
|
||||
const serialized: ISerializedBoundedLinkedMap<T> = { entries: [] };
|
||||
|
||||
this.map.forEach(entry => {
|
||||
serialized.entries.push({ key: entry.key, value: entry.value });
|
||||
});
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
public set(key: string, value: T): boolean {
|
||||
if (this.map.has(key)) {
|
||||
return false; // already present!
|
||||
}
|
||||
|
||||
const entry: LinkedEntry<string, T> = { key, value };
|
||||
this.push(entry);
|
||||
|
||||
if (this.size > this.limit) {
|
||||
this.trim();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get(key: string): T {
|
||||
const entry = this.map.get(key);
|
||||
|
||||
return entry ? entry.value : null;
|
||||
}
|
||||
|
||||
public getOrSet(k: string, t: T): T {
|
||||
const res = this.get(k);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
this.set(k, t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public delete(key: string): T {
|
||||
const entry = this.map.get(key);
|
||||
|
||||
if (entry) {
|
||||
this.map.delete(key);
|
||||
|
||||
if (entry.next) {
|
||||
entry.next.prev = entry.prev; // [A]<-[x]<-[C] = [A]<-[C]
|
||||
} else {
|
||||
this.head = entry.prev; // [A]-[x] = [A]
|
||||
}
|
||||
|
||||
if (entry.prev) {
|
||||
entry.prev.next = entry.next; // [A]->[x]->[C] = [A]->[C]
|
||||
} else {
|
||||
this.tail = entry.next; // [x]-[A] = [A]
|
||||
}
|
||||
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public has(key: string): boolean {
|
||||
return this.map.has(key);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.map.clear();
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
}
|
||||
|
||||
private push(entry: LinkedEntry<string, T>): void {
|
||||
if (this.head) {
|
||||
// [A]-[B] = [A]-[B]->[X]
|
||||
entry.prev = this.head;
|
||||
this.head.next = entry;
|
||||
}
|
||||
|
||||
if (!this.tail) {
|
||||
this.tail = entry;
|
||||
}
|
||||
|
||||
this.head = entry;
|
||||
|
||||
this.map.set(entry.key, entry);
|
||||
}
|
||||
|
||||
private trim(): void {
|
||||
if (this.tail) {
|
||||
|
||||
// Remove all elements until ratio is reached
|
||||
if (this.ratio < this.limit) {
|
||||
let index = 0;
|
||||
let current = this.tail;
|
||||
while (current.next) {
|
||||
|
||||
// Remove the entry
|
||||
this.map.delete(current.key);
|
||||
|
||||
// if we reached the element that overflows our ratio condition
|
||||
// make its next element the new tail of the Map and adjust the size
|
||||
if (index === this.ratio) {
|
||||
this.tail = current.next;
|
||||
this.tail.prev = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move on
|
||||
current = current.next;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Just remove the tail element
|
||||
else {
|
||||
this.map.delete(this.tail.key);
|
||||
|
||||
// [x]-[B] = [B]
|
||||
this.tail = this.tail.next;
|
||||
if (this.tail) {
|
||||
this.tail.prev = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IKeyIterator {
|
||||
reset(key: string): this;
|
||||
next(): this;
|
||||
@@ -975,6 +789,10 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
|
||||
return super.get(key, Touch.AsNew);
|
||||
}
|
||||
|
||||
public peek(key: K): V | undefined {
|
||||
return super.get(key, Touch.None);
|
||||
}
|
||||
|
||||
public set(key: K, value: V): void {
|
||||
super.set(key, value, Touch.AsNew);
|
||||
this.checkTrim();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { BoundedMap } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
/**
|
||||
@@ -243,18 +243,18 @@ export function regExpContainsBackreference(regexpValue: string): boolean {
|
||||
*/
|
||||
export const canNormalize = typeof ((<any>'').normalize) === 'function';
|
||||
|
||||
const nfcCache = new BoundedMap<string>(10000); // bounded to 10000 elements
|
||||
const nfcCache = new LRUCache<string, string>(10000); // bounded to 10000 elements
|
||||
export function normalizeNFC(str: string): string {
|
||||
return normalize(str, 'NFC', nfcCache);
|
||||
}
|
||||
|
||||
const nfdCache = new BoundedMap<string>(10000); // bounded to 10000 elements
|
||||
const nfdCache = new LRUCache<string, string>(10000); // bounded to 10000 elements
|
||||
export function normalizeNFD(str: string): string {
|
||||
return normalize(str, 'NFD', nfdCache);
|
||||
}
|
||||
|
||||
const nonAsciiCharactersPattern = /[^\u0000-\u0080]/;
|
||||
function normalize(str: string, form: string, normalizedCache: BoundedMap<string>): string {
|
||||
function normalize(str: string, form: string, normalizedCache: LRUCache<string, string>): string {
|
||||
if (!canNormalize || !str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
import { BoundedMap, ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map';
|
||||
import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map';
|
||||
import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
@@ -130,7 +129,7 @@ suite('Map', () => {
|
||||
assert.ok(!map.has('1'));
|
||||
});
|
||||
|
||||
test('LinkedMap - LRUCache simple', () => {
|
||||
test('LinkedMap - LRU Cache simple', () => {
|
||||
const cache = new LRUCache<number, number>(5);
|
||||
|
||||
[1, 2, 3, 4, 5].forEach(value => cache.set(value, value));
|
||||
@@ -146,6 +145,21 @@ suite('Map', () => {
|
||||
assert.deepStrictEqual(values, [3, 4, 5, 6, 7]);
|
||||
});
|
||||
|
||||
test('LinkedMap - LRU Cache get', () => {
|
||||
const cache = new LRUCache<number, number>(5);
|
||||
|
||||
[1, 2, 3, 4, 5].forEach(value => cache.set(value, value));
|
||||
assert.strictEqual(cache.size, 5);
|
||||
assert.deepStrictEqual(cache.keys(), [1, 2, 3, 4, 5]);
|
||||
cache.get(3);
|
||||
assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]);
|
||||
cache.peek(4);
|
||||
assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]);
|
||||
let values: number[] = [];
|
||||
[1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)));
|
||||
assert.deepStrictEqual(values, [1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
test('LinkedMap - LRU Cache limit', () => {
|
||||
const cache = new LRUCache<number, number>(10);
|
||||
|
||||
@@ -170,7 +184,7 @@ suite('Map', () => {
|
||||
assert.deepStrictEqual(cache.values(), values);
|
||||
});
|
||||
|
||||
test('LinkedMap - LRU Cache limit with ration', () => {
|
||||
test('LinkedMap - LRU Cache limit with ratio', () => {
|
||||
const cache = new LRUCache<number, number>(10, 0.5);
|
||||
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
@@ -186,305 +200,6 @@ suite('Map', () => {
|
||||
assert.deepStrictEqual(cache.values(), values);
|
||||
});
|
||||
|
||||
test('BoundedMap - basics', function () {
|
||||
const map = new BoundedMap<any>();
|
||||
|
||||
assert.equal(map.size, 0);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', '2');
|
||||
map.set('3', true);
|
||||
|
||||
const obj = Object.create(null);
|
||||
map.set('4', obj);
|
||||
|
||||
const date = Date.now();
|
||||
map.set('5', date);
|
||||
|
||||
assert.equal(map.size, 5);
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), '2');
|
||||
assert.equal(map.get('3'), true);
|
||||
assert.equal(map.get('4'), obj);
|
||||
assert.equal(map.get('5'), date);
|
||||
assert.ok(!map.get('6'));
|
||||
|
||||
map.delete('6');
|
||||
assert.equal(map.size, 5);
|
||||
assert.equal(map.delete('1'), 1);
|
||||
assert.equal(map.delete('2'), '2');
|
||||
assert.equal(map.delete('3'), true);
|
||||
assert.equal(map.delete('4'), obj);
|
||||
assert.equal(map.delete('5'), date);
|
||||
|
||||
assert.equal(map.size, 0);
|
||||
assert.ok(!map.get('5'));
|
||||
assert.ok(!map.get('4'));
|
||||
assert.ok(!map.get('3'));
|
||||
assert.ok(!map.get('2'));
|
||||
assert.ok(!map.get('1'));
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', '2');
|
||||
assert.ok(map.set('3', true)); // adding an element returns true
|
||||
assert.ok(!map.set('3', true)); // adding it again returns false
|
||||
|
||||
assert.ok(map.has('1'));
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), '2');
|
||||
assert.equal(map.get('3'), true);
|
||||
|
||||
map.clear();
|
||||
|
||||
assert.equal(map.size, 0);
|
||||
assert.ok(!map.get('1'));
|
||||
assert.ok(!map.get('2'));
|
||||
assert.ok(!map.get('3'));
|
||||
assert.ok(!map.has('1'));
|
||||
|
||||
const res = map.getOrSet('foo', 'bar');
|
||||
assert.equal(map.get('foo'), res);
|
||||
assert.equal(res, 'bar');
|
||||
});
|
||||
|
||||
test('BoundedMap - serialization', function () {
|
||||
const map = new BoundedMap<any>(5);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', '2');
|
||||
map.set('3', true);
|
||||
|
||||
const obj = Object.create(null);
|
||||
map.set('4', obj);
|
||||
|
||||
const date = Date.now();
|
||||
map.set('5', date);
|
||||
|
||||
const mapClone = new BoundedMap<any>(5, 1, map.serialize());
|
||||
|
||||
assert.deepEqual(map.serialize(), mapClone.serialize());
|
||||
|
||||
assert.equal(mapClone.size, 5);
|
||||
assert.equal(mapClone.get('1'), 1);
|
||||
assert.equal(mapClone.get('2'), '2');
|
||||
assert.equal(mapClone.get('3'), true);
|
||||
assert.equal(mapClone.get('4'), obj);
|
||||
assert.equal(mapClone.get('5'), date);
|
||||
assert.ok(!mapClone.get('6'));
|
||||
|
||||
mapClone.set('6', '6');
|
||||
assert.equal(mapClone.size, 5);
|
||||
assert.ok(!mapClone.get('1'));
|
||||
});
|
||||
|
||||
test('BoundedMap - setLimit', function () {
|
||||
const map = new BoundedMap<any>(5);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', '2');
|
||||
map.set('3', true);
|
||||
|
||||
const obj = Object.create(null);
|
||||
map.set('4', obj);
|
||||
|
||||
const date = Date.now();
|
||||
map.set('5', date);
|
||||
|
||||
assert.equal(map.size, 5);
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), '2');
|
||||
assert.equal(map.get('3'), true);
|
||||
assert.equal(map.get('4'), obj);
|
||||
assert.equal(map.get('5'), date);
|
||||
assert.ok(!map.get('6'));
|
||||
|
||||
map.setLimit(3);
|
||||
|
||||
assert.equal(map.size, 3);
|
||||
assert.ok(!map.get('1'));
|
||||
assert.ok(!map.get('2'));
|
||||
assert.equal(map.get('3'), true);
|
||||
assert.equal(map.get('4'), obj);
|
||||
assert.equal(map.get('5'), date);
|
||||
|
||||
map.setLimit(0);
|
||||
|
||||
assert.equal(map.size, 0);
|
||||
assert.ok(!map.get('3'));
|
||||
assert.ok(!map.get('4'));
|
||||
assert.ok(!map.get('5'));
|
||||
|
||||
map.set('6', 6);
|
||||
|
||||
assert.equal(map.size, 0);
|
||||
assert.ok(!map.get('6'));
|
||||
|
||||
map.setLimit(100);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', '2');
|
||||
map.set('3', true);
|
||||
map.set('4', obj);
|
||||
map.set('5', date);
|
||||
|
||||
assert.equal(map.size, 5);
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), '2');
|
||||
assert.equal(map.get('3'), true);
|
||||
assert.equal(map.get('4'), obj);
|
||||
assert.equal(map.get('5'), date);
|
||||
});
|
||||
|
||||
test('BoundedMap - bounded', function () {
|
||||
const map = new BoundedMap<number>(5);
|
||||
|
||||
assert.equal(0, map.size);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', 2);
|
||||
map.set('3', 3);
|
||||
map.set('4', 4);
|
||||
map.set('5', 5);
|
||||
|
||||
assert.equal(5, map.size);
|
||||
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), 2);
|
||||
assert.equal(map.get('3'), 3);
|
||||
assert.equal(map.get('4'), 4);
|
||||
assert.equal(map.get('5'), 5);
|
||||
|
||||
map.set('6', 6);
|
||||
|
||||
assert.equal(5, map.size);
|
||||
assert.ok(!map.get('1'));
|
||||
assert.equal(map.get('2'), 2);
|
||||
assert.equal(map.get('3'), 3);
|
||||
assert.equal(map.get('4'), 4);
|
||||
assert.equal(map.get('5'), 5);
|
||||
assert.equal(map.get('6'), 6);
|
||||
|
||||
map.set('7', 7);
|
||||
map.set('8', 8);
|
||||
map.set('9', 9);
|
||||
|
||||
assert.equal(5, map.size);
|
||||
assert.ok(!map.get('1'));
|
||||
assert.ok(!map.get('2'));
|
||||
assert.ok(!map.get('3'));
|
||||
assert.ok(!map.get('4'));
|
||||
|
||||
assert.equal(map.get('5'), 5);
|
||||
assert.equal(map.get('6'), 6);
|
||||
assert.equal(map.get('7'), 7);
|
||||
assert.equal(map.get('8'), 8);
|
||||
assert.equal(map.get('9'), 9);
|
||||
|
||||
map.delete('5');
|
||||
map.delete('7');
|
||||
|
||||
assert.equal(3, map.size);
|
||||
assert.ok(!map.get('5'));
|
||||
assert.ok(!map.get('7'));
|
||||
assert.equal(map.get('6'), 6);
|
||||
assert.equal(map.get('8'), 8);
|
||||
assert.equal(map.get('9'), 9);
|
||||
|
||||
map.set('10', 10);
|
||||
map.set('11', 11);
|
||||
map.set('12', 12);
|
||||
map.set('13', 13);
|
||||
map.set('14', 14);
|
||||
|
||||
assert.equal(5, map.size);
|
||||
assert.equal(map.get('10'), 10);
|
||||
assert.equal(map.get('11'), 11);
|
||||
assert.equal(map.get('12'), 12);
|
||||
assert.equal(map.get('13'), 13);
|
||||
assert.equal(map.get('14'), 14);
|
||||
});
|
||||
|
||||
test('BoundedMap - bounded with ratio', function () {
|
||||
const map = new BoundedMap<number>(6, 0.5);
|
||||
|
||||
assert.equal(0, map.size);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', 2);
|
||||
map.set('3', 3);
|
||||
map.set('4', 4);
|
||||
map.set('5', 5);
|
||||
map.set('6', 6);
|
||||
|
||||
assert.equal(6, map.size);
|
||||
|
||||
map.set('7', 7);
|
||||
|
||||
assert.equal(3, map.size);
|
||||
assert.ok(!map.has('1'));
|
||||
assert.ok(!map.has('2'));
|
||||
assert.ok(!map.has('3'));
|
||||
assert.ok(!map.has('4'));
|
||||
assert.equal(map.get('5'), 5);
|
||||
assert.equal(map.get('6'), 6);
|
||||
assert.equal(map.get('7'), 7);
|
||||
|
||||
map.set('8', 8);
|
||||
map.set('9', 9);
|
||||
map.set('10', 10);
|
||||
|
||||
assert.equal(6, map.size);
|
||||
assert.equal(map.get('5'), 5);
|
||||
assert.equal(map.get('6'), 6);
|
||||
assert.equal(map.get('7'), 7);
|
||||
assert.equal(map.get('8'), 8);
|
||||
assert.equal(map.get('9'), 9);
|
||||
assert.equal(map.get('10'), 10);
|
||||
});
|
||||
|
||||
test('BoundedMap - MRU order', function () {
|
||||
const map = new BoundedMap<number>(3);
|
||||
|
||||
function peek(key) {
|
||||
const res = map.get(key);
|
||||
if (res) {
|
||||
map.delete(key);
|
||||
map.set(key, res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
assert.equal(0, map.size);
|
||||
|
||||
map.set('1', 1);
|
||||
map.set('2', 2);
|
||||
map.set('3', 3);
|
||||
|
||||
assert.equal(3, map.size);
|
||||
|
||||
assert.equal(map.get('1'), 1);
|
||||
assert.equal(map.get('2'), 2);
|
||||
assert.equal(map.get('3'), 3);
|
||||
|
||||
map.set('4', 4);
|
||||
|
||||
assert.equal(3, map.size);
|
||||
assert.equal(peek('4'), 4); // this changes MRU order
|
||||
assert.equal(peek('3'), 3);
|
||||
assert.equal(peek('2'), 2);
|
||||
|
||||
map.set('5', 5);
|
||||
map.set('6', 6);
|
||||
|
||||
assert.equal(3, map.size);
|
||||
assert.equal(peek('2'), 2);
|
||||
assert.equal(peek('5'), 5);
|
||||
assert.equal(peek('6'), 6);
|
||||
assert.ok(!map.has('3'));
|
||||
assert.ok(!map.has('4'));
|
||||
});
|
||||
|
||||
test('PathIterator', function () {
|
||||
const iter = new PathIterator();
|
||||
iter.reset('file:///usr/bin/file.txt');
|
||||
|
||||
@@ -29,7 +29,7 @@ import { registerEditorAction, EditorAction } from 'vs/editor/browser/editorExte
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { BoundedMap, ISerializedBoundedLinkedMap } from 'vs/base/common/map';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -39,9 +39,14 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
export const ALL_COMMANDS_PREFIX = '>';
|
||||
|
||||
let lastCommandPaletteInput: string;
|
||||
let commandHistory: BoundedMap<number>;
|
||||
let commandHistory: LRUCache<string, number>;
|
||||
let commandCounter = 1;
|
||||
|
||||
interface ISerializedCommandHistory {
|
||||
usesLRU?: boolean;
|
||||
entries: { key: string; value: number }[];
|
||||
}
|
||||
|
||||
function resolveCommandHistory(configurationService: IConfigurationService): number {
|
||||
const config = <IWorkbenchQuickOpenConfiguration>configurationService.getValue();
|
||||
|
||||
@@ -77,22 +82,31 @@ class CommandsHistory {
|
||||
this.commandHistoryLength = resolveCommandHistory(this.configurationService);
|
||||
|
||||
if (commandHistory) {
|
||||
commandHistory.setLimit(this.commandHistoryLength);
|
||||
commandHistory.limit = this.commandHistoryLength;
|
||||
}
|
||||
}
|
||||
|
||||
private load(): void {
|
||||
const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE);
|
||||
let deserializedCache: ISerializedBoundedLinkedMap<number>;
|
||||
let serializedCache: ISerializedCommandHistory;
|
||||
if (raw) {
|
||||
try {
|
||||
deserializedCache = JSON.parse(raw);
|
||||
serializedCache = JSON.parse(raw);
|
||||
} catch (error) {
|
||||
// invalid data
|
||||
}
|
||||
}
|
||||
|
||||
commandHistory = new BoundedMap<number>(this.commandHistoryLength, 1, deserializedCache);
|
||||
commandHistory = new LRUCache<string, number>(this.commandHistoryLength, 1);
|
||||
if (serializedCache) {
|
||||
let entries: { key: string; value: number }[];
|
||||
if (serializedCache.usesLRU) {
|
||||
entries = serializedCache.entries;
|
||||
} else {
|
||||
entries = serializedCache.entries.sort((a, b) => a.value - b.value);
|
||||
}
|
||||
entries.forEach(entry => commandHistory.set(entry.key, entry.value));
|
||||
}
|
||||
commandCounter = this.storageService.getInteger(CommandsHistory.PREF_KEY_COUNTER, void 0, commandCounter);
|
||||
}
|
||||
|
||||
@@ -102,21 +116,19 @@ class CommandsHistory {
|
||||
}
|
||||
|
||||
private save(): void {
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(commandHistory.serialize()));
|
||||
let serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] };
|
||||
commandHistory.forEach((value, key) => serializedCache.entries.push({ key, value }));
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache));
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_COUNTER, commandCounter);
|
||||
}
|
||||
|
||||
public push(commandId: string): void {
|
||||
|
||||
// make MRU by deleting it first
|
||||
commandHistory.delete(commandId);
|
||||
|
||||
// set counter to command
|
||||
commandHistory.set(commandId, commandCounter++);
|
||||
}
|
||||
|
||||
public get(commandId: string): number {
|
||||
return commandHistory.get(commandId);
|
||||
public peek(commandId: string): number {
|
||||
return commandHistory.peek(commandId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +178,7 @@ export class ClearCommandHistoryAction extends Action {
|
||||
public run(context?: any): TPromise<void> {
|
||||
const commandHistoryLength = resolveCommandHistory(this.configurationService);
|
||||
if (commandHistoryLength > 0) {
|
||||
commandHistory = new BoundedMap<number>(commandHistoryLength);
|
||||
commandHistory = new LRUCache<string, number>(commandHistoryLength);
|
||||
commandCounter = 1;
|
||||
}
|
||||
|
||||
@@ -435,8 +447,8 @@ export class CommandsHandler extends QuickOpenHandler {
|
||||
|
||||
// Sort by MRU order and fallback to name otherwie
|
||||
entries = entries.sort((elementA, elementB) => {
|
||||
const counterA = this.commandsHistory.get(elementA.getCommandId());
|
||||
const counterB = this.commandsHistory.get(elementB.getCommandId());
|
||||
const counterA = this.commandsHistory.peek(elementA.getCommandId());
|
||||
const counterB = this.commandsHistory.peek(elementB.getCommandId());
|
||||
|
||||
if (counterA && counterB) {
|
||||
return counterA > counterB ? -1 : 1; // use more recently used command before older
|
||||
@@ -457,11 +469,11 @@ export class CommandsHandler extends QuickOpenHandler {
|
||||
// Introduce group marker border between recently used and others
|
||||
// only if we have recently used commands in the result set
|
||||
const firstEntry = entries[0];
|
||||
if (firstEntry && this.commandsHistory.get(firstEntry.getCommandId())) {
|
||||
if (firstEntry && this.commandsHistory.peek(firstEntry.getCommandId())) {
|
||||
firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used"));
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
if (!this.commandsHistory.get(entry.getCommandId())) {
|
||||
if (!this.commandsHistory.peek(entry.getCommandId())) {
|
||||
entry.setShowBorder(true);
|
||||
entry.setGroupLabel(nls.localize('morecCommands', "other commands"));
|
||||
break;
|
||||
@@ -546,7 +558,7 @@ export class CommandsHandler extends QuickOpenHandler {
|
||||
|
||||
if (autoFocusPrefixMatch && this.commandHistoryEnabled) {
|
||||
const firstEntry = context.model && context.model.entries[0];
|
||||
if (firstEntry instanceof BaseCommandEntry && this.commandsHistory.get(firstEntry.getCommandId())) {
|
||||
if (firstEntry instanceof BaseCommandEntry && this.commandsHistory.peek(firstEntry.getCommandId())) {
|
||||
autoFocusPrefixMatch = void 0; // keep focus on MRU element if we have history elements
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user