/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { InvalidBracketAstNode, ListAstNode, PairAstNode } from './ast.js'; import { BeforeEditPositionMapper } from './beforeEditPositionMapper.js'; import { SmallImmutableSet } from './smallImmutableSet.js'; import { lengthIsZero, lengthLessThan } from './length.js'; import { concat23Trees, concat23TreesOfSameHeight } from './concat23Trees.js'; import { NodeReader } from './nodeReader.js'; /** * Non incrementally built ASTs are immutable. */ export function parseDocument(tokenizer, edits, oldNode, createImmutableLists) { const parser = new Parser(tokenizer, edits, oldNode, createImmutableLists); return parser.parseDocument(); } /** * Non incrementally built ASTs are immutable. */ class Parser { constructor(tokenizer, edits, oldNode, createImmutableLists) { this.tokenizer = tokenizer; this.createImmutableLists = createImmutableLists; this._itemsConstructed = 0; this._itemsFromCache = 0; if (oldNode && createImmutableLists) { throw new Error('Not supported'); } this.oldNodeReader = oldNode ? new NodeReader(oldNode) : undefined; this.positionMapper = new BeforeEditPositionMapper(edits, tokenizer.length); } parseDocument() { this._itemsConstructed = 0; this._itemsFromCache = 0; let result = this.parseList(SmallImmutableSet.getEmpty()); if (!result) { result = ListAstNode.getEmpty(); } return result; } parseList(openedBracketIds) { const items = new Array(); while (true) { const token = this.tokenizer.peek(); if (!token || (token.kind === 2 /* TokenKind.ClosingBracket */ && token.bracketIds.intersects(openedBracketIds))) { break; } const child = this.parseChild(openedBracketIds); if (child.kind === 4 /* AstNodeKind.List */ && child.childrenLength === 0) { continue; } items.push(child); } // When there is no oldNodeReader, all items are created from scratch and must have the same height. const result = this.oldNodeReader ? concat23Trees(items) : concat23TreesOfSameHeight(items, this.createImmutableLists); return result; } parseChild(openedBracketIds) { if (this.oldNodeReader) { const maxCacheableLength = this.positionMapper.getDistanceToNextChange(this.tokenizer.offset); if (!lengthIsZero(maxCacheableLength)) { const cachedNode = this.oldNodeReader.readLongestNodeAt(this.positionMapper.getOffsetBeforeChange(this.tokenizer.offset), curNode => { if (!lengthLessThan(curNode.length, maxCacheableLength)) { // Either the node contains edited text or touches edited text. // In the latter case, brackets might have been extended (`end` -> `ending`), so even touching nodes cannot be reused. return false; } const canBeReused = curNode.canBeReused(openedBracketIds); return canBeReused; }); if (cachedNode) { this._itemsFromCache++; this.tokenizer.skip(cachedNode.length); return cachedNode; } } } this._itemsConstructed++; const token = this.tokenizer.read(); switch (token.kind) { case 2 /* TokenKind.ClosingBracket */: return new InvalidBracketAstNode(token.bracketIds, token.length); case 0 /* TokenKind.Text */: return token.astNode; case 1 /* TokenKind.OpeningBracket */: { const set = openedBracketIds.merge(token.bracketIds); const child = this.parseList(set); const nextToken = this.tokenizer.peek(); if (nextToken && nextToken.kind === 2 /* TokenKind.ClosingBracket */ && (nextToken.bracketId === token.bracketId || nextToken.bracketIds.intersects(token.bracketIds))) { this.tokenizer.read(); return PairAstNode.create(token.astNode, child, nextToken.astNode); } else { return PairAstNode.create(token.astNode, child, null); } } default: throw new Error('unexpected'); } } }