main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. 'use strict';
  6. var FullTextDocument = /** @class */ (function () {
  7. function FullTextDocument(uri, languageId, version, content) {
  8. this._uri = uri;
  9. this._languageId = languageId;
  10. this._version = version;
  11. this._content = content;
  12. this._lineOffsets = undefined;
  13. }
  14. Object.defineProperty(FullTextDocument.prototype, "uri", {
  15. get: function () {
  16. return this._uri;
  17. },
  18. enumerable: true,
  19. configurable: true
  20. });
  21. Object.defineProperty(FullTextDocument.prototype, "languageId", {
  22. get: function () {
  23. return this._languageId;
  24. },
  25. enumerable: true,
  26. configurable: true
  27. });
  28. Object.defineProperty(FullTextDocument.prototype, "version", {
  29. get: function () {
  30. return this._version;
  31. },
  32. enumerable: true,
  33. configurable: true
  34. });
  35. FullTextDocument.prototype.getText = function (range) {
  36. if (range) {
  37. var start = this.offsetAt(range.start);
  38. var end = this.offsetAt(range.end);
  39. return this._content.substring(start, end);
  40. }
  41. return this._content;
  42. };
  43. FullTextDocument.prototype.update = function (changes, version) {
  44. for (var _i = 0, changes_1 = changes; _i < changes_1.length; _i++) {
  45. var change = changes_1[_i];
  46. if (FullTextDocument.isIncremental(change)) {
  47. // makes sure start is before end
  48. var range = getWellformedRange(change.range);
  49. // update content
  50. var startOffset = this.offsetAt(range.start);
  51. var endOffset = this.offsetAt(range.end);
  52. this._content = this._content.substring(0, startOffset) + change.text + this._content.substring(endOffset, this._content.length);
  53. // update the offsets
  54. var startLine = Math.max(range.start.line, 0);
  55. var endLine = Math.max(range.end.line, 0);
  56. var lineOffsets = this._lineOffsets;
  57. var addedLineOffsets = computeLineOffsets(change.text, false, startOffset);
  58. if (endLine - startLine === addedLineOffsets.length) {
  59. for (var i = 0, len = addedLineOffsets.length; i < len; i++) {
  60. lineOffsets[i + startLine + 1] = addedLineOffsets[i];
  61. }
  62. }
  63. else {
  64. if (addedLineOffsets.length < 10000) {
  65. lineOffsets.splice.apply(lineOffsets, [startLine + 1, endLine - startLine].concat(addedLineOffsets));
  66. }
  67. else { // avoid too many arguments for splice
  68. this._lineOffsets = lineOffsets = lineOffsets.slice(0, startLine + 1).concat(addedLineOffsets, lineOffsets.slice(endLine + 1));
  69. }
  70. }
  71. var diff = change.text.length - (endOffset - startOffset);
  72. if (diff !== 0) {
  73. for (var i = startLine + 1 + addedLineOffsets.length, len = lineOffsets.length; i < len; i++) {
  74. lineOffsets[i] = lineOffsets[i] + diff;
  75. }
  76. }
  77. }
  78. else if (FullTextDocument.isFull(change)) {
  79. this._content = change.text;
  80. this._lineOffsets = undefined;
  81. }
  82. else {
  83. throw new Error('Unknown change event received');
  84. }
  85. }
  86. this._version = version;
  87. };
  88. FullTextDocument.prototype.getLineOffsets = function () {
  89. if (this._lineOffsets === undefined) {
  90. this._lineOffsets = computeLineOffsets(this._content, true);
  91. }
  92. return this._lineOffsets;
  93. };
  94. FullTextDocument.prototype.positionAt = function (offset) {
  95. offset = Math.max(Math.min(offset, this._content.length), 0);
  96. var lineOffsets = this.getLineOffsets();
  97. var low = 0, high = lineOffsets.length;
  98. if (high === 0) {
  99. return { line: 0, character: offset };
  100. }
  101. while (low < high) {
  102. var mid = Math.floor((low + high) / 2);
  103. if (lineOffsets[mid] > offset) {
  104. high = mid;
  105. }
  106. else {
  107. low = mid + 1;
  108. }
  109. }
  110. // low is the least x for which the line offset is larger than the current offset
  111. // or array.length if no line offset is larger than the current offset
  112. var line = low - 1;
  113. return { line: line, character: offset - lineOffsets[line] };
  114. };
  115. FullTextDocument.prototype.offsetAt = function (position) {
  116. var lineOffsets = this.getLineOffsets();
  117. if (position.line >= lineOffsets.length) {
  118. return this._content.length;
  119. }
  120. else if (position.line < 0) {
  121. return 0;
  122. }
  123. var lineOffset = lineOffsets[position.line];
  124. var nextLineOffset = (position.line + 1 < lineOffsets.length) ? lineOffsets[position.line + 1] : this._content.length;
  125. return Math.max(Math.min(lineOffset + position.character, nextLineOffset), lineOffset);
  126. };
  127. Object.defineProperty(FullTextDocument.prototype, "lineCount", {
  128. get: function () {
  129. return this.getLineOffsets().length;
  130. },
  131. enumerable: true,
  132. configurable: true
  133. });
  134. FullTextDocument.isIncremental = function (event) {
  135. var candidate = event;
  136. return candidate !== undefined && candidate !== null &&
  137. typeof candidate.text === 'string' && candidate.range !== undefined &&
  138. (candidate.rangeLength === undefined || typeof candidate.rangeLength === 'number');
  139. };
  140. FullTextDocument.isFull = function (event) {
  141. var candidate = event;
  142. return candidate !== undefined && candidate !== null &&
  143. typeof candidate.text === 'string' && candidate.range === undefined && candidate.rangeLength === undefined;
  144. };
  145. return FullTextDocument;
  146. }());
  147. export var TextDocument;
  148. (function (TextDocument) {
  149. /**
  150. * Creates a new text document.
  151. *
  152. * @param uri The document's uri.
  153. * @param languageId The document's language Id.
  154. * @param version The document's initial version number.
  155. * @param content The document's content.
  156. */
  157. function create(uri, languageId, version, content) {
  158. return new FullTextDocument(uri, languageId, version, content);
  159. }
  160. TextDocument.create = create;
  161. /**
  162. * Updates a TextDocument by modifing its content.
  163. *
  164. * @param document the document to update. Only documents created by TextDocument.create are valid inputs.
  165. * @param changes the changes to apply to the document.
  166. * @returns The updated TextDocument. Note: That's the same document instance passed in as first parameter.
  167. *
  168. */
  169. function update(document, changes, version) {
  170. if (document instanceof FullTextDocument) {
  171. document.update(changes, version);
  172. return document;
  173. }
  174. else {
  175. throw new Error('TextDocument.update: document must be created by TextDocument.create');
  176. }
  177. }
  178. TextDocument.update = update;
  179. function applyEdits(document, edits) {
  180. var text = document.getText();
  181. var sortedEdits = mergeSort(edits.map(getWellformedEdit), function (a, b) {
  182. var diff = a.range.start.line - b.range.start.line;
  183. if (diff === 0) {
  184. return a.range.start.character - b.range.start.character;
  185. }
  186. return diff;
  187. });
  188. var lastModifiedOffset = 0;
  189. var spans = [];
  190. for (var _i = 0, sortedEdits_1 = sortedEdits; _i < sortedEdits_1.length; _i++) {
  191. var e = sortedEdits_1[_i];
  192. var startOffset = document.offsetAt(e.range.start);
  193. if (startOffset < lastModifiedOffset) {
  194. throw new Error('Overlapping edit');
  195. }
  196. else if (startOffset > lastModifiedOffset) {
  197. spans.push(text.substring(lastModifiedOffset, startOffset));
  198. }
  199. if (e.newText.length) {
  200. spans.push(e.newText);
  201. }
  202. lastModifiedOffset = document.offsetAt(e.range.end);
  203. }
  204. spans.push(text.substr(lastModifiedOffset));
  205. return spans.join('');
  206. }
  207. TextDocument.applyEdits = applyEdits;
  208. })(TextDocument || (TextDocument = {}));
  209. function mergeSort(data, compare) {
  210. if (data.length <= 1) {
  211. // sorted
  212. return data;
  213. }
  214. var p = (data.length / 2) | 0;
  215. var left = data.slice(0, p);
  216. var right = data.slice(p);
  217. mergeSort(left, compare);
  218. mergeSort(right, compare);
  219. var leftIdx = 0;
  220. var rightIdx = 0;
  221. var i = 0;
  222. while (leftIdx < left.length && rightIdx < right.length) {
  223. var ret = compare(left[leftIdx], right[rightIdx]);
  224. if (ret <= 0) {
  225. // smaller_equal -> take left to preserve order
  226. data[i++] = left[leftIdx++];
  227. }
  228. else {
  229. // greater -> take right
  230. data[i++] = right[rightIdx++];
  231. }
  232. }
  233. while (leftIdx < left.length) {
  234. data[i++] = left[leftIdx++];
  235. }
  236. while (rightIdx < right.length) {
  237. data[i++] = right[rightIdx++];
  238. }
  239. return data;
  240. }
  241. function computeLineOffsets(text, isAtLineStart, textOffset) {
  242. if (textOffset === void 0) { textOffset = 0; }
  243. var result = isAtLineStart ? [textOffset] : [];
  244. for (var i = 0; i < text.length; i++) {
  245. var ch = text.charCodeAt(i);
  246. if (ch === 13 /* CarriageReturn */ || ch === 10 /* LineFeed */) {
  247. if (ch === 13 /* CarriageReturn */ && i + 1 < text.length && text.charCodeAt(i + 1) === 10 /* LineFeed */) {
  248. i++;
  249. }
  250. result.push(textOffset + i + 1);
  251. }
  252. }
  253. return result;
  254. }
  255. function getWellformedRange(range) {
  256. var start = range.start;
  257. var end = range.end;
  258. if (start.line > end.line || (start.line === end.line && start.character > end.character)) {
  259. return { start: end, end: start };
  260. }
  261. return range;
  262. }
  263. function getWellformedEdit(textEdit) {
  264. var range = getWellformedRange(textEdit.range);
  265. if (range !== textEdit.range) {
  266. return { newText: textEdit.newText, range: range };
  267. }
  268. return textEdit;
  269. }