ast.ts ×76

Frontier kind: Code frontier

unlabeled · c_121609fee4d9

868 tests · 6241 LOC · 35 files · introduces 0 tests · 311 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
76 ranges311 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1005 ranges6241 lines · 35 files · Browse complete extent
All tests (intent)
868 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: 311 introduced LOC across 76 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts 311 introduced LOC · 76 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- ast.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 { BugIndicatingError } from '../../../../../base/common/errors.js';
7 > import { CursorColumns } from '../../../core/cursorColumns.js';
8 > import { BracketKind } from '../../../languages/supports/languageBracketsConfiguration.js';
9 > import { ITextModel } from '../../../model.js';
10 > import { Length, lengthAdd, lengthGetLineCount, lengthToObj, lengthZero } from './length.js';
11 > import { SmallImmutableSet } from './smallImmutableSet.js';
12 > import { OpeningBracketId } from './tokenizer.js';
13 >
14 > export const enum AstNodeKind {
15 > Text = 0,
16 > Bracket = 1,
17 > Pair = 2,
18 > UnexpectedClosingBracket = 3,
19 > List = 4,
20 > }
21 >
22 > export type AstNode = PairAstNode | ListAstNode | BracketAstNode | InvalidBracketAstNode | TextAstNode;
23 >
24 > /**
25 > * The base implementation for all AST nodes.
26 > */
27 > abstract class BaseAstNode {
28 > public abstract readonly kind: AstNodeKind;
29 >
30 > public abstract readonly childrenLength: number;
31 >
32 > /**
33 > * Might return null even if {@link idx} is smaller than {@link BaseAstNode.childrenLength}.
34 > */
35 > public abstract getChild(idx: number): AstNode | null;
36 >
37 > /**
38 > * Try to avoid using this property, as implementations might need to allocate the resulting array.
39 > */
40 > public abstract readonly children: readonly AstNode[];
41 >
42 > /**
43 > * Represents the set of all (potentially) missing opening bracket ids in this node.
44 > * E.g. in `{ ] ) }` that set is {`[`, `(` }.
45 > */
46 > public abstract readonly missingOpeningBracketIds: SmallImmutableSet<OpeningBracketId>;
47 >
48 > /**
49 > * In case of a list, determines the height of the (2,3) tree.
50 > */
51 > public abstract readonly listHeight: number;
52 >
53 > protected _length: Length;
54 >
55 > /**
56 > * The length of the entire node, which should equal the sum of lengths of all children.
57 > */
58 > public get length(): Length {
59 return this._length;
60 }
61 > ast.ts
62 > public constructor(length: Length) {
63 this._length = length;
64 }
65 > ast.ts
66 > /**
67 > * @param openBracketIds The set of all opening brackets that have not yet been closed.
68 > */
69 > public abstract canBeReused(
70 > openBracketIds: SmallImmutableSet<OpeningBracketId>
71 > ): boolean;
72 >
73 > /**
74 > * Flattens all lists in this AST. Only for debugging.
75 > */
76 > public abstract flattenLists(): AstNode;
77 >
78 > /**
79 > * Creates a deep clone.
80 > */
81 > public abstract deepClone(): AstNode;
82 >
83 > public abstract computeMinIndentation(offset: Length, textModel: ITextModel): number;
84 > }
85 >
86 > /**
87 > * Represents a bracket pair including its child (e.g. `{ ... }`).
88 > * Might be unclosed.
89 > * Immutable, if all children are immutable.
90 > */
91 > export class PairAstNode extends BaseAstNode {
92 > public static create(
93 > openingBracket: BracketAstNode,
94 > child: AstNode | null,
95 > closingBracket: BracketAstNode | null
96 > ) {
97 > let length = openingBracket.length;
98 > if (child) {
99 > length = lengthAdd(length, child.length);
100 > }
101 > if (closingBracket) {
102 > length = lengthAdd(length, closingBracket.length);
103 > }
104 > return new PairAstNode(length, openingBracket, child, closingBracket, child ? child.missingOpeningBracketIds : SmallImmutableSet.getEmpty());
105 > }
106 >
107 > public get kind(): AstNodeKind.Pair {
108 return AstNodeKind.Pair;
109 }
110 > public get listHeight() { ast.ts
111 return 0;
112 }
113 > public get childrenLength(): number { ast.ts
114 return 3;
115 }
116 > public getChild(idx: number): AstNode | null { ast.ts
117 switch (idx) {
118 case 0: return this.openingBracket;
122 throw new Error('Invalid child index');
123 }
124 > ast.ts
125 > /**
126 > * Avoid using this property, it allocates an array!
127 > */
128 > public get children() {
129 const result: AstNode[] = [];
130 result.push(this.openingBracket);
137 return result;
138 }
139 > ast.ts
140 > private constructor(
141 length: Length,
142 public readonly openingBracket: BracketAstNode,
147 super(length);
148 }
149 > ast.ts
150 > public canBeReused(openBracketIds: SmallImmutableSet<OpeningBracketId>) {
151 if (this.closingBracket === null) {
152 // Unclosed pair ast nodes only
166 return true;
167 }
168 > ast.ts
169 > public flattenLists(): PairAstNode {
170 return PairAstNode.create(
171 this.openingBracket.flattenLists(),
174 );
175 }
176 > ast.ts
177 > public deepClone(): PairAstNode {
178 return new PairAstNode(
179 this.length,
184 );
185 }
186 > ast.ts
187 > public computeMinIndentation(offset: Length, textModel: ITextModel): number {
188 return this.child ? this.child.computeMinIndentation(lengthAdd(offset, this.openingBracket.length), textModel) : Number.MAX_SAFE_INTEGER;
189 }
190 > } ast.ts
191 >
192 > export abstract class ListAstNode extends BaseAstNode {
193 > /**
194 > * This method uses more memory-efficient list nodes that can only store 2 or 3 children.
195 > */
196 > public static create23(item1: AstNode, item2: AstNode, item3: AstNode | null, immutable: boolean = false): ListAstNode {
197 > let length = item1.length;
198 > let missingBracketIds = item1.missingOpeningBracketIds;
199 >
200 > if (item1.listHeight !== item2.listHeight) {
201 throw new Error('Invalid list heights');
202 }
203 > ast.ts
204 > length = lengthAdd(length, item2.length);
205 > missingBracketIds = missingBracketIds.merge(item2.missingOpeningBracketIds);
206 >
207 > if (item3) {
208 if (item1.listHeight !== item3.listHeight) {
209 throw new Error('Invalid list heights');
212 missingBracketIds = missingBracketIds.merge(item3.missingOpeningBracketIds);
213 }
214 > return immutable ast.ts
215 ? new Immutable23ListAstNode(length, item1.listHeight + 1, item1, item2, item3, missingBracketIds)
216 : new TwoThreeListAstNode(length, item1.listHeight + 1, item1, item2, item3, missingBracketIds);
217 > } ast.ts
218 >
219 > public static create(items: AstNode[], immutable: boolean = false): ListAstNode {
220 if (items.length === 0) {
221 return this.getEmpty();
232 }
233 }
234 > ast.ts
235 > public static getEmpty() {
236 return new ImmutableArrayListAstNode(lengthZero, 0, [], SmallImmutableSet.getEmpty());
237 }
238 > ast.ts
239 > public get kind(): AstNodeKind.List {
240 return AstNodeKind.List;
241 }
242 > ast.ts
243 > public get missingOpeningBracketIds(): SmallImmutableSet<OpeningBracketId> {
244 return this._missingOpeningBracketIds;
245 }
246 > ast.ts
247 > private cachedMinIndentation: number = -1;
248 >
249 > /**
250 > * Use ListAstNode.create.
251 > */
252 > constructor(
253 length: Length,
254 public readonly listHeight: number,
257 super(length);
258 }
259 > ast.ts
260 > protected throwIfImmutable(): void {
261 // NOOP
262 }
263 > ast.ts
264 > protected abstract setChild(idx: number, child: AstNode): void;
265 >
266 > public makeLastElementMutable(): AstNode | undefined {
267 this.throwIfImmutable();
268 const childCount = this.childrenLength;
277 return mutable;
278 }
279 > ast.ts
280 > public makeFirstElementMutable(): AstNode | undefined {
281 this.throwIfImmutable();
282 const childCount = this.childrenLength;
291 return mutable;
292 }
293 > ast.ts
294 > public canBeReused(openBracketIds: SmallImmutableSet<OpeningBracketId>): boolean {
295 if (openBracketIds.intersects(this.missingOpeningBracketIds)) {
296 return false;
314 return lastChild.canBeReused(openBracketIds);
315 }
316 > ast.ts
317 > public handleChildrenChanged(): void {
318 this.throwIfImmutable();
319
333 this.cachedMinIndentation = -1;
334 }
335 > ast.ts
336 > public flattenLists(): ListAstNode {
337 const items: AstNode[] = [];
338 for (const c of this.children) {
346 return ListAstNode.create(items);
347 }
348 > ast.ts
349 > public computeMinIndentation(offset: Length, textModel: ITextModel): number {
350 if (this.cachedMinIndentation !== -1) {
351 return this.cachedMinIndentation;
365 return minIndentation;
366 }
367 > ast.ts
368 > /**
369 > * Creates a shallow clone that is mutable, or itself if it is already mutable.
370 > */
371 > public abstract toMutable(): ListAstNode;
372 >
373 > public abstract appendChildOfSameHeight(node: AstNode): void;
374 > public abstract unappendChild(): AstNode | undefined;
375 > public abstract prependChildOfSameHeight(node: AstNode): void;
376 > public abstract unprependChild(): AstNode | undefined;
377 > }
378 >
379 > class TwoThreeListAstNode extends ListAstNode {
380 > public get childrenLength(): number {
381 > return this._item3 !== null ? 3 : 2;
382 > }
383 > public getChild(idx: number): AstNode | null {
384 switch (idx) {
385 case 0: return this._item1;
389 throw new Error('Invalid child index');
390 }
391 > protected setChild(idx: number, node: AstNode): void { ast.ts
392 switch (idx) {
393 case 0: this._item1 = node; return;
397 throw new Error('Invalid child index');
398 }
399 > ast.ts
400 > public get children(): readonly AstNode[] {
401 return this._item3 ? [this._item1, this._item2, this._item3] : [this._item1, this._item2];
402 }
403 > ast.ts
404 > public get item1(): AstNode {
405 return this._item1;
406 }
407 > public get item2(): AstNode { ast.ts
408 return this._item2;
409 }
410 > public get item3(): AstNode | null { ast.ts
411 return this._item3;
412 }
413 > ast.ts
414 > public constructor(
415 length: Length,
416 listHeight: number,
422 super(length, listHeight, missingOpeningBracketIds);
423 }
424 > ast.ts
425 > public deepClone(): ListAstNode {
426 return new TwoThreeListAstNode(
427 this.length,
433 );
434 }
435 > ast.ts
436 > public appendChildOfSameHeight(node: AstNode): void {
437 if (this._item3) {
438 throw new Error('Cannot append to a full (2,3) tree node');
442 this.handleChildrenChanged();
443 }
444 > ast.ts
445 > public unappendChild(): AstNode | undefined {
446 if (!this._item3) {
447 throw new Error('Cannot remove from a non-full (2,3) tree node');
453 return result;
454 }
455 > ast.ts
456 > public prependChildOfSameHeight(node: AstNode): void {
457 if (this._item3) {
458 throw new Error('Cannot prepend to a full (2,3) tree node');
464 this.handleChildrenChanged();
465 }
466 > ast.ts
467 > public unprependChild(): AstNode | undefined {
468 if (!this._item3) {
469 throw new Error('Cannot remove from a non-full (2,3) tree node');
478 return result;
479 }
480 > ast.ts
481 > override toMutable(): ListAstNode {
482 return this;
483 }
484 > } ast.ts
485 >
486 > /**
487 > * Immutable, if all children are immutable.
488 > */
489 > class Immutable23ListAstNode extends TwoThreeListAstNode {
490 > override toMutable(): ListAstNode {
491 return new TwoThreeListAstNode(this.length, this.listHeight, this.item1, this.item2, this.item3, this.missingOpeningBracketIds);
492 }
493 > ast.ts
494 > protected override throwIfImmutable(): void {
495 throw new Error('this instance is immutable');
496 }
497 > } ast.ts
498 >
499 > /**
500 > * For debugging.
501 > */
502 > class ArrayListAstNode extends ListAstNode {
503 > get childrenLength(): number {
504 > return this._children.length;
505 > }
506 > getChild(idx: number): AstNode | null {
507 return this._children[idx];
508 }
509 > protected setChild(idx: number, child: AstNode): void { ast.ts
510 this._children[idx] = child;
511 }
512 > get children(): readonly AstNode[] { ast.ts
513 return this._children;
514 }
515 > ast.ts
516 > constructor(
517 length: Length,
518 listHeight: number,
522 super(length, listHeight, missingOpeningBracketIds);
523 }
524 > ast.ts
525 > deepClone(): ListAstNode {
526 const children = new Array<AstNode>(this._children.length);
527 for (let i = 0; i < this._children.length; i++) {
530 return new ArrayListAstNode(this.length, this.listHeight, children, this.missingOpeningBracketIds);
531 }
532 > ast.ts
533 > public appendChildOfSameHeight(node: AstNode): void {
534 this.throwIfImmutable();
535 this._children.push(node);
536 this.handleChildrenChanged();
537 }
538 > ast.ts
539 > public unappendChild(): AstNode | undefined {
540 this.throwIfImmutable();
541 const item = this._children.pop();
543 return item;
544 }
545 > ast.ts
546 > public prependChildOfSameHeight(node: AstNode): void {
547 this.throwIfImmutable();
548 this._children.unshift(node);
549 this.handleChildrenChanged();
550 }
551 > ast.ts
552 > public unprependChild(): AstNode | undefined {
553 this.throwIfImmutable();
554 const item = this._children.shift();
556 return item;
557 }
558 > ast.ts
559 > public override toMutable(): ListAstNode {
560 return this;
561 }
562 > } ast.ts
563 >
564 > /**
565 > * Immutable, if all children are immutable.
566 > */
567 > class ImmutableArrayListAstNode extends ArrayListAstNode {
568 > override toMutable(): ListAstNode {
569 return new ArrayListAstNode(this.length, this.listHeight, [...this.children], this.missingOpeningBracketIds);
570 }
571 > ast.ts
572 > protected override throwIfImmutable(): void {
573 throw new Error('this instance is immutable');
574 }
575 > } ast.ts
576 >
577 > const emptyArray: readonly AstNode[] = [];
578 >
579 > abstract class ImmutableLeafAstNode extends BaseAstNode {
580 > public get listHeight() {
581 return 0;
582 }
583 > public get childrenLength(): number { ast.ts
584 return 0;
585 }
586 > public getChild(idx: number): AstNode | null { ast.ts
587 return null;
588 }
589 > public get children(): readonly AstNode[] { ast.ts
590 return emptyArray;
591 }
592 > ast.ts
593 > public flattenLists(): this & AstNode {
594 return this as this & AstNode;
595 }
596 > public deepClone(): this & AstNode { ast.ts
597 return this as this & AstNode;
598 }
599 > } ast.ts
600 >
601 > export class TextAstNode extends ImmutableLeafAstNode {
602 > public get kind(): AstNodeKind.Text {
603 return AstNodeKind.Text;
604 }
605 > public get missingOpeningBracketIds(): SmallImmutableSet<OpeningBracketId> { ast.ts
606 return SmallImmutableSet.getEmpty();
607 }
608 > ast.ts
609 > public canBeReused(_openedBracketIds: SmallImmutableSet<OpeningBracketId>) {
610 return true;
611 }
612 > ast.ts
613 > public computeMinIndentation(offset: Length, textModel: ITextModel): number {
614 const start = lengthToObj(offset);
615 // Text ast nodes don't have partial indentation (ensured by the tokenizer).
633 return result;
634 }
635 > } ast.ts
636 >
637 > export class BracketAstNode extends ImmutableLeafAstNode {
638 > public static create(
639 > length: Length,
640 > bracketInfo: BracketKind,
641 > bracketIds: SmallImmutableSet<OpeningBracketId>
642 > ): BracketAstNode {
643 > const node = new BracketAstNode(length, bracketInfo, bracketIds);
644 > return node;
645 > }
646 >
647 > public get kind(): AstNodeKind.Bracket {
648 return AstNodeKind.Bracket;
649 }
650 > ast.ts
651 > public get missingOpeningBracketIds(): SmallImmutableSet<OpeningBracketId> {
652 return SmallImmutableSet.getEmpty();
653 }
654 > ast.ts
655 > private constructor(
656 length: Length,
657 public readonly bracketInfo: BracketKind,
664 super(length);
665 }
666 > ast.ts
667 > public get text() {
668 return this.bracketInfo.bracketText;
669 }
670 > ast.ts
671 > public get languageId() {
672 return this.bracketInfo.languageId;
673 }
674 > ast.ts
675 > public canBeReused(_openedBracketIds: SmallImmutableSet<OpeningBracketId>) {
676 // These nodes could be reused,
677 // but not in a general way.
679 return false;
680 }
681 > ast.ts
682 > public computeMinIndentation(offset: Length, textModel: ITextModel): number {
683 return Number.MAX_SAFE_INTEGER;
684 }
685 > } ast.ts
686 >
687 > export class InvalidBracketAstNode extends ImmutableLeafAstNode {
688 > public get kind(): AstNodeKind.UnexpectedClosingBracket {
689 return AstNodeKind.UnexpectedClosingBracket;
690 }
691 > ast.ts
692 > public readonly missingOpeningBracketIds: SmallImmutableSet<OpeningBracketId>;
693 >
694 > public constructor(closingBrackets: SmallImmutableSet<OpeningBracketId>, length: Length) {
695 super(length);
696 this.missingOpeningBracketIds = closingBrackets;
697 }
698 > ast.ts
699 > public canBeReused(openedBracketIds: SmallImmutableSet<OpeningBracketId>) {
700 return !openedBracketIds.intersects(this.missingOpeningBracketIds);
701 }
702 > ast.ts
703 > public computeMinIndentation(offset: Length, textModel: ITextModel): number {
704 return Number.MAX_SAFE_INTEGER;
705 }
706 > } ast.ts