From 80859dd7deb6a456e25f69bd290e07b3ea959db2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 13 Oct 2017 15:18:32 +0200 Subject: [PATCH] IntervalNode implements IModelDecoration --- src/vs/editor/common/model/intervalTree.ts | 100 +++++++++++++++--- .../test/common/model/intervalTree.test.ts | 13 ++- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 7160e8b546c..d89868bc61a 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -4,10 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDecoration } from 'vs/editor/common/editorCommon'; + // // The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. // +// TODO@interval!!! +export const ClassName = { + EditorInfoDecoration: 'infosquiggly', + EditorWarningDecoration: 'warningsquiggly', + EditorErrorDecoration: 'errorsquiggly' +}; + export class Interval { _intervalBrand: void; @@ -25,11 +36,11 @@ export const enum NodeColor { Black } -export class IntervalNode { +export class IntervalNode implements IModelDecoration { + public parent: IntervalNode; public left: IntervalNode; public right: IntervalNode; - public parent: IntervalNode; public color: NodeColor; public start: number; @@ -37,9 +48,17 @@ export class IntervalNode { public delta: number; public maxEnd: number; - public absoluteInterval: Interval; + public id: string; + public ownerId: number; + public options: ModelDecorationOptions; + public isForValidation: boolean; - constructor(start: number, end: number) { + public cachedVersionId: number; + public cachedAbsoluteStart: number; + public cachedAbsoluteEnd: number; + public range: Range; + + constructor(id: string, start: number, end: number) { this.parent = null; this.left = null; this.right = null; @@ -50,7 +69,34 @@ export class IntervalNode { this.delta = start; this.maxEnd = end; - this.absoluteInterval = new Interval(0, 0); + this.id = id; + this.ownerId = 0; + this.options = null; + this.isForValidation = false; + + this.cachedVersionId = 0; + this.cachedAbsoluteStart = start; + this.cachedAbsoluteEnd = end; + this.range = null; + } + + public setOptions(options: ModelDecorationOptions) { + this.options = options; + this.isForValidation = ( + this.options.className === ClassName.EditorErrorDecoration + || this.options.className === ClassName.EditorWarningDecoration + ); + } + + public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void { + this.cachedVersionId = cachedVersionId; + if (this.cachedAbsoluteStart === absoluteStart && this.cachedAbsoluteEnd === absoluteEnd) { + // no change + return; + } + this.cachedAbsoluteStart = absoluteStart; + this.cachedAbsoluteEnd = absoluteEnd; + this.range = null; } public detach(): void { @@ -60,7 +106,7 @@ export class IntervalNode { } } -const SENTINEL: IntervalNode = new IntervalNode(0, 0); +const SENTINEL: IntervalNode = new IntervalNode(null, 0, 0); SENTINEL.parent = SENTINEL; SENTINEL.left = SENTINEL; SENTINEL.right = SENTINEL; @@ -74,12 +120,12 @@ export class IntervalTree { this.root = SENTINEL; } - public intervalSearch(interval: Interval): IntervalNode[] { + public intervalSearch(interval: Interval, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { if (this.root === SENTINEL) { return []; } let result: IntervalNode[] = []; - intervalSearchRecursive(this.root, 0, interval.start, interval.end, result); + intervalSearchRecursive(this.root, 0, interval.start, interval.end, filterOwnerId, filterOutValidation, cachedVersionId, result); return result; } @@ -87,10 +133,24 @@ export class IntervalTree { rbTreeInsert(this, node); } - public delete(node: IntervalNode) { + public delete(node: IntervalNode): void { rbTreeDelete(this, node); } + public resolveNode(node: IntervalNode, cachedVersionId: number): void { + let delta = 0; + while (node !== this.root) { + if (node === node.parent.right) { + delta += node.parent.delta; + } + node = node.parent; + } + + const nodeStart = node.start + delta; + const nodeEnd = node.end + delta; + node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId); + } + public assertInvariants(): void { assert(SENTINEL.color === NodeColor.Black); assert(SENTINEL.parent === SENTINEL); @@ -150,7 +210,7 @@ export class IntervalTree { } //#region Searching -function intervalSearchRecursive(node: IntervalNode, delta: number, intervalStart: number, intervalEnd: number, result: IntervalNode[]): void { +function intervalSearchRecursive(node: IntervalNode, delta: number, intervalStart: number, intervalEnd: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, result: IntervalNode[]): void { // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree // Now, it is known that two intervals A and B overlap only when both // A.low <= B.high and A.high >= B.low. When searching the trees for @@ -165,7 +225,7 @@ function intervalSearchRecursive(node: IntervalNode, delta: number, intervalStar } if (node.left !== SENTINEL) { - intervalSearchRecursive(node.left, delta, intervalStart, intervalEnd, result); + intervalSearchRecursive(node.left, delta, intervalStart, intervalEnd, filterOwnerId, filterOutValidation, cachedVersionId, result); } const nodeStart = delta + node.start; @@ -178,13 +238,23 @@ function intervalSearchRecursive(node: IntervalNode, delta: number, intervalStar const nodeEnd = delta + node.end; if (nodeEnd >= intervalStart) { // There is overlap - node.absoluteInterval.start = nodeStart; - node.absoluteInterval.end = nodeEnd; - result.push(node); + node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId); + + let include = true; + if (filterOwnerId && node.ownerId && node.ownerId !== filterOwnerId) { + include = false; + } + if (filterOutValidation && node.isForValidation) { + include = false; + } + + if (include) { + result.push(node); + } } if (node.right !== SENTINEL) { - intervalSearchRecursive(node.right, delta + node.delta, intervalStart, intervalEnd, result); + intervalSearchRecursive(node.right, delta + node.delta, intervalStart, intervalEnd, filterOwnerId, filterOutValidation, cachedVersionId, result); } } //#endregion diff --git a/src/vs/editor/test/common/model/intervalTree.test.ts b/src/vs/editor/test/common/model/intervalTree.test.ts index d8518b27a68..7df8a19e0e3 100644 --- a/src/vs/editor/test/common/model/intervalTree.test.ts +++ b/src/vs/editor/test/common/model/intervalTree.test.ts @@ -65,13 +65,12 @@ suite('IntervalTree', () => { public acceptOp(op: IOperation): void { - if (op.type === 'insert') { if (PRINT_TREE) { console.log(`insert: {${JSON.stringify(new Interval(op.begin, op.end))}}`); } let nodeId = (++this._lastNodeId); - this._treeNodes[nodeId] = new IntervalNode(op.begin, op.end); + this._treeNodes[nodeId] = new IntervalNode(null, op.begin, op.end); this._tree.insert(this._treeNodes[nodeId]); this._oracleNodes[nodeId] = this._oracle.insert(new Interval(op.begin, op.end)); } else if (op.type === 'delete') { @@ -84,8 +83,8 @@ suite('IntervalTree', () => { this._treeNodes[op.id] = null; this._oracleNodes[op.id] = null; } else { - let actualNodes = this._tree.intervalSearch(new Interval(op.begin, op.end)); - let actual = actualNodes.map(n => n.absoluteInterval); + let actualNodes = this._tree.intervalSearch(new Interval(op.begin, op.end), 0, false, 0); + let actual = actualNodes.map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd)); let expected = this._oracle.search(new Interval(op.begin, op.end)); assert.deepEqual(actual, expected); return; @@ -418,7 +417,7 @@ suite('IntervalTree', () => { [19, 20] ]; data.forEach((int) => { - let node = new IntervalNode(int[0], int[1]); + let node = new IntervalNode(null, int[0], int[1]); r.insert(node); }); return r; @@ -427,8 +426,8 @@ suite('IntervalTree', () => { const T = createCormenTree(); function assertIntervalSearch(start: number, end: number, expected: [number, number][]): void { - let actualNodes = T.intervalSearch(new Interval(start, end)); - let actual = actualNodes.map((n) => <[number, number]>[n.absoluteInterval.start, n.absoluteInterval.end]); + let actualNodes = T.intervalSearch(new Interval(start, end), 0, false, 0); + let actual = actualNodes.map((n) => <[number, number]>[n.cachedAbsoluteStart, n.cachedAbsoluteEnd]); assert.deepEqual(actual, expected); } -- GitLab