69c414af6313c88f783bee647f404fc90513dd763a18c6bdf8d8bbe3fee21d67a0fb4a88a8818b0bc41fc8c3183c01e319379f371ed8b082505d2f0c08f5f5 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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 * as strings from '../../../../base/common/strings.js';
  6. import { StringBuffer, createLineStarts, createLineStartsFast } from './pieceTreeBase.js';
  7. import { PieceTreeTextBuffer } from './pieceTreeTextBuffer.js';
  8. export class PieceTreeTextBufferFactory {
  9. constructor(_chunks, _bom, _cr, _lf, _crlf, _containsRTL, _containsUnusualLineTerminators, _isBasicASCII, _normalizeEOL) {
  10. this._chunks = _chunks;
  11. this._bom = _bom;
  12. this._cr = _cr;
  13. this._lf = _lf;
  14. this._crlf = _crlf;
  15. this._containsRTL = _containsRTL;
  16. this._containsUnusualLineTerminators = _containsUnusualLineTerminators;
  17. this._isBasicASCII = _isBasicASCII;
  18. this._normalizeEOL = _normalizeEOL;
  19. }
  20. _getEOL(defaultEOL) {
  21. const totalEOLCount = this._cr + this._lf + this._crlf;
  22. const totalCRCount = this._cr + this._crlf;
  23. if (totalEOLCount === 0) {
  24. // This is an empty file or a file with precisely one line
  25. return (defaultEOL === 1 /* DefaultEndOfLine.LF */ ? '\n' : '\r\n');
  26. }
  27. if (totalCRCount > totalEOLCount / 2) {
  28. // More than half of the file contains \r\n ending lines
  29. return '\r\n';
  30. }
  31. // At least one line more ends in \n
  32. return '\n';
  33. }
  34. create(defaultEOL) {
  35. const eol = this._getEOL(defaultEOL);
  36. const chunks = this._chunks;
  37. if (this._normalizeEOL &&
  38. ((eol === '\r\n' && (this._cr > 0 || this._lf > 0))
  39. || (eol === '\n' && (this._cr > 0 || this._crlf > 0)))) {
  40. // Normalize pieces
  41. for (let i = 0, len = chunks.length; i < len; i++) {
  42. const str = chunks[i].buffer.replace(/\r\n|\r|\n/g, eol);
  43. const newLineStart = createLineStartsFast(str);
  44. chunks[i] = new StringBuffer(str, newLineStart);
  45. }
  46. }
  47. const textBuffer = new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL);
  48. return { textBuffer: textBuffer, disposable: textBuffer };
  49. }
  50. }
  51. export class PieceTreeTextBufferBuilder {
  52. constructor() {
  53. this.chunks = [];
  54. this.BOM = '';
  55. this._hasPreviousChar = false;
  56. this._previousChar = 0;
  57. this._tmpLineStarts = [];
  58. this.cr = 0;
  59. this.lf = 0;
  60. this.crlf = 0;
  61. this.containsRTL = false;
  62. this.containsUnusualLineTerminators = false;
  63. this.isBasicASCII = true;
  64. }
  65. acceptChunk(chunk) {
  66. if (chunk.length === 0) {
  67. return;
  68. }
  69. if (this.chunks.length === 0) {
  70. if (strings.startsWithUTF8BOM(chunk)) {
  71. this.BOM = strings.UTF8_BOM_CHARACTER;
  72. chunk = chunk.substr(1);
  73. }
  74. }
  75. const lastChar = chunk.charCodeAt(chunk.length - 1);
  76. if (lastChar === 13 /* CharCode.CarriageReturn */ || (lastChar >= 0xD800 && lastChar <= 0xDBFF)) {
  77. // last character is \r or a high surrogate => keep it back
  78. this._acceptChunk1(chunk.substr(0, chunk.length - 1), false);
  79. this._hasPreviousChar = true;
  80. this._previousChar = lastChar;
  81. }
  82. else {
  83. this._acceptChunk1(chunk, false);
  84. this._hasPreviousChar = false;
  85. this._previousChar = lastChar;
  86. }
  87. }
  88. _acceptChunk1(chunk, allowEmptyStrings) {
  89. if (!allowEmptyStrings && chunk.length === 0) {
  90. // Nothing to do
  91. return;
  92. }
  93. if (this._hasPreviousChar) {
  94. this._acceptChunk2(String.fromCharCode(this._previousChar) + chunk);
  95. }
  96. else {
  97. this._acceptChunk2(chunk);
  98. }
  99. }
  100. _acceptChunk2(chunk) {
  101. const lineStarts = createLineStarts(this._tmpLineStarts, chunk);
  102. this.chunks.push(new StringBuffer(chunk, lineStarts.lineStarts));
  103. this.cr += lineStarts.cr;
  104. this.lf += lineStarts.lf;
  105. this.crlf += lineStarts.crlf;
  106. if (this.isBasicASCII) {
  107. this.isBasicASCII = lineStarts.isBasicASCII;
  108. }
  109. if (!this.isBasicASCII && !this.containsRTL) {
  110. // No need to check if it is basic ASCII
  111. this.containsRTL = strings.containsRTL(chunk);
  112. }
  113. if (!this.isBasicASCII && !this.containsUnusualLineTerminators) {
  114. // No need to check if it is basic ASCII
  115. this.containsUnusualLineTerminators = strings.containsUnusualLineTerminators(chunk);
  116. }
  117. }
  118. finish(normalizeEOL = true) {
  119. this._finish();
  120. return new PieceTreeTextBufferFactory(this.chunks, this.BOM, this.cr, this.lf, this.crlf, this.containsRTL, this.containsUnusualLineTerminators, this.isBasicASCII, normalizeEOL);
  121. }
  122. _finish() {
  123. if (this.chunks.length === 0) {
  124. this._acceptChunk1('', true);
  125. }
  126. if (this._hasPreviousChar) {
  127. this._hasPreviousChar = false;
  128. // recreate last chunk
  129. const lastChunk = this.chunks[this.chunks.length - 1];
  130. lastChunk.buffer += String.fromCharCode(this._previousChar);
  131. const newLineStarts = createLineStartsFast(lastChunk.buffer);
  132. lastChunk.lineStarts = newLineStarts;
  133. if (this._previousChar === 13 /* CharCode.CarriageReturn */) {
  134. this.cr++;
  135. }
  136. }
  137. }
  138. }