216 lines
8.4 KiB
Plaintext
216 lines
8.4 KiB
Plaintext
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.createDocuments = exports.combineContinuousChangeRanges = void 0;
|
||
|
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
|
||
|
const vscode = require("vscode-languageserver");
|
||
|
const uriMap_1 = require("./utils/uriMap");
|
||
|
class IncrementalScriptSnapshot {
|
||
|
constructor(uri, languageId, version, text) {
|
||
|
this.uri = uri;
|
||
|
this.document = vscode_languageserver_textdocument_1.TextDocument.create(uri, languageId, version, text);
|
||
|
this.changes = [
|
||
|
{
|
||
|
applied: true,
|
||
|
changeRange: undefined,
|
||
|
version,
|
||
|
contentChange: undefined,
|
||
|
snapshot: undefined,
|
||
|
}
|
||
|
];
|
||
|
}
|
||
|
get version() {
|
||
|
return this.changes[this.changes.length - 1].version;
|
||
|
}
|
||
|
get languageId() {
|
||
|
return this.document.languageId;
|
||
|
}
|
||
|
update(params) {
|
||
|
vscode_languageserver_textdocument_1.TextDocument.update(this.document, params.contentChanges, params.textDocument.version);
|
||
|
this.changes = [
|
||
|
{
|
||
|
applied: true,
|
||
|
changeRange: undefined,
|
||
|
version: params.textDocument.version,
|
||
|
contentChange: undefined,
|
||
|
snapshot: undefined,
|
||
|
}
|
||
|
];
|
||
|
}
|
||
|
getSnapshot() {
|
||
|
this.clearUnReferenceVersions();
|
||
|
const lastChange = this.changes[this.changes.length - 1];
|
||
|
if (!lastChange.snapshot) {
|
||
|
this.applyVersionChanges(lastChange.version, false);
|
||
|
const text = this.document.getText();
|
||
|
const cache = new WeakMap();
|
||
|
const snapshot = {
|
||
|
getText: (start, end) => text.substring(start, end),
|
||
|
getLength: () => text.length,
|
||
|
getChangeRange: (oldSnapshot) => {
|
||
|
if (!cache.has(oldSnapshot)) {
|
||
|
const oldIndex = this.changes.findIndex(change => change.snapshot?.deref() === oldSnapshot);
|
||
|
if (oldIndex >= 0) {
|
||
|
const start = oldIndex + 1;
|
||
|
const end = this.changes.indexOf(lastChange) + 1;
|
||
|
const changeRanges = this.changes.slice(start, end).map(change => change.changeRange);
|
||
|
const result = combineContinuousChangeRanges.apply(null, changeRanges);
|
||
|
cache.set(oldSnapshot, result);
|
||
|
}
|
||
|
else {
|
||
|
cache.set(oldSnapshot, undefined);
|
||
|
}
|
||
|
}
|
||
|
return cache.get(oldSnapshot);
|
||
|
},
|
||
|
};
|
||
|
lastChange.snapshot = new WeakRef(snapshot);
|
||
|
}
|
||
|
return lastChange.snapshot.deref();
|
||
|
}
|
||
|
getDocument() {
|
||
|
this.clearUnReferenceVersions();
|
||
|
const lastChange = this.changes[this.changes.length - 1];
|
||
|
if (!lastChange.applied) {
|
||
|
this.applyVersionChanges(lastChange.version, false);
|
||
|
}
|
||
|
return this.document;
|
||
|
}
|
||
|
clearUnReferenceVersions() {
|
||
|
let versionToApply;
|
||
|
for (let i = 0; i <= this.changes.length - 2; i++) {
|
||
|
const change = this.changes[i];
|
||
|
const nextChange = this.changes[i + 1];
|
||
|
if (!change.snapshot?.deref()) {
|
||
|
if (change.version !== nextChange.version) {
|
||
|
versionToApply = change.version;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (versionToApply !== undefined) {
|
||
|
this.applyVersionChanges(versionToApply, true);
|
||
|
}
|
||
|
}
|
||
|
applyVersionChanges(version, removeBeforeVersions) {
|
||
|
let removeEnd = -1;
|
||
|
for (let i = 0; i < this.changes.length; i++) {
|
||
|
const change = this.changes[i];
|
||
|
if (change.version > version) {
|
||
|
break;
|
||
|
}
|
||
|
if (!change.applied) {
|
||
|
if (change.contentChange) {
|
||
|
change.changeRange = {
|
||
|
span: {
|
||
|
start: this.document.offsetAt(change.contentChange.range.start),
|
||
|
length: this.document.offsetAt(change.contentChange.range.end) - this.document.offsetAt(change.contentChange.range.start),
|
||
|
},
|
||
|
newLength: change.contentChange.text.length,
|
||
|
};
|
||
|
vscode_languageserver_textdocument_1.TextDocument.update(this.document, [change.contentChange], change.version);
|
||
|
}
|
||
|
change.applied = true;
|
||
|
}
|
||
|
removeEnd = i + 1;
|
||
|
}
|
||
|
if (removeBeforeVersions && removeEnd >= 1) {
|
||
|
this.changes.splice(0, removeEnd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function combineContinuousChangeRanges(...changeRanges) {
|
||
|
if (changeRanges.length === 1) {
|
||
|
return changeRanges[0];
|
||
|
}
|
||
|
let changeRange = changeRanges[0];
|
||
|
for (let i = 1; i < changeRanges.length; i++) {
|
||
|
const nextChangeRange = changeRanges[i];
|
||
|
changeRange = _combineContinuousChangeRanges(changeRange, nextChangeRange);
|
||
|
}
|
||
|
return changeRange;
|
||
|
}
|
||
|
exports.combineContinuousChangeRanges = combineContinuousChangeRanges;
|
||
|
// https://tsplay.dev/mMldVN - @browsnet
|
||
|
function _combineContinuousChangeRanges(a, b) {
|
||
|
const aStart = a.span.start;
|
||
|
const aEnd = a.span.start + a.span.length;
|
||
|
const aDiff = a.newLength - a.span.length;
|
||
|
const changeBegin = aStart + Math.min(a.span.length, a.newLength);
|
||
|
const rollback = (start) => start > changeBegin ? Math.max(aStart, start - aDiff) : start;
|
||
|
const bStart = rollback(b.span.start);
|
||
|
const bEnd = rollback(b.span.start + b.span.length);
|
||
|
const bDiff = b.newLength - b.span.length;
|
||
|
const start = Math.min(aStart, bStart);
|
||
|
const end = Math.max(aEnd, bEnd);
|
||
|
const length = end - start;
|
||
|
const newLength = aDiff + bDiff + length;
|
||
|
return { span: { start, length }, newLength };
|
||
|
}
|
||
|
function createDocuments(env, connection) {
|
||
|
const snapshots = (0, uriMap_1.createUriMap)(env.fileNameToUri);
|
||
|
const onDidChangeContents = new Set();
|
||
|
const onDidCloses = new Set();
|
||
|
connection.onDidOpenTextDocument(params => {
|
||
|
if (params.textDocument.uri.startsWith('git:/'))
|
||
|
return;
|
||
|
snapshots.uriSet(params.textDocument.uri, new IncrementalScriptSnapshot(params.textDocument.uri, params.textDocument.languageId, params.textDocument.version, params.textDocument.text));
|
||
|
for (const cb of onDidChangeContents) {
|
||
|
cb({ textDocument: params.textDocument, contentChanges: [{ text: params.textDocument.text }] });
|
||
|
}
|
||
|
});
|
||
|
connection.onDidChangeTextDocument(params => {
|
||
|
if (params.textDocument.uri.startsWith('git:/'))
|
||
|
return;
|
||
|
const incrementalSnapshot = snapshots.uriGet(params.textDocument.uri);
|
||
|
if (incrementalSnapshot) {
|
||
|
if (params.contentChanges.every(vscode.TextDocumentContentChangeEvent.isIncremental)) {
|
||
|
for (const contentChange of params.contentChanges) {
|
||
|
incrementalSnapshot.changes.push({
|
||
|
applied: false,
|
||
|
changeRange: undefined,
|
||
|
contentChange,
|
||
|
version: params.textDocument.version,
|
||
|
snapshot: undefined,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
incrementalSnapshot.update(params);
|
||
|
}
|
||
|
}
|
||
|
for (const cb of onDidChangeContents) {
|
||
|
cb(params);
|
||
|
}
|
||
|
});
|
||
|
connection.onDidCloseTextDocument(params => {
|
||
|
if (params.textDocument.uri.startsWith('git:/'))
|
||
|
return;
|
||
|
snapshots.uriDelete(params.textDocument.uri);
|
||
|
for (const cb of onDidCloses) {
|
||
|
cb(params);
|
||
|
}
|
||
|
});
|
||
|
return {
|
||
|
data: snapshots,
|
||
|
onDidChangeContent: (cb) => {
|
||
|
onDidChangeContents.add(cb);
|
||
|
return {
|
||
|
dispose() {
|
||
|
onDidChangeContents.delete(cb);
|
||
|
},
|
||
|
};
|
||
|
},
|
||
|
onDidClose: (cb) => {
|
||
|
onDidCloses.add(cb);
|
||
|
return {
|
||
|
dispose() {
|
||
|
onDidCloses.delete(cb);
|
||
|
},
|
||
|
};
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
exports.createDocuments = createDocuments;
|
||
|
//# sourceMappingURL=documents.js.map
|