Skip to content

Commit d3a81d9

Browse files
authored
hack for auto import completion mapping (#317)
1 parent 4e11f13 commit d3a81d9

File tree

12 files changed

+83
-29
lines changed

12 files changed

+83
-29
lines changed

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

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,19 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
203203
}
204204

205205
const actions = detail?.codeActions;
206+
const isImport = !!detail?.source;
207+
206208
if (actions) {
207209
const edit: TextEdit[] = [];
208210

209211
for (const action of actions) {
210212
for (const change of action.changes) {
211-
edit.push(...this.codeActionChangesToTextEdit(document, fragment, change));
213+
edit.push(...this.codeActionChangesToTextEdit(
214+
document,
215+
fragment,
216+
change,
217+
isImport
218+
));
212219
}
213220
}
214221

@@ -241,16 +248,18 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
241248
doc: Document,
242249
fragment: SvelteSnapshotFragment,
243250
changes: ts.FileTextChanges,
251+
isImport: boolean,
244252
): TextEdit[] {
245253
return changes.textChanges.map((change) =>
246-
this.codeActionChangeToTextEdit(doc, fragment, change),
254+
this.codeActionChangeToTextEdit(doc, fragment, change, isImport),
247255
);
248256
}
249257

250258
private codeActionChangeToTextEdit(
251259
doc: Document,
252260
fragment: SvelteSnapshotFragment,
253261
change: ts.TextChange,
262+
isImport: boolean,
254263
): TextEdit {
255264
change.newText = this.changeSvelteComponentName(change.newText);
256265

@@ -264,28 +273,58 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
264273
}
265274

266275
const { span } = change;
267-
// prevent newText from being placed like this: <script>import {} from ''
268-
if (span.start === 0) {
269-
change.newText = ts.sys.newLine + change.newText;
276+
277+
const virutalRange = convertRange(fragment, span);
278+
let range: Range;
279+
const isNewImport = isImport && virutalRange.start.character === 0;
280+
281+
// Since new import always can't be mapped, we'll have special treatment here
282+
// but only hack this when there is multiple line in script
283+
if (isNewImport && virutalRange.start.line > 1) {
284+
range = this.mapRangeForNewImport(fragment, virutalRange);
285+
} else {
286+
range = mapRangeToOriginal(fragment, virutalRange);
270287
}
271288

272-
let range = mapRangeToOriginal(fragment, convertRange(fragment, span));
273-
// If range is somehow not mapped in parent or the import is mapped wrong,
289+
// If range is somehow not mapped in parent,
290+
// the import is mapped wrong or is outside script tag,
274291
// use script starting point instead.
275292
// This happens among other things if the completion is the first import of the file.
276293
if (
277294
range.start.line === -1 ||
278-
(range.start.line === 0 && range.start.character <= 1 && span.length === 0)
295+
(range.start.line === 0 && range.start.character <= 1 && span.length === 0) ||
296+
doc.offsetAt(range.start) > scriptTagInfo.end
279297
) {
280298
range = convertRange(doc, {
281299
start: scriptTagInfo.start,
282300
length: span.length,
283301
});
284302
}
303+
// prevent newText from being placed like this: <script>import {} from ''
304+
if (range.start.line === 0) {
305+
change.newText = ts.sys.newLine + change.newText;
306+
}
285307

286308
return TextEdit.replace(range, change.newText);
287309
}
288310

311+
private mapRangeForNewImport(
312+
fragment: SvelteSnapshotFragment,
313+
virtualRange: Range
314+
) {
315+
const sourceMapableRange = this.offsetLinesAndMovetoStartOfLine(virtualRange, -1);
316+
const mappableRange = mapRangeToOriginal(
317+
fragment, sourceMapableRange);
318+
return this.offsetLinesAndMovetoStartOfLine(mappableRange, 1);
319+
}
320+
321+
private offsetLinesAndMovetoStartOfLine({ start, end }: Range, offsetLines: number) {
322+
return Range.create(
323+
Position.create(start.line + offsetLines, 0),
324+
Position.create(end.line + offsetLines, 0)
325+
);
326+
}
327+
289328
private isSvelteComponentImport(className: string) {
290329
return className.endsWith('__SvelteComponent_');
291330
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ describe('CodeActionsProvider', () => {
191191
},
192192
},
193193
textRange: {
194-
pos: 129,
195-
end: 163,
194+
pos: 130,
195+
end: 164,
196196
},
197197
},
198198
],
@@ -281,8 +281,8 @@ describe('CodeActionsProvider', () => {
281281
},
282282
},
283283
textRange: {
284-
pos: 129,
285-
end: 163,
284+
pos: 130,
285+
end: 164,
286286
},
287287
},
288288
],

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ describe('CompletionProviderImpl', () => {
258258

259259
assert.deepEqual(
260260
additionalTextEdits![0]?.range,
261-
Range.create(Position.create(0, 8), Position.create(0, 8)),
261+
Range.create(Position.create(2, 0), Position.create(2, 0)),
262262
);
263263
});
264264

