2 if (typeof module === "object" && typeof module.exports === "object") {
3 var v = factory(require, exports);
4 if (v !== undefined) module.exports = v;
6 else if (typeof define === "function" && define.amd) {
7 define(["require", "exports"], factory);
9 })(function (require, exports) {
10 /* --------------------------------------------------------------------------------------------
11 * Copyright (c) Microsoft Corporation. All rights reserved.
12 * Licensed under the MIT License. See License.txt in the project root for license information.
13 * ------------------------------------------------------------------------------------------ */
15 Object.defineProperty(exports, "__esModule", { value: true });
16 exports.TextDocument = void 0;
17 class FullTextDocument {
18 constructor(uri, languageId, version, content) {
20 this._languageId = languageId;
21 this._version = version;
22 this._content = content;
23 this._lineOffsets = undefined;
29 return this._languageId;
36 const start = this.offsetAt(range.start);
37 const end = this.offsetAt(range.end);
38 return this._content.substring(start, end);
42 update(changes, version) {
43 for (let change of changes) {
44 if (FullTextDocument.isIncremental(change)) {
45 // makes sure start is before end
46 const range = getWellformedRange(change.range);
48 const startOffset = this.offsetAt(range.start);
49 const endOffset = this.offsetAt(range.end);
50 this._content = this._content.substring(0, startOffset) + change.text + this._content.substring(endOffset, this._content.length);
52 const startLine = Math.max(range.start.line, 0);
53 const endLine = Math.max(range.end.line, 0);
54 let lineOffsets = this._lineOffsets;
55 const addedLineOffsets = computeLineOffsets(change.text, false, startOffset);
56 if (endLine - startLine === addedLineOffsets.length) {
57 for (let i = 0, len = addedLineOffsets.length; i < len; i++) {
58 lineOffsets[i + startLine + 1] = addedLineOffsets[i];
62 if (addedLineOffsets.length < 10000) {
63 lineOffsets.splice(startLine + 1, endLine - startLine, ...addedLineOffsets);
65 else { // avoid too many arguments for splice
66 this._lineOffsets = lineOffsets = lineOffsets.slice(0, startLine + 1).concat(addedLineOffsets, lineOffsets.slice(endLine + 1));
69 const diff = change.text.length - (endOffset - startOffset);
71 for (let i = startLine + 1 + addedLineOffsets.length, len = lineOffsets.length; i < len; i++) {
72 lineOffsets[i] = lineOffsets[i] + diff;
76 else if (FullTextDocument.isFull(change)) {
77 this._content = change.text;
78 this._lineOffsets = undefined;
81 throw new Error('Unknown change event received');
84 this._version = version;
87 if (this._lineOffsets === undefined) {
88 this._lineOffsets = computeLineOffsets(this._content, true);
90 return this._lineOffsets;
93 offset = Math.max(Math.min(offset, this._content.length), 0);
94 let lineOffsets = this.getLineOffsets();
95 let low = 0, high = lineOffsets.length;
97 return { line: 0, character: offset };
100 let mid = Math.floor((low + high) / 2);
101 if (lineOffsets[mid] > offset) {
108 // low is the least x for which the line offset is larger than the current offset
109 // or array.length if no line offset is larger than the current offset
111 return { line, character: offset - lineOffsets[line] };
114 let lineOffsets = this.getLineOffsets();
115 if (position.line >= lineOffsets.length) {
116 return this._content.length;
118 else if (position.line < 0) {
121 let lineOffset = lineOffsets[position.line];
122 let nextLineOffset = (position.line + 1 < lineOffsets.length) ? lineOffsets[position.line + 1] : this._content.length;
123 return Math.max(Math.min(lineOffset + position.character, nextLineOffset), lineOffset);
126 return this.getLineOffsets().length;
128 static isIncremental(event) {
129 let candidate = event;
130 return candidate !== undefined && candidate !== null &&
131 typeof candidate.text === 'string' && candidate.range !== undefined &&
132 (candidate.rangeLength === undefined || typeof candidate.rangeLength === 'number');
134 static isFull(event) {
135 let candidate = event;
136 return candidate !== undefined && candidate !== null &&
137 typeof candidate.text === 'string' && candidate.range === undefined && candidate.rangeLength === undefined;
141 (function (TextDocument) {
143 * Creates a new text document.
145 * @param uri The document's uri.
146 * @param languageId The document's language Id.
147 * @param version The document's initial version number.
148 * @param content The document's content.
150 function create(uri, languageId, version, content) {
151 return new FullTextDocument(uri, languageId, version, content);
153 TextDocument.create = create;
155 * Updates a TextDocument by modifying its content.
157 * @param document the document to update. Only documents created by TextDocument.create are valid inputs.
158 * @param changes the changes to apply to the document.
159 * @param version the changes version for the document.
160 * @returns The updated TextDocument. Note: That's the same document instance passed in as first parameter.
163 function update(document, changes, version) {
164 if (document instanceof FullTextDocument) {
165 document.update(changes, version);
169 throw new Error('TextDocument.update: document must be created by TextDocument.create');
172 TextDocument.update = update;
173 function applyEdits(document, edits) {
174 let text = document.getText();
175 let sortedEdits = mergeSort(edits.map(getWellformedEdit), (a, b) => {
176 let diff = a.range.start.line - b.range.start.line;
178 return a.range.start.character - b.range.start.character;
182 let lastModifiedOffset = 0;
184 for (const e of sortedEdits) {
185 let startOffset = document.offsetAt(e.range.start);
186 if (startOffset < lastModifiedOffset) {
187 throw new Error('Overlapping edit');
189 else if (startOffset > lastModifiedOffset) {
190 spans.push(text.substring(lastModifiedOffset, startOffset));
192 if (e.newText.length) {
193 spans.push(e.newText);
195 lastModifiedOffset = document.offsetAt(e.range.end);
197 spans.push(text.substr(lastModifiedOffset));
198 return spans.join('');
200 TextDocument.applyEdits = applyEdits;
201 })(TextDocument = exports.TextDocument || (exports.TextDocument = {}));
202 function mergeSort(data, compare) {
203 if (data.length <= 1) {
207 const p = (data.length / 2) | 0;
208 const left = data.slice(0, p);
209 const right = data.slice(p);
210 mergeSort(left, compare);
211 mergeSort(right, compare);
215 while (leftIdx < left.length && rightIdx < right.length) {
216 let ret = compare(left[leftIdx], right[rightIdx]);
218 // smaller_equal -> take left to preserve order
219 data[i++] = left[leftIdx++];
222 // greater -> take right
223 data[i++] = right[rightIdx++];
226 while (leftIdx < left.length) {
227 data[i++] = left[leftIdx++];
229 while (rightIdx < right.length) {
230 data[i++] = right[rightIdx++];
234 function computeLineOffsets(text, isAtLineStart, textOffset = 0) {
235 const result = isAtLineStart ? [textOffset] : [];
236 for (let i = 0; i < text.length; i++) {
237 let ch = text.charCodeAt(i);
238 if (ch === 13 /* CarriageReturn */ || ch === 10 /* LineFeed */) {
239 if (ch === 13 /* CarriageReturn */ && i + 1 < text.length && text.charCodeAt(i + 1) === 10 /* LineFeed */) {
242 result.push(textOffset + i + 1);
247 function getWellformedRange(range) {
248 const start = range.start;
249 const end = range.end;
250 if (start.line > end.line || (start.line === end.line && start.character > end.character)) {
251 return { start: end, end: start };
255 function getWellformedEdit(textEdit) {
256 const range = getWellformedRange(textEdit.range);
257 if (range !== textEdit.range) {
258 return { newText: textEdit.newText, range };