UNPKG

monaco-editor-core

Version:

A browser based code editor

330 lines (329 loc) • 14.9 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { LineTokens } from '../tokens/lineTokens.js'; import { Position } from '../core/position.js'; import { LineInjectedText } from '../textModelEvents.js'; import { SingleLineInlineDecoration, ViewLineData } from '../viewModel.js'; export function createModelLineProjection(lineBreakData, isVisible) { if (lineBreakData === null) { // No mapping needed if (isVisible) { return IdentityModelLineProjection.INSTANCE; } return HiddenModelLineProjection.INSTANCE; } else { return new ModelLineProjection(lineBreakData, isVisible); } } /** * This projection is used to * * wrap model lines * * inject text */ class ModelLineProjection { constructor(lineBreakData, isVisible) { this._projectionData = lineBreakData; this._isVisible = isVisible; } isVisible() { return this._isVisible; } setVisible(isVisible) { this._isVisible = isVisible; return this; } getProjectionData() { return this._projectionData; } getViewLineCount() { if (!this._isVisible) { return 0; } return this._projectionData.getOutputLineCount(); } getViewLineContent(model, modelLineNumber, outputLineIndex) { this._assertVisible(); const startOffsetInInputWithInjections = outputLineIndex > 0 ? this._projectionData.breakOffsets[outputLineIndex - 1] : 0; const endOffsetInInputWithInjections = this._projectionData.breakOffsets[outputLineIndex]; let r; if (this._projectionData.injectionOffsets !== null) { const injectedTexts = this._projectionData.injectionOffsets.map((offset, idx) => new LineInjectedText(0, 0, offset + 1, this._projectionData.injectionOptions[idx], 0)); const lineWithInjections = LineInjectedText.applyInjectedText(model.getLineContent(modelLineNumber), injectedTexts); r = lineWithInjections.substring(startOffsetInInputWithInjections, endOffsetInInputWithInjections); } else { r = model.getValueInRange({ startLineNumber: modelLineNumber, startColumn: startOffsetInInputWithInjections + 1, endLineNumber: modelLineNumber, endColumn: endOffsetInInputWithInjections + 1 }); } if (outputLineIndex > 0) { r = spaces(this._projectionData.wrappedTextIndentLength) + r; } return r; } getViewLineLength(model, modelLineNumber, outputLineIndex) { this._assertVisible(); return this._projectionData.getLineLength(outputLineIndex); } getViewLineMinColumn(_model, _modelLineNumber, outputLineIndex) { this._assertVisible(); return this._projectionData.getMinOutputOffset(outputLineIndex) + 1; } getViewLineMaxColumn(model, modelLineNumber, outputLineIndex) { this._assertVisible(); return this._projectionData.getMaxOutputOffset(outputLineIndex) + 1; } /** * Try using {@link getViewLinesData} instead. */ getViewLineData(model, modelLineNumber, outputLineIndex) { const arr = new Array(); this.getViewLinesData(model, modelLineNumber, outputLineIndex, 1, 0, [true], arr); return arr[0]; } getViewLinesData(model, modelLineNumber, outputLineIdx, lineCount, globalStartIndex, needed, result) { this._assertVisible(); const lineBreakData = this._projectionData; const injectionOffsets = lineBreakData.injectionOffsets; const injectionOptions = lineBreakData.injectionOptions; let inlineDecorationsPerOutputLine = null; if (injectionOffsets) { inlineDecorationsPerOutputLine = []; let totalInjectedTextLengthBefore = 0; let currentInjectedOffset = 0; for (let outputLineIndex = 0; outputLineIndex < lineBreakData.getOutputLineCount(); outputLineIndex++) { const inlineDecorations = new Array(); inlineDecorationsPerOutputLine[outputLineIndex] = inlineDecorations; const lineStartOffsetInInputWithInjections = outputLineIndex > 0 ? lineBreakData.breakOffsets[outputLineIndex - 1] : 0; const lineEndOffsetInInputWithInjections = lineBreakData.breakOffsets[outputLineIndex]; while (currentInjectedOffset < injectionOffsets.length) { const length = injectionOptions[currentInjectedOffset].content.length; const injectedTextStartOffsetInInputWithInjections = injectionOffsets[currentInjectedOffset] + totalInjectedTextLengthBefore; const injectedTextEndOffsetInInputWithInjections = injectedTextStartOffsetInInputWithInjections + length; if (injectedTextStartOffsetInInputWithInjections > lineEndOffsetInInputWithInjections) { // Injected text only starts in later wrapped lines. break; } if (lineStartOffsetInInputWithInjections < injectedTextEndOffsetInInputWithInjections) { // Injected text ends after or in this line (but also starts in or before this line). const options = injectionOptions[currentInjectedOffset]; if (options.inlineClassName) { const offset = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0); const start = offset + Math.max(injectedTextStartOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, 0); const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections); if (start !== end) { inlineDecorations.push(new SingleLineInlineDecoration(start, end, options.inlineClassName, options.inlineClassNameAffectsLetterSpacing)); } } } if (injectedTextEndOffsetInInputWithInjections <= lineEndOffsetInInputWithInjections) { totalInjectedTextLengthBefore += length; currentInjectedOffset++; } else { // injected text breaks into next line, process it again break; } } } } let lineWithInjections; if (injectionOffsets) { lineWithInjections = model.tokenization.getLineTokens(modelLineNumber).withInserted(injectionOffsets.map((offset, idx) => ({ offset, text: injectionOptions[idx].content, tokenMetadata: LineTokens.defaultTokenMetadata }))); } else { lineWithInjections = model.tokenization.getLineTokens(modelLineNumber); } for (let outputLineIndex = outputLineIdx; outputLineIndex < outputLineIdx + lineCount; outputLineIndex++) { const globalIndex = globalStartIndex + outputLineIndex - outputLineIdx; if (!needed[globalIndex]) { result[globalIndex] = null; continue; } result[globalIndex] = this._getViewLineData(lineWithInjections, inlineDecorationsPerOutputLine ? inlineDecorationsPerOutputLine[outputLineIndex] : null, outputLineIndex); } } _getViewLineData(lineWithInjections, inlineDecorations, outputLineIndex) { this._assertVisible(); const lineBreakData = this._projectionData; const deltaStartIndex = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0); const lineStartOffsetInInputWithInjections = outputLineIndex > 0 ? lineBreakData.breakOffsets[outputLineIndex - 1] : 0; const lineEndOffsetInInputWithInjections = lineBreakData.breakOffsets[outputLineIndex]; const tokens = lineWithInjections.sliceAndInflate(lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections, deltaStartIndex); let lineContent = tokens.getLineContent(); if (outputLineIndex > 0) { lineContent = spaces(lineBreakData.wrappedTextIndentLength) + lineContent; } const minColumn = this._projectionData.getMinOutputOffset(outputLineIndex) + 1; const maxColumn = lineContent.length + 1; const continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); const startVisibleColumn = (outputLineIndex === 0 ? 0 : lineBreakData.breakOffsetsVisibleColumn[outputLineIndex - 1]); return new ViewLineData(lineContent, continuesWithWrappedLine, minColumn, maxColumn, startVisibleColumn, tokens, inlineDecorations); } getModelColumnOfViewPosition(outputLineIndex, outputColumn) { this._assertVisible(); return this._projectionData.translateToInputOffset(outputLineIndex, outputColumn - 1) + 1; } getViewPositionOfModelPosition(deltaLineNumber, inputColumn, affinity = 2 /* PositionAffinity.None */) { this._assertVisible(); const r = this._projectionData.translateToOutputPosition(inputColumn - 1, affinity); return r.toPosition(deltaLineNumber); } getViewLineNumberOfModelPosition(deltaLineNumber, inputColumn) { this._assertVisible(); const r = this._projectionData.translateToOutputPosition(inputColumn - 1); return deltaLineNumber + r.outputLineIndex; } normalizePosition(outputLineIndex, outputPosition, affinity) { const baseViewLineNumber = outputPosition.lineNumber - outputLineIndex; const normalizedOutputPosition = this._projectionData.normalizeOutputPosition(outputLineIndex, outputPosition.column - 1, affinity); const result = normalizedOutputPosition.toPosition(baseViewLineNumber); return result; } getInjectedTextAt(outputLineIndex, outputColumn) { return this._projectionData.getInjectedText(outputLineIndex, outputColumn - 1); } _assertVisible() { if (!this._isVisible) { throw new Error('Not supported'); } } } /** * This projection does not change the model line. */ class IdentityModelLineProjection { static { this.INSTANCE = new IdentityModelLineProjection(); } constructor() { } isVisible() { return true; } setVisible(isVisible) { if (isVisible) { return this; } return HiddenModelLineProjection.INSTANCE; } getProjectionData() { return null; } getViewLineCount() { return 1; } getViewLineContent(model, modelLineNumber, _outputLineIndex) { return model.getLineContent(modelLineNumber); } getViewLineLength(model, modelLineNumber, _outputLineIndex) { return model.getLineLength(modelLineNumber); } getViewLineMinColumn(model, modelLineNumber, _outputLineIndex) { return model.getLineMinColumn(modelLineNumber); } getViewLineMaxColumn(model, modelLineNumber, _outputLineIndex) { return model.getLineMaxColumn(modelLineNumber); } getViewLineData(model, modelLineNumber, _outputLineIndex) { const lineTokens = model.tokenization.getLineTokens(modelLineNumber); const lineContent = lineTokens.getLineContent(); return new ViewLineData(lineContent, false, 1, lineContent.length + 1, 0, lineTokens.inflate(), null); } getViewLinesData(model, modelLineNumber, _fromOuputLineIndex, _toOutputLineIndex, globalStartIndex, needed, result) { if (!needed[globalStartIndex]) { result[globalStartIndex] = null; return; } result[globalStartIndex] = this.getViewLineData(model, modelLineNumber, 0); } getModelColumnOfViewPosition(_outputLineIndex, outputColumn) { return outputColumn; } getViewPositionOfModelPosition(deltaLineNumber, inputColumn) { return new Position(deltaLineNumber, inputColumn); } getViewLineNumberOfModelPosition(deltaLineNumber, _inputColumn) { return deltaLineNumber; } normalizePosition(outputLineIndex, outputPosition, affinity) { return outputPosition; } getInjectedTextAt(_outputLineIndex, _outputColumn) { return null; } } /** * This projection hides the model line. */ class HiddenModelLineProjection { static { this.INSTANCE = new HiddenModelLineProjection(); } constructor() { } isVisible() { return false; } setVisible(isVisible) { if (!isVisible) { return this; } return IdentityModelLineProjection.INSTANCE; } getProjectionData() { return null; } getViewLineCount() { return 0; } getViewLineContent(_model, _modelLineNumber, _outputLineIndex) { throw new Error('Not supported'); } getViewLineLength(_model, _modelLineNumber, _outputLineIndex) { throw new Error('Not supported'); } getViewLineMinColumn(_model, _modelLineNumber, _outputLineIndex) { throw new Error('Not supported'); } getViewLineMaxColumn(_model, _modelLineNumber, _outputLineIndex) { throw new Error('Not supported'); } getViewLineData(_model, _modelLineNumber, _outputLineIndex) { throw new Error('Not supported'); } getViewLinesData(_model, _modelLineNumber, _fromOuputLineIndex, _toOutputLineIndex, _globalStartIndex, _needed, _result) { throw new Error('Not supported'); } getModelColumnOfViewPosition(_outputLineIndex, _outputColumn) { throw new Error('Not supported'); } getViewPositionOfModelPosition(_deltaLineNumber, _inputColumn) { throw new Error('Not supported'); } getViewLineNumberOfModelPosition(_deltaLineNumber, _inputColumn) { throw new Error('Not supported'); } normalizePosition(outputLineIndex, outputPosition, affinity) { throw new Error('Not supported'); } getInjectedTextAt(_outputLineIndex, _outputColumn) { throw new Error('Not supported'); } } const _spaces = ['']; function spaces(count) { if (count >= _spaces.length) { for (let i = 1; i <= count; i++) { _spaces[i] = _makeSpaces(i); } } return _spaces[count]; } function _makeSpaces(count) { return new Array(count + 1).join(' '); }