fix54492: allow editor to check for original file extension for rename (#56680)

This commit is contained in:
Isabel Duan 2023-12-08 09:32:52 -08:00 committed by GitHub
parent e3d234cfc8
commit 8dfbfcb058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 9 deletions

View File

@ -1336,6 +1336,7 @@ export interface RenameInfoSuccess {
/**
* Full display name of item to be renamed.
* If item to be renamed is a file, then this is the original text of the module specifer
*/
fullDisplayName: string;

View File

@ -185,17 +185,17 @@ function getRenameInfoForModule(node: StringLiteralLike, sourceFile: SourceFile,
const moduleSourceFile = moduleSymbol.declarations && find(moduleSymbol.declarations, isSourceFile);
if (!moduleSourceFile) return undefined;
const withoutIndex = endsWith(node.text, "/index") || endsWith(node.text, "/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
const fileName = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
const kind = withoutIndex === undefined ? ScriptElementKind.moduleElement : ScriptElementKind.directory;
const indexAfterLastSlash = node.text.lastIndexOf("/") + 1;
// Span should only be the last component of the path. + 1 to account for the quote character.
const triggerSpan = createTextSpan(node.getStart(sourceFile) + 1 + indexAfterLastSlash, node.text.length - indexAfterLastSlash);
return {
canRename: true,
fileToRename: name,
fileToRename: fileName,
kind,
displayName: name,
fullDisplayName: name,
displayName: fileName,
fullDisplayName: node.text,
kindModifiers: ScriptElementKindModifier.none,
triggerSpan,
};

View File

@ -1291,6 +1291,10 @@ export interface RenameInfoSuccess {
*/
fileToRename?: string;
displayName: string;
/**
* Full display name of item to be renamed.
* If item to be renamed is a file, then this is the original text of the module specifer
*/
fullDisplayName: string;
kind: ScriptElementKind;
kindModifiers: string;

View File

@ -193,4 +193,23 @@ describe("unittests:: tsserver:: rename", () => {
});
baselineTsserverLogs("rename", "with symlinks and case difference", session);
});
it("rename TS file with js extension", () => {
const aTs: File = { path: "/a.ts", content: "export const a = 1;" };
const bTs: File = { path: "/b.ts", content: `import * as foo from './a.js';` };
const host = createServerHost([aTs, bTs]);
const session = new TestSession(host);
openFilesForSession([aTs, bTs], session);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { allowRenameOfImportPath: true } },
});
session.executeCommandSeq<ts.server.protocol.RenameRequest>({
command: ts.server.protocol.CommandTypes.Rename,
arguments: protocolFileLocationFromSubstring(bTs, "a.js"),
});
baselineTsserverLogs("rename", "rename TS file with js extension", session);
});
});

View File

@ -1087,6 +1087,7 @@ declare namespace ts {
displayName: string;
/**
* Full display name of item to be renamed.
* If item to be renamed is a file, then this is the original text of the module specifer
*/
fullDisplayName: string;
/**
@ -11092,6 +11093,10 @@ declare namespace ts {
*/
fileToRename?: string;
displayName: string;
/**
* Full display name of item to be renamed.
* If item to be renamed is a file, then this is the original text of the module specifer
*/
fullDisplayName: string;
kind: ScriptElementKind;
kindModifiers: string;

View File

@ -0,0 +1,197 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
Before request
//// [/a.ts]
export const a = 1;
//// [/b.ts]
import * as foo from './a.js';
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/a.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
/a.ts SVC-1-0 "export const a = 1;"
a.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/b.ts"
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /b.ts :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject2*
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject2* WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject2* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject2*' (Inferred)
Info seq [hh:mm:ss:mss] Files (2)
/a.ts SVC-1-0 "export const a = 1;"
/b.ts SVC-1-0 "import * as foo from './a.js';"
a.ts
Imported via './a.js' from file 'b.ts'
b.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] `remove Project::
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
/a.ts
a.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] FileWatcher:: Close:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject2*' (Inferred)
Info seq [hh:mm:ss:mss] Files (2)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject2*
Info seq [hh:mm:ss:mss] FileName: /b.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject2*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "configure",
"arguments": {
"preferences": {
"allowRenameOfImportPath": true
}
},
"seq": 3,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"seq": 0,
"type": "response",
"command": "configure",
"request_seq": 3,
"success": true,
"performanceData": {
"updateGraphDurationMs": *
}
}
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "rename",
"arguments": {
"file": "/b.ts",
"line": 1,
"offset": 25
},
"seq": 4,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": {
"info": {
"canRename": true,
"fileToRename": "/a.ts",
"displayName": "/a.ts",
"fullDisplayName": "./a.js",
"kind": "module",
"kindModifiers": "",
"triggerSpan": {
"start": {
"line": 1,
"offset": 25
},
"end": {
"line": 1,
"offset": 29
}
}
},
"locs": [
{
"file": "/b.ts",
"locs": [
{
"start": {
"line": 1,
"offset": 23
},
"end": {
"line": 1,
"offset": 29
},
"contextStart": {
"line": 1,
"offset": 1
},
"contextEnd": {
"line": 1,
"offset": 31
}
}
]
}
]
},
"responseRequired": true
}
After request

View File

@ -132,7 +132,7 @@ Info seq [hh:mm:ss:mss] response:
"canRename": true,
"fileToRename": "/a.ts",
"displayName": "/a.ts",
"fullDisplayName": "/a.ts",
"fullDisplayName": "./a",
"kind": "module",
"kindModifiers": "",
"triggerSpan": {
@ -259,7 +259,7 @@ Info seq [hh:mm:ss:mss] response:
"canRename": true,
"fileToRename": "/a.ts",
"displayName": "/a.ts",
"fullDisplayName": "/a.ts",
"fullDisplayName": "./a",
"kind": "module",
"kindModifiers": "",
"triggerSpan": {

View File

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

View File

@ -24,9 +24,10 @@
verify.noErrors();
goTo.eachRange(range => {
const target = range.marker && range.marker.data && range.marker.data.target;
const name = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
const displayName = target === "dir" ? "./dir" : target === "dir/index" ? "./dir/index" : "./a";
const fileName = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
const kind = target === "dir" ? "directory" : "module";
verify.renameInfoSucceeded(/*displayName*/ name, /*fullDisplayName*/ name, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ name, range);
verify.renameInfoSucceeded(/*displayName*/ fileName, /*fullDisplayName*/ displayName, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ fileName, range);
verify.renameInfoFailed("You cannot rename this element.", { allowRenameOfImportPath: false });
});