intervalTree.ts ×44

Frontier kind: Code frontier

unlabeled · c_4358f2fd2d73

886 tests · 5279 LOC · 21 files · introduces 0 tests · 277 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
44 ranges277 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
547 ranges5279 lines · 21 files · Browse complete extent
All tests (intent)
886 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 277 introduced LOC across 44 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/editor/common/model/intervalTree.ts 277 introduced LOC · 44 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- intervalTree.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Range } from '../core/range.js';
7 > import { TrackedRangeStickiness, TrackedRangeStickiness as ActualTrackedRangeStickiness } from '../model.js';
8 > import { ModelDecorationOptions } from './textModel.js';
9 >
10 > //
11 > // The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
12 > //
13 >
14 > export const enum ClassName {
15 > EditorHintDecoration = 'squiggly-hint',
16 > EditorInfoDecoration = 'squiggly-info',
17 > EditorWarningDecoration = 'squiggly-warning',
18 > EditorErrorDecoration = 'squiggly-error',
19 > EditorUnnecessaryDecoration = 'squiggly-unnecessary',
20 > EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary',
21 > EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated'
22 > }
23 >
24 > export const enum NodeColor {
25 > Black = 0,
26 > Red = 1,
27 > }
28 >
29 > const enum Constants {
30 > ColorMask = 0b00000001,
31 > ColorMaskInverse = 0b11111110,
32 > ColorOffset = 0,
33 >
34 > IsVisitedMask = 0b00000010,
35 > IsVisitedMaskInverse = 0b11111101,
36 > IsVisitedOffset = 1,
37 >
38 > IsForValidationMask = 0b00000100,
39 > IsForValidationMaskInverse = 0b11111011,
40 > IsForValidationOffset = 2,
41 >
42 > StickinessMask = 0b00011000,
43 > StickinessMaskInverse = 0b11100111,
44 > StickinessOffset = 3,
45 >
46 > CollapseOnReplaceEditMask = 0b00100000,
47 > CollapseOnReplaceEditMaskInverse = 0b11011111,
48 > CollapseOnReplaceEditOffset = 5,
49 >
50 > IsMarginMask = 0b01000000,
51 > IsMarginMaskInverse = 0b10111111,
52 > IsMarginOffset = 6,
53 >
54 > AffectsFontMask = 0b10000000,
55 > AffectsFontMaskInverse = 0b01111111,
56 > AffectsFontOffset = 7,
57 >
58 > /**
59 > * Due to how deletion works (in order to avoid always walking the right subtree of the deleted node),
60 > * the deltas for nodes can grow and shrink dramatically. It has been observed, in practice, that unless
61 > * the deltas are corrected, integer overflow will occur.
62 > *
63 > * The integer overflow occurs when 53 bits are used in the numbers, but we will try to avoid it as
64 > * a node's delta gets below a negative 30 bits number.
65 > *
66 > * MIN SMI (SMall Integer) as defined in v8.
67 > * one bit is lost for boxing/unboxing flag.
68 > * one bit is lost for sign flag.
69 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
70 > */
71 > MIN_SAFE_DELTA = -(1 << 30),
72 > /**
73 > * MAX SMI (SMall Integer) as defined in v8.
74 > * one bit is lost for boxing/unboxing flag.
75 > * one bit is lost for sign flag.
76 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
77 > */
78 > MAX_SAFE_DELTA = 1 << 30,
79 > }
80 >
81 > export function getNodeColor(node: IntervalNode): NodeColor {
82 return ((node.metadata & Constants.ColorMask) >>> Constants.ColorOffset);
83 }
84 > function setNodeColor(node: IntervalNode, color: NodeColor): void { intervalTree.ts
85 > node.metadata = (
86 > (node.metadata & Constants.ColorMaskInverse) | (color << Constants.ColorOffset)
87 > );
88 > }
89 function getNodeIsVisited(node: IntervalNode): boolean {
90 return ((node.metadata & Constants.IsVisitedMask) >>> Constants.IsVisitedOffset) === 1;
91 }
92 > function setNodeIsVisited(node: IntervalNode, value: boolean): void { intervalTree.ts
93 > node.metadata = (
94 > (node.metadata & Constants.IsVisitedMaskInverse) | ((value ? 1 : 0) << Constants.IsVisitedOffset)
95 > );
96 > }
97 function getNodeIsForValidation(node: IntervalNode): boolean {
98 return ((node.metadata & Constants.IsForValidationMask) >>> Constants.IsForValidationOffset) === 1;
99 }
100 > function setNodeIsForValidation(node: IntervalNode, value: boolean): void { intervalTree.ts
101 > node.metadata = (
102 > (node.metadata & Constants.IsForValidationMaskInverse) | ((value ? 1 : 0) << Constants.IsForValidationOffset)
103 > );
104 > }
105 function getNodeIsInGlyphMargin(node: IntervalNode): boolean {
106 return ((node.metadata & Constants.IsMarginMask) >>> Constants.IsMarginOffset) === 1;
107 }
108 > function setNodeIsInGlyphMargin(node: IntervalNode, value: boolean): void { intervalTree.ts
109 > node.metadata = (
110 > (node.metadata & Constants.IsMarginMaskInverse) | ((value ? 1 : 0) << Constants.IsMarginOffset)
111 > );
112 > }
113 function getNodeAffectsFont(node: IntervalNode): boolean {
114 return ((node.metadata & Constants.AffectsFontMask) >>> Constants.AffectsFontOffset) === 1;
115 }
116 > function setNodeAffectsFont(node: IntervalNode, value: boolean): void { intervalTree.ts
117 > node.metadata = (
118 > (node.metadata & Constants.AffectsFontMaskInverse) | ((value ? 1 : 0) << Constants.AffectsFontOffset)
119 > );
120 > }
121 function getNodeStickiness(node: IntervalNode): TrackedRangeStickiness {
122 return ((node.metadata & Constants.StickinessMask) >>> Constants.StickinessOffset);
123 }
124 > function _setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickiness): void { intervalTree.ts
125 > node.metadata = (
126 > (node.metadata & Constants.StickinessMaskInverse) | (stickiness << Constants.StickinessOffset)
127 > );
128 > }
129 function getCollapseOnReplaceEdit(node: IntervalNode): boolean {
130 return ((node.metadata & Constants.CollapseOnReplaceEditMask) >>> Constants.CollapseOnReplaceEditOffset) === 1;
131 }
132 > function setCollapseOnReplaceEdit(node: IntervalNode, value: boolean): void { intervalTree.ts
133 > node.metadata = (
134 > (node.metadata & Constants.CollapseOnReplaceEditMaskInverse) | ((value ? 1 : 0) << Constants.CollapseOnReplaceEditOffset)
135 > );
136 > }
137 > export function setNodeStickiness(node: IntervalNode, stickiness: ActualTrackedRangeStickiness): void {
138 _setNodeStickiness(node, <number>stickiness);
139 }
141 > export class IntervalNode {
142 >
143 > /**
144 > * contains binary encoded information for color, visited, isForValidation and stickiness.
145 > */
146 > public metadata: number;
147 >
148 > public parent: IntervalNode;
149 > public left: IntervalNode;
150 > public right: IntervalNode;
151 >
152 > public start: number;
153 > public end: number;
154 > public delta: number;
155 > public maxEnd: number;
156 >
157 > public id: string;
158 > public ownerId: number;
159 > public options: ModelDecorationOptions;
160 >
161 > public cachedVersionId: number;
162 > public cachedAbsoluteStart: number;
163 > public cachedAbsoluteEnd: number;
164 > public range: Range | null;
165 >
166 > constructor(id: string, start: number, end: number) {
167 > this.metadata = 0;
168 >
169 > this.parent = this;
170 > this.left = this;
171 > this.right = this;
172 > setNodeColor(this, NodeColor.Red);
173 >
174 > this.start = start;
175 > this.end = end;
176 > // FORCE_OVERFLOWING_TEST: this.delta = start;
177 > this.delta = 0;
178 > this.maxEnd = end;
179 >
180 > this.id = id;
181 > this.ownerId = 0;
182 > this.options = null!;
183 > setNodeIsForValidation(this, false);
184 > setNodeIsInGlyphMargin(this, false);
185 > _setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
186 > setCollapseOnReplaceEdit(this, false);
187 > setNodeAffectsFont(this, false);
188 >
189 > this.cachedVersionId = 0;
190 > this.cachedAbsoluteStart = start;
191 > this.cachedAbsoluteEnd = end;
192 > this.range = null;
193 >
194 > setNodeIsVisited(this, false);
195 > }
196 >
197 > public reset(versionId: number, start: number, end: number, range: Range): void {
198 this.start = start;
199 this.end = end;
204 this.range = range;
205 }
207 > public setOptions(options: ModelDecorationOptions) {
208 this.options = options;
209 const className = this.options.className;
218 setNodeAffectsFont(this, this.options.affectsFont ?? false);
219 }
221 > public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void {
222 if (this.cachedVersionId !== cachedVersionId) {
223 this.range = null;
227 this.cachedAbsoluteEnd = absoluteEnd;
228 }
230 > public detach(): void {
231 this.parent = null!;
232 this.left = null!;
233 this.right = null!;
234 }
235 > } intervalTree.ts
236 >
237 > export const SENTINEL: IntervalNode = new IntervalNode(null!, 0, 0);
238 > SENTINEL.parent = SENTINEL;
239 > SENTINEL.left = SENTINEL;
240 > SENTINEL.right = SENTINEL;
241 > setNodeColor(SENTINEL, NodeColor.Black);
242 >
243 > export class IntervalTree {
244 >
245 > public root: IntervalNode;
246 > public requestNormalizeDelta: boolean;
247 >
248 > constructor() {
249 this.root = SENTINEL;
250 this.requestNormalizeDelta = false;
251 }
253 > public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
254 if (this.root === SENTINEL) {
255 return [];
257 return intervalSearch(this, start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
258 }
260 > public search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
261 if (this.root === SENTINEL) {
262 return [];
264 return search(this, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
265 }
267 > /**
268 > * Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes!
269 > */
270 > public collectNodesFromOwner(ownerId: number): IntervalNode[] {
271 return collectNodesFromOwner(this, ownerId);
272 }
274 > /**
275 > * Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes!
276 > */
277 > public collectNodesPostOrder(): IntervalNode[] {
278 return collectNodesPostOrder(this);
279 }
281 > public insert(node: IntervalNode): void {
282 rbTreeInsert(this, node);
283 this._normalizeDeltaIfNecessary();
284 }
286 > public delete(node: IntervalNode): void {
287 rbTreeDelete(this, node);
288 this._normalizeDeltaIfNecessary();
289 }
291 > public resolveNode(node: IntervalNode, cachedVersionId: number): void {
292 const initialNode = node;
293 let delta = 0;
303 initialNode.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId);
304 }
306 > public acceptReplace(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void {
307 // Our strategy is to remove all directly impacted nodes, and then add them back to the tree.
308
332 this._normalizeDeltaIfNecessary();
333 }
335 > public getAllInOrder(): IntervalNode[] {
336 return search(this, 0, false, false, 0, false);
337 }
339 > private _normalizeDeltaIfNecessary(): void {
340 if (!this.requestNormalizeDelta) {
341 return;
344 normalizeDelta(this);
345 }
346 > } intervalTree.ts
347 >
348 > //#region Delta Normalization
349 function normalizeDelta(T: IntervalTree): void {
350 let node = T.root;
384 setNodeIsVisited(T.root, false);
385 }
386 > //#endregion intervalTree.ts
387 >
388 > //#region Editing
389 >
390 > const enum MarkerMoveSemantics {
391 > MarkerDefined = 0,
392 > ForceMove = 1,
393 > ForceStay = 2
394 > }
395 >
396 function adjustMarkerBeforeColumn(markerOffset: number, markerStickToPreviousCharacter: boolean, checkOffset: number, moveSemantics: MarkerMoveSemantics): boolean {
397 if (markerOffset < checkOffset) {
409 return markerStickToPreviousCharacter;
410 }
412 > /**
413 > * This is a lot more complicated than strictly necessary to maintain the same behaviour
414 > * as when decorations were implemented using two markers.
415 > */
416 > export function nodeAcceptEdit(node: IntervalNode, start: number, end: number, textLength: number, forceMoveMarkers: boolean): void {
417 const nodeStickiness = getNodeStickiness(node);
418 const startStickToPreviousCharacter = (
489 }
490 }
492 function searchForEditing(T: IntervalTree, start: number, end: number): IntervalNode[] {
493 // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
561 return result;
562 }
564 function noOverlapReplace(T: IntervalTree, start: number, end: number, textLength: number): void {
565 // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
631 setNodeIsVisited(T.root, false);
632 }
634 > //#endregion
635 >
636 > //#region Searching
637 >
638 function collectNodesFromOwner(T: IntervalTree, ownerId: number): IntervalNode[] {
639 let node = T.root;
673 return result;
674 }
676 function collectNodesPostOrder(T: IntervalTree): IntervalNode[] {
677 let node = T.root;
708 return result;
709 }
711 function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
712 let node = T.root;
772 return result;
773 }
775 function intervalSearch(T: IntervalTree, intervalStart: number, intervalEnd: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
776 // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
865 return result;
866 }
868 > //#endregion
869 >
870 > //#region Insertion
871 function rbTreeInsert(T: IntervalTree, newNode: IntervalNode): IntervalNode {
872 if (T.root === SENTINEL) {
927 return newNode;
928 }
930 function treeInsert(T: IntervalTree, z: IntervalNode): void {
931 let delta: number = 0;
968 setNodeColor(z, NodeColor.Red);
969 }
970 > //#endregion intervalTree.ts
971 >
972 > //#region Deletion
973 function rbTreeDelete(T: IntervalTree, z: IntervalNode): void {
974
1154 resetSentinel();
1155 }
1157 function leftest(node: IntervalNode): IntervalNode {
1158 while (node.left !== SENTINEL) {
1161 return node;
1162 }
1164 function resetSentinel(): void {
1165 SENTINEL.parent = SENTINEL;
1168 SENTINEL.end = 0; // optional
1169 }
1170 > //#endregion intervalTree.ts
1171 >
1172 > //#region Rotations
1173 function leftRotate(T: IntervalTree, x: IntervalNode): void {
1174 const y = x.right; // set y.
1200 recomputeMaxEnd(y);
1201 }
1203 function rightRotate(T: IntervalTree, y: IntervalNode): void {
1204 const x = y.left;
1230 recomputeMaxEnd(x);
1231 }
1232 > //#endregion intervalTree.ts
1233 >
1234 > //#region max end computation
1235 >
1236 function computeMaxEnd(node: IntervalNode): number {
1237 let maxEnd = node.end;
1250 return maxEnd;
1251 }
1253 > export function recomputeMaxEnd(node: IntervalNode): void {
1254 node.maxEnd = computeMaxEnd(node);
1255 }
1257 function recomputeMaxEndWalkToRoot(node: IntervalNode): void {
1258 while (node !== SENTINEL) {
1269 }
1270 }
1272 > //#endregion
1273 >
1274 > //#region utils
1275 > export function intervalCompare(aStart: number, aEnd: number, bStart: number, bEnd: number): number {
1276 if (aStart === bStart) {
1277 return aEnd - bEnd;