UNPKG

monaco-editor

Version:
119 lines (118 loc) 5.58 kB
/*--------------------------------------------------------------------------------------------- * 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, TextAstNode } 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); } parseDocument() { this._itemsConstructed = 0; this._itemsFromCache = 0; let result = this.parseList(SmallImmutableSet.getEmpty(), 0); if (!result) { result = ListAstNode.getEmpty(); } return result; } parseList(openedBracketIds, level) { const items = []; while (true) { let child = this.tryReadChildFromCache(openedBracketIds); if (!child) { const token = this.tokenizer.peek(); if (!token || (token.kind === 2 /* TokenKind.ClosingBracket */ && token.bracketIds.intersects(openedBracketIds))) { break; } child = this.parseChild(openedBracketIds, level + 1); } 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; } tryReadChildFromCache(openedBracketIds) { if (this.oldNodeReader) { const maxCacheableLength = this.positionMapper.getDistanceToNextChange(this.tokenizer.offset); if (maxCacheableLength === null || !lengthIsZero(maxCacheableLength)) { const cachedNode = this.oldNodeReader.readLongestNodeAt(this.positionMapper.getOffsetBeforeChange(this.tokenizer.offset), curNode => { // The edit could extend the ending token, thus we cannot re-use nodes that touch the edit. // If there is no edit anymore, we can re-use the node in any case. if (maxCacheableLength !== null && !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; } } } return undefined; } parseChild(openedBracketIds, level) { 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 */: { if (level > 300) { // To prevent stack overflows return new TextAstNode(token.length); } const set = openedBracketIds.merge(token.bracketIds); const child = this.parseList(set, level + 1); 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'); } } }