Skip to content

Commit c3ffb3a

Browse files
authored
fix: prevent extra bracket with import statement completion (#2713)
1 parent 58a86ba commit c3ffb3a

File tree

5 files changed

+94
-13
lines changed

5 files changed

+94
-13
lines changed

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
} from '../utils';
4343
import { getJsDocTemplateCompletion } from './getJsDocTemplateCompletion';
4444
import {
45+
checkRangeMappingWithGeneratedSemi,
4546
getComponentAtPosition,
4647
getFormatCodeBasis,
4748
getNewScriptStartTag,
@@ -334,7 +335,9 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionRe
334335
this.fixTextEditRange(
335336
wordInfo.range,
336337
mapCompletionItemToOriginal(tsDoc, completion),
337-
isHandlerCompletion
338+
isHandlerCompletion,
339+
completion.textEdit,
340+
tsDoc
338341
)
339342
);
340343
}
@@ -876,7 +879,9 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionRe
876879
private fixTextEditRange(
877880
wordRange: Range,
878881
completionItem: CompletionItem,
879-
isHandlerCompletion: boolean
882+
isHandlerCompletion: boolean,
883+
generatedTextEdit: CompletionItem['textEdit'] | undefined,
884+
tsDoc: SvelteDocumentSnapshot
880885
) {
881886
if (isHandlerCompletion && completionItem.label.startsWith('on:')) {
882887
completionItem.textEdit = TextEdit.replace(
@@ -892,6 +897,10 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionRe
892897
return completionItem;
893898
}
894899

900+
if (TextEdit.is(generatedTextEdit)) {
901+
checkRangeMappingWithGeneratedSemi(textEdit.range, generatedTextEdit.range, tsDoc);
902+
}
903+
895904
const {
896905
newText,
897906
range: { start }

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { SelectionRangeProvider } from '../../interfaces';
55
import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
66
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
77
import { convertRange } from '../utils';
8+
import { checkRangeMappingWithGeneratedSemi } from './utils';
89

910
export class SelectionRangeProviderImpl implements SelectionRangeProvider {
1011
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
@@ -43,16 +44,7 @@ export class SelectionRangeProviderImpl implements SelectionRangeProvider {
4344
const { range, parent } = selectionRange;
4445
const originalRange = mapRangeToOriginal(tsDoc, range);
4546

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-
}
47+
checkRangeMappingWithGeneratedSemi(originalRange, range, tsDoc);
5648

5749
if (!parent) {
5850
return SelectionRange.create(originalRange);

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ts from 'typescript';
2-
import { Position } from 'vscode-languageserver';
2+
import { Position, Range } from 'vscode-languageserver';
33
import {
44
Document,
55
getLineAtPosition,
@@ -434,3 +434,20 @@ export function getNewScriptStartTag(lsConfig: Readonly<LSConfig>) {
434434
const scriptLang = lang === 'none' ? '' : ` lang="${lang}"`;
435435
return `<script${scriptLang}>${ts.sys.newLine}`;
436436
}
437+
438+
export function checkRangeMappingWithGeneratedSemi(
439+
originalRange: Range,
440+
generatedRange: Range,
441+
tsDoc: SvelteDocumentSnapshot
442+
) {
443+
const originalLength = originalRange.end.character - originalRange.start.character;
444+
const generatedLength = generatedRange.end.character - generatedRange.start.character;
445+
446+
// sourcemap off by one character issue + a generated semicolon
447+
if (
448+
originalLength === generatedLength - 2 &&
449+
tsDoc.getFullText()[tsDoc.offsetAt(generatedRange.end) - 1] === ';'
450+
) {
451+
originalRange.end.character += 1;
452+
}
453+
}

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,66 @@ describe('CompletionProviderImpl', function () {
12461246
});
12471247
});
12481248

1249+
it('provides import statement completion with brackets', async () => {
1250+
const { completionProvider, document } = setup('importstatementcompletions2.svelte');
1251+
1252+
const completions = await completionProvider.getCompletions(
1253+
document,
1254+
{
1255+
line: 1,
1256+
character: 15
1257+
},
1258+
{
1259+
triggerKind: CompletionTriggerKind.Invoked
1260+
}
1261+
);
1262+
1263+
const item = completions?.items.find((item) => item.label === 'blubb');
1264+
1265+
delete item?.data;
1266+
1267+
assert.deepStrictEqual(item, {
1268+
additionalTextEdits: [
1269+
{
1270+
newText: 'import ',
1271+
range: {
1272+
end: {
1273+
character: 11,
1274+
line: 1
1275+
},
1276+
start: {
1277+
character: 4,
1278+
line: 1
1279+
}
1280+
}
1281+
}
1282+
],
1283+
label: 'blubb',
1284+
insertText: 'import { blubb$1 } from "../definitions";',
1285+
insertTextFormat: 2,
1286+
kind: CompletionItemKind.Function,
1287+
sortText: '11',
1288+
commitCharacters: undefined,
1289+
preselect: undefined,
1290+
labelDetails: {
1291+
description: '../definitions'
1292+
},
1293+
textEdit: {
1294+
newText: '{ blubb$1 } from "../definitions";',
1295+
range: {
1296+
end: {
1297+
character: 16,
1298+
line: 1
1299+
},
1300+
start: {
1301+
character: 11,
1302+
line: 1
1303+
}
1304+
}
1305+
}
1306+
});
1307+
});
1308+
12491309
it('provides optional chaining completion', async () => {
12501310
const { completionProvider, document } = setup('completions-auto-optional-chain.svelte');
12511311

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script lang="ts">
2+
import {blu}
3+
</script>

0 commit comments

Comments
 (0)