Skip to content

Commit e2090ee

Browse files
committed
fix: Sublime LSP incremental edit range merged incorrectly
fix sublimelsp/LSP-volar#120
1 parent 5963838 commit e2090ee

File tree

2 files changed

+37
-85
lines changed

2 files changed

+37
-85
lines changed

Diff for: packages/vue-language-server/src/snapshots.ts

+37-59
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,49 @@ import * as vscode from 'vscode-languageserver';
33
import * as shared from '@volar/shared';
44
import type * as ts from 'typescript/lib/tsserverlibrary';
55

6-
interface IncrementalScriptSnapshotVersion {
6+
interface IncrementalScriptSnapshotChange {
77
applyed: boolean,
88
changeRange: ts.TextChangeRange | undefined,
99
version: number,
10-
contentChanges: {
10+
contentChange: {
1111
range: vscode.Range;
1212
text: string;
13-
}[] | undefined,
13+
} | undefined,
1414
snapshot: WeakRef<ts.IScriptSnapshot> | undefined,
1515
}
1616

1717
class IncrementalScriptSnapshot {
1818

1919
private document: TextDocument;
2020
uri: string;
21-
versions: IncrementalScriptSnapshotVersion[];
21+
changes: IncrementalScriptSnapshotChange[];
2222

2323
constructor(uri: string, languageId: string, version: number, text: string) {
2424
this.uri = uri;
2525
this.document = TextDocument.create(uri, languageId, version, text);
26-
this.versions = [
26+
this.changes = [
2727
{
2828
applyed: true,
2929
changeRange: undefined,
3030
version,
31-
contentChanges: undefined,
31+
contentChange: undefined,
3232
snapshot: undefined,
3333
}
3434
];
3535
}
3636

3737
get version() {
38-
if (this.versions.length) {
39-
return this.versions[this.versions.length - 1].version;
40-
}
41-
return this.document.version;
38+
return this.changes[this.changes.length - 1].version;
4239
}
4340

4441
update(params: vscode.DidChangeTextDocumentParams) {
4542
TextDocument.update(this.document, params.contentChanges, params.textDocument.version);
46-
this.versions = [
43+
this.changes = [
4744
{
4845
applyed: true,
4946
changeRange: undefined,
5047
version: params.textDocument.version,
51-
contentChanges: undefined,
48+
contentChange: undefined,
5249
snapshot: undefined,
5350
}
5451
];
@@ -58,7 +55,7 @@ class IncrementalScriptSnapshot {
5855

5956
this.clearUnReferenceVersions();
6057

61-
const lastChange = this.versions[this.versions.length - 1];
58+
const lastChange = this.changes[this.changes.length - 1];
6259
if (!lastChange.snapshot) {
6360
this.applyVersionToRootDocument(lastChange.version, false);
6461
const text = this.document.getText();
@@ -68,10 +65,10 @@ class IncrementalScriptSnapshot {
6865
getLength: () => text.length,
6966
getChangeRange: (oldSnapshot) => {
7067
if (!cache.has(oldSnapshot)) {
71-
const start = this.versions.findIndex(change => change.snapshot?.deref() === oldSnapshot) + 1;
72-
const end = this.versions.indexOf(lastChange) + 1;
68+
const start = this.changes.findIndex(change => change.snapshot?.deref() === oldSnapshot) + 1;
69+
const end = this.changes.indexOf(lastChange) + 1;
7370
if (start >= 0 && end >= 0) {
74-
const changeRanges = this.versions.slice(start, end).map(change => change.changeRange).filter(shared.notEmpty);
71+
const changeRanges = this.changes.slice(start, end).map(change => change.changeRange).filter(shared.notEmpty);
7572
const result = combineContinuousChangeRanges.apply(null, changeRanges);
7673
cache.set(oldSnapshot, result);
7774
}
@@ -92,7 +89,7 @@ class IncrementalScriptSnapshot {
9289

9390
this.clearUnReferenceVersions();
9491

95-
const lastChange = this.versions[this.versions.length - 1];
92+
const lastChange = this.changes[this.changes.length - 1];
9693
if (!lastChange.applyed) {
9794
this.applyVersionToRootDocument(lastChange.version, false);
9895
}
@@ -102,9 +99,10 @@ class IncrementalScriptSnapshot {
10299

103100
clearUnReferenceVersions() {
104101
let versionToApply: number | undefined;
105-
for (let i = 0; i < this.versions.length - 1; i++) {
106-
const change = this.versions[i];
107-
if (!change.snapshot?.deref()) {
102+
const lastVersion = this.changes[this.changes.length - 1].version;
103+
for (let i = 0; i <= this.changes.length - 2; i++) {
104+
const change = this.changes[i];
105+
if (change.version !== lastVersion && !change.snapshot?.deref()) {
108106
versionToApply = change.version;
109107
}
110108
else {
@@ -118,29 +116,28 @@ class IncrementalScriptSnapshot {
118116

119117
applyVersionToRootDocument(version: number, removeBeforeVersions: boolean) {
120118
let removeEnd = -1;
121-
for (let i = 0; i < this.versions.length; i++) {
122-
const change = this.versions[i];
119+
for (let i = 0; i < this.changes.length; i++) {
120+
const change = this.changes[i];
123121
if (change.version > version) {
124122
break;
125123
}
126124
if (!change.applyed) {
127-
if (change.contentChanges) {
128-
const changeRanges: ts.TextChangeRange[] = change.contentChanges.map(edit => ({
125+
if (change.contentChange) {
126+
change.changeRange = {
129127
span: {
130-
start: this.document.offsetAt(edit.range.start),
131-
length: this.document.offsetAt(edit.range.end) - this.document.offsetAt(edit.range.start),
128+
start: this.document.offsetAt(change.contentChange.range.start),
129+
length: this.document.offsetAt(change.contentChange.range.end) - this.document.offsetAt(change.contentChange.range.start),
132130
},
133-
newLength: edit.text.length,
134-
}));
135-
change.changeRange = combineMultiLineChangeRanges.apply(null, changeRanges);
136-
TextDocument.update(this.document, change.contentChanges, change.version);
131+
newLength: change.contentChange.text.length,
132+
};
133+
TextDocument.update(this.document, [change.contentChange], change.version);
137134
}
138135
change.applyed = true;
139136
}
140137
removeEnd = i + 1;
141138
}
142139
if (removeBeforeVersions && removeEnd >= 1) {
143-
this.versions.splice(0, removeEnd);
140+
this.changes.splice(0, removeEnd);
144141
}
145142
}
146143
}
@@ -174,27 +171,6 @@ function _combineContinuousChangeRanges(a: ts.TextChangeRange, b: ts.TextChangeR
174171
return { span: { start, length }, newLength };
175172
}
176173

177-
export function combineMultiLineChangeRanges(...changeRanges: ts.TextChangeRange[]) {
178-
if (changeRanges.length === 1) {
179-
return changeRanges[0];
180-
}
181-
const starts = changeRanges.map(change => change.span.start);
182-
const ends = changeRanges.map(change => change.span.start + change.span.length);
183-
const start = Math.min(...starts);
184-
const end = Math.max(...ends);
185-
const lengthDiff = changeRanges.map(change => change.newLength - change.span.length).reduce((a, b) => a + b, 0);
186-
const lastChangeRange = changeRanges.sort((a, b) => b.span.start - a.span.start)[0];
187-
const newEnd = lastChangeRange.span.start + lastChangeRange.span.length + lengthDiff;
188-
const lastChange: ts.TextChangeRange = {
189-
span: {
190-
start,
191-
length: end - start,
192-
},
193-
newLength: newEnd - start,
194-
};
195-
return lastChange;
196-
}
197-
198174
export function createSnapshots(connection: vscode.Connection) {
199175

200176
const snapshots = shared.createPathMap<IncrementalScriptSnapshot>();
@@ -217,13 +193,15 @@ export function createSnapshots(connection: vscode.Connection) {
217193
const incrementalSnapshot = snapshots.uriGet(params.textDocument.uri);
218194
if (incrementalSnapshot) {
219195
if (params.contentChanges.every(vscode.TextDocumentContentChangeEvent.isIncremental)) {
220-
incrementalSnapshot.versions.push({
221-
applyed: false,
222-
changeRange: undefined,
223-
contentChanges: params.contentChanges,
224-
version: params.textDocument.version,
225-
snapshot: undefined,
226-
});
196+
for (const contentChange of params.contentChanges) {
197+
incrementalSnapshot.changes.push({
198+
applyed: false,
199+
changeRange: undefined,
200+
contentChange,
201+
version: params.textDocument.version,
202+
snapshot: undefined,
203+
});
204+
}
227205
}
228206
else {
229207
incrementalSnapshot.update(params);

Diff for: packages/vue-language-server/tests/combineMultiLineChangeRanges.spec.ts

-26
This file was deleted.

0 commit comments

Comments
 (0)