| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { lengthAdd, lengthZero, lengthLessThan } from './length.js';
- /**
- * Allows to efficiently find a longest child at a given offset in a fixed node.
- * The requested offsets must increase monotonously.
- */
- export class NodeReader {
- constructor(node) {
- this.lastOffset = lengthZero;
- this.nextNodes = [node];
- this.offsets = [lengthZero];
- this.idxs = [];
- }
- /**
- * Returns the longest node at `offset` that satisfies the predicate.
- * @param offset must be greater than or equal to the last offset this method has been called with!
- */
- readLongestNodeAt(offset, predicate) {
- if (lengthLessThan(offset, this.lastOffset)) {
- throw new Error('Invalid offset');
- }
- this.lastOffset = offset;
- // Find the longest node of all those that are closest to the current offset.
- while (true) {
- const curNode = lastOrUndefined(this.nextNodes);
- if (!curNode) {
- return undefined;
- }
- const curNodeOffset = lastOrUndefined(this.offsets);
- if (lengthLessThan(offset, curNodeOffset)) {
- // The next best node is not here yet.
- // The reader must advance before a cached node is hit.
- return undefined;
- }
- if (lengthLessThan(curNodeOffset, offset)) {
- // The reader is ahead of the current node.
- if (lengthAdd(curNodeOffset, curNode.length) <= offset) {
- // The reader is after the end of the current node.
- this.nextNodeAfterCurrent();
- }
- else {
- // The reader is somewhere in the current node.
- const nextChildIdx = getNextChildIdx(curNode);
- if (nextChildIdx !== -1) {
- // Go to the first child and repeat.
- this.nextNodes.push(curNode.getChild(nextChildIdx));
- this.offsets.push(curNodeOffset);
- this.idxs.push(nextChildIdx);
- }
- else {
- // We don't have children
- this.nextNodeAfterCurrent();
- }
- }
- }
- else {
- // readerOffsetBeforeChange === curNodeOffset
- if (predicate(curNode)) {
- this.nextNodeAfterCurrent();
- return curNode;
- }
- else {
- const nextChildIdx = getNextChildIdx(curNode);
- // look for shorter node
- if (nextChildIdx === -1) {
- // There is no shorter node.
- this.nextNodeAfterCurrent();
- return undefined;
- }
- else {
- // Descend into first child & repeat.
- this.nextNodes.push(curNode.getChild(nextChildIdx));
- this.offsets.push(curNodeOffset);
- this.idxs.push(nextChildIdx);
- }
- }
- }
- }
- }
- // Navigates to the longest node that continues after the current node.
- nextNodeAfterCurrent() {
- while (true) {
- const currentOffset = lastOrUndefined(this.offsets);
- const currentNode = lastOrUndefined(this.nextNodes);
- this.nextNodes.pop();
- this.offsets.pop();
- if (this.idxs.length === 0) {
- // We just popped the root node, there is no next node.
- break;
- }
- // Parent is not undefined, because idxs is not empty
- const parent = lastOrUndefined(this.nextNodes);
- const nextChildIdx = getNextChildIdx(parent, this.idxs[this.idxs.length - 1]);
- if (nextChildIdx !== -1) {
- this.nextNodes.push(parent.getChild(nextChildIdx));
- this.offsets.push(lengthAdd(currentOffset, currentNode.length));
- this.idxs[this.idxs.length - 1] = nextChildIdx;
- break;
- }
- else {
- this.idxs.pop();
- }
- // We fully consumed the parent.
- // Current node is now parent, so call nextNodeAfterCurrent again
- }
- }
- }
- function getNextChildIdx(node, curIdx = -1) {
- while (true) {
- curIdx++;
- if (curIdx >= node.childrenLength) {
- return -1;
- }
- if (node.getChild(curIdx)) {
- return curIdx;
- }
- }
- }
- function lastOrUndefined(arr) {
- return arr.length > 0 ? arr[arr.length - 1] : undefined;
- }
|