diff --git a/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts b/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts index 3bdbf4b1c..48b6db53f 100644 --- a/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/CompletionProvider.ts @@ -42,6 +42,7 @@ import { } from '../utils'; import { getJsDocTemplateCompletion } from './getJsDocTemplateCompletion'; import { + checkRangeMappingWithGeneratedSemi, getComponentAtPosition, getFormatCodeBasis, getNewScriptStartTag, @@ -334,7 +335,9 @@ export class CompletionsProviderImpl implements CompletionsProvider) { const scriptLang = lang === 'none' ? '' : ` lang="${lang}"`; return `${ts.sys.newLine}`; } + +export function checkRangeMappingWithGeneratedSemi( + originalRange: Range, + generatedRange: Range, + tsDoc: SvelteDocumentSnapshot +) { + const originalLength = originalRange.end.character - originalRange.start.character; + const generatedLength = generatedRange.end.character - generatedRange.start.character; + + // sourcemap off by one character issue + a generated semicolon + if ( + originalLength === generatedLength - 2 && + tsDoc.getFullText()[tsDoc.offsetAt(generatedRange.end) - 1] === ';' + ) { + originalRange.end.character += 1; + } +} diff --git a/packages/language-server/test/plugins/typescript/features/CompletionProvider.test.ts b/packages/language-server/test/plugins/typescript/features/CompletionProvider.test.ts index 6523e3bfc..e06fe7278 100644 --- a/packages/language-server/test/plugins/typescript/features/CompletionProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/CompletionProvider.test.ts @@ -1246,6 +1246,66 @@ describe('CompletionProviderImpl', function () { }); }); + it('provides import statement completion with brackets', async () => { + const { completionProvider, document } = setup('importstatementcompletions2.svelte'); + + const completions = await completionProvider.getCompletions( + document, + { + line: 1, + character: 15 + }, + { + triggerKind: CompletionTriggerKind.Invoked + } + ); + + const item = completions?.items.find((item) => item.label === 'blubb'); + + delete item?.data; + + assert.deepStrictEqual(item, { + additionalTextEdits: [ + { + newText: 'import ', + range: { + end: { + character: 11, + line: 1 + }, + start: { + character: 4, + line: 1 + } + } + } + ], + label: 'blubb', + insertText: 'import { blubb$1 } from "../definitions";', + insertTextFormat: 2, + kind: CompletionItemKind.Function, + sortText: '11', + commitCharacters: undefined, + preselect: undefined, + labelDetails: { + description: '../definitions' + }, + textEdit: { + newText: '{ blubb$1 } from "../definitions";', + range: { + end: { + character: 16, + line: 1 + }, + start: { + character: 11, + line: 1 + } + } + } + }); + }); + it('provides optional chaining completion', async () => { const { completionProvider, document } = setup('completions-auto-optional-chain.svelte'); diff --git a/packages/language-server/test/plugins/typescript/testfiles/completions/importstatementcompletions2.svelte b/packages/language-server/test/plugins/typescript/testfiles/completions/importstatementcompletions2.svelte new file mode 100644 index 000000000..6a7db8891 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/completions/importstatementcompletions2.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file