Skip to content

Commit 8dfbfcb

Browse files
authored
fix54492: allow editor to check for original file extension for rename (microsoft#56680)
1 parent e3d234c commit 8dfbfcb

File tree

9 files changed

+236
-9
lines changed

9 files changed

+236
-9
lines changed

src/server/protocol.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ export interface RenameInfoSuccess {
13361336

13371337
/**
13381338
* Full display name of item to be renamed.
1339+
* If item to be renamed is a file, then this is the original text of the module specifer
13391340
*/
13401341
fullDisplayName: string;
13411342

src/services/rename.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,17 @@ function getRenameInfoForModule(node: StringLiteralLike, sourceFile: SourceFile,
185185
const moduleSourceFile = moduleSymbol.declarations && find(moduleSymbol.declarations, isSourceFile);
186186
if (!moduleSourceFile) return undefined;
187187
const withoutIndex = endsWith(node.text, "/index") || endsWith(node.text, "/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
188-
const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
188+
const fileName = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
189189
const kind = withoutIndex === undefined ? ScriptElementKind.moduleElement : ScriptElementKind.directory;
190190
const indexAfterLastSlash = node.text.lastIndexOf("/") + 1;
191191
// Span should only be the last component of the path. + 1 to account for the quote character.
192192
const triggerSpan = createTextSpan(node.getStart(sourceFile) + 1 + indexAfterLastSlash, node.text.length - indexAfterLastSlash);
193193
return {
194194
canRename: true,
195-
fileToRename: name,
195+
fileToRename: fileName,
196196
kind,
197-
displayName: name,
198-
fullDisplayName: name,
197+
displayName: fileName,
198+
fullDisplayName: node.text,
199199
kindModifiers: ScriptElementKindModifier.none,
200200
triggerSpan,
201201
};

src/services/types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,10 @@ export interface RenameInfoSuccess {
12911291
*/
12921292
fileToRename?: string;
12931293
displayName: string;
1294+
/**
1295+
* Full display name of item to be renamed.
1296+
* If item to be renamed is a file, then this is the original text of the module specifer
1297+
*/
12941298
fullDisplayName: string;
12951299
kind: ScriptElementKind;
12961300
kindModifiers: string;

src/testRunner/unittests/tsserver/rename.ts

+19
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,23 @@ describe("unittests:: tsserver:: rename", () => {
193193
});
194194
baselineTsserverLogs("rename", "with symlinks and case difference", session);
195195
});
196+
197+
it("rename TS file with js extension", () => {
198+
const aTs: File = { path: "/a.ts", content: "export const a = 1;" };
199+
const bTs: File = { path: "/b.ts", content: `import * as foo from './a.js';` };
200+
201+
const host = createServerHost([aTs, bTs]);
202+
const session = new TestSession(host);
203+
openFilesForSession([aTs, bTs], session);
204+
205+
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
206+
command: ts.server.protocol.CommandTypes.Configure,
207+
arguments: { preferences: { allowRenameOfImportPath: true } },
208+
});
209+
session.executeCommandSeq<ts.server.protocol.RenameRequest>({
210+
command: ts.server.protocol.CommandTypes.Rename,
211+
arguments: protocolFileLocationFromSubstring(bTs, "a.js"),
212+
});
213+
baselineTsserverLogs("rename", "rename TS file with js extension", session);
214+
});
196215
});

