diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 19f4ce01c1d01b696ba92b03df5f6842be5e15d4..57eee9c0f10090ae6471ce6fa697611093fb89e8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -5,6 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; +import { compareIgnoreCase } from 'vs/base/common/strings'; /** * @deprecated ES6: use `[...SetOrMap.values()]` @@ -55,8 +56,8 @@ export function setToString(set: Set): string { return `Set(${set.size}) {${entries.join(', ')}}`; } -export interface IKeyIterator { - reset(key: string): this; +export interface IKeyIterator { + reset(key: K): this; next(): this; hasNext(): boolean; @@ -64,7 +65,7 @@ export interface IKeyIterator { value(): string; } -export class StringIterator implements IKeyIterator { +export class StringIterator implements IKeyIterator { private _value: string = ''; private _pos: number = 0; @@ -95,7 +96,7 @@ export class StringIterator implements IKeyIterator { } } -export class PathIterator implements IKeyIterator { +export class PathIterator implements IKeyIterator { private _value!: string; private _from!: number; @@ -162,33 +163,116 @@ export class PathIterator implements IKeyIterator { } } -class TernarySearchTreeNode { +const enum UriIteratorState { + Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5 +} + +export class UriIterator implements IKeyIterator { + + private _pathIterator = new PathIterator(false); + private _value!: URI; + private _states: UriIteratorState[] = []; + private _stateIdx: number = 0; + + reset(key: URI): this { + this._value = key; + this._states = []; + if (this._value.scheme) { + this._states.push(UriIteratorState.Scheme); + } + if (this._value.authority) { + this._states.push(UriIteratorState.Authority); + } + if (this._value.path) { + this._states.push(UriIteratorState.Path); + this._pathIterator.reset(key.path); + } + if (this._value.query) { + this._states.push(UriIteratorState.Query); + } + if (this._value.fragment) { + this._states.push(UriIteratorState.Fragment); + } + this._stateIdx = 0; + return this; + } + + next(): this { + if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) { + this._pathIterator.next(); + } else { + this._stateIdx += 1; + } + return this; + } + + hasNext(): boolean { + return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) + || this._stateIdx < this._states.length - 1; + } + + cmp(a: string): number { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return compareIgnoreCase(a, this._value.scheme); + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return compareIgnoreCase(a, this._value.authority); + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.cmp(a); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return compareIgnoreCase(a, this._value.scheme); + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return compareIgnoreCase(a, this._value.fragment); + } + throw new Error(); + } + + value(): string { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return this._value.scheme; + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return this._value.authority; + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.value(); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return this._value.query; + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return this._value.fragment; + } + throw new Error(); + } +} + +class TernarySearchTreeNode { segment!: string; - value: E | undefined; - key!: string; - left: TernarySearchTreeNode | undefined; - mid: TernarySearchTreeNode | undefined; - right: TernarySearchTreeNode | undefined; + value: V | undefined; + key!: K; + left: TernarySearchTreeNode | undefined; + mid: TernarySearchTreeNode | undefined; + right: TernarySearchTreeNode | undefined; isEmpty(): boolean { return !this.left && !this.mid && !this.right && !this.value; } } -export class TernarySearchTree { +export class TernarySearchTree { + + static forUris(): TernarySearchTree { + return new TernarySearchTree(new UriIterator()); + } - static forPaths(): TernarySearchTree { - return new TernarySearchTree(new PathIterator()); + static forPaths(): TernarySearchTree { + return new TernarySearchTree(new PathIterator()); } - static forStrings(): TernarySearchTree { - return new TernarySearchTree(new StringIterator()); + static forStrings(): TernarySearchTree { + return new TernarySearchTree(new StringIterator()); } - private _iter: IKeyIterator; - private _root: TernarySearchTreeNode | undefined; + private _iter: IKeyIterator; + private _root: TernarySearchTreeNode | undefined; - constructor(segments: IKeyIterator) { + constructor(segments: IKeyIterator) { this._iter = segments; } @@ -196,12 +280,12 @@ export class TernarySearchTree { this._root = undefined; } - set(key: string, element: E): E | undefined { + set(key: K, element: V): V | undefined { const iter = this._iter.reset(key); - let node: TernarySearchTreeNode; + let node: TernarySearchTreeNode; if (!this._root) { - this._root = new TernarySearchTreeNode(); + this._root = new TernarySearchTreeNode(); this._root.segment = iter.value(); } @@ -211,7 +295,7 @@ export class TernarySearchTree { if (val > 0) { // left if (!node.left) { - node.left = new TernarySearchTreeNode(); + node.left = new TernarySearchTreeNode(); node.left.segment = iter.value(); } node = node.left; @@ -219,7 +303,7 @@ export class TernarySearchTree { } else if (val < 0) { // right if (!node.right) { - node.right = new TernarySearchTreeNode(); + node.right = new TernarySearchTreeNode(); node.right.segment = iter.value(); } node = node.right; @@ -228,7 +312,7 @@ export class TernarySearchTree { // mid iter.next(); if (!node.mid) { - node.mid = new TernarySearchTreeNode(); + node.mid = new TernarySearchTreeNode(); node.mid.segment = iter.value(); } node = node.mid; @@ -242,7 +326,7 @@ export class TernarySearchTree { return oldElement; } - get(key: string): E | undefined { + get(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -264,10 +348,10 @@ export class TernarySearchTree { return node ? node.value : undefined; } - delete(key: string): void { + delete(key: K): void { const iter = this._iter.reset(key); - const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; + const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; let node = this._root; // find and unset node @@ -305,10 +389,10 @@ export class TernarySearchTree { } } - findSubstr(key: string): E | undefined { + findSubstr(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; - let candidate: E | undefined = undefined; + let candidate: V | undefined = undefined; while (node) { const val = iter.cmp(node.segment); if (val > 0) { @@ -329,7 +413,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: string): Iterator | undefined { + findSuperstr(key: K): Iterator | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -356,11 +440,11 @@ export class TernarySearchTree { return undefined; } - private _nodeIterator(node: TernarySearchTreeNode): Iterator { - let res: { done: false; value: E; }; + private _nodeIterator(node: TernarySearchTreeNode): Iterator { + let res: { done: false; value: V; }; let idx: number; - let data: E[]; - const next = (): IteratorResult => { + let data: V[]; + const next = (): IteratorResult => { if (!data) { // lazy till first invocation data = []; @@ -381,11 +465,11 @@ export class TernarySearchTree { return { next }; } - forEach(callback: (value: E, index: string) => any) { + forEach(callback: (value: V, index: K) => any) { this._forEach(this._root, callback); } - private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: E, index: string) => any) { + private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: V, index: K) => any) { if (node) { // left this._forEach(node.left, callback); diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index acd7ae00bfce0b932b0b25f99d9653daa12b1954..abee7da4ee0a5db5c0f376a47647878fa10eab5c 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -336,7 +336,7 @@ export namespace DataUri { export class ResourceGlobMatcher { private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); + private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); constructor( globalExpression: IExpression, diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index c2faa20632fefd8bb9207b662b5442a18bcee3a1..2799ffc718da5a192675dde4cd08fbd7f6eb449f 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -21,7 +21,7 @@ import { getMac } from 'vs/base/node/macAddress'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs?: TernarySearchTree; + private _virtualMachineOUIs?: TernarySearchTree; private _value?: number; private _isVirtualMachineMacAdress(mac: string): boolean { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 4ce9fbd7d1e461c7cd48136626b59bdfd618ff44..5a5c35dbd86ca229586a136d2b1ee6963a4533fe 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; @@ -312,7 +312,64 @@ suite('Map', () => { assert.equal(iter.hasNext(), false); }); - function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { + test('URIIterator', function () { + const iter = new UriIterator(); + iter.reset(URI.parse('file:///usr/bin/file.txt')); + + assert.equal(iter.value(), 'file'); + assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), false); + + + iter.reset(URI.parse('file://share/usr/bin/file.txt?foo')); + + // scheme + assert.equal(iter.value(), 'file'); + assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // authority + assert.equal(iter.value(), 'share'); + assert.equal(iter.cmp('SHARe'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // query + assert.equal(iter.value(), 'foo'); + assert.equal(iter.cmp('z') > 0, true); + assert.equal(iter.cmp('a') < 0, true); + assert.equal(iter.hasNext(), false); + }); + + function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { const map = new Map(); for (const [key, value] of elements) { map.set(key, value); @@ -378,7 +435,7 @@ suite('Map', () => { }); test('TernarySearchTree - basics', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('bar', 2); @@ -408,7 +465,7 @@ suite('Map', () => { }); test('TernarySearchTree - delete & cleanup', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); @@ -418,7 +475,7 @@ suite('Map', () => { }); test('TernarySearchTree (PathSegments) - basics', function () { - let trie = new TernarySearchTree(new PathIterator()); + let trie = new TernarySearchTree(new PathIterator()); trie.set('/user/foo/bar', 1); trie.set('/user/foo', 2); @@ -442,7 +499,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - lookup', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); @@ -456,7 +513,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - superstr', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index 01b942cf531cdd6c2f5f6814f096aeb7e409e345..c8e4cfd5ddef801d5bac7de15f0f5a87811522ad 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -18,7 +18,7 @@ export class TestConfigurationService implements IConfigurationService { this.configuration = configuration || Object.create(null); } - private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); + private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); public reloadConfiguration(): Promise { return Promise.resolve(this.getValue()); diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index 5ab93b98fd43a005af00b7ced80c57f9dc171a66..748fb25f2b963849c67e897d96c583f728f804ce 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -182,7 +182,7 @@ export class FileService extends Disposable implements IFileService { const stat = await provider.stat(resource); - let trie: TernarySearchTree | undefined; + let trie: TernarySearchTree | undefined; return this.toFileStat(provider, resource, stat, undefined, !!resolveMetadata, (stat, siblings) => { diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index b10c023d005b946e9273504722f8ce8eca616af9..e26297f34451c51d4c3a4c2b03827c4c14de48f1 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -144,7 +144,7 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData { export class Workspace implements IWorkspace { - private _foldersMap: TernarySearchTree = TernarySearchTree.forPaths(); + private _foldersMap: TernarySearchTree = TernarySearchTree.forPaths(); private _folders!: WorkspaceFolder[]; constructor( diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 904c5afd8c8e6d54d336c8e3b9873c792a167b4b..741a5b73800642d5bd66da1007566a69c7e3c0e5 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -90,7 +90,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio private readonly _storage: ExtHostStorage; private readonly _storagePath: IExtensionStoragePaths; private readonly _activator: ExtensionsActivator; - private _extensionPathIndex: Promise> | null; + private _extensionPathIndex: Promise> | null; private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; }; @@ -236,7 +236,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): Promise> { + public getExtensionPathIndex(): Promise> { if (!this._extensionPathIndex) { const tree = TernarySearchTree.forPaths(); const extensions = this._registry.getAllExtensionDescriptions().map(ext => { @@ -792,6 +792,6 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic deactivateAll(): Promise; getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined; getExtensionRegistry(): Promise; - getExtensionPathIndex(): Promise>; + getExtensionPathIndex(): Promise>; registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable; } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index c06e86c83051c9a92fe0ba7cb34fc0be71fbe3fa..97839338566cff584a4e2c08814cef658d43f464 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -91,7 +91,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IExtensionApiFactory, - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: TernarySearchTree, private readonly _extensionRegistry: ExtensionDescriptionRegistry, private readonly _configProvider: ExtHostConfigProvider, private readonly _logService: ILogService, @@ -230,7 +230,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { private _mainThreadTelemetry: MainThreadTelemetryShape; constructor( - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: TernarySearchTree, private readonly _appUriScheme: string, @IExtHostRpcService rpcService: IExtHostRpcService, ) { diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index ca24d175027d403a71aff3a898927ff47858a171..4a7837976ca21b595365756cd34ced1e39da5f2a 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -696,7 +696,7 @@ export class SearchResult extends Disposable { private _folderMatches: FolderMatchWithResource[] = []; private _otherFilesMatch: FolderMatch | null = null; - private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); + private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); private _showHighlights: boolean = false; private _query: ITextQuery | null = null;