Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/nodeReader.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AstNode } from './ast.js';6import { lengthAdd, lengthZero, Length, lengthLessThan } from './length.js';78/**9* Allows to efficiently find a longest child at a given offset in a fixed node.10* The requested offsets must increase monotonously.11*/12export class NodeReader {13private readonly nextNodes: AstNode[];14private readonly offsets: Length[];15private readonly idxs: number[];16private lastOffset: Length = lengthZero;1718constructor(node: AstNode) {19this.nextNodes = [node];20this.offsets = [lengthZero];21this.idxs = [];22}2324/**25* Returns the longest node at `offset` that satisfies the predicate.26* @param offset must be greater than or equal to the last offset this method has been called with!27*/28readLongestNodeAt(offset: Length, predicate: (node: AstNode) => boolean): AstNode | undefined {29if (lengthLessThan(offset, this.lastOffset)) {30throw new Error('Invalid offset');31}32this.lastOffset = offset;3334// Find the longest node of all those that are closest to the current offset.35while (true) {36const curNode = lastOrUndefined(this.nextNodes);3738if (!curNode) {39return undefined;40}41const curNodeOffset = lastOrUndefined(this.offsets)!;4243if (lengthLessThan(offset, curNodeOffset)) {44// The next best node is not here yet.45// The reader must advance before a cached node is hit.46return undefined;47}4849if (lengthLessThan(curNodeOffset, offset)) {50// The reader is ahead of the current node.51if (lengthAdd(curNodeOffset, curNode.length) <= offset) {52// The reader is after the end of the current node.53this.nextNodeAfterCurrent();54} else {55// The reader is somewhere in the current node.56const nextChildIdx = getNextChildIdx(curNode);57if (nextChildIdx !== -1) {58// Go to the first child and repeat.59this.nextNodes.push(curNode.getChild(nextChildIdx)!);60this.offsets.push(curNodeOffset);61this.idxs.push(nextChildIdx);62} else {63// We don't have children64this.nextNodeAfterCurrent();65}66}67} else {68// readerOffsetBeforeChange === curNodeOffset69if (predicate(curNode)) {70this.nextNodeAfterCurrent();71return curNode;72} else {73const nextChildIdx = getNextChildIdx(curNode);74// look for shorter node75if (nextChildIdx === -1) {76// There is no shorter node.77this.nextNodeAfterCurrent();78return undefined;79} else {80// Descend into first child & repeat.81this.nextNodes.push(curNode.getChild(nextChildIdx)!);82this.offsets.push(curNodeOffset);83this.idxs.push(nextChildIdx);84}85}86}87}88}8990// Navigates to the longest node that continues after the current node.91private nextNodeAfterCurrent(): void {92while (true) {93const currentOffset = lastOrUndefined(this.offsets);94const currentNode = lastOrUndefined(this.nextNodes);95this.nextNodes.pop();96this.offsets.pop();9798if (this.idxs.length === 0) {99// We just popped the root node, there is no next node.100break;101}102103// Parent is not undefined, because idxs is not empty104const parent = lastOrUndefined(this.nextNodes)!;105const nextChildIdx = getNextChildIdx(parent, this.idxs[this.idxs.length - 1]);106107if (nextChildIdx !== -1) {108this.nextNodes.push(parent.getChild(nextChildIdx)!);109this.offsets.push(lengthAdd(currentOffset!, currentNode!.length));110this.idxs[this.idxs.length - 1] = nextChildIdx;111break;112} else {113this.idxs.pop();114}115// We fully consumed the parent.116// Current node is now parent, so call nextNodeAfterCurrent again117}118}119}120121function getNextChildIdx(node: AstNode, curIdx: number = -1): number | -1 {122while (true) {123curIdx++;124if (curIdx >= node.childrenLength) {125return -1;126}127if (node.getChild(curIdx)) {128return curIdx;129}130}131}132133function lastOrUndefined<T>(arr: readonly T[]): T | undefined {134return arr.length > 0 ? arr[arr.length - 1] : undefined;135}136137138