tests/baselines/reference/api/typescript.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@ declare namespace ts {
10871087
displayName: string;
10881088
/**
10891089
* Full display name of item to be renamed.
1090+
* If item to be renamed is a file, then this is the original text of the module specifer
10901091
*/
10911092
fullDisplayName: string;
10921093
/**
@@ -11092,6 +11093,10 @@ declare namespace ts {
1109211093
*/
1109311094
fileToRename?: string;
1109411095
displayName: string;
11096+
/**
11097+
* Full display name of item to be renamed.
11098+
* If item to be renamed is a file, then this is the original text of the module specifer
11099+
*/
1109511100
fullDisplayName: string;
1109611101
kind: ScriptElementKind;
1109711102
kindModifiers: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
currentDirectory:: / useCaseSensitiveFileNames: false
2+
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
3+
Before request
4+
//// [/a.ts]
5+
export const a = 1;
6+
7+
//// [/b.ts]
8+
import * as foo from './a.js';
9+
10+
11+
Info seq [hh:mm:ss:mss] request:
12+
{
13+
"command": "open",
14+
"arguments": {
15+
"file": "/a.ts"
16+
},
17+
"seq": 1,
18+
"type": "request"
19+
}
20+
Info seq [hh:mm:ss:mss] Search path: /
21+
Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found.
22+
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
23+
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
24+
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
25+
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
26+
Info seq [hh:mm:ss:mss] Files (1)
27+
/a.ts SVC-1-0 "export const a = 1;"
28+
29+
30+
a.ts
31+
Root file specified for compilation
32+
33+
Info seq [hh:mm:ss:mss] -----------------------------------------------
34+
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
35+
Info seq [hh:mm:ss:mss] Files (1)
36+
37+
Info seq [hh:mm:ss:mss] -----------------------------------------------
38+
Info seq [hh:mm:ss:mss] Open files:
39+
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
40+
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
41+
Info seq [hh:mm:ss:mss] response:
42+
{
43+
"responseRequired": false
44+
}
45+
After request
46+
47+
PolledWatches::
48+
/a/lib/lib.d.ts: *new*
49+
{"pollingInterval":500}
50+
51+
Before request
52+
53+
Info seq [hh:mm:ss:mss] request:
54+
{
55+
"command": "open",
56+
"arguments": {
57+
"file": "/b.ts"
58+
},
59+
"seq": 2,
60+
"type": "request"
61+
}
62+
Info seq [hh:mm:ss:mss] Search path: /
63+
Info seq [hh:mm:ss:mss] For info: /b.ts :: No config files found.
64+
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject2*
65+
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject2* WatchType: Missing file
66+
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject2* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
67+
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject2*' (Inferred)
68+
Info seq [hh:mm:ss:mss] Files (2)
69+
/a.ts SVC-1-0 "export const a = 1;"
70+
/b.ts SVC-1-0 "import * as foo from './a.js';"
71+
72+
73+
a.ts
74+
Imported via './a.js' from file 'b.ts'
75+
b.ts
76+
Root file specified for compilation
77+
78+
Info seq [hh:mm:ss:mss] -----------------------------------------------
79+
Info seq [hh:mm:ss:mss] `remove Project::
80+
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
81+
Info seq [hh:mm:ss:mss] Files (1)
82+
/a.ts
83+
84+
85+
a.ts
86+
Root file specified for compilation
87+
88+
Info seq [hh:mm:ss:mss] -----------------------------------------------
89+
Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
90+
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject2*' (Inferred)
91+
Info seq [hh:mm:ss:mss] Files (2)
92+
93+
Info seq [hh:mm:ss:mss] -----------------------------------------------
94+
Info seq [hh:mm:ss:mss] Open files:
95+
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
96+
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject2*
97+
Info seq [hh:mm:ss:mss] FileName: /b.ts ProjectRootPath: undefined
98+
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject2*
99+
Info seq [hh:mm:ss:mss] response:
100+
{
101+
"responseRequired": false
102+
}
103+
After request
104+
105+
Before request
106+
107+
Info seq [hh:mm:ss:mss] request:
108+
{
109+
"command": "configure",
110+
"arguments": {
111+
"preferences": {
112+
"allowRenameOfImportPath": true
113+
}
114+
},
115+
"seq": 3,
116+
"type": "request"
117+
}
118+
Info seq [hh:mm:ss:mss] response:
119+
{
120+
"seq": 0,
121+
"type": "response",
122+
"command": "configure",
123+
"request_seq": 3,
124+
"success": true,
125+
"performanceData": {
126+
"updateGraphDurationMs": *
127+
}
128+
}
129+
Info seq [hh:mm:ss:mss] response:
130+
{
131+
"responseRequired": false
132+
}
133+
After request
134+
135+
Before request
136+
137+
Info seq [hh:mm:ss:mss] request:
138+
{
139+
"command": "rename",
140+
"arguments": {
141+
"file": "/b.ts",
142+
"line": 1,
143+
"offset": 25
144+
},
145+
"seq": 4,
146+
"type": "request"
147+
}
148+
Info seq [hh:mm:ss:mss] response:
149+
{
150+
"response": {
151+
"info": {
152+
"canRename": true,
153+
"fileToRename": "/a.ts",
154+
"displayName": "/a.ts",
155+
"fullDisplayName": "./a.js",
156+
"kind": "module",
157+
"kindModifiers": "",
158+
"triggerSpan": {
159+
"start": {
160+
"line": 1,
161+
"offset": 25
162+
},
163+
"end": {
164+
"line": 1,
165+
"offset": 29
166+
}
167+
}
168+
},
169+
"locs": [
170+
{
171+
"file": "/b.ts",
172+
"locs": [
173+
{
174+
"start": {
175+
"line": 1,
176+
"offset": 23
177+
},
178+
"end": {
179+
"line": 1,
180+
"offset": 29
181+
},
182+
"contextStart": {
183+
"line": 1,
184+
"offset": 1
185+
},
186+
"contextEnd": {
187+
"line": 1,
188+
"offset": 31
189+
}
190+
}
191+
]
192+
}
193+
]
194+
},
195+
"responseRequired": true
196+
}
197+
After request

tests/baselines/reference/tsserver/rename/works-with-fileToRename.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ Info seq [hh:mm:ss:mss] response:
132132
"canRename": true,
133133
"fileToRename": "/a.ts",
134134
"displayName": "/a.ts",
135-
"fullDisplayName": "/a.ts",
135+
"fullDisplayName": "./a",
136136
"kind": "module",
137137
"kindModifiers": "",
138138
"triggerSpan": {
@@ -259,7 +259,7 @@ Info seq [hh:mm:ss:mss] response:
259259
"canRename": true,
260260
"fileToRename": "/a.ts",
261261
"displayName": "/a.ts",
262-
"fullDisplayName": "/a.ts",
262+
"fullDisplayName": "./a",
263263
"kind": "module",
264264
"kindModifiers": "",
265265
"triggerSpan": {

tests/cases/fourslash/findAllRefs_importType_exportEquals.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ verify.baselineFindAllReferences('0', '1', '2', '3', '4', 'export');
1717
verify.baselineRename([r0, r1, r2]);
1818
for (const range of [r3b, r4b]) {
1919
goTo.rangeStart(range);
20-
verify.renameInfoSucceeded(/*displayName*/ "/a.ts", /*fullDisplayName*/ "/a.ts", /*kind*/ "module", /*kindModifiers*/ "", /*fileToRename*/ "/a.ts", range);
20+
verify.renameInfoSucceeded(/*displayName*/ "/a.ts", /*fullDisplayName*/ "./a", /*kind*/ "module", /*kindModifiers*/ "", /*fileToRename*/ "/a.ts", range);
2121
verify.renameInfoFailed("You cannot rename this element.", { allowRenameOfImportPath: false });
2222
}

tests/cases/fourslash/renameImport.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
verify.noErrors();
2525
goTo.eachRange(range => {
2626
const target = range.marker && range.marker.data && range.marker.data.target;
27-
const name = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
27+
const displayName = target === "dir" ? "./dir" : target === "dir/index" ? "./dir/index" : "./a";
28+
const fileName = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
2829
const kind = target === "dir" ? "directory" : "module";
29-
verify.renameInfoSucceeded(/*displayName*/ name, /*fullDisplayName*/ name, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ name, range);
30+
verify.renameInfoSucceeded(/*displayName*/ fileName, /*fullDisplayName*/ displayName, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ fileName, range);
3031
verify.renameInfoFailed("You cannot rename this element.", { allowRenameOfImportPath: false });
3132
});
3233

0 commit comments

Comments
 (0)