UNPKG

monaco-editor

Version:
344 lines (343 loc) • 17.5 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Emitter } from '../../../../../base/common/event.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { BracketInfo, BracketPairWithMinIndentationInfo } from '../../../textModelBracketPairs.js'; import { TextEditInfo } from './beforeEditPositionMapper.js'; import { LanguageAgnosticBracketTokens } from './brackets.js'; import { lengthAdd, lengthGreaterThanEqual, lengthLessThan, lengthLessThanEqual, lengthsToRange, lengthZero, positionToLength, toLength } from './length.js'; import { parseDocument } from './parser.js'; import { DenseKeyProvider } from './smallImmutableSet.js'; import { FastTokenizer, TextBufferTokenizer } from './tokenizer.js'; import { CallbackIterable } from '../../../../../base/common/arrays.js'; import { combineTextEditInfos } from './combineTextEditInfos.js'; export class BracketPairsTree extends Disposable { didLanguageChange(languageId) { return this.brackets.didLanguageChange(languageId); } constructor(textModel, getLanguageConfiguration) { super(); this.textModel = textModel; this.getLanguageConfiguration = getLanguageConfiguration; this.didChangeEmitter = new Emitter(); this.denseKeyProvider = new DenseKeyProvider(); this.brackets = new LanguageAgnosticBracketTokens(this.denseKeyProvider, this.getLanguageConfiguration); this.onDidChange = this.didChangeEmitter.event; this.queuedTextEditsForInitialAstWithoutTokens = []; this.queuedTextEdits = []; if (!textModel.tokenization.hasTokens) { const brackets = this.brackets.getSingleLanguageBracketTokens(this.textModel.getLanguageId()); const tokenizer = new FastTokenizer(this.textModel.getValue(), brackets); this.initialAstWithoutTokens = parseDocument(tokenizer, [], undefined, true); this.astWithTokens = this.initialAstWithoutTokens; } else if (textModel.tokenization.backgroundTokenizationState === 2 /* BackgroundTokenizationState.Completed */) { // Skip the initial ast, as there is no flickering. // Directly create the tree with token information. this.initialAstWithoutTokens = undefined; this.astWithTokens = this.parseDocumentFromTextBuffer([], undefined, false); } else { // We missed some token changes already, so we cannot use the fast tokenizer + delta increments this.initialAstWithoutTokens = this.parseDocumentFromTextBuffer([], undefined, true); this.astWithTokens = this.initialAstWithoutTokens; } } //#region TextModel events handleDidChangeBackgroundTokenizationState() { if (this.textModel.tokenization.backgroundTokenizationState === 2 /* BackgroundTokenizationState.Completed */) { const wasUndefined = this.initialAstWithoutTokens === undefined; // Clear the initial tree as we can use the tree with token information now. this.initialAstWithoutTokens = undefined; if (!wasUndefined) { this.didChangeEmitter.fire(); } } } handleDidChangeTokens({ ranges }) { const edits = ranges.map(r => new TextEditInfo(toLength(r.fromLineNumber - 1, 0), toLength(r.toLineNumber, 0), toLength(r.toLineNumber - r.fromLineNumber + 1, 0))); this.handleEdits(edits, true); if (!this.initialAstWithoutTokens) { this.didChangeEmitter.fire(); } } handleContentChanged(change) { const edits = TextEditInfo.fromModelContentChanges(change.changes); this.handleEdits(edits, false); } handleEdits(edits, tokenChange) { // Lazily queue the edits and only apply them when the tree is accessed. const result = combineTextEditInfos(this.queuedTextEdits, edits); this.queuedTextEdits = result; if (this.initialAstWithoutTokens && !tokenChange) { this.queuedTextEditsForInitialAstWithoutTokens = combineTextEditInfos(this.queuedTextEditsForInitialAstWithoutTokens, edits); } } //#endregion flushQueue() { if (this.queuedTextEdits.length > 0) { this.astWithTokens = this.parseDocumentFromTextBuffer(this.queuedTextEdits, this.astWithTokens, false); this.queuedTextEdits = []; } if (this.queuedTextEditsForInitialAstWithoutTokens.length > 0) { if (this.initialAstWithoutTokens) { this.initialAstWithoutTokens = this.parseDocumentFromTextBuffer(this.queuedTextEditsForInitialAstWithoutTokens, this.initialAstWithoutTokens, false); } this.queuedTextEditsForInitialAstWithoutTokens = []; } } /** * @pure (only if isPure = true) */ parseDocumentFromTextBuffer(edits, previousAst, immutable) { // Is much faster if `isPure = false`. const isPure = false; const previousAstClone = isPure ? previousAst === null || previousAst === void 0 ? void 0 : previousAst.deepClone() : previousAst; const tokenizer = new TextBufferTokenizer(this.textModel, this.brackets); const result = parseDocument(tokenizer, edits, previousAstClone, immutable); return result; } getBracketsInRange(range, onlyColorizedBrackets) { this.flushQueue(); const startOffset = toLength(range.startLineNumber - 1, range.startColumn - 1); const endOffset = toLength(range.endLineNumber - 1, range.endColumn - 1); return new CallbackIterable(cb => { const node = this.initialAstWithoutTokens || this.astWithTokens; collectBrackets(node, lengthZero, node.length, startOffset, endOffset, cb, 0, 0, new Map(), onlyColorizedBrackets); }); } getBracketPairsInRange(range, includeMinIndentation) { this.flushQueue(); const startLength = positionToLength(range.getStartPosition()); const endLength = positionToLength(range.getEndPosition()); return new CallbackIterable(cb => { const node = this.initialAstWithoutTokens || this.astWithTokens; const context = new CollectBracketPairsContext(cb, includeMinIndentation, this.textModel); collectBracketPairs(node, lengthZero, node.length, startLength, endLength, context, 0, new Map()); }); } getFirstBracketAfter(position) { this.flushQueue(); const node = this.initialAstWithoutTokens || this.astWithTokens; return getFirstBracketAfter(node, lengthZero, node.length, positionToLength(position)); } getFirstBracketBefore(position) { this.flushQueue(); const node = this.initialAstWithoutTokens || this.astWithTokens; return getFirstBracketBefore(node, lengthZero, node.length, positionToLength(position)); } } function getFirstBracketBefore(node, nodeOffsetStart, nodeOffsetEnd, position) { if (node.kind === 4 /* AstNodeKind.List */ || node.kind === 2 /* AstNodeKind.Pair */) { const lengths = []; for (const child of node.children) { nodeOffsetEnd = lengthAdd(nodeOffsetStart, child.length); lengths.push({ nodeOffsetStart, nodeOffsetEnd }); nodeOffsetStart = nodeOffsetEnd; } for (let i = lengths.length - 1; i >= 0; i--) { const { nodeOffsetStart, nodeOffsetEnd } = lengths[i]; if (lengthLessThan(nodeOffsetStart, position)) { const result = getFirstBracketBefore(node.children[i], nodeOffsetStart, nodeOffsetEnd, position); if (result) { return result; } } } return null; } else if (node.kind === 3 /* AstNodeKind.UnexpectedClosingBracket */) { return null; } else if (node.kind === 1 /* AstNodeKind.Bracket */) { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); return { bracketInfo: node.bracketInfo, range }; } return null; } function getFirstBracketAfter(node, nodeOffsetStart, nodeOffsetEnd, position) { if (node.kind === 4 /* AstNodeKind.List */ || node.kind === 2 /* AstNodeKind.Pair */) { for (const child of node.children) { nodeOffsetEnd = lengthAdd(nodeOffsetStart, child.length); if (lengthLessThan(position, nodeOffsetEnd)) { const result = getFirstBracketAfter(child, nodeOffsetStart, nodeOffsetEnd, position); if (result) { return result; } } nodeOffsetStart = nodeOffsetEnd; } return null; } else if (node.kind === 3 /* AstNodeKind.UnexpectedClosingBracket */) { return null; } else if (node.kind === 1 /* AstNodeKind.Bracket */) { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); return { bracketInfo: node.bracketInfo, range }; } return null; } function collectBrackets(node, nodeOffsetStart, nodeOffsetEnd, startOffset, endOffset, push, level, nestingLevelOfEqualBracketType, levelPerBracketType, onlyColorizedBrackets, parentPairIsIncomplete = false) { if (level > 200) { return true; } whileLoop: while (true) { switch (node.kind) { case 4 /* AstNodeKind.List */: { const childCount = node.childrenLength; for (let i = 0; i < childCount; i++) { const child = node.getChild(i); if (!child) { continue; } nodeOffsetEnd = lengthAdd(nodeOffsetStart, child.length); if (lengthLessThanEqual(nodeOffsetStart, endOffset) && lengthGreaterThanEqual(nodeOffsetEnd, startOffset)) { const childEndsAfterEnd = lengthGreaterThanEqual(nodeOffsetEnd, endOffset); if (childEndsAfterEnd) { // No child after this child in the requested window, don't recurse node = child; continue whileLoop; } const shouldContinue = collectBrackets(child, nodeOffsetStart, nodeOffsetEnd, startOffset, endOffset, push, level, 0, levelPerBracketType, onlyColorizedBrackets); if (!shouldContinue) { return false; } } nodeOffsetStart = nodeOffsetEnd; } return true; } case 2 /* AstNodeKind.Pair */: { const colorize = !onlyColorizedBrackets || !node.closingBracket || node.closingBracket.bracketInfo.closesColorized(node.openingBracket.bracketInfo); let levelPerBracket = 0; if (levelPerBracketType) { let existing = levelPerBracketType.get(node.openingBracket.text); if (existing === undefined) { existing = 0; } levelPerBracket = existing; if (colorize) { existing++; levelPerBracketType.set(node.openingBracket.text, existing); } } const childCount = node.childrenLength; for (let i = 0; i < childCount; i++) { const child = node.getChild(i); if (!child) { continue; } nodeOffsetEnd = lengthAdd(nodeOffsetStart, child.length); if (lengthLessThanEqual(nodeOffsetStart, endOffset) && lengthGreaterThanEqual(nodeOffsetEnd, startOffset)) { const childEndsAfterEnd = lengthGreaterThanEqual(nodeOffsetEnd, endOffset); if (childEndsAfterEnd && child.kind !== 1 /* AstNodeKind.Bracket */) { // No child after this child in the requested window, don't recurse // Don't do this for brackets because of unclosed/unopened brackets node = child; if (colorize) { level++; nestingLevelOfEqualBracketType = levelPerBracket + 1; } else { nestingLevelOfEqualBracketType = levelPerBracket; } continue whileLoop; } if (colorize || child.kind !== 1 /* AstNodeKind.Bracket */ || !node.closingBracket) { const shouldContinue = collectBrackets(child, nodeOffsetStart, nodeOffsetEnd, startOffset, endOffset, push, colorize ? level + 1 : level, colorize ? levelPerBracket + 1 : levelPerBracket, levelPerBracketType, onlyColorizedBrackets, !node.closingBracket); if (!shouldContinue) { return false; } } } nodeOffsetStart = nodeOffsetEnd; } levelPerBracketType === null || levelPerBracketType === void 0 ? void 0 : levelPerBracketType.set(node.openingBracket.text, levelPerBracket); return true; } case 3 /* AstNodeKind.UnexpectedClosingBracket */: { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); return push(new BracketInfo(range, level - 1, 0, true)); } case 1 /* AstNodeKind.Bracket */: { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); return push(new BracketInfo(range, level - 1, nestingLevelOfEqualBracketType - 1, parentPairIsIncomplete)); } case 0 /* AstNodeKind.Text */: return true; } } } class CollectBracketPairsContext { constructor(push, includeMinIndentation, textModel) { this.push = push; this.includeMinIndentation = includeMinIndentation; this.textModel = textModel; } } function collectBracketPairs(node, nodeOffsetStart, nodeOffsetEnd, startOffset, endOffset, context, level, levelPerBracketType) { var _a; if (level > 200) { return true; } let shouldContinue = true; if (node.kind === 2 /* AstNodeKind.Pair */) { let levelPerBracket = 0; if (levelPerBracketType) { let existing = levelPerBracketType.get(node.openingBracket.text); if (existing === undefined) { existing = 0; } levelPerBracket = existing; existing++; levelPerBracketType.set(node.openingBracket.text, existing); } const openingBracketEnd = lengthAdd(nodeOffsetStart, node.openingBracket.length); let minIndentation = -1; if (context.includeMinIndentation) { minIndentation = node.computeMinIndentation(nodeOffsetStart, context.textModel); } shouldContinue = context.push(new BracketPairWithMinIndentationInfo(lengthsToRange(nodeOffsetStart, nodeOffsetEnd), lengthsToRange(nodeOffsetStart, openingBracketEnd), node.closingBracket ? lengthsToRange(lengthAdd(openingBracketEnd, ((_a = node.child) === null || _a === void 0 ? void 0 : _a.length) || lengthZero), nodeOffsetEnd) : undefined, level, levelPerBracket, node, minIndentation)); nodeOffsetStart = openingBracketEnd; if (shouldContinue && node.child) { const child = node.child; nodeOffsetEnd = lengthAdd(nodeOffsetStart, child.length); if (lengthLessThanEqual(nodeOffsetStart, endOffset) && lengthGreaterThanEqual(nodeOffsetEnd, startOffset)) { shouldContinue = collectBracketPairs(child, nodeOffsetStart, nodeOffsetEnd, startOffset, endOffset, context, level + 1, levelPerBracketType); if (!shouldContinue) { return false; } } } levelPerBracketType === null || levelPerBracketType === void 0 ? void 0 : levelPerBracketType.set(node.openingBracket.text, levelPerBracket); } else { let curOffset = nodeOffsetStart; for (const child of node.children) { const childOffset = curOffset; curOffset = lengthAdd(curOffset, child.length); if (lengthLessThanEqual(childOffset, endOffset) && lengthLessThanEqual(startOffset, curOffset)) { shouldContinue = collectBracketPairs(child, childOffset, curOffset, startOffset, endOffset, context, level, levelPerBracketType); if (!shouldContinue) { return false; } } } } return shouldContinue; }