10b1a15e0a8adf2966e76131a2b5fbac6eccfe0752d2a2873ca8acdc73d604d0a3c98483e2eda70e524cfbcb3fc92ae06f943f333a265db7b12e7d90434590 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  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 { Position } from '../../core/position.js';
  6. import { Range } from '../../core/range.js';
  7. import { FindMatch } from '../../model.js';
  8. import { SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from './rbTreeBase.js';
  9. import { Searcher, createFindMatch, isValidMatch } from '../textModelSearch.js';
  10. // const lfRegex = new RegExp(/\r\n|\r|\n/g);
  11. export const AverageBufferSize = 65535;
  12. export function createUintArray(arr) {
  13. let r;
  14. if (arr[arr.length - 1] < 65536) {
  15. r = new Uint16Array(arr.length);
  16. }
  17. else {
  18. r = new Uint32Array(arr.length);
  19. }
  20. r.set(arr, 0);
  21. return r;
  22. }
  23. export class LineStarts {
  24. constructor(lineStarts, cr, lf, crlf, isBasicASCII) {
  25. this.lineStarts = lineStarts;
  26. this.cr = cr;
  27. this.lf = lf;
  28. this.crlf = crlf;
  29. this.isBasicASCII = isBasicASCII;
  30. }
  31. }
  32. export function createLineStartsFast(str, readonly = true) {
  33. const r = [0];
  34. let rLength = 1;
  35. for (let i = 0, len = str.length; i < len; i++) {
  36. const chr = str.charCodeAt(i);
  37. if (chr === 13 /* CharCode.CarriageReturn */) {
  38. if (i + 1 < len && str.charCodeAt(i + 1) === 10 /* CharCode.LineFeed */) {
  39. // \r\n... case
  40. r[rLength++] = i + 2;
  41. i++; // skip \n
  42. }
  43. else {
  44. // \r... case
  45. r[rLength++] = i + 1;
  46. }
  47. }
  48. else if (chr === 10 /* CharCode.LineFeed */) {
  49. r[rLength++] = i + 1;
  50. }
  51. }
  52. if (readonly) {
  53. return createUintArray(r);
  54. }
  55. else {
  56. return r;
  57. }
  58. }
  59. export function createLineStarts(r, str) {
  60. r.length = 0;
  61. r[0] = 0;
  62. let rLength = 1;
  63. let cr = 0, lf = 0, crlf = 0;
  64. let isBasicASCII = true;
  65. for (let i = 0, len = str.length; i < len; i++) {
  66. const chr = str.charCodeAt(i);
  67. if (chr === 13 /* CharCode.CarriageReturn */) {
  68. if (i + 1 < len && str.charCodeAt(i + 1) === 10 /* CharCode.LineFeed */) {
  69. // \r\n... case
  70. crlf++;
  71. r[rLength++] = i + 2;
  72. i++; // skip \n
  73. }
  74. else {
  75. cr++;
  76. // \r... case
  77. r[rLength++] = i + 1;
  78. }
  79. }
  80. else if (chr === 10 /* CharCode.LineFeed */) {
  81. lf++;
  82. r[rLength++] = i + 1;
  83. }
  84. else {
  85. if (isBasicASCII) {
  86. if (chr !== 9 /* CharCode.Tab */ && (chr < 32 || chr > 126)) {
  87. isBasicASCII = false;
  88. }
  89. }
  90. }
  91. }
  92. const result = new LineStarts(createUintArray(r), cr, lf, crlf, isBasicASCII);
  93. r.length = 0;
  94. return result;
  95. }
  96. export class Piece {
  97. constructor(bufferIndex, start, end, lineFeedCnt, length) {
  98. this.bufferIndex = bufferIndex;
  99. this.start = start;
  100. this.end = end;
  101. this.lineFeedCnt = lineFeedCnt;
  102. this.length = length;
  103. }
  104. }
  105. export class StringBuffer {
  106. constructor(buffer, lineStarts) {
  107. this.buffer = buffer;
  108. this.lineStarts = lineStarts;
  109. }
  110. }
  111. /**
  112. * Readonly snapshot for piece tree.
  113. * In a real multiple thread environment, to make snapshot reading always work correctly, we need to
  114. * 1. Make TreeNode.piece immutable, then reading and writing can run in parallel.
  115. * 2. TreeNode/Buffers normalization should not happen during snapshot reading.
  116. */
  117. class PieceTreeSnapshot {
  118. constructor(tree, BOM) {
  119. this._pieces = [];
  120. this._tree = tree;
  121. this._BOM = BOM;
  122. this._index = 0;
  123. if (tree.root !== SENTINEL) {
  124. tree.iterate(tree.root, node => {
  125. if (node !== SENTINEL) {
  126. this._pieces.push(node.piece);
  127. }
  128. return true;
  129. });
  130. }
  131. }
  132. read() {
  133. if (this._pieces.length === 0) {
  134. if (this._index === 0) {
  135. this._index++;
  136. return this._BOM;
  137. }
  138. else {
  139. return null;
  140. }
  141. }
  142. if (this._index > this._pieces.length - 1) {
  143. return null;
  144. }
  145. if (this._index === 0) {
  146. return this._BOM + this._tree.getPieceContent(this._pieces[this._index++]);
  147. }
  148. return this._tree.getPieceContent(this._pieces[this._index++]);
  149. }
  150. }
  151. class PieceTreeSearchCache {
  152. constructor(limit) {
  153. this._limit = limit;
  154. this._cache = [];
  155. }
  156. get(offset) {
  157. for (let i = this._cache.length - 1; i >= 0; i--) {
  158. const nodePos = this._cache[i];
  159. if (nodePos.nodeStartOffset <= offset && nodePos.nodeStartOffset + nodePos.node.piece.length >= offset) {
  160. return nodePos;
  161. }
  162. }
  163. return null;
  164. }
  165. get2(lineNumber) {
  166. for (let i = this._cache.length - 1; i >= 0; i--) {
  167. const nodePos = this._cache[i];
  168. if (nodePos.nodeStartLineNumber && nodePos.nodeStartLineNumber < lineNumber && nodePos.nodeStartLineNumber + nodePos.node.piece.lineFeedCnt >= lineNumber) {
  169. return nodePos;
  170. }
  171. }
  172. return null;
  173. }
  174. set(nodePosition) {
  175. if (this._cache.length >= this._limit) {
  176. this._cache.shift();
  177. }
  178. this._cache.push(nodePosition);
  179. }
  180. validate(offset) {
  181. let hasInvalidVal = false;
  182. const tmp = this._cache;
  183. for (let i = 0; i < tmp.length; i++) {
  184. const nodePos = tmp[i];
  185. if (nodePos.node.parent === null || nodePos.nodeStartOffset >= offset) {
  186. tmp[i] = null;
  187. hasInvalidVal = true;
  188. continue;
  189. }
  190. }
  191. if (hasInvalidVal) {
  192. const newArr = [];
  193. for (const entry of tmp) {
  194. if (entry !== null) {
  195. newArr.push(entry);
  196. }
  197. }
  198. this._cache = newArr;
  199. }
  200. }
  201. }
  202. export class PieceTreeBase {
  203. constructor(chunks, eol, eolNormalized) {
  204. this.create(chunks, eol, eolNormalized);
  205. }
  206. create(chunks, eol, eolNormalized) {
  207. this._buffers = [
  208. new StringBuffer('', [0])
  209. ];
  210. this._lastChangeBufferPos = { line: 0, column: 0 };
  211. this.root = SENTINEL;
  212. this._lineCnt = 1;
  213. this._length = 0;
  214. this._EOL = eol;
  215. this._EOLLength = eol.length;
  216. this._EOLNormalized = eolNormalized;
  217. let lastNode = null;
  218. for (let i = 0, len = chunks.length; i < len; i++) {
  219. if (chunks[i].buffer.length > 0) {
  220. if (!chunks[i].lineStarts) {
  221. chunks[i].lineStarts = createLineStartsFast(chunks[i].buffer);
  222. }
  223. const piece = new Piece(i + 1, { line: 0, column: 0 }, { line: chunks[i].lineStarts.length - 1, column: chunks[i].buffer.length - chunks[i].lineStarts[chunks[i].lineStarts.length - 1] }, chunks[i].lineStarts.length - 1, chunks[i].buffer.length);
  224. this._buffers.push(chunks[i]);
  225. lastNode = this.rbInsertRight(lastNode, piece);
  226. }
  227. }
  228. this._searchCache = new PieceTreeSearchCache(1);
  229. this._lastVisitedLine = { lineNumber: 0, value: '' };
  230. this.computeBufferMetadata();
  231. }
  232. normalizeEOL(eol) {
  233. const averageBufferSize = AverageBufferSize;
  234. const min = averageBufferSize - Math.floor(averageBufferSize / 3);
  235. const max = min * 2;
  236. let tempChunk = '';
  237. let tempChunkLen = 0;
  238. const chunks = [];
  239. this.iterate(this.root, node => {
  240. const str = this.getNodeContent(node);
  241. const len = str.length;
  242. if (tempChunkLen <= min || tempChunkLen + len < max) {
  243. tempChunk += str;
  244. tempChunkLen += len;
  245. return true;
  246. }
  247. // flush anyways
  248. const text = tempChunk.replace(/\r\n|\r|\n/g, eol);
  249. chunks.push(new StringBuffer(text, createLineStartsFast(text)));
  250. tempChunk = str;
  251. tempChunkLen = len;
  252. return true;
  253. });
  254. if (tempChunkLen > 0) {
  255. const text = tempChunk.replace(/\r\n|\r|\n/g, eol);
  256. chunks.push(new StringBuffer(text, createLineStartsFast(text)));
  257. }
  258. this.create(chunks, eol, true);
  259. }
  260. // #region Buffer API
  261. getEOL() {
  262. return this._EOL;
  263. }
  264. setEOL(newEOL) {
  265. this._EOL = newEOL;
  266. this._EOLLength = this._EOL.length;
  267. this.normalizeEOL(newEOL);
  268. }
  269. createSnapshot(BOM) {
  270. return new PieceTreeSnapshot(this, BOM);
  271. }
  272. getOffsetAt(lineNumber, column) {
  273. let leftLen = 0; // inorder
  274. let x = this.root;
  275. while (x !== SENTINEL) {
  276. if (x.left !== SENTINEL && x.lf_left + 1 >= lineNumber) {
  277. x = x.left;
  278. }
  279. else if (x.lf_left + x.piece.lineFeedCnt + 1 >= lineNumber) {
  280. leftLen += x.size_left;
  281. // lineNumber >= 2
  282. const accumualtedValInCurrentIndex = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2);
  283. return leftLen += accumualtedValInCurrentIndex + column - 1;
  284. }
  285. else {
  286. lineNumber -= x.lf_left + x.piece.lineFeedCnt;
  287. leftLen += x.size_left + x.piece.length;
  288. x = x.right;
  289. }
  290. }
  291. return leftLen;
  292. }
  293. getPositionAt(offset) {
  294. offset = Math.floor(offset);
  295. offset = Math.max(0, offset);
  296. let x = this.root;
  297. let lfCnt = 0;
  298. const originalOffset = offset;
  299. while (x !== SENTINEL) {
  300. if (x.size_left !== 0 && x.size_left >= offset) {
  301. x = x.left;
  302. }
  303. else if (x.size_left + x.piece.length >= offset) {
  304. const out = this.getIndexOf(x, offset - x.size_left);
  305. lfCnt += x.lf_left + out.index;
  306. if (out.index === 0) {
  307. const lineStartOffset = this.getOffsetAt(lfCnt + 1, 1);
  308. const column = originalOffset - lineStartOffset;
  309. return new Position(lfCnt + 1, column + 1);
  310. }
  311. return new Position(lfCnt + 1, out.remainder + 1);
  312. }
  313. else {
  314. offset -= x.size_left + x.piece.length;
  315. lfCnt += x.lf_left + x.piece.lineFeedCnt;
  316. if (x.right === SENTINEL) {
  317. // last node
  318. const lineStartOffset = this.getOffsetAt(lfCnt + 1, 1);
  319. const column = originalOffset - offset - lineStartOffset;
  320. return new Position(lfCnt + 1, column + 1);
  321. }
  322. else {
  323. x = x.right;
  324. }
  325. }
  326. }
  327. return new Position(1, 1);
  328. }
  329. getValueInRange(range, eol) {
  330. if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) {
  331. return '';
  332. }
  333. const startPosition = this.nodeAt2(range.startLineNumber, range.startColumn);
  334. const endPosition = this.nodeAt2(range.endLineNumber, range.endColumn);
  335. const value = this.getValueInRange2(startPosition, endPosition);
  336. if (eol) {
  337. if (eol !== this._EOL || !this._EOLNormalized) {
  338. return value.replace(/\r\n|\r|\n/g, eol);
  339. }
  340. if (eol === this.getEOL() && this._EOLNormalized) {
  341. if (eol === '\r\n') {
  342. }
  343. return value;
  344. }
  345. return value.replace(/\r\n|\r|\n/g, eol);
  346. }
  347. return value;
  348. }
  349. getValueInRange2(startPosition, endPosition) {
  350. if (startPosition.node === endPosition.node) {
  351. const node = startPosition.node;
  352. const buffer = this._buffers[node.piece.bufferIndex].buffer;
  353. const startOffset = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start);
  354. return buffer.substring(startOffset + startPosition.remainder, startOffset + endPosition.remainder);
  355. }
  356. let x = startPosition.node;
  357. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  358. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  359. let ret = buffer.substring(startOffset + startPosition.remainder, startOffset + x.piece.length);
  360. x = x.next();
  361. while (x !== SENTINEL) {
  362. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  363. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  364. if (x === endPosition.node) {
  365. ret += buffer.substring(startOffset, startOffset + endPosition.remainder);
  366. break;
  367. }
  368. else {
  369. ret += buffer.substr(startOffset, x.piece.length);
  370. }
  371. x = x.next();
  372. }
  373. return ret;
  374. }
  375. getLinesContent() {
  376. const lines = [];
  377. let linesLength = 0;
  378. let currentLine = '';
  379. let danglingCR = false;
  380. this.iterate(this.root, node => {
  381. if (node === SENTINEL) {
  382. return true;
  383. }
  384. const piece = node.piece;
  385. let pieceLength = piece.length;
  386. if (pieceLength === 0) {
  387. return true;
  388. }
  389. const buffer = this._buffers[piece.bufferIndex].buffer;
  390. const lineStarts = this._buffers[piece.bufferIndex].lineStarts;
  391. const pieceStartLine = piece.start.line;
  392. const pieceEndLine = piece.end.line;
  393. let pieceStartOffset = lineStarts[pieceStartLine] + piece.start.column;
  394. if (danglingCR) {
  395. if (buffer.charCodeAt(pieceStartOffset) === 10 /* CharCode.LineFeed */) {
  396. // pretend the \n was in the previous piece..
  397. pieceStartOffset++;
  398. pieceLength--;
  399. }
  400. lines[linesLength++] = currentLine;
  401. currentLine = '';
  402. danglingCR = false;
  403. if (pieceLength === 0) {
  404. return true;
  405. }
  406. }
  407. if (pieceStartLine === pieceEndLine) {
  408. // this piece has no new lines
  409. if (!this._EOLNormalized && buffer.charCodeAt(pieceStartOffset + pieceLength - 1) === 13 /* CharCode.CarriageReturn */) {
  410. danglingCR = true;
  411. currentLine += buffer.substr(pieceStartOffset, pieceLength - 1);
  412. }
  413. else {
  414. currentLine += buffer.substr(pieceStartOffset, pieceLength);
  415. }
  416. return true;
  417. }
  418. // add the text before the first line start in this piece
  419. currentLine += (this._EOLNormalized
  420. ? buffer.substring(pieceStartOffset, Math.max(pieceStartOffset, lineStarts[pieceStartLine + 1] - this._EOLLength))
  421. : buffer.substring(pieceStartOffset, lineStarts[pieceStartLine + 1]).replace(/(\r\n|\r|\n)$/, ''));
  422. lines[linesLength++] = currentLine;
  423. for (let line = pieceStartLine + 1; line < pieceEndLine; line++) {
  424. currentLine = (this._EOLNormalized
  425. ? buffer.substring(lineStarts[line], lineStarts[line + 1] - this._EOLLength)
  426. : buffer.substring(lineStarts[line], lineStarts[line + 1]).replace(/(\r\n|\r|\n)$/, ''));
  427. lines[linesLength++] = currentLine;
  428. }
  429. if (!this._EOLNormalized && buffer.charCodeAt(lineStarts[pieceEndLine] + piece.end.column - 1) === 13 /* CharCode.CarriageReturn */) {
  430. danglingCR = true;
  431. if (piece.end.column === 0) {
  432. // The last line ended with a \r, let's undo the push, it will be pushed by next iteration
  433. linesLength--;
  434. }
  435. else {
  436. currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column - 1);
  437. }
  438. }
  439. else {
  440. currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column);
  441. }
  442. return true;
  443. });
  444. if (danglingCR) {
  445. lines[linesLength++] = currentLine;
  446. currentLine = '';
  447. }
  448. lines[linesLength++] = currentLine;
  449. return lines;
  450. }
  451. getLength() {
  452. return this._length;
  453. }
  454. getLineCount() {
  455. return this._lineCnt;
  456. }
  457. getLineContent(lineNumber) {
  458. if (this._lastVisitedLine.lineNumber === lineNumber) {
  459. return this._lastVisitedLine.value;
  460. }
  461. this._lastVisitedLine.lineNumber = lineNumber;
  462. if (lineNumber === this._lineCnt) {
  463. this._lastVisitedLine.value = this.getLineRawContent(lineNumber);
  464. }
  465. else if (this._EOLNormalized) {
  466. this._lastVisitedLine.value = this.getLineRawContent(lineNumber, this._EOLLength);
  467. }
  468. else {
  469. this._lastVisitedLine.value = this.getLineRawContent(lineNumber).replace(/(\r\n|\r|\n)$/, '');
  470. }
  471. return this._lastVisitedLine.value;
  472. }
  473. _getCharCode(nodePos) {
  474. if (nodePos.remainder === nodePos.node.piece.length) {
  475. // the char we want to fetch is at the head of next node.
  476. const matchingNode = nodePos.node.next();
  477. if (!matchingNode) {
  478. return 0;
  479. }
  480. const buffer = this._buffers[matchingNode.piece.bufferIndex];
  481. const startOffset = this.offsetInBuffer(matchingNode.piece.bufferIndex, matchingNode.piece.start);
  482. return buffer.buffer.charCodeAt(startOffset);
  483. }
  484. else {
  485. const buffer = this._buffers[nodePos.node.piece.bufferIndex];
  486. const startOffset = this.offsetInBuffer(nodePos.node.piece.bufferIndex, nodePos.node.piece.start);
  487. const targetOffset = startOffset + nodePos.remainder;
  488. return buffer.buffer.charCodeAt(targetOffset);
  489. }
  490. }
  491. getLineCharCode(lineNumber, index) {
  492. const nodePos = this.nodeAt2(lineNumber, index + 1);
  493. return this._getCharCode(nodePos);
  494. }
  495. getLineLength(lineNumber) {
  496. if (lineNumber === this.getLineCount()) {
  497. const startOffset = this.getOffsetAt(lineNumber, 1);
  498. return this.getLength() - startOffset;
  499. }
  500. return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength;
  501. }
  502. findMatchesInNode(node, searcher, startLineNumber, startColumn, startCursor, endCursor, searchData, captureMatches, limitResultCount, resultLen, result) {
  503. const buffer = this._buffers[node.piece.bufferIndex];
  504. const startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start);
  505. const start = this.offsetInBuffer(node.piece.bufferIndex, startCursor);
  506. const end = this.offsetInBuffer(node.piece.bufferIndex, endCursor);
  507. let m;
  508. // Reset regex to search from the beginning
  509. const ret = { line: 0, column: 0 };
  510. let searchText;
  511. let offsetInBuffer;
  512. if (searcher._wordSeparators) {
  513. searchText = buffer.buffer.substring(start, end);
  514. offsetInBuffer = (offset) => offset + start;
  515. searcher.reset(0);
  516. }
  517. else {
  518. searchText = buffer.buffer;
  519. offsetInBuffer = (offset) => offset;
  520. searcher.reset(start);
  521. }
  522. do {
  523. m = searcher.next(searchText);
  524. if (m) {
  525. if (offsetInBuffer(m.index) >= end) {
  526. return resultLen;
  527. }
  528. this.positionInBuffer(node, offsetInBuffer(m.index) - startOffsetInBuffer, ret);
  529. const lineFeedCnt = this.getLineFeedCnt(node.piece.bufferIndex, startCursor, ret);
  530. const retStartColumn = ret.line === startCursor.line ? ret.column - startCursor.column + startColumn : ret.column + 1;
  531. const retEndColumn = retStartColumn + m[0].length;
  532. result[resultLen++] = createFindMatch(new Range(startLineNumber + lineFeedCnt, retStartColumn, startLineNumber + lineFeedCnt, retEndColumn), m, captureMatches);
  533. if (offsetInBuffer(m.index) + m[0].length >= end) {
  534. return resultLen;
  535. }
  536. if (resultLen >= limitResultCount) {
  537. return resultLen;
  538. }
  539. }
  540. } while (m);
  541. return resultLen;
  542. }
  543. findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount) {
  544. const result = [];
  545. let resultLen = 0;
  546. const searcher = new Searcher(searchData.wordSeparators, searchData.regex);
  547. let startPosition = this.nodeAt2(searchRange.startLineNumber, searchRange.startColumn);
  548. if (startPosition === null) {
  549. return [];
  550. }
  551. const endPosition = this.nodeAt2(searchRange.endLineNumber, searchRange.endColumn);
  552. if (endPosition === null) {
  553. return [];
  554. }
  555. let start = this.positionInBuffer(startPosition.node, startPosition.remainder);
  556. const end = this.positionInBuffer(endPosition.node, endPosition.remainder);
  557. if (startPosition.node === endPosition.node) {
  558. this.findMatchesInNode(startPosition.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
  559. return result;
  560. }
  561. let startLineNumber = searchRange.startLineNumber;
  562. let currentNode = startPosition.node;
  563. while (currentNode !== endPosition.node) {
  564. const lineBreakCnt = this.getLineFeedCnt(currentNode.piece.bufferIndex, start, currentNode.piece.end);
  565. if (lineBreakCnt >= 1) {
  566. // last line break position
  567. const lineStarts = this._buffers[currentNode.piece.bufferIndex].lineStarts;
  568. const startOffsetInBuffer = this.offsetInBuffer(currentNode.piece.bufferIndex, currentNode.piece.start);
  569. const nextLineStartOffset = lineStarts[start.line + lineBreakCnt];
  570. const startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn : 1;
  571. resultLen = this.findMatchesInNode(currentNode, searcher, startLineNumber, startColumn, start, this.positionInBuffer(currentNode, nextLineStartOffset - startOffsetInBuffer), searchData, captureMatches, limitResultCount, resultLen, result);
  572. if (resultLen >= limitResultCount) {
  573. return result;
  574. }
  575. startLineNumber += lineBreakCnt;
  576. }
  577. const startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn - 1 : 0;
  578. // search for the remaining content
  579. if (startLineNumber === searchRange.endLineNumber) {
  580. const text = this.getLineContent(startLineNumber).substring(startColumn, searchRange.endColumn - 1);
  581. resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
  582. return result;
  583. }
  584. resultLen = this._findMatchesInLine(searchData, searcher, this.getLineContent(startLineNumber).substr(startColumn), startLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
  585. if (resultLen >= limitResultCount) {
  586. return result;
  587. }
  588. startLineNumber++;
  589. startPosition = this.nodeAt2(startLineNumber, 1);
  590. currentNode = startPosition.node;
  591. start = this.positionInBuffer(startPosition.node, startPosition.remainder);
  592. }
  593. if (startLineNumber === searchRange.endLineNumber) {
  594. const startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn - 1 : 0;
  595. const text = this.getLineContent(startLineNumber).substring(startColumn, searchRange.endColumn - 1);
  596. resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
  597. return result;
  598. }
  599. const startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn : 1;
  600. resultLen = this.findMatchesInNode(endPosition.node, searcher, startLineNumber, startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
  601. return result;
  602. }
  603. _findMatchesInLine(searchData, searcher, text, lineNumber, deltaOffset, resultLen, result, captureMatches, limitResultCount) {
  604. const wordSeparators = searchData.wordSeparators;
  605. if (!captureMatches && searchData.simpleSearch) {
  606. const searchString = searchData.simpleSearch;
  607. const searchStringLen = searchString.length;
  608. const textLength = text.length;
  609. let lastMatchIndex = -searchStringLen;
  610. while ((lastMatchIndex = text.indexOf(searchString, lastMatchIndex + searchStringLen)) !== -1) {
  611. if (!wordSeparators || isValidMatch(wordSeparators, text, textLength, lastMatchIndex, searchStringLen)) {
  612. result[resultLen++] = new FindMatch(new Range(lineNumber, lastMatchIndex + 1 + deltaOffset, lineNumber, lastMatchIndex + 1 + searchStringLen + deltaOffset), null);
  613. if (resultLen >= limitResultCount) {
  614. return resultLen;
  615. }
  616. }
  617. }
  618. return resultLen;
  619. }
  620. let m;
  621. // Reset regex to search from the beginning
  622. searcher.reset(0);
  623. do {
  624. m = searcher.next(text);
  625. if (m) {
  626. result[resultLen++] = createFindMatch(new Range(lineNumber, m.index + 1 + deltaOffset, lineNumber, m.index + 1 + m[0].length + deltaOffset), m, captureMatches);
  627. if (resultLen >= limitResultCount) {
  628. return resultLen;
  629. }
  630. }
  631. } while (m);
  632. return resultLen;
  633. }
  634. // #endregion
  635. // #region Piece Table
  636. insert(offset, value, eolNormalized = false) {
  637. this._EOLNormalized = this._EOLNormalized && eolNormalized;
  638. this._lastVisitedLine.lineNumber = 0;
  639. this._lastVisitedLine.value = '';
  640. if (this.root !== SENTINEL) {
  641. const { node, remainder, nodeStartOffset } = this.nodeAt(offset);
  642. const piece = node.piece;
  643. const bufferIndex = piece.bufferIndex;
  644. const insertPosInBuffer = this.positionInBuffer(node, remainder);
  645. if (node.piece.bufferIndex === 0 &&
  646. piece.end.line === this._lastChangeBufferPos.line &&
  647. piece.end.column === this._lastChangeBufferPos.column &&
  648. (nodeStartOffset + piece.length === offset) &&
  649. value.length < AverageBufferSize) {
  650. // changed buffer
  651. this.appendToNode(node, value);
  652. this.computeBufferMetadata();
  653. return;
  654. }
  655. if (nodeStartOffset === offset) {
  656. this.insertContentToNodeLeft(value, node);
  657. this._searchCache.validate(offset);
  658. }
  659. else if (nodeStartOffset + node.piece.length > offset) {
  660. // we are inserting into the middle of a node.
  661. const nodesToDel = [];
  662. let newRightPiece = new Piece(piece.bufferIndex, insertPosInBuffer, piece.end, this.getLineFeedCnt(piece.bufferIndex, insertPosInBuffer, piece.end), this.offsetInBuffer(bufferIndex, piece.end) - this.offsetInBuffer(bufferIndex, insertPosInBuffer));
  663. if (this.shouldCheckCRLF() && this.endWithCR(value)) {
  664. const headOfRight = this.nodeCharCodeAt(node, remainder);
  665. if (headOfRight === 10 /** \n */) {
  666. const newStart = { line: newRightPiece.start.line + 1, column: 0 };
  667. newRightPiece = new Piece(newRightPiece.bufferIndex, newStart, newRightPiece.end, this.getLineFeedCnt(newRightPiece.bufferIndex, newStart, newRightPiece.end), newRightPiece.length - 1);
  668. value += '\n';
  669. }
  670. }
  671. // reuse node for content before insertion point.
  672. if (this.shouldCheckCRLF() && this.startWithLF(value)) {
  673. const tailOfLeft = this.nodeCharCodeAt(node, remainder - 1);
  674. if (tailOfLeft === 13 /** \r */) {
  675. const previousPos = this.positionInBuffer(node, remainder - 1);
  676. this.deleteNodeTail(node, previousPos);
  677. value = '\r' + value;
  678. if (node.piece.length === 0) {
  679. nodesToDel.push(node);
  680. }
  681. }
  682. else {
  683. this.deleteNodeTail(node, insertPosInBuffer);
  684. }
  685. }
  686. else {
  687. this.deleteNodeTail(node, insertPosInBuffer);
  688. }
  689. const newPieces = this.createNewPieces(value);
  690. if (newRightPiece.length > 0) {
  691. this.rbInsertRight(node, newRightPiece);
  692. }
  693. let tmpNode = node;
  694. for (let k = 0; k < newPieces.length; k++) {
  695. tmpNode = this.rbInsertRight(tmpNode, newPieces[k]);
  696. }
  697. this.deleteNodes(nodesToDel);
  698. }
  699. else {
  700. this.insertContentToNodeRight(value, node);
  701. }
  702. }
  703. else {
  704. // insert new node
  705. const pieces = this.createNewPieces(value);
  706. let node = this.rbInsertLeft(null, pieces[0]);
  707. for (let k = 1; k < pieces.length; k++) {
  708. node = this.rbInsertRight(node, pieces[k]);
  709. }
  710. }
  711. // todo, this is too brutal. Total line feed count should be updated the same way as lf_left.
  712. this.computeBufferMetadata();
  713. }
  714. delete(offset, cnt) {
  715. this._lastVisitedLine.lineNumber = 0;
  716. this._lastVisitedLine.value = '';
  717. if (cnt <= 0 || this.root === SENTINEL) {
  718. return;
  719. }
  720. const startPosition = this.nodeAt(offset);
  721. const endPosition = this.nodeAt(offset + cnt);
  722. const startNode = startPosition.node;
  723. const endNode = endPosition.node;
  724. if (startNode === endNode) {
  725. const startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder);
  726. const endSplitPosInBuffer = this.positionInBuffer(startNode, endPosition.remainder);
  727. if (startPosition.nodeStartOffset === offset) {
  728. if (cnt === startNode.piece.length) { // delete node
  729. const next = startNode.next();
  730. rbDelete(this, startNode);
  731. this.validateCRLFWithPrevNode(next);
  732. this.computeBufferMetadata();
  733. return;
  734. }
  735. this.deleteNodeHead(startNode, endSplitPosInBuffer);
  736. this._searchCache.validate(offset);
  737. this.validateCRLFWithPrevNode(startNode);
  738. this.computeBufferMetadata();
  739. return;
  740. }
  741. if (startPosition.nodeStartOffset + startNode.piece.length === offset + cnt) {
  742. this.deleteNodeTail(startNode, startSplitPosInBuffer);
  743. this.validateCRLFWithNextNode(startNode);
  744. this.computeBufferMetadata();
  745. return;
  746. }
  747. // delete content in the middle, this node will be splitted to nodes
  748. this.shrinkNode(startNode, startSplitPosInBuffer, endSplitPosInBuffer);
  749. this.computeBufferMetadata();
  750. return;
  751. }
  752. const nodesToDel = [];
  753. const startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder);
  754. this.deleteNodeTail(startNode, startSplitPosInBuffer);
  755. this._searchCache.validate(offset);
  756. if (startNode.piece.length === 0) {
  757. nodesToDel.push(startNode);
  758. }
  759. // update last touched node
  760. const endSplitPosInBuffer = this.positionInBuffer(endNode, endPosition.remainder);
  761. this.deleteNodeHead(endNode, endSplitPosInBuffer);
  762. if (endNode.piece.length === 0) {
  763. nodesToDel.push(endNode);
  764. }
  765. // delete nodes in between
  766. const secondNode = startNode.next();
  767. for (let node = secondNode; node !== SENTINEL && node !== endNode; node = node.next()) {
  768. nodesToDel.push(node);
  769. }
  770. const prev = startNode.piece.length === 0 ? startNode.prev() : startNode;
  771. this.deleteNodes(nodesToDel);
  772. this.validateCRLFWithNextNode(prev);
  773. this.computeBufferMetadata();
  774. }
  775. insertContentToNodeLeft(value, node) {
  776. // we are inserting content to the beginning of node
  777. const nodesToDel = [];
  778. if (this.shouldCheckCRLF() && this.endWithCR(value) && this.startWithLF(node)) {
  779. // move `\n` to new node.
  780. const piece = node.piece;
  781. const newStart = { line: piece.start.line + 1, column: 0 };
  782. const nPiece = new Piece(piece.bufferIndex, newStart, piece.end, this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end), piece.length - 1);
  783. node.piece = nPiece;
  784. value += '\n';
  785. updateTreeMetadata(this, node, -1, -1);
  786. if (node.piece.length === 0) {
  787. nodesToDel.push(node);
  788. }
  789. }
  790. const newPieces = this.createNewPieces(value);
  791. let newNode = this.rbInsertLeft(node, newPieces[newPieces.length - 1]);
  792. for (let k = newPieces.length - 2; k >= 0; k--) {
  793. newNode = this.rbInsertLeft(newNode, newPieces[k]);
  794. }
  795. this.validateCRLFWithPrevNode(newNode);
  796. this.deleteNodes(nodesToDel);
  797. }
  798. insertContentToNodeRight(value, node) {
  799. // we are inserting to the right of this node.
  800. if (this.adjustCarriageReturnFromNext(value, node)) {
  801. // move \n to the new node.
  802. value += '\n';
  803. }
  804. const newPieces = this.createNewPieces(value);
  805. const newNode = this.rbInsertRight(node, newPieces[0]);
  806. let tmpNode = newNode;
  807. for (let k = 1; k < newPieces.length; k++) {
  808. tmpNode = this.rbInsertRight(tmpNode, newPieces[k]);
  809. }
  810. this.validateCRLFWithPrevNode(newNode);
  811. }
  812. positionInBuffer(node, remainder, ret) {
  813. const piece = node.piece;
  814. const bufferIndex = node.piece.bufferIndex;
  815. const lineStarts = this._buffers[bufferIndex].lineStarts;
  816. const startOffset = lineStarts[piece.start.line] + piece.start.column;
  817. const offset = startOffset + remainder;
  818. // binary search offset between startOffset and endOffset
  819. let low = piece.start.line;
  820. let high = piece.end.line;
  821. let mid = 0;
  822. let midStop = 0;
  823. let midStart = 0;
  824. while (low <= high) {
  825. mid = low + ((high - low) / 2) | 0;
  826. midStart = lineStarts[mid];
  827. if (mid === high) {
  828. break;
  829. }
  830. midStop = lineStarts[mid + 1];
  831. if (offset < midStart) {
  832. high = mid - 1;
  833. }
  834. else if (offset >= midStop) {
  835. low = mid + 1;
  836. }
  837. else {
  838. break;
  839. }
  840. }
  841. if (ret) {
  842. ret.line = mid;
  843. ret.column = offset - midStart;
  844. return null;
  845. }
  846. return {
  847. line: mid,
  848. column: offset - midStart
  849. };
  850. }
  851. getLineFeedCnt(bufferIndex, start, end) {
  852. // we don't need to worry about start: abc\r|\n, or abc|\r, or abc|\n, or abc|\r\n doesn't change the fact that, there is one line break after start.
  853. // now let's take care of end: abc\r|\n, if end is in between \r and \n, we need to add line feed count by 1
  854. if (end.column === 0) {
  855. return end.line - start.line;
  856. }
  857. const lineStarts = this._buffers[bufferIndex].lineStarts;
  858. if (end.line === lineStarts.length - 1) { // it means, there is no \n after end, otherwise, there will be one more lineStart.
  859. return end.line - start.line;
  860. }
  861. const nextLineStartOffset = lineStarts[end.line + 1];
  862. const endOffset = lineStarts[end.line] + end.column;
  863. if (nextLineStartOffset > endOffset + 1) { // there are more than 1 character after end, which means it can't be \n
  864. return end.line - start.line;
  865. }
  866. // endOffset + 1 === nextLineStartOffset
  867. // character at endOffset is \n, so we check the character before first
  868. // if character at endOffset is \r, end.column is 0 and we can't get here.
  869. const previousCharOffset = endOffset - 1; // end.column > 0 so it's okay.
  870. const buffer = this._buffers[bufferIndex].buffer;
  871. if (buffer.charCodeAt(previousCharOffset) === 13) {
  872. return end.line - start.line + 1;
  873. }
  874. else {
  875. return end.line - start.line;
  876. }
  877. }
  878. offsetInBuffer(bufferIndex, cursor) {
  879. const lineStarts = this._buffers[bufferIndex].lineStarts;
  880. return lineStarts[cursor.line] + cursor.column;
  881. }
  882. deleteNodes(nodes) {
  883. for (let i = 0; i < nodes.length; i++) {
  884. rbDelete(this, nodes[i]);
  885. }
  886. }
  887. createNewPieces(text) {
  888. if (text.length > AverageBufferSize) {
  889. // the content is large, operations like substring, charCode becomes slow
  890. // so here we split it into smaller chunks, just like what we did for CR/LF normalization
  891. const newPieces = [];
  892. while (text.length > AverageBufferSize) {
  893. const lastChar = text.charCodeAt(AverageBufferSize - 1);
  894. let splitText;
  895. if (lastChar === 13 /* CharCode.CarriageReturn */ || (lastChar >= 0xD800 && lastChar <= 0xDBFF)) {
  896. // last character is \r or a high surrogate => keep it back
  897. splitText = text.substring(0, AverageBufferSize - 1);
  898. text = text.substring(AverageBufferSize - 1);
  899. }
  900. else {
  901. splitText = text.substring(0, AverageBufferSize);
  902. text = text.substring(AverageBufferSize);
  903. }
  904. const lineStarts = createLineStartsFast(splitText);
  905. newPieces.push(new Piece(this._buffers.length, /* buffer index */ { line: 0, column: 0 }, { line: lineStarts.length - 1, column: splitText.length - lineStarts[lineStarts.length - 1] }, lineStarts.length - 1, splitText.length));
  906. this._buffers.push(new StringBuffer(splitText, lineStarts));
  907. }
  908. const lineStarts = createLineStartsFast(text);
  909. newPieces.push(new Piece(this._buffers.length, /* buffer index */ { line: 0, column: 0 }, { line: lineStarts.length - 1, column: text.length - lineStarts[lineStarts.length - 1] }, lineStarts.length - 1, text.length));
  910. this._buffers.push(new StringBuffer(text, lineStarts));
  911. return newPieces;
  912. }
  913. let startOffset = this._buffers[0].buffer.length;
  914. const lineStarts = createLineStartsFast(text, false);
  915. let start = this._lastChangeBufferPos;
  916. if (this._buffers[0].lineStarts[this._buffers[0].lineStarts.length - 1] === startOffset
  917. && startOffset !== 0
  918. && this.startWithLF(text)
  919. && this.endWithCR(this._buffers[0].buffer) // todo, we can check this._lastChangeBufferPos's column as it's the last one
  920. ) {
  921. this._lastChangeBufferPos = { line: this._lastChangeBufferPos.line, column: this._lastChangeBufferPos.column + 1 };
  922. start = this._lastChangeBufferPos;
  923. for (let i = 0; i < lineStarts.length; i++) {
  924. lineStarts[i] += startOffset + 1;
  925. }
  926. this._buffers[0].lineStarts = this._buffers[0].lineStarts.concat(lineStarts.slice(1));
  927. this._buffers[0].buffer += '_' + text;
  928. startOffset += 1;
  929. }
  930. else {
  931. if (startOffset !== 0) {
  932. for (let i = 0; i < lineStarts.length; i++) {
  933. lineStarts[i] += startOffset;
  934. }
  935. }
  936. this._buffers[0].lineStarts = this._buffers[0].lineStarts.concat(lineStarts.slice(1));
  937. this._buffers[0].buffer += text;
  938. }
  939. const endOffset = this._buffers[0].buffer.length;
  940. const endIndex = this._buffers[0].lineStarts.length - 1;
  941. const endColumn = endOffset - this._buffers[0].lineStarts[endIndex];
  942. const endPos = { line: endIndex, column: endColumn };
  943. const newPiece = new Piece(0, /** todo@peng */ start, endPos, this.getLineFeedCnt(0, start, endPos), endOffset - startOffset);
  944. this._lastChangeBufferPos = endPos;
  945. return [newPiece];
  946. }
  947. getLineRawContent(lineNumber, endOffset = 0) {
  948. let x = this.root;
  949. let ret = '';
  950. const cache = this._searchCache.get2(lineNumber);
  951. if (cache) {
  952. x = cache.node;
  953. const prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber - 1);
  954. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  955. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  956. if (cache.nodeStartLineNumber + x.piece.lineFeedCnt === lineNumber) {
  957. ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length);
  958. }
  959. else {
  960. const accumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber);
  961. return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset);
  962. }
  963. }
  964. else {
  965. let nodeStartOffset = 0;
  966. const originalLineNumber = lineNumber;
  967. while (x !== SENTINEL) {
  968. if (x.left !== SENTINEL && x.lf_left >= lineNumber - 1) {
  969. x = x.left;
  970. }
  971. else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) {
  972. const prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2);
  973. const accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1);
  974. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  975. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  976. nodeStartOffset += x.size_left;
  977. this._searchCache.set({
  978. node: x,
  979. nodeStartOffset,
  980. nodeStartLineNumber: originalLineNumber - (lineNumber - 1 - x.lf_left)
  981. });
  982. return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset);
  983. }
  984. else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) {
  985. const prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2);
  986. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  987. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  988. ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length);
  989. break;
  990. }
  991. else {
  992. lineNumber -= x.lf_left + x.piece.lineFeedCnt;
  993. nodeStartOffset += x.size_left + x.piece.length;
  994. x = x.right;
  995. }
  996. }
  997. }
  998. // search in order, to find the node contains end column
  999. x = x.next();
  1000. while (x !== SENTINEL) {
  1001. const buffer = this._buffers[x.piece.bufferIndex].buffer;
  1002. if (x.piece.lineFeedCnt > 0) {
  1003. const accumulatedValue = this.getAccumulatedValue(x, 0);
  1004. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  1005. ret += buffer.substring(startOffset, startOffset + accumulatedValue - endOffset);
  1006. return ret;
  1007. }
  1008. else {
  1009. const startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start);
  1010. ret += buffer.substr(startOffset, x.piece.length);
  1011. }
  1012. x = x.next();
  1013. }
  1014. return ret;
  1015. }
  1016. computeBufferMetadata() {
  1017. let x = this.root;
  1018. let lfCnt = 1;
  1019. let len = 0;
  1020. while (x !== SENTINEL) {
  1021. lfCnt += x.lf_left + x.piece.lineFeedCnt;
  1022. len += x.size_left + x.piece.length;
  1023. x = x.right;
  1024. }
  1025. this._lineCnt = lfCnt;
  1026. this._length = len;
  1027. this._searchCache.validate(this._length);
  1028. }
  1029. // #region node operations
  1030. getIndexOf(node, accumulatedValue) {
  1031. const piece = node.piece;
  1032. const pos = this.positionInBuffer(node, accumulatedValue);
  1033. const lineCnt = pos.line - piece.start.line;
  1034. if (this.offsetInBuffer(piece.bufferIndex, piece.end) - this.offsetInBuffer(piece.bufferIndex, piece.start) === accumulatedValue) {
  1035. // we are checking the end of this node, so a CRLF check is necessary.
  1036. const realLineCnt = this.getLineFeedCnt(node.piece.bufferIndex, piece.start, pos);
  1037. if (realLineCnt !== lineCnt) {
  1038. // aha yes, CRLF
  1039. return { index: realLineCnt, remainder: 0 };
  1040. }
  1041. }
  1042. return { index: lineCnt, remainder: pos.column };
  1043. }
  1044. getAccumulatedValue(node, index) {
  1045. if (index < 0) {
  1046. return 0;
  1047. }
  1048. const piece = node.piece;
  1049. const lineStarts = this._buffers[piece.bufferIndex].lineStarts;
  1050. const expectedLineStartIndex = piece.start.line + index + 1;
  1051. if (expectedLineStartIndex > piece.end.line) {
  1052. return lineStarts[piece.end.line] + piece.end.column - lineStarts[piece.start.line] - piece.start.column;
  1053. }
  1054. else {
  1055. return lineStarts[expectedLineStartIndex] - lineStarts[piece.start.line] - piece.start.column;
  1056. }
  1057. }
  1058. deleteNodeTail(node, pos) {
  1059. const piece = node.piece;
  1060. const originalLFCnt = piece.lineFeedCnt;
  1061. const originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
  1062. const newEnd = pos;
  1063. const newEndOffset = this.offsetInBuffer(piece.bufferIndex, newEnd);
  1064. const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
  1065. const lf_delta = newLineFeedCnt - originalLFCnt;
  1066. const size_delta = newEndOffset - originalEndOffset;
  1067. const newLength = piece.length + size_delta;
  1068. node.piece = new Piece(piece.bufferIndex, piece.start, newEnd, newLineFeedCnt, newLength);
  1069. updateTreeMetadata(this, node, size_delta, lf_delta);
  1070. }
  1071. deleteNodeHead(node, pos) {
  1072. const piece = node.piece;
  1073. const originalLFCnt = piece.lineFeedCnt;
  1074. const originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
  1075. const newStart = pos;
  1076. const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
  1077. const newStartOffset = this.offsetInBuffer(piece.bufferIndex, newStart);
  1078. const lf_delta = newLineFeedCnt - originalLFCnt;
  1079. const size_delta = originalStartOffset - newStartOffset;
  1080. const newLength = piece.length + size_delta;
  1081. node.piece = new Piece(piece.bufferIndex, newStart, piece.end, newLineFeedCnt, newLength);
  1082. updateTreeMetadata(this, node, size_delta, lf_delta);
  1083. }
  1084. shrinkNode(node, start, end) {
  1085. const piece = node.piece;
  1086. const originalStartPos = piece.start;
  1087. const originalEndPos = piece.end;
  1088. // old piece, originalStartPos, start
  1089. const oldLength = piece.length;
  1090. const oldLFCnt = piece.lineFeedCnt;
  1091. const newEnd = start;
  1092. const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
  1093. const newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos);
  1094. node.piece = new Piece(piece.bufferIndex, piece.start, newEnd, newLineFeedCnt, newLength);
  1095. updateTreeMetadata(this, node, newLength - oldLength, newLineFeedCnt - oldLFCnt);
  1096. // new right piece, end, originalEndPos
  1097. const newPiece = new Piece(piece.bufferIndex, end, originalEndPos, this.getLineFeedCnt(piece.bufferIndex, end, originalEndPos), this.offsetInBuffer(piece.bufferIndex, originalEndPos) - this.offsetInBuffer(piece.bufferIndex, end));
  1098. const newNode = this.rbInsertRight(node, newPiece);
  1099. this.validateCRLFWithPrevNode(newNode);
  1100. }
  1101. appendToNode(node, value) {
  1102. if (this.adjustCarriageReturnFromNext(value, node)) {
  1103. value += '\n';
  1104. }
  1105. const hitCRLF = this.shouldCheckCRLF() && this.startWithLF(value) && this.endWithCR(node);
  1106. const startOffset = this._buffers[0].buffer.length;
  1107. this._buffers[0].buffer += value;
  1108. const lineStarts = createLineStartsFast(value, false);
  1109. for (let i = 0; i < lineStarts.length; i++) {
  1110. lineStarts[i] += startOffset;
  1111. }
  1112. if (hitCRLF) {
  1113. const prevStartOffset = this._buffers[0].lineStarts[this._buffers[0].lineStarts.length - 2];
  1114. this._buffers[0].lineStarts.pop();
  1115. // _lastChangeBufferPos is already wrong
  1116. this._lastChangeBufferPos = { line: this._lastChangeBufferPos.line - 1, column: startOffset - prevStartOffset };
  1117. }
  1118. this._buffers[0].lineStarts = this._buffers[0].lineStarts.concat(lineStarts.slice(1));
  1119. const endIndex = this._buffers[0].lineStarts.length - 1;
  1120. const endColumn = this._buffers[0].buffer.length - this._buffers[0].lineStarts[endIndex];
  1121. const newEnd = { line: endIndex, column: endColumn };
  1122. const newLength = node.piece.length + value.length;
  1123. const oldLineFeedCnt = node.piece.lineFeedCnt;
  1124. const newLineFeedCnt = this.getLineFeedCnt(0, node.piece.start, newEnd);
  1125. const lf_delta = newLineFeedCnt - oldLineFeedCnt;
  1126. node.piece = new Piece(node.piece.bufferIndex, node.piece.start, newEnd, newLineFeedCnt, newLength);
  1127. this._lastChangeBufferPos = newEnd;
  1128. updateTreeMetadata(this, node, value.length, lf_delta);
  1129. }
  1130. nodeAt(offset) {
  1131. let x = this.root;
  1132. const cache = this._searchCache.get(offset);
  1133. if (cache) {
  1134. return {
  1135. node: cache.node,
  1136. nodeStartOffset: cache.nodeStartOffset,
  1137. remainder: offset - cache.nodeStartOffset
  1138. };
  1139. }
  1140. let nodeStartOffset = 0;
  1141. while (x !== SENTINEL) {
  1142. if (x.size_left > offset) {
  1143. x = x.left;
  1144. }
  1145. else if (x.size_left + x.piece.length >= offset) {
  1146. nodeStartOffset += x.size_left;
  1147. const ret = {
  1148. node: x,
  1149. remainder: offset - x.size_left,
  1150. nodeStartOffset
  1151. };
  1152. this._searchCache.set(ret);
  1153. return ret;
  1154. }
  1155. else {
  1156. offset -= x.size_left + x.piece.length;
  1157. nodeStartOffset += x.size_left + x.piece.length;
  1158. x = x.right;
  1159. }
  1160. }
  1161. return null;
  1162. }
  1163. nodeAt2(lineNumber, column) {
  1164. let x = this.root;
  1165. let nodeStartOffset = 0;
  1166. while (x !== SENTINEL) {
  1167. if (x.left !== SENTINEL && x.lf_left >= lineNumber - 1) {
  1168. x = x.left;
  1169. }
  1170. else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) {
  1171. const prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2);
  1172. const accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1);
  1173. nodeStartOffset += x.size_left;
  1174. return {
  1175. node: x,
  1176. remainder: Math.min(prevAccumualtedValue + column - 1, accumulatedValue),
  1177. nodeStartOffset
  1178. };
  1179. }
  1180. else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) {
  1181. const prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2);
  1182. if (prevAccumualtedValue + column - 1 <= x.piece.length) {
  1183. return {
  1184. node: x,
  1185. remainder: prevAccumualtedValue + column - 1,
  1186. nodeStartOffset
  1187. };
  1188. }
  1189. else {
  1190. column -= x.piece.length - prevAccumualtedValue;
  1191. break;
  1192. }
  1193. }
  1194. else {
  1195. lineNumber -= x.lf_left + x.piece.lineFeedCnt;
  1196. nodeStartOffset += x.size_left + x.piece.length;
  1197. x = x.right;
  1198. }
  1199. }
  1200. // search in order, to find the node contains position.column
  1201. x = x.next();
  1202. while (x !== SENTINEL) {
  1203. if (x.piece.lineFeedCnt > 0) {
  1204. const accumulatedValue = this.getAccumulatedValue(x, 0);
  1205. const nodeStartOffset = this.offsetOfNode(x);
  1206. return {
  1207. node: x,
  1208. remainder: Math.min(column - 1, accumulatedValue),
  1209. nodeStartOffset
  1210. };
  1211. }
  1212. else {
  1213. if (x.piece.length >= column - 1) {
  1214. const nodeStartOffset = this.offsetOfNode(x);
  1215. return {
  1216. node: x,
  1217. remainder: column - 1,
  1218. nodeStartOffset
  1219. };
  1220. }
  1221. else {
  1222. column -= x.piece.length;
  1223. }
  1224. }
  1225. x = x.next();
  1226. }
  1227. return null;
  1228. }
  1229. nodeCharCodeAt(node, offset) {
  1230. if (node.piece.lineFeedCnt < 1) {
  1231. return -1;
  1232. }
  1233. const buffer = this._buffers[node.piece.bufferIndex];
  1234. const newOffset = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start) + offset;
  1235. return buffer.buffer.charCodeAt(newOffset);
  1236. }
  1237. offsetOfNode(node) {
  1238. if (!node) {
  1239. return 0;
  1240. }
  1241. let pos = node.size_left;
  1242. while (node !== this.root) {
  1243. if (node.parent.right === node) {
  1244. pos += node.parent.size_left + node.parent.piece.length;
  1245. }
  1246. node = node.parent;
  1247. }
  1248. return pos;
  1249. }
  1250. // #endregion
  1251. // #region CRLF
  1252. shouldCheckCRLF() {
  1253. return !(this._EOLNormalized && this._EOL === '\n');
  1254. }
  1255. startWithLF(val) {
  1256. if (typeof val === 'string') {
  1257. return val.charCodeAt(0) === 10;
  1258. }
  1259. if (val === SENTINEL || val.piece.lineFeedCnt === 0) {
  1260. return false;
  1261. }
  1262. const piece = val.piece;
  1263. const lineStarts = this._buffers[piece.bufferIndex].lineStarts;
  1264. const line = piece.start.line;
  1265. const startOffset = lineStarts[line] + piece.start.column;
  1266. if (line === lineStarts.length - 1) {
  1267. // last line, so there is no line feed at the end of this line
  1268. return false;
  1269. }
  1270. const nextLineOffset = lineStarts[line + 1];
  1271. if (nextLineOffset > startOffset + 1) {
  1272. return false;
  1273. }
  1274. return this._buffers[piece.bufferIndex].buffer.charCodeAt(startOffset) === 10;
  1275. }
  1276. endWithCR(val) {
  1277. if (typeof val === 'string') {
  1278. return val.charCodeAt(val.length - 1) === 13;
  1279. }
  1280. if (val === SENTINEL || val.piece.lineFeedCnt === 0) {
  1281. return false;
  1282. }
  1283. return this.nodeCharCodeAt(val, val.piece.length - 1) === 13;
  1284. }
  1285. validateCRLFWithPrevNode(nextNode) {
  1286. if (this.shouldCheckCRLF() && this.startWithLF(nextNode)) {
  1287. const node = nextNode.prev();
  1288. if (this.endWithCR(node)) {
  1289. this.fixCRLF(node, nextNode);
  1290. }
  1291. }
  1292. }
  1293. validateCRLFWithNextNode(node) {
  1294. if (this.shouldCheckCRLF() && this.endWithCR(node)) {
  1295. const nextNode = node.next();
  1296. if (this.startWithLF(nextNode)) {
  1297. this.fixCRLF(node, nextNode);
  1298. }
  1299. }
  1300. }
  1301. fixCRLF(prev, next) {
  1302. const nodesToDel = [];
  1303. // update node
  1304. const lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts;
  1305. let newEnd;
  1306. if (prev.piece.end.column === 0) {
  1307. // it means, last line ends with \r, not \r\n
  1308. newEnd = { line: prev.piece.end.line - 1, column: lineStarts[prev.piece.end.line] - lineStarts[prev.piece.end.line - 1] - 1 };
  1309. }
  1310. else {
  1311. // \r\n
  1312. newEnd = { line: prev.piece.end.line, column: prev.piece.end.column - 1 };
  1313. }
  1314. const prevNewLength = prev.piece.length - 1;
  1315. const prevNewLFCnt = prev.piece.lineFeedCnt - 1;
  1316. prev.piece = new Piece(prev.piece.bufferIndex, prev.piece.start, newEnd, prevNewLFCnt, prevNewLength);
  1317. updateTreeMetadata(this, prev, -1, -1);
  1318. if (prev.piece.length === 0) {
  1319. nodesToDel.push(prev);
  1320. }
  1321. // update nextNode
  1322. const newStart = { line: next.piece.start.line + 1, column: 0 };
  1323. const newLength = next.piece.length - 1;
  1324. const newLineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, newStart, next.piece.end);
  1325. next.piece = new Piece(next.piece.bufferIndex, newStart, next.piece.end, newLineFeedCnt, newLength);
  1326. updateTreeMetadata(this, next, -1, -1);
  1327. if (next.piece.length === 0) {
  1328. nodesToDel.push(next);
  1329. }
  1330. // create new piece which contains \r\n
  1331. const pieces = this.createNewPieces('\r\n');
  1332. this.rbInsertRight(prev, pieces[0]);
  1333. // delete empty nodes
  1334. for (let i = 0; i < nodesToDel.length; i++) {
  1335. rbDelete(this, nodesToDel[i]);
  1336. }
  1337. }
  1338. adjustCarriageReturnFromNext(value, node) {
  1339. if (this.shouldCheckCRLF() && this.endWithCR(value)) {
  1340. const nextNode = node.next();
  1341. if (this.startWithLF(nextNode)) {
  1342. // move `\n` forward
  1343. value += '\n';
  1344. if (nextNode.piece.length === 1) {
  1345. rbDelete(this, nextNode);
  1346. }
  1347. else {
  1348. const piece = nextNode.piece;
  1349. const newStart = { line: piece.start.line + 1, column: 0 };
  1350. const newLength = piece.length - 1;
  1351. const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
  1352. nextNode.piece = new Piece(piece.bufferIndex, newStart, piece.end, newLineFeedCnt, newLength);
  1353. updateTreeMetadata(this, nextNode, -1, -1);
  1354. }
  1355. return true;
  1356. }
  1357. }
  1358. return false;
  1359. }
  1360. // #endregion
  1361. // #endregion
  1362. // #region Tree operations
  1363. iterate(node, callback) {
  1364. if (node === SENTINEL) {
  1365. return callback(SENTINEL);
  1366. }
  1367. const leftRet = this.iterate(node.left, callback);
  1368. if (!leftRet) {
  1369. return leftRet;
  1370. }
  1371. return callback(node) && this.iterate(node.right, callback);
  1372. }
  1373. getNodeContent(node) {
  1374. if (node === SENTINEL) {
  1375. return '';
  1376. }
  1377. const buffer = this._buffers[node.piece.bufferIndex];
  1378. const piece = node.piece;
  1379. const startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
  1380. const endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
  1381. const currentContent = buffer.buffer.substring(startOffset, endOffset);
  1382. return currentContent;
  1383. }
  1384. getPieceContent(piece) {
  1385. const buffer = this._buffers[piece.bufferIndex];
  1386. const startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
  1387. const endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
  1388. const currentContent = buffer.buffer.substring(startOffset, endOffset);
  1389. return currentContent;
  1390. }
  1391. /**
  1392. * node node
  1393. * / \ / \
  1394. * a b <---- a b
  1395. * /
  1396. * z
  1397. */
  1398. rbInsertRight(node, p) {
  1399. const z = new TreeNode(p, 1 /* NodeColor.Red */);
  1400. z.left = SENTINEL;
  1401. z.right = SENTINEL;
  1402. z.parent = SENTINEL;
  1403. z.size_left = 0;
  1404. z.lf_left = 0;
  1405. const x = this.root;
  1406. if (x === SENTINEL) {
  1407. this.root = z;
  1408. z.color = 0 /* NodeColor.Black */;
  1409. }
  1410. else if (node.right === SENTINEL) {
  1411. node.right = z;
  1412. z.parent = node;
  1413. }
  1414. else {
  1415. const nextNode = leftest(node.right);
  1416. nextNode.left = z;
  1417. z.parent = nextNode;
  1418. }
  1419. fixInsert(this, z);
  1420. return z;
  1421. }
  1422. /**
  1423. * node node
  1424. * / \ / \
  1425. * a b ----> a b
  1426. * \
  1427. * z
  1428. */
  1429. rbInsertLeft(node, p) {
  1430. const z = new TreeNode(p, 1 /* NodeColor.Red */);
  1431. z.left = SENTINEL;
  1432. z.right = SENTINEL;
  1433. z.parent = SENTINEL;
  1434. z.size_left = 0;
  1435. z.lf_left = 0;
  1436. if (this.root === SENTINEL) {
  1437. this.root = z;
  1438. z.color = 0 /* NodeColor.Black */;
  1439. }
  1440. else if (node.left === SENTINEL) {
  1441. node.left = z;
  1442. z.parent = node;
  1443. }
  1444. else {
  1445. const prevNode = righttest(node.left); // a
  1446. prevNode.right = z;
  1447. z.parent = prevNode;
  1448. }
  1449. fixInsert(this, z);
  1450. return z;
  1451. }
  1452. }