032e9eb8ce9e0a852689c95c1ae87df93e64450b3f0041cb9e60b4941f6dce3e200eabac7663d83e471294609498ec71cbf6cc278471a2adabc50c7cebe190 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { splitLines } from '../../../base/common/strings.js';
  6. import { Position } from '../core/position.js';
  7. import { PrefixSumComputer } from './prefixSumComputer.js';
  8. export class MirrorTextModel {
  9. constructor(uri, lines, eol, versionId) {
  10. this._uri = uri;
  11. this._lines = lines;
  12. this._eol = eol;
  13. this._versionId = versionId;
  14. this._lineStarts = null;
  15. this._cachedTextValue = null;
  16. }
  17. dispose() {
  18. this._lines.length = 0;
  19. }
  20. get version() {
  21. return this._versionId;
  22. }
  23. getText() {
  24. if (this._cachedTextValue === null) {
  25. this._cachedTextValue = this._lines.join(this._eol);
  26. }
  27. return this._cachedTextValue;
  28. }
  29. onEvents(e) {
  30. if (e.eol && e.eol !== this._eol) {
  31. this._eol = e.eol;
  32. this._lineStarts = null;
  33. }
  34. // Update my lines
  35. const changes = e.changes;
  36. for (const change of changes) {
  37. this._acceptDeleteRange(change.range);
  38. this._acceptInsertText(new Position(change.range.startLineNumber, change.range.startColumn), change.text);
  39. }
  40. this._versionId = e.versionId;
  41. this._cachedTextValue = null;
  42. }
  43. _ensureLineStarts() {
  44. if (!this._lineStarts) {
  45. const eolLength = this._eol.length;
  46. const linesLength = this._lines.length;
  47. const lineStartValues = new Uint32Array(linesLength);
  48. for (let i = 0; i < linesLength; i++) {
  49. lineStartValues[i] = this._lines[i].length + eolLength;
  50. }
  51. this._lineStarts = new PrefixSumComputer(lineStartValues);
  52. }
  53. }
  54. /**
  55. * All changes to a line's text go through this method
  56. */
  57. _setLineText(lineIndex, newValue) {
  58. this._lines[lineIndex] = newValue;
  59. if (this._lineStarts) {
  60. // update prefix sum
  61. this._lineStarts.setValue(lineIndex, this._lines[lineIndex].length + this._eol.length);
  62. }
  63. }
  64. _acceptDeleteRange(range) {
  65. if (range.startLineNumber === range.endLineNumber) {
  66. if (range.startColumn === range.endColumn) {
  67. // Nothing to delete
  68. return;
  69. }
  70. // Delete text on the affected line
  71. this._setLineText(range.startLineNumber - 1, this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1)
  72. + this._lines[range.startLineNumber - 1].substring(range.endColumn - 1));
  73. return;
  74. }
  75. // Take remaining text on last line and append it to remaining text on first line
  76. this._setLineText(range.startLineNumber - 1, this._lines[range.startLineNumber - 1].substring(0, range.startColumn - 1)
  77. + this._lines[range.endLineNumber - 1].substring(range.endColumn - 1));
  78. // Delete middle lines
  79. this._lines.splice(range.startLineNumber, range.endLineNumber - range.startLineNumber);
  80. if (this._lineStarts) {
  81. // update prefix sum
  82. this._lineStarts.removeValues(range.startLineNumber, range.endLineNumber - range.startLineNumber);
  83. }
  84. }
  85. _acceptInsertText(position, insertText) {
  86. if (insertText.length === 0) {
  87. // Nothing to insert
  88. return;
  89. }
  90. const insertLines = splitLines(insertText);
  91. if (insertLines.length === 1) {
  92. // Inserting text on one line
  93. this._setLineText(position.lineNumber - 1, this._lines[position.lineNumber - 1].substring(0, position.column - 1)
  94. + insertLines[0]
  95. + this._lines[position.lineNumber - 1].substring(position.column - 1));
  96. return;
  97. }
  98. // Append overflowing text from first line to the end of text to insert
  99. insertLines[insertLines.length - 1] += this._lines[position.lineNumber - 1].substring(position.column - 1);
  100. // Delete overflowing text from first line and insert text on first line
  101. this._setLineText(position.lineNumber - 1, this._lines[position.lineNumber - 1].substring(0, position.column - 1)
  102. + insertLines[0]);
  103. // Insert new lines & store lengths
  104. const newLengths = new Uint32Array(insertLines.length - 1);
  105. for (let i = 1; i < insertLines.length; i++) {
  106. this._lines.splice(position.lineNumber + i - 1, 0, insertLines[i]);
  107. newLengths[i - 1] = insertLines[i].length + this._eol.length;
  108. }
  109. if (this._lineStarts) {
  110. // update prefix sum
  111. this._lineStarts.insertValues(position.lineNumber, newLengths);
  112. }
  113. }
  114. }