提交 5bacd045 编写于 作者: J Johannes Rieken

debt - move TrieMap to map.ts, add tests

上级 ecdfddab
...@@ -296,3 +296,75 @@ export class LRUCache<T> extends BoundedLinkedMap<T> { ...@@ -296,3 +296,75 @@ export class LRUCache<T> extends BoundedLinkedMap<T> {
return null; return null;
} }
} }
// --- trie'ish datastructure
interface Node<E> {
element?: E;
children: { [key: string]: Node<E> };
}
/**
* A trie map that allows for fast look up when keys are substrings
* to the actual search keys (dir/subdir-problem).
*/
export class TrieMap<E> {
static PathSplitter = s => s.split(/[\\/]/);
private _splitter: (s: string) => string[];
private _root: Node<E> = { children: Object.create(null) };
constructor(splitter: (s: string) => string[]) {
this._splitter = splitter;
}
insert(path: string, element: E): void {
const parts = this._splitter(path);
let i = 0;
// find insertion node
let node = this._root;
for (; i < parts.length; i++) {
let child = node.children[parts[i]];
if (child) {
node = child;
continue;
}
break;
}
// create new nodes
let newNode: Node<E>;
for (; i < parts.length; i++) {
newNode = { children: Object.create(null) };
node.children[parts[i]] = newNode;
node = newNode;
}
node.element = element;
}
findSubstr(path: string): E {
const parts = this._splitter(path);
let lastNode: Node<E>;
let {children} = this._root;
for (const part of parts) {
const node = children[part];
if (!node) {
break;
}
if (node.element) {
lastNode = node;
}
children = node.children;
}
// return the last matching node
// that had an element
if (lastNode) {
return lastNode.element;
}
}
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
'use strict'; 'use strict';
import { BoundedLinkedMap, LRUCache, LinkedMap } from 'vs/base/common/map'; import { BoundedLinkedMap, LRUCache, LinkedMap, TrieMap } from 'vs/base/common/map';
import * as assert from 'assert'; import * as assert from 'assert';
suite('Map', () => { suite('Map', () => {
...@@ -271,4 +271,23 @@ suite('Map', () => { ...@@ -271,4 +271,23 @@ suite('Map', () => {
assert.ok(!cache.has('3')); assert.ok(!cache.has('3'));
assert.ok(!cache.has('4')); assert.ok(!cache.has('4'));
}); });
test('TrieMap - basics', function () {
const map = new TrieMap<number>(TrieMap.PathSplitter);
map.insert('/user/foo/bar', 1);
map.insert('/user/foo', 2);
map.insert('/user/foo/flip/flop', 3);
assert.equal(map.findSubstr('/user/bar'), undefined);
assert.equal(map.findSubstr('/user/foo'), 2);
assert.equal(map.findSubstr('\\user\\foo'), 2);
assert.equal(map.findSubstr('/user/foo/ba'), 2);
assert.equal(map.findSubstr('/user/foo/far/boo'), 2);
assert.equal(map.findSubstr('/user/foo/bar'), 1);
assert.equal(map.findSubstr('/user/foo/bar/far/boo'), 1);
});
}); });
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
'use strict'; 'use strict';
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { TrieMap } from 'vs/base/common/map';
import { score } from 'vs/editor/common/modes/languageSelector'; import { score } from 'vs/editor/common/modes/languageSelector';
import * as Platform from 'vs/base/common/platform'; import * as Platform from 'vs/base/common/platform';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
...@@ -425,7 +426,7 @@ export function defineAPI(factory: IExtensionApiFactory, extensions: IExtensionD ...@@ -425,7 +426,7 @@ export function defineAPI(factory: IExtensionApiFactory, extensions: IExtensionD
let defaultApiImpl: typeof vscode; let defaultApiImpl: typeof vscode;
// create trie to enable fast 'filename -> extension id' look up // create trie to enable fast 'filename -> extension id' look up
const trie = new TrieMap<IExtensionDescription>(); const trie = new TrieMap<IExtensionDescription>(TrieMap.PathSplitter);
for (const ext of extensions) { for (const ext of extensions) {
if (ext.name) { if (ext.name) {
const path = realpathSync(ext.extensionFolderPath); const path = realpathSync(ext.extensionFolderPath);
...@@ -457,68 +458,3 @@ export function defineAPI(factory: IExtensionApiFactory, extensions: IExtensionD ...@@ -457,68 +458,3 @@ export function defineAPI(factory: IExtensionApiFactory, extensions: IExtensionD
return defaultApiImpl; return defaultApiImpl;
}; };
} }
// --- trie'ish datastructure
interface Node<E> {
element?: E;
children: { [key: string]: Node<E> };
}
class TrieMap<E> {
private static _sep = /[\\/]/;
private _root: Node<E> = {
children: Object.create(null)
};
insert(path: string, element: E): void {
const parts = path.split(TrieMap._sep);
let i = 0;
// find insertion node
let node = this._root;
for (; i < parts.length; i++) {
let child = node.children[parts[i]];
if (child) {
node = child;
continue;
}
break;
}
// create new nodes
let newNode: Node<E>;
for (; i < parts.length; i++) {
newNode = { children: Object.create(null) };
node.children[parts[i]] = newNode;
node = newNode;
}
newNode.element = element;
}
findSubstr(path: string): E {
const parts = path.split(TrieMap._sep);
let lastNode: Node<E>;
let {children} = this._root;
for (const part of parts) {
const node = children[part];
if (!node) {
break;
}
if (node.element) {
lastNode = node;
}
children = node.children;
}
// return the last matching node
// that had an element
if (lastNode) {
return lastNode.element;
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册