Skip to content

Commit c830841

Browse files
authored
fix: correct the selection range of the last import that doesn't have a semicolon (#2651)
1 parent 4ba7760 commit c830841

File tree

3 files changed

+98
-11
lines changed

3 files changed

+98
-11
lines changed

packages/language-server/src/plugins/typescript/features/SelectionRangeProvider.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ts from 'typescript';
22
import { Position, Range, SelectionRange } from 'vscode-languageserver';
3-
import { Document, mapSelectionRangeToParent } from '../../../lib/documents';
3+
import { Document, mapRangeToOriginal } from '../../../lib/documents';
44
import { SelectionRangeProvider } from '../../interfaces';
55
import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
66
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
@@ -20,7 +20,7 @@ export class SelectionRangeProviderImpl implements SelectionRangeProvider {
2020
tsDoc.offsetAt(tsDoc.getGeneratedPosition(position))
2121
);
2222
const selectionRange = this.toSelectionRange(tsDoc, tsSelectionRange);
23-
const mappedRange = mapSelectionRangeToParent(tsDoc, selectionRange);
23+
const mappedRange = this.mapSelectionRangeToParent(tsDoc, document, selectionRange);
2424

2525
return this.filterOutUnmappedRange(mappedRange);
2626
}
@@ -35,6 +35,35 @@ export class SelectionRangeProviderImpl implements SelectionRangeProvider {
3535
};
3636
}
3737

38+
private mapSelectionRangeToParent(
39+
tsDoc: SvelteDocumentSnapshot,
40+
document: Document,
41+
selectionRange: SelectionRange
42+
): SelectionRange {
43+
const { range, parent } = selectionRange;
44+
const originalRange = mapRangeToOriginal(tsDoc, range);
45+
46+
const originalLength = originalRange.end.character - originalRange.start.character;
47+
const generatedLength = range.end.character - range.start.character;
48+
49+
// sourcemap off by one character issue + a generated semicolon
50+
if (
51+
originalLength === generatedLength - 2 &&
52+
tsDoc.getFullText()[tsDoc.offsetAt(range.end) - 1] === ';'
53+
) {
54+
originalRange.end.character += 1;
55+
}
56+
57+
if (!parent) {
58+
return SelectionRange.create(originalRange);
59+
}
60+
61+
return SelectionRange.create(
62+
originalRange,
63+
this.mapSelectionRangeToParent(tsDoc, document, parent)
64+
);
65+
}
66+
3867
private filterOutUnmappedRange(selectionRange: SelectionRange): SelectionRange | null {
3968
const flattened = this.flattenAndReverseSelectionRange(selectionRange);
4069
const filtered = flattened.filter((range) => range.start.line > 0 && range.end.line > 0);

packages/language-server/test/plugins/typescript/features/SelectionRangeProvider.test.ts

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,11 @@ const selectionRangeTestDir = path.join(testDir, 'testfiles', 'selection-range')
1515
describe('SelectionRangeProvider', function () {
1616
serviceWarmup(this, selectionRangeTestDir, pathToUrl(testDir));
1717

18-
function setup() {
18+
function setup(fileName: string) {
1919
const docManager = new DocumentManager(
2020
(textDocument) => new Document(textDocument.uri, textDocument.text)
2121
);
22-
const filePath = path.join(
23-
testDir,
24-
'testfiles',
25-
'selection-range',
26-
'selection-range.svelte'
27-
);
22+
const filePath = path.join(testDir, 'testfiles', 'selection-range', fileName);
2823
const lsAndTsDocResolver = new LSAndTSDocResolver(
2924
docManager,
3025
[pathToUrl(testDir)],
@@ -39,7 +34,7 @@ describe('SelectionRangeProvider', function () {
3934
}
4035

4136
it('provides selection range', async () => {
42-
const { provider, document } = setup();
37+
const { provider, document } = setup('selection-range.svelte');
4338

4439
const selectionRange = await provider.getSelectionRange(document, Position.create(1, 9));
4540

@@ -72,8 +67,67 @@ describe('SelectionRangeProvider', function () {
7267
});
7368
});
7469

70+
it('provides selection range for import without semicolon', async () => {
71+
const { provider, document } = setup('selection-range-import.svelte');
72+
73+
const selectionRange = await provider.getSelectionRange(document, Position.create(2, 28));
74+
75+
assert.deepStrictEqual(selectionRange, <SelectionRange>{
76+
parent: {
77+
parent: {
78+
parent: {
79+
parent: undefined,
80+
range: {
81+
end: {
82+
character: 34,
83+
line: 2
84+
},
85+
start: {
86+
character: 4,
87+
line: 1
88+
}
89+
}
90+
},
91+
// import {onMount} from 'svelte';
92+
range: {
93+
end: {
94+
character: 34,
95+
line: 2
96+
},
97+
start: {
98+
character: 4,
99+
line: 2
100+
}
101+
}
102+
},
103+
// 'svelte';
104+
range: {
105+
end: {
106+
character: 34,
107+
line: 2
108+
},
109+
start: {
110+
character: 26,
111+
line: 2
112+
}
113+
}
114+
},
115+
// svelte
116+
range: {
117+
end: {
118+
character: 33,
119+
line: 2
120+
},
121+
start: {
122+
character: 27,
123+
line: 2
124+
}
125+
}
126+
});
127+
});
128+
75129
it('return null when in style', async () => {
76-
const { provider, document } = setup();
130+
const { provider, document } = setup('selection-range.svelte');
77131

78132
const selectionRange = await provider.getSelectionRange(document, Position.create(5, 0));
79133

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<script>
2+
import {} from 'svelte'
3+
import {onMount} from 'svelte'
4+
</script>

0 commit comments

Comments
 (0)