text: /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  import * as strings from '../../../../base/common/strings';
  import { Range } from '../../core/range';
  import { ApplyEditsResult } from '../../model';
  import { PieceTreeBase } from './pieceTreeBase';
  var PieceTreeTextBuffer = /** @class */ (function () {
      function PieceTreeTextBuffer(chunks, BOM, eol, containsRTL, isBasicASCII, eolNormalized) {
          this._BOM = BOM;
          this._mightContainNonBasicASCII = !isBasicASCII;
          this._mightContainRTL = containsRTL;
          this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized);
      }
      PieceTreeTextBuffer.prototype.mightContainRTL = function () {
          return this._mightContainRTL;
      };
      PieceTreeTextBuffer.prototype.mightContainNonBasicASCII = function () {
          return this._mightContainNonBasicASCII;
      };
      PieceTreeTextBuffer.prototype.getBOM = function () {
          return this._BOM;
      };
      PieceTreeTextBuffer.prototype.getEOL = function () {
          return this._pieceTree.getEOL();
      };
      PieceTreeTextBuffer.prototype.getOffsetAt = function (lineNumber, column) {
          return this._pieceTree.getOffsetAt(lineNumber, column);
      };
      PieceTreeTextBuffer.prototype.getPositionAt = function (offset) {
          return this._pieceTree.getPositionAt(offset);
      };
      PieceTreeTextBuffer.prototype.getRangeAt = function (start, length) {
          var end = start + length;
          var startPosition = this.getPositionAt(start);
          var endPosition = this.getPositionAt(end);
          return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
      };
      PieceTreeTextBuffer.prototype.getValueInRange = function (range, eol) {
          if (eol === void 0) { eol = 0 /* TextDefined */; }
          if (range.isEmpty()) {
              return '';
          }
          var lineEnding = this._getEndOfLine(eol);
          return this._pieceTree.getValueInRange(range, lineEnding);
      };
      PieceTreeTextBuffer.prototype.getValueLengthInRange = function (range, eol) {
          if (eol === void 0) { eol = 0 /* TextDefined */; }
          if (range.isEmpty()) {
              return 0;
          }
          if (range.startLineNumber === range.endLineNumber) {
              return (range.endColumn - range.startColumn);
          }
          var startOffset = this.getOffsetAt(range.startLineNumber, range.startColumn);
          var endOffset = this.getOffsetAt(range.endLineNumber, range.endColumn);
          return endOffset - startOffset;
      };
      PieceTreeTextBuffer.prototype.getCharacterCountInRange = function (range, eol) {
          if (eol === void 0) { eol = 0 /* TextDefined */; }
          if (this._mightContainNonBasicASCII) {
              // we must count by iterating
              var result = 0;
              var fromLineNumber = range.startLineNumber;
              var toLineNumber = range.endLineNumber;
              for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
                  var lineContent = this.getLineContent(lineNumber);
                  var fromOffset = (lineNumber === fromLineNumber ? range.startColumn - 1 : 0);
                  var toOffset = (lineNumber === toLineNumber ? range.endColumn - 1 : lineContent.length);
                  for (var offset = fromOffset; offset < toOffset; offset++) {
                      if (strings.isHighSurrogate(lineContent.charCodeAt(offset))) {
                          result = result + 1;
                          offset = offset + 1;
                      }
                      else {
                          result = result + 1;
                      }
                  }
              }
              result += this._getEndOfLine(eol).length * (toLineNumber - fromLineNumber);
              return result;
          }
          return this.getValueLengthInRange(range, eol);
      };
      PieceTreeTextBuffer.prototype.getLength = function () {
          return this._pieceTree.getLength();
      };
      PieceTreeTextBuffer.prototype.getLineCount = function () {
          return this._pieceTree.getLineCount();
      };
      PieceTreeTextBuffer.prototype.getLinesContent = function () {
          return this._pieceTree.getLinesContent();
      };
      PieceTreeTextBuffer.prototype.getLineContent = function (lineNumber) {
          return this._pieceTree.getLineContent(lineNumber);
      };
      PieceTreeTextBuffer.prototype.getLineCharCode = function (lineNumber, index) {
          return this._pieceTree.getLineCharCode(lineNumber, index);
      };
      PieceTreeTextBuffer.prototype.getLineLength = function (lineNumber) {
          return this._pieceTree.getLineLength(lineNumber);
      };
      PieceTreeTextBuffer.prototype.getLineFirstNonWhitespaceColumn = function (lineNumber) {
          var result = strings.firstNonWhitespaceIndex(this.getLineContent(lineNumber));
          if (result === -1) {
              return 0;
          }
          return result + 1;
      };
      PieceTreeTextBuffer.prototype.getLineLastNonWhitespaceColumn = function (lineNumber) {
          var result = strings.lastNonWhitespaceIndex(this.getLineContent(lineNumber));
          if (result === -1) {
              return 0;
          }
          return result + 2;
      };
      PieceTreeTextBuffer.prototype._getEndOfLine = function (eol) {
          switch (eol) {
              case 1 /* LF */:
                  return '\n';
              case 2 /* CRLF */:
                  return '\r\n';
              case 0 /* TextDefined */:
                  return this.getEOL();
          }
          throw new Error('Unknown EOL preference');
      };
      PieceTreeTextBuffer.prototype.setEOL = function (newEOL) {
          this._pieceTree.setEOL(newEOL);
      };
      PieceTreeTextBuffer.prototype.applyEdits = function (rawOperations, recordTrimAutoWhitespace) {
          var mightContainRTL = this._mightContainRTL;
          var mightContainNonBasicASCII = this._mightContainNonBasicASCII;
          var canReduceOperations = true;
          var operations = [];
          for (var i = 0; i < rawOperations.length; i++) {
              var op = rawOperations[i];
              if (canReduceOperations && op._isTracked) {
                  canReduceOperations = false;
              }
              var validatedRange = op.range;
              if (!mightContainRTL && op.text) {
                  // check if the new inserted text contains RTL
                  mightContainRTL = strings.containsRTL(op.text);
              }
              if (!mightContainNonBasicASCII && op.text) {
                  mightContainNonBasicASCII = !strings.isBasicASCII(op.text);
              }
              operations[i] = {
                  sortIndex: i,
                  identifier: op.identifier || null,
                  range: validatedRange,
                  rangeOffset: this.getOffsetAt(validatedRange.startLineNumber, validatedRange.startColumn),
                  rangeLength: this.getValueLengthInRange(validatedRange),
                  lines: op.text ? op.text.split(/\r\n|\r|\n/) : null,
                  forceMoveMarkers: Boolean(op.forceMoveMarkers),
                  isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false
              };
          }
          // Sort operations ascending
          operations.sort(PieceTreeTextBuffer._sortOpsAscending);
          var hasTouchingRanges = false;
          for (var i = 0, count = operations.length - 1; i < count; i++) {
              var rangeEnd = operations[i].range.getEndPosition();
              var nextRangeStart = operations[i + 1].range.getStartPosition();
              if (nextRangeStart.isBeforeOrEqual(rangeEnd)) {
                  if (nextRangeStart.isBefore(rangeEnd)) {
                      // overlapping ranges
                      throw new Error('Overlapping ranges are not allowed!');
                  }
                  hasTouchingRanges = true;
              }
          }
          if (canReduceOperations) {
              operations = this._reduceOperations(operations);
          }
          // Delta encode operations
          var reverseRanges = PieceTreeTextBuffer._getInverseEditRanges(operations);
          var newTrimAutoWhitespaceCandidates = [];
          for (var i = 0; i < operations.length; i++) {
              var op = operations[i];
              var reverseRange = reverseRanges[i];
              if (recordTrimAutoWhitespace && op.isAutoWhitespaceEdit && op.range.isEmpty()) {
                  // Record already the future line numbers that might be auto whitespace removal candidates on next edit
                  for (var lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) {
                      var currentLineContent = '';
                      if (lineNumber === reverseRange.startLineNumber) {
                          currentLineContent = this.getLineContent(op.range.startLineNumber);
                          if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) {
                              continue;
                          }
                      }
                      newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent });
                  }
              }
          }
          var reverseOperations = [];
          for (var i = 0; i < operations.length; i++) {
              var op = operations[i];
              var reverseRange = reverseRanges[i];
              reverseOperations[i] = {
                  sortIndex: op.sortIndex,
                  identifier: op.identifier,
                  range: reverseRange,
                  text: this.getValueInRange(op.range),
                  forceMoveMarkers: op.forceMoveMarkers
              };
          }
          // Can only sort reverse operations when the order is not significant
          if (!hasTouchingRanges) {
              reverseOperations.sort(function (a, b) { return a.sortIndex - b.sortIndex; });
          }
          this._mightContainRTL = mightContainRTL;
          this._mightContainNonBasicASCII = mightContainNonBasicASCII;
          var contentChanges = this._doApplyEdits(operations);
          var trimAutoWhitespaceLineNumbers = null;
          if (recordTrimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) {
              // sort line numbers auto whitespace removal candidates for next edit descending
              newTrimAutoWhitespaceCandidates.sort(function (a, b) { return b.lineNumber - a.lineNumber; });
              trimAutoWhitespaceLineNumbers = [];
              for (var i = 0, len = newTrimAutoWhitespaceCandidates.length; i < len; i++) {
                  var lineNumber = newTrimAutoWhitespaceCandidates[i].lineNumber;
                  if (i > 0 && newTrimAutoWhitespaceCandidates[i - 1].lineNumber === lineNumber) {
                      // Do not have the same line number twice
                      continue;
                  }
                  var prevContent = newTrimAutoWhitespaceCandidates[i].oldContent;
                  var lineContent = this.getLineContent(lineNumber);
                  if (lineContent.length === 0 || lineContent === prevContent || strings.firstNonWhitespaceIndex(lineContent) !== -1) {
                      continue;
                  }
                  trimAutoWhitespaceLineNumbers.push(lineNumber);
              }
          }
          return new ApplyEditsResult(reverseOperations, contentChanges, trimAutoWhitespaceLineNumbers);
      };
      /**
       * Transform operations such that they represent the same logic edit,
       * but that they also do not cause OOM crashes.
       */
      PieceTreeTextBuffer.prototype._reduceOperations = function (operations) {
          if (operations.length < 1000) {
              // We know from empirical testing that a thousand edits work fine regardless of their shape.
              return operations;
          }
          // At one point, due to how events are emitted and how each operation is handled,
          // some operations can trigger a high amount of temporary string allocations,
          // that will immediately get edited again.
          // e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line
          // Therefore, the strategy is to collapse all the operations into a huge single edit operation
          return [this._toSingleEditOperation(operations)];
      };
      PieceTreeTextBuffer.prototype._toSingleEditOperation = function (operations) {
          var forceMoveMarkers = false, firstEditRange = operations[0].range, lastEditRange = operations[operations.length - 1].range, entireEditRange = new Range(firstEditRange.startLineNumber, firstEditRange.startColumn, lastEditRange.endLineNumber, lastEditRange.endColumn), lastEndLineNumber = firstEditRange.startLineNumber, lastEndColumn = firstEditRange.startColumn, result = [];
          for (var i = 0, len = operations.length; i < len; i++) {
              var operation = operations[i], range = operation.range;
              forceMoveMarkers = forceMoveMarkers || operation.forceMoveMarkers;
              // (1) -- Push old text
              for (var lineNumber = lastEndLineNumber; lineNumber < range.startLineNumber; lineNumber++) {
                  if (lineNumber === lastEndLineNumber) {
                      result.push(this.getLineContent(lineNumber).substring(lastEndColumn - 1));
                  }
                  else {
                      result.push('\n');
                      result.push(this.getLineContent(lineNumber));
                  }
              }
              if (range.startLineNumber === lastEndLineNumber) {
                  result.push(this.getLineContent(range.startLineNumber).substring(lastEndColumn - 1, range.startColumn - 1));
              }
              else {
                  result.push('\n');
                  result.push(this.getLineContent(range.startLineNumber).substring(0, range.startColumn - 1));
              }
              // (2) -- Push new text
              if (operation.lines) {
                  for (var j = 0, lenJ = operation.lines.length; j < lenJ; j++) {
                      if (j !== 0) {
                          result.push('\n');
                      }
                      result.push(operation.lines[j]);
                  }
              }
              lastEndLineNumber = operation.range.endLineNumber;
              lastEndColumn = operation.range.endColumn;
          }
          return {
              sortIndex: 0,
              identifier: operations[0].identifier,
              range: entireEditRange,
              rangeOffset: this.getOffsetAt(entireEditRange.startLineNumber, entireEditRange.startColumn),
              rangeLength: this.getValueLengthInRange(entireEditRange, 0 /* TextDefined */),
              lines: result.join('').split('\n'),
              forceMoveMarkers: forceMoveMarkers,
              isAutoWhitespaceEdit: false
          };
      };
      PieceTreeTextBuffer.prototype._doApplyEdits = function (operations) {
          operations.sort(PieceTreeTextBuffer._sortOpsDescending);
          var contentChanges = [];
          // operations are from bottom to top
          for (var i = 0; i < operations.length; i++) {
              var op = operations[i];
              var startLineNumber = op.range.startLineNumber;
              var startColumn = op.range.startColumn;
              var endLineNumber = op.range.endLineNumber;
              var endColumn = op.range.endColumn;
              if (startLineNumber === endLineNumber && startColumn === endColumn && (!op.lines || op.lines.length === 0)) {
                  // no-op
                  continue;
              }
              var deletingLinesCnt = endLineNumber - startLineNumber;
              var insertingLinesCnt = (op.lines ? op.lines.length - 1 : 0);
              var editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
              var text = (op.lines ? op.lines.join(this.getEOL()) : '');
              if (text) {
                  // replacement
                  this._pieceTree.delete(op.rangeOffset, op.rangeLength);
                  this._pieceTree.insert(op.rangeOffset, text, true);
              }
              else {
                  // deletion
                  this._pieceTree.delete(op.rangeOffset, op.rangeLength);
              }
              if (editingLinesCnt < insertingLinesCnt) {
                  var newLinesContent = [];
                  for (var j = editingLinesCnt + 1; j <= insertingLinesCnt; j++) {
                      newLinesContent.push(op.lines[j]);
                  }
                  newLinesContent[newLinesContent.length - 1] = this.getLineContent(startLineNumber + insertingLinesCnt - 1);
              }
              var contentChangeRange = new Range(startLineNumber, startColumn, endLineNumber, endColumn);
              contentChanges.push({
                  range: contentChangeRange,
                  rangeLength: op.rangeLength,
                  text: text,
                  rangeOffset: op.rangeOffset,
                  forceMoveMarkers: op.forceMoveMarkers
              });
          }
          return contentChanges;
      };
      PieceTreeTextBuffer.prototype.findMatchesLineByLine = function (searchRange, searchData, captureMatches, limitResultCount) {
          return this._pieceTree.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
      };
      /**
       * Assumes `operations` are validated and sorted ascending
       */
      PieceTreeTextBuffer._getInverseEditRanges = function (operations) {
          var result = [];
          var prevOpEndLineNumber = 0;
          var prevOpEndColumn = 0;
          var prevOp = null;
          for (var i = 0, len = operations.length; i < len; i++) {
              var op = operations[i];
              var startLineNumber = void 0;
              var startColumn = void 0;
              if (prevOp) {
                  if (prevOp.range.endLineNumber === op.range.startLineNumber) {
                      startLineNumber = prevOpEndLineNumber;
                      startColumn = prevOpEndColumn + (op.range.startColumn - prevOp.range.endColumn);
                  }
                  else {
                      startLineNumber = prevOpEndLineNumber + (op.range.startLineNumber - prevOp.range.endLineNumber);
                      startColumn = op.range.startColumn;
                  }
              }
              else {
                  startLineNumber = op.range.startLineNumber;
                  startColumn = op.range.startColumn;
              }
              var resultRange = void 0;
              if (op.lines && op.lines.length > 0) {
                  // the operation inserts something
                  var lineCount = op.lines.length;
                  var firstLine = op.lines[0];
                  var lastLine = op.lines[lineCount - 1];
                  if (lineCount === 1) {
                      // single line insert
                      resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + firstLine.length);
                  }
                  else {
                      // multi line insert
                      resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, lastLine.length + 1);
                  }
              }
              else {
                  // There is nothing to insert
                  resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn);
              }
              prevOpEndLineNumber = resultRange.endLineNumber;
              prevOpEndColumn = resultRange.endColumn;
              result.push(resultRange);
              prevOp = op;
          }
          return result;
      };
      PieceTreeTextBuffer._sortOpsAscending = function (a, b) {
          var r = Range.compareRangesUsingEnds(a.range, b.range);
          if (r === 0) {
              return a.sortIndex - b.sortIndex;
          }
          return r;
      };
      PieceTreeTextBuffer._sortOpsDescending = function (a, b) {
          var r = Range.compareRangesUsingEnds(a.range, b.range);
          if (r === 0) {
              return b.sortIndex - a.sortIndex;
          }
          return -r;
      };
      return PieceTreeTextBuffer;
  }());
  export { PieceTreeTextBuffer };
  