text: /*---------------------------------------------------------------------------------------------
   *  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);
      }
  }
  