123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /*---------------------------------------------------------------------------------------------
- * 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';
- /**
- * *input*:
- * ```
- * xxxxxxxxxxxxxxxxxxxxxxxxxxx
- * ```
- *
- * -> Applying injections `[i...i]`, *inputWithInjections*:
- * ```
- * xxxxxx[iiiiiiiiii]xxxxxxxxxxxxxxxxx[ii]xxxx
- * ```
- *
- * -> breaking at offsets `|` in `xxxxxx[iiiiiii|iii]xxxxxxxxxxx|xxxxxx[ii]xxxx|`:
- * ```
- * xxxxxx[iiiiiii
- * iii]xxxxxxxxxxx
- * xxxxxx[ii]xxxx
- * ```
- *
- * -> applying wrappedTextIndentLength, *output*:
- * ```
- * xxxxxx[iiiiiii
- * iii]xxxxxxxxxxx
- * xxxxxx[ii]xxxx
- * ```
- */
- export class ModelLineProjectionData {
- constructor(injectionOffsets,
- /**
- * `injectionOptions.length` must equal `injectionOffsets.length`
- */
- injectionOptions,
- /**
- * Refers to offsets after applying injections to the source.
- * The last break offset indicates the length of the source after applying injections.
- */
- breakOffsets,
- /**
- * Refers to offsets after applying injections
- */
- breakOffsetsVisibleColumn, wrappedTextIndentLength) {
- this.injectionOffsets = injectionOffsets;
- this.injectionOptions = injectionOptions;
- this.breakOffsets = breakOffsets;
- this.breakOffsetsVisibleColumn = breakOffsetsVisibleColumn;
- this.wrappedTextIndentLength = wrappedTextIndentLength;
- }
- getOutputLineCount() {
- return this.breakOffsets.length;
- }
- getMinOutputOffset(outputLineIndex) {
- if (outputLineIndex > 0) {
- return this.wrappedTextIndentLength;
- }
- return 0;
- }
- getLineLength(outputLineIndex) {
- // These offsets refer to model text with injected text.
- const startOffset = outputLineIndex > 0 ? this.breakOffsets[outputLineIndex - 1] : 0;
- const endOffset = this.breakOffsets[outputLineIndex];
- let lineLength = endOffset - startOffset;
- if (outputLineIndex > 0) {
- lineLength += this.wrappedTextIndentLength;
- }
- return lineLength;
- }
- getMaxOutputOffset(outputLineIndex) {
- return this.getLineLength(outputLineIndex);
- }
- translateToInputOffset(outputLineIndex, outputOffset) {
- if (outputLineIndex > 0) {
- outputOffset = Math.max(0, outputOffset - this.wrappedTextIndentLength);
- }
- const offsetInInputWithInjection = outputLineIndex === 0 ? outputOffset : this.breakOffsets[outputLineIndex - 1] + outputOffset;
- let offsetInInput = offsetInInputWithInjection;
- if (this.injectionOffsets !== null) {
- for (let i = 0; i < this.injectionOffsets.length; i++) {
- if (offsetInInput > this.injectionOffsets[i]) {
- if (offsetInInput < this.injectionOffsets[i] + this.injectionOptions[i].content.length) {
- // `inputOffset` is within injected text
- offsetInInput = this.injectionOffsets[i];
- }
- else {
- offsetInInput -= this.injectionOptions[i].content.length;
- }
- }
- else {
- break;
- }
- }
- }
- return offsetInInput;
- }
- translateToOutputPosition(inputOffset, affinity = 2 /* None */) {
- let inputOffsetInInputWithInjection = inputOffset;
- if (this.injectionOffsets !== null) {
- for (let i = 0; i < this.injectionOffsets.length; i++) {
- if (inputOffset < this.injectionOffsets[i]) {
- break;
- }
- if (affinity !== 1 /* Right */ && inputOffset === this.injectionOffsets[i]) {
- break;
- }
- inputOffsetInInputWithInjection += this.injectionOptions[i].content.length;
- }
- }
- return this.offsetInInputWithInjectionsToOutputPosition(inputOffsetInInputWithInjection, affinity);
- }
- offsetInInputWithInjectionsToOutputPosition(offsetInInputWithInjections, affinity = 2 /* None */) {
- let low = 0;
- let high = this.breakOffsets.length - 1;
- let mid = 0;
- let midStart = 0;
- while (low <= high) {
- mid = low + ((high - low) / 2) | 0;
- const midStop = this.breakOffsets[mid];
- midStart = mid > 0 ? this.breakOffsets[mid - 1] : 0;
- if (affinity === 0 /* Left */) {
- if (offsetInInputWithInjections <= midStart) {
- high = mid - 1;
- }
- else if (offsetInInputWithInjections > midStop) {
- low = mid + 1;
- }
- else {
- break;
- }
- }
- else {
- if (offsetInInputWithInjections < midStart) {
- high = mid - 1;
- }
- else if (offsetInInputWithInjections >= midStop) {
- low = mid + 1;
- }
- else {
- break;
- }
- }
- }
- let outputOffset = offsetInInputWithInjections - midStart;
- if (mid > 0) {
- outputOffset += this.wrappedTextIndentLength;
- }
- return new OutputPosition(mid, outputOffset);
- }
- normalizeOutputPosition(outputLineIndex, outputOffset, affinity) {
- if (this.injectionOffsets !== null) {
- const offsetInInputWithInjections = this.outputPositionToOffsetInInputWithInjections(outputLineIndex, outputOffset);
- const normalizedOffsetInUnwrappedLine = this.normalizeOffsetInInputWithInjectionsAroundInjections(offsetInInputWithInjections, affinity);
- if (normalizedOffsetInUnwrappedLine !== offsetInInputWithInjections) {
- // injected text caused a change
- return this.offsetInInputWithInjectionsToOutputPosition(normalizedOffsetInUnwrappedLine, affinity);
- }
- }
- if (affinity === 0 /* Left */) {
- if (outputLineIndex > 0 && outputOffset === this.getMinOutputOffset(outputLineIndex)) {
- return new OutputPosition(outputLineIndex - 1, this.getMaxOutputOffset(outputLineIndex - 1));
- }
- }
- else if (affinity === 1 /* Right */) {
- const maxOutputLineIndex = this.getOutputLineCount() - 1;
- if (outputLineIndex < maxOutputLineIndex && outputOffset === this.getMaxOutputOffset(outputLineIndex)) {
- return new OutputPosition(outputLineIndex + 1, this.getMinOutputOffset(outputLineIndex + 1));
- }
- }
- return new OutputPosition(outputLineIndex, outputOffset);
- }
- outputPositionToOffsetInInputWithInjections(outputLineIndex, outputOffset) {
- if (outputLineIndex > 0) {
- outputOffset = Math.max(0, outputOffset - this.wrappedTextIndentLength);
- }
- const result = (outputLineIndex > 0 ? this.breakOffsets[outputLineIndex - 1] : 0) + outputOffset;
- return result;
- }
- normalizeOffsetInInputWithInjectionsAroundInjections(offsetInInputWithInjections, affinity) {
- const injectedText = this.getInjectedTextAtOffset(offsetInInputWithInjections);
- if (!injectedText) {
- return offsetInInputWithInjections;
- }
- if (affinity === 2 /* None */) {
- if (offsetInInputWithInjections === injectedText.offsetInInputWithInjections + injectedText.length) {
- // go to the end of this injected text
- return injectedText.offsetInInputWithInjections + injectedText.length;
- }
- else {
- // go to the start of this injected text
- return injectedText.offsetInInputWithInjections;
- }
- }
- if (affinity === 1 /* Right */) {
- let result = injectedText.offsetInInputWithInjections + injectedText.length;
- let index = injectedText.injectedTextIndex;
- // traverse all injected text that touch each other
- while (index + 1 < this.injectionOffsets.length && this.injectionOffsets[index + 1] === this.injectionOffsets[index]) {
- result += this.injectionOptions[index + 1].content.length;
- index++;
- }
- return result;
- }
- // affinity is left
- let result = injectedText.offsetInInputWithInjections;
- let index = injectedText.injectedTextIndex;
- // traverse all injected text that touch each other
- while (index - 1 >= 0 && this.injectionOffsets[index - 1] === this.injectionOffsets[index]) {
- result -= this.injectionOptions[index - 1].content.length;
- index++;
- }
- return result;
- }
- getInjectedText(outputLineIndex, outputOffset) {
- const offset = this.outputPositionToOffsetInInputWithInjections(outputLineIndex, outputOffset);
- const injectedText = this.getInjectedTextAtOffset(offset);
- if (!injectedText) {
- return null;
- }
- return {
- options: this.injectionOptions[injectedText.injectedTextIndex]
- };
- }
- getInjectedTextAtOffset(offsetInInputWithInjections) {
- const injectionOffsets = this.injectionOffsets;
- const injectionOptions = this.injectionOptions;
- if (injectionOffsets !== null) {
- let totalInjectedTextLengthBefore = 0;
- for (let i = 0; i < injectionOffsets.length; i++) {
- const length = injectionOptions[i].content.length;
- const injectedTextStartOffsetInInputWithInjections = injectionOffsets[i] + totalInjectedTextLengthBefore;
- const injectedTextEndOffsetInInputWithInjections = injectionOffsets[i] + totalInjectedTextLengthBefore + length;
- if (injectedTextStartOffsetInInputWithInjections > offsetInInputWithInjections) {
- // Injected text starts later.
- break; // All later injected texts have an even larger offset.
- }
- if (offsetInInputWithInjections <= injectedTextEndOffsetInInputWithInjections) {
- // Injected text ends after or with the given position (but also starts with or before it).
- return {
- injectedTextIndex: i,
- offsetInInputWithInjections: injectedTextStartOffsetInInputWithInjections,
- length
- };
- }
- totalInjectedTextLengthBefore += length;
- }
- }
- return undefined;
- }
- }
- export class InjectedText {
- constructor(options) {
- this.options = options;
- }
- }
- export class OutputPosition {
- constructor(outputLineIndex, outputOffset) {
- this.outputLineIndex = outputLineIndex;
- this.outputOffset = outputOffset;
- }
- toString() {
- return `${this.outputLineIndex}:${this.outputOffset}`;
- }
- toPosition(baseLineNumber) {
- return new Position(baseLineNumber + this.outputLineIndex, this.outputOffset + 1);
- }
- }
|