mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -220,110 +220,315 @@ export class BoundedMap<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// --- trie'ish datastructure
|
||||
export interface IKeyIterator {
|
||||
reset(key: string): this;
|
||||
next(): this;
|
||||
join(parts: string[]): string;
|
||||
|
||||
class Node<E> {
|
||||
element?: E;
|
||||
readonly children = new Map<string, Node<E>>();
|
||||
hasNext(): boolean;
|
||||
cmp(a: string): number;
|
||||
value(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
export class StringIterator implements IKeyIterator {
|
||||
|
||||
static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s);
|
||||
private _value: string = '';
|
||||
private _pos: number = 0;
|
||||
|
||||
private readonly _splitter: (s: string) => string[];
|
||||
private _root = new Node<E>();
|
||||
|
||||
constructor(splitter: (s: string) => string[] = TrieMap.PathSplitter) {
|
||||
this._splitter = s => splitter(s).filter(s => Boolean(s));
|
||||
reset(key: string): this {
|
||||
this._value = key;
|
||||
this._pos = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
insert(path: string, element: E): void {
|
||||
const parts = this._splitter(path);
|
||||
let i = 0;
|
||||
next(): this {
|
||||
this._pos += 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// find insertion node
|
||||
let node = this._root;
|
||||
for (; i < parts.length; i++) {
|
||||
let child = node.children.get(parts[i]);
|
||||
if (child) {
|
||||
node = child;
|
||||
continue;
|
||||
join(parts: string[]): string {
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
hasNext(): boolean {
|
||||
return this._pos < this._value.length - 1;
|
||||
}
|
||||
|
||||
cmp(a: string): number {
|
||||
let aCode = a.charCodeAt(0);
|
||||
let thisCode = this._value.charCodeAt(this._pos);
|
||||
return aCode - thisCode;
|
||||
}
|
||||
|
||||
value(): string {
|
||||
return this._value[this._pos];
|
||||
}
|
||||
}
|
||||
|
||||
export class PathIterator implements IKeyIterator {
|
||||
|
||||
private static _fwd = '/'.charCodeAt(0);
|
||||
private static _bwd = '\\'.charCodeAt(0);
|
||||
|
||||
private _value: string;
|
||||
private _from: number;
|
||||
private _to: number;
|
||||
|
||||
reset(key: string): this {
|
||||
this._value = key.replace(/\\$|\/$/, '');
|
||||
this._from = 0;
|
||||
this._to = 0;
|
||||
return this.next();
|
||||
}
|
||||
|
||||
hasNext(): boolean {
|
||||
return this._to < this._value.length;
|
||||
}
|
||||
|
||||
join(parts: string[]): string {
|
||||
return parts.join('/');
|
||||
}
|
||||
|
||||
next(): this {
|
||||
// this._data = key.split(/[\\/]/).filter(s => !!s);
|
||||
this._from = this._to;
|
||||
let justSeps = true;
|
||||
for (; this._to < this._value.length; this._to++) {
|
||||
const ch = this._value.charCodeAt(this._to);
|
||||
if (ch === PathIterator._fwd || ch === PathIterator._bwd) {
|
||||
if (justSeps) {
|
||||
this._from++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
justSeps = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
cmp(a: string): number {
|
||||
|
||||
let aPos = 0;
|
||||
let aLen = a.length;
|
||||
let thisPos = this._from;
|
||||
|
||||
while (aPos < aLen && thisPos < this._to) {
|
||||
let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
|
||||
if (cmp !== 0) {
|
||||
return cmp;
|
||||
}
|
||||
aPos += 1;
|
||||
thisPos += 1;
|
||||
}
|
||||
|
||||
// create new nodes
|
||||
let newNode: Node<E>;
|
||||
for (; i < parts.length; i++) {
|
||||
newNode = new Node<E>();
|
||||
node.children.set(parts[i], newNode);
|
||||
node = newNode;
|
||||
if (aLen === this._to - this._from) {
|
||||
return 0;
|
||||
} else if (aPos < aLen) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
value(): string {
|
||||
return this._value.substring(this._from, this._to);
|
||||
}
|
||||
}
|
||||
|
||||
class TernarySearchTreeNode<E> {
|
||||
str: string;
|
||||
element: E;
|
||||
left: TernarySearchTreeNode<E>;
|
||||
mid: TernarySearchTreeNode<E>;
|
||||
right: TernarySearchTreeNode<E>;
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this.left && !this.mid && !this.right && !this.element;
|
||||
}
|
||||
}
|
||||
|
||||
export class TernarySearchTree<E> {
|
||||
|
||||
static forPaths<E>(): TernarySearchTree<E> {
|
||||
return new TernarySearchTree<E>(new PathIterator());
|
||||
}
|
||||
|
||||
static forStrings<E>(): TernarySearchTree<E> {
|
||||
return new TernarySearchTree<E>(new StringIterator());
|
||||
}
|
||||
|
||||
private _iter: IKeyIterator;
|
||||
private _root: TernarySearchTreeNode<E>;
|
||||
|
||||
constructor(segments: IKeyIterator) {
|
||||
this._iter = segments;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._root = undefined;
|
||||
}
|
||||
|
||||
set(key: string, element: E): void {
|
||||
let iter = this._iter.reset(key);
|
||||
let node: TernarySearchTreeNode<E>;
|
||||
|
||||
if (!this._root) {
|
||||
this._root = new TernarySearchTreeNode<E>();
|
||||
this._root.str = iter.value();
|
||||
}
|
||||
|
||||
node = this._root;
|
||||
while (true) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
if (!node.left) {
|
||||
node.left = new TernarySearchTreeNode<E>();
|
||||
node.left.str = iter.value();
|
||||
}
|
||||
node = node.left;
|
||||
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
if (!node.right) {
|
||||
node.right = new TernarySearchTreeNode<E>();
|
||||
node.right.str = iter.value();
|
||||
}
|
||||
node = node.right;
|
||||
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
if (!node.mid) {
|
||||
node.mid = new TernarySearchTreeNode<E>();
|
||||
node.mid.str = iter.value();
|
||||
}
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node.element = element;
|
||||
}
|
||||
|
||||
lookUp(path: string): E {
|
||||
const parts = this._splitter(path);
|
||||
|
||||
let { children } = this._root;
|
||||
let node: Node<E>;
|
||||
for (const part of parts) {
|
||||
node = children.get(part);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
children = node.children;
|
||||
}
|
||||
|
||||
return node.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.get(part);
|
||||
if (!node) {
|
||||
get(key: string): E {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (node.element) {
|
||||
lastNode = node;
|
||||
}
|
||||
children = node.children;
|
||||
}
|
||||
return node ? node.element : undefined;
|
||||
}
|
||||
|
||||
delete(key: string): void {
|
||||
this._delete(this._root, this._iter.reset(key));
|
||||
}
|
||||
|
||||
private _delete(node: TernarySearchTreeNode<E>, iter: IKeyIterator): TernarySearchTreeNode<E> {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
const cmp = iter.cmp(node.str);
|
||||
if (cmp > 0) {
|
||||
// left
|
||||
node.left = this._delete(node.left, iter);
|
||||
} else if (cmp < 0) {
|
||||
// right
|
||||
node.right = this._delete(node.right, iter);
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
node.mid = this._delete(node.mid, iter.next());
|
||||
} else {
|
||||
// remove element
|
||||
node.element = undefined;
|
||||
}
|
||||
|
||||
// return the last matching node
|
||||
// that had an element
|
||||
if (lastNode) {
|
||||
return lastNode.element;
|
||||
return node.isEmpty() ? undefined : node;
|
||||
}
|
||||
|
||||
findSubstr(key: string): E {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
let candidate: E;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
candidate = node.element || candidate;
|
||||
node = node.mid;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return node && node.element || candidate;
|
||||
}
|
||||
|
||||
findSuperstr(key: string): TernarySearchTree<E> {
|
||||
let iter = this._iter.reset(key);
|
||||
let node = this._root;
|
||||
while (node) {
|
||||
let val = iter.cmp(node.str);
|
||||
if (val > 0) {
|
||||
// left
|
||||
node = node.left;
|
||||
} else if (val < 0) {
|
||||
// right
|
||||
node = node.right;
|
||||
} else if (iter.hasNext()) {
|
||||
// mid
|
||||
iter.next();
|
||||
node = node.mid;
|
||||
} else {
|
||||
// collect
|
||||
if (!node.mid) {
|
||||
return undefined;
|
||||
}
|
||||
let ret = new TernarySearchTree<E>(this._iter);
|
||||
ret._root = node.mid;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
findSuperstr(path: string): TrieMap<E> {
|
||||
const parts = this._splitter(path);
|
||||
forEach(callback: (value: E, index: string) => any) {
|
||||
this._forEach(this._root, [], callback);
|
||||
}
|
||||
|
||||
let { children } = this._root;
|
||||
let node: Node<E>;
|
||||
for (const part of parts) {
|
||||
node = children.get(part);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
children = node.children;
|
||||
private _forEach(node: TernarySearchTreeNode<E>, parts: string[], callback: (value: E, index: string) => any) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = new TrieMap<E>(this._splitter);
|
||||
result._root = node;
|
||||
return result;
|
||||
this._forEach(node.left, parts, callback);
|
||||
this._forEach(node.right, parts, callback);
|
||||
let newParts = parts.slice();
|
||||
newParts.push(node.str);
|
||||
if (node.element) {
|
||||
callback(node.element, this._iter.join(newParts));
|
||||
}
|
||||
this._forEach(node.mid, newParts, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user