Skip to content

Commit b53c11b

Browse files
authored
(fix) Fix svelte component rename when using bind:this (#376)
Don't rename svelte2tsx-internal `__sveltets_instanceOf(TheComponentToRename)` #308
1 parent a67284a commit b53c11b

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class RenameProviderImpl implements RenameProvider {
5858
const docs = new Map<string, SnapshotFragment>([[tsDoc.filePath, fragment]]);
5959
let convertedRenameLocations: (ts.RenameLocation & {
6060
range: Range;
61-
})[] = await this.mapRenameLocationsToParent(renameLocations, docs);
61+
})[] = await this.mapAndFilterRenameLocations(renameLocations, docs);
6262
// eslint-disable-next-line max-len
6363
const additionalRenameForPropRenameInsideComponentWithProp = await this.getAdditionLocationsForRenameOfPropInsideComponentWithProp(
6464
document,
@@ -127,7 +127,7 @@ export class RenameProviderImpl implements RenameProvider {
127127
if (
128128
!renameInfo.canRename ||
129129
renameInfo.kind === ts.ScriptElementKind.jsxAttribute ||
130-
renameInfo.fullDisplayName?.includes("JSX.IntrinsicElements")
130+
renameInfo.fullDisplayName?.includes('JSX.IntrinsicElements')
131131
) {
132132
return null;
133133
}
@@ -189,7 +189,7 @@ export class RenameProviderImpl implements RenameProvider {
189189
rename.fileName !== updatePropLocation.fileName ||
190190
this.isInSvelte2TsxPropLine(fragment, rename),
191191
);
192-
return await this.mapRenameLocationsToParent(replacementsForProp, fragments);
192+
return await this.mapAndFilterRenameLocations(replacementsForProp, fragments);
193193
}
194194

195195
/**
@@ -222,7 +222,7 @@ export class RenameProviderImpl implements RenameProvider {
222222
const idx = (match.index || 0) + match[0].lastIndexOf(match[1]);
223223
const replacementsForProp =
224224
lang.findRenameLocations(updatePropLocation.fileName, idx, false, false) || [];
225-
return await this.mapRenameLocationsToParent(replacementsForProp, fragments);
225+
return await this.mapAndFilterRenameLocations(replacementsForProp, fragments);
226226
}
227227

228228
// --------> svelte2tsx?
@@ -278,12 +278,13 @@ export class RenameProviderImpl implements RenameProvider {
278278
* The rename locations the ts language services hands back are relative to the
279279
* svelte2tsx generated code -> map it back to the original document positions.
280280
* Some of those positions could be unmapped (line=-1), these are handled elsewhere.
281+
* Also filter out wrong renames.
281282
*/
282-
private async mapRenameLocationsToParent(
283+
private async mapAndFilterRenameLocations(
283284
renameLocations: readonly ts.RenameLocation[],
284285
fragments: Map<string, SnapshotFragment>,
285286
): Promise<(ts.RenameLocation & { range: Range })[]> {
286-
return Promise.all(
287+
const mappedLocations = await Promise.all(
287288
renameLocations.map(async (loc) => {
288289
let doc = fragments.get(loc.fileName);
289290
if (!doc) {
@@ -297,6 +298,27 @@ export class RenameProviderImpl implements RenameProvider {
297298
};
298299
}),
299300
);
301+
return this.filterWrongRenameLocations(mappedLocations);
302+
}
303+
304+
private filterWrongRenameLocations(
305+
mappedLocations: (ts.RenameLocation & { range: Range })[],
306+
): (ts.RenameLocation & { range: Range })[] {
307+
return mappedLocations.filter((loc) => {
308+
const snapshot = this.getSnapshot(loc.fileName);
309+
if (!(snapshot instanceof SvelteDocumentSnapshot)) {
310+
return true;
311+
}
312+
313+
const content = snapshot.getText(0, snapshot.getLength());
314+
const svelteInstanceOfFn = '__sveltets_instanceOf(';
315+
// When the user renames a Svelte component, ts will also want to rename
316+
// `__sveltets_instanceOf(TheComponentToRename)`. Prevent that.
317+
return (
318+
content.lastIndexOf(svelteInstanceOfFn, loc.textSpan.start) !==
319+
loc.textSpan.start - svelteInstanceOfFn.length
320+
);
321+
});
300322
}
301323

302324
private mapRangeToOriginal(doc: SnapshotFragment, textSpan: ts.TextSpan): Range {

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ describe('RenameProvider', () => {
2727
const renameDoc1 = await openDoc('rename.svelte');
2828
const renameDoc2 = await openDoc('rename2.svelte');
2929
const renameDoc3 = await openDoc('rename3.svelte');
30-
return { provider, renameDoc1, renameDoc2, renameDoc3, docManager };
30+
const renameDoc4 = await openDoc('rename4.svelte');
31+
return { provider, renameDoc1, renameDoc2, renameDoc3, renameDoc4, docManager };
3132

3233
async function openDoc(filename: string) {
3334
const filePath = getFullPath(filename);
@@ -234,6 +235,57 @@ describe('RenameProvider', () => {
234235
});
235236
});
236237

238+
it('should do rename of svelte component', async () => {
239+
const { provider, renameDoc4 } = await setup();
240+
const result = await provider.rename(renameDoc4, Position.create(1, 12), 'ChildNew');
241+
242+
assert.deepStrictEqual(result, {
243+
changes: {
244+
[getUri('rename4.svelte')]: [
245+
{
246+
newText: 'ChildNew',
247+
range: {
248+
start: {
249+
line: 1,
250+
character: 11,
251+
},
252+
end: {
253+
line: 1,
254+
character: 16,
255+
},
256+
},
257+
},
258+
{
259+
newText: 'ChildNew',
260+
range: {
261+
start: {
262+
line: 7,
263+
character: 5,
264+
},
265+
end: {
266+
line: 7,
267+
character: 10,
268+
},
269+
},
270+
},
271+
{
272+
newText: 'ChildNew',
273+
range: {
274+
start: {
275+
line: 8,
276+
character: 5,
277+
},
278+
end: {
279+
line: 8,
280+
character: 10,
281+
},
282+
},
283+
},
284+
],
285+
},
286+
});
287+
});
288+
237289
it('should allow rename of variable', async () => {
238290
const { provider, renameDoc1 } = await setup();
239291
const result = await provider.prepareRename(renameDoc1, Position.create(1, 25));
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import Child from './rename3.svelte';
3+
4+
let componentRef;
5+
</script>
6+
7+
<main>
8+
<Child bind:this={componentRef} />
9+
<Child />
10+
</main>

0 commit comments

Comments
 (0)