@@ -285,7 +285,7 @@ describe('CompletionProviderImpl', () => {
285285

286286
assert.strictEqual(
287287
harmonizeNewLines(additionalTextEdits![0]?.newText),
288-
`import { blubb } from './definitions';${newLine}`,
288+
`${newLine}import { blubb } from './definitions';${newLine}`,
289289
);
290290

291291
assert.deepEqual(

packages/svelte2tsx/src/svelte2tsx.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
792792
}
793793
}
794794

795+
const firstImport = tsAst.statements
796+
.filter(ts.isImportDeclaration)
797+
.sort((a, b) => a.end - b.end)[0];
798+
if (firstImport) {
799+
str.appendRight(firstImport.getStart() + astOffset, '\n');
800+
}
801+
795802
return {
796803
exportedNames,
797804
uses$$props,

packages/svelte2tsx/test/sourcemaps/repl.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
chapter: await res.json()
1212
};
1313
}
14-
;<></>;import Repl from '@sveltejs/svelte-repl';
14+
;<></>;
15+
import Repl from '@sveltejs/svelte-repl';
1516
import { getContext } from 'svelte';
1617
import ScreenToggle from '../../../components/ScreenToggle.svelte';
1718
import TableOfContents from './_TableOfContents.svelte';

packages/svelte2tsx/test/svelte2tsx/samples/await-with-$store/expected.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<></>;import { readable } from 'svelte/store';
1+
<></>;
2+
import { readable } from 'svelte/store';
23
function render() {
34

45

@@ -16,4 +17,4 @@ return { props: {}, slots: {} }}
1617
export default class Input__SvelteComponent_ {
1718
$$prop_def = __sveltets_partial(render().props)
1819
$$slot_def = render().slots
19-
}
20+
}

packages/svelte2tsx/test/svelte2tsx/samples/import-single-quote/expected.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<></>;import Test from './Test.svelte';
1+
<></>;
2+
import Test from './Test.svelte';
23
function render() {
34

45

@@ -10,4 +11,4 @@ return { props: {}, slots: {} }}
1011
export default class Input__SvelteComponent_ {
1112
$$prop_def = __sveltets_partial(render().props)
1213
$$slot_def = render().slots
13-
}
14+
}

packages/svelte2tsx/test/svelte2tsx/samples/imports/expected.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<></>;import { a as b } from "./test.svelte"
1+
<></>;
2+
import { a as b } from "./test.svelte"
23
import * as c from "b.ts"
34
function render() {
45

@@ -14,4 +15,4 @@ return { props: {world: world}, slots: {} }}
1415
export default class Input__SvelteComponent_ {
1516
$$prop_def = __sveltets_partial(render().props)
1617
$$slot_def = render().slots
17-
}
18+
}

packages/svelte2tsx/test/svelte2tsx/samples/self-closing-component/expected.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<></>;import Test from './Test.svelte';
1+
<></>;
2+
import Test from './Test.svelte';
23
function render() {
34

45

@@ -11,4 +12,4 @@ return { props: {}, slots: {} }}
1112
export default class Input__SvelteComponent_ {
1213
$$prop_def = __sveltets_partial(render().props)
1314
$$slot_def = render().slots
14-
}
15+
}

packages/svelte2tsx/test/svelte2tsx/samples/uses-$store-with-assignment-operators/expected.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<></>;import { writable } from 'svelte/store';
1+
<></>;
2+
import { writable } from 'svelte/store';
23
function render() {
34

45

@@ -36,4 +37,4 @@ return { props: {}, slots: {} }}
3637
export default class Input__SvelteComponent_ {
3738
$$prop_def = __sveltets_partial(render().props)
3839
$$slot_def = render().slots
39-
}
40+
}

0 commit comments

Comments
 (0)