UNPKG

monaco-editor-core

Version:

A browser based code editor

166 lines (165 loc) 9.18 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; import { InlineDecoration, ViewModelDecoration } from '../viewModel.js'; import { filterValidationDecorations } from '../config/editorOptions.js'; export class ViewModelDecorations { constructor(editorId, model, configuration, linesCollection, coordinatesConverter) { this.editorId = editorId; this.model = model; this.configuration = configuration; this._linesCollection = linesCollection; this._coordinatesConverter = coordinatesConverter; this._decorationsCache = Object.create(null); this._cachedModelDecorationsResolver = null; this._cachedModelDecorationsResolverViewRange = null; } _clearCachedModelDecorationsResolver() { this._cachedModelDecorationsResolver = null; this._cachedModelDecorationsResolverViewRange = null; } dispose() { this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); } reset() { this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); } onModelDecorationsChanged() { this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); } onLineMappingChanged() { this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); } _getOrCreateViewModelDecoration(modelDecoration) { const id = modelDecoration.id; let r = this._decorationsCache[id]; if (!r) { const modelRange = modelDecoration.range; const options = modelDecoration.options; let viewRange; if (options.isWholeLine) { const start = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.startLineNumber, 1), 0 /* PositionAffinity.Left */, false, true); const end = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.endLineNumber, this.model.getLineMaxColumn(modelRange.endLineNumber)), 1 /* PositionAffinity.Right */); viewRange = new Range(start.lineNumber, start.column, end.lineNumber, end.column); } else { // For backwards compatibility reasons, we want injected text before any decoration. // Thus, move decorations to the right. viewRange = this._coordinatesConverter.convertModelRangeToViewRange(modelRange, 1 /* PositionAffinity.Right */); } r = new ViewModelDecoration(viewRange, options); this._decorationsCache[id] = r; } return r; } getMinimapDecorationsInRange(range) { return this._getDecorationsInRange(range, true, false).decorations; } getDecorationsViewportData(viewRange) { let cacheIsValid = (this._cachedModelDecorationsResolver !== null); cacheIsValid = cacheIsValid && (viewRange.equalsRange(this._cachedModelDecorationsResolverViewRange)); if (!cacheIsValid) { this._cachedModelDecorationsResolver = this._getDecorationsInRange(viewRange, false, false); this._cachedModelDecorationsResolverViewRange = viewRange; } return this._cachedModelDecorationsResolver; } getInlineDecorationsOnLine(lineNumber, onlyMinimapDecorations = false, onlyMarginDecorations = false) { const range = new Range(lineNumber, this._linesCollection.getViewLineMinColumn(lineNumber), lineNumber, this._linesCollection.getViewLineMaxColumn(lineNumber)); return this._getDecorationsInRange(range, onlyMinimapDecorations, onlyMarginDecorations).inlineDecorations[0]; } _getDecorationsInRange(viewRange, onlyMinimapDecorations, onlyMarginDecorations) { const modelDecorations = this._linesCollection.getDecorationsInRange(viewRange, this.editorId, filterValidationDecorations(this.configuration.options), onlyMinimapDecorations, onlyMarginDecorations); const startLineNumber = viewRange.startLineNumber; const endLineNumber = viewRange.endLineNumber; const decorationsInViewport = []; let decorationsInViewportLen = 0; const inlineDecorations = []; for (let j = startLineNumber; j <= endLineNumber; j++) { inlineDecorations[j - startLineNumber] = []; } for (let i = 0, len = modelDecorations.length; i < len; i++) { const modelDecoration = modelDecorations[i]; const decorationOptions = modelDecoration.options; if (!isModelDecorationVisible(this.model, modelDecoration)) { continue; } const viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration); const viewRange = viewModelDecoration.range; decorationsInViewport[decorationsInViewportLen++] = viewModelDecoration; if (decorationOptions.inlineClassName) { const inlineDecoration = new InlineDecoration(viewRange, decorationOptions.inlineClassName, decorationOptions.inlineClassNameAffectsLetterSpacing ? 3 /* InlineDecorationType.RegularAffectingLetterSpacing */ : 0 /* InlineDecorationType.Regular */); const intersectedStartLineNumber = Math.max(startLineNumber, viewRange.startLineNumber); const intersectedEndLineNumber = Math.min(endLineNumber, viewRange.endLineNumber); for (let j = intersectedStartLineNumber; j <= intersectedEndLineNumber; j++) { inlineDecorations[j - startLineNumber].push(inlineDecoration); } } if (decorationOptions.beforeContentClassName) { if (startLineNumber <= viewRange.startLineNumber && viewRange.startLineNumber <= endLineNumber) { const inlineDecoration = new InlineDecoration(new Range(viewRange.startLineNumber, viewRange.startColumn, viewRange.startLineNumber, viewRange.startColumn), decorationOptions.beforeContentClassName, 1 /* InlineDecorationType.Before */); inlineDecorations[viewRange.startLineNumber - startLineNumber].push(inlineDecoration); } } if (decorationOptions.afterContentClassName) { if (startLineNumber <= viewRange.endLineNumber && viewRange.endLineNumber <= endLineNumber) { const inlineDecoration = new InlineDecoration(new Range(viewRange.endLineNumber, viewRange.endColumn, viewRange.endLineNumber, viewRange.endColumn), decorationOptions.afterContentClassName, 2 /* InlineDecorationType.After */); inlineDecorations[viewRange.endLineNumber - startLineNumber].push(inlineDecoration); } } } return { decorations: decorationsInViewport, inlineDecorations: inlineDecorations }; } } export function isModelDecorationVisible(model, decoration) { if (decoration.options.hideInCommentTokens && isModelDecorationInComment(model, decoration)) { return false; } if (decoration.options.hideInStringTokens && isModelDecorationInString(model, decoration)) { return false; } return true; } export function isModelDecorationInComment(model, decoration) { return testTokensInRange(model, decoration.range, (tokenType) => tokenType === 1 /* StandardTokenType.Comment */); } export function isModelDecorationInString(model, decoration) { return testTokensInRange(model, decoration.range, (tokenType) => tokenType === 2 /* StandardTokenType.String */); } /** * Calls the callback for every token that intersects the range. * If the callback returns `false`, iteration stops and `false` is returned. * Otherwise, `true` is returned. */ function testTokensInRange(model, range, callback) { for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { const lineTokens = model.tokenization.getLineTokens(lineNumber); const isFirstLine = lineNumber === range.startLineNumber; const isEndLine = lineNumber === range.endLineNumber; let tokenIdx = isFirstLine ? lineTokens.findTokenIndexAtOffset(range.startColumn - 1) : 0; while (tokenIdx < lineTokens.getCount()) { if (isEndLine) { const startOffset = lineTokens.getStartOffset(tokenIdx); if (startOffset > range.endColumn - 1) { break; } } const callbackResult = callback(lineTokens.getStandardTokenType(tokenIdx)); if (!callbackResult) { return false; } tokenIdx++; } } return true; }