Skip to content

Commit 24a5bdd

Browse files
author
Andy
authored
Add 'fileToRename' property to RenameInfo (#24702)
* Add 'fileToRename' property to RenameInfo * Update tests * Support directory rename
1 parent af8e44a commit 24a5bdd

File tree

11 files changed

+77
-17
lines changed

11 files changed

+77
-17
lines changed

src/harness/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ namespace ts.server {
397397

398398
return this.lastRenameEntry = {
399399
canRename: body.info.canRename,
400+
fileToRename: body.info.fileToRename,
400401
displayName: body.info.displayName,
401402
fullDisplayName: body.info.fullDisplayName,
402403
kind: body.info.kind,

src/harness/fourslash.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,12 @@ namespace FourSlash {
420420
}
421421
}
422422

423-
public goToEachRange(action: () => void) {
423+
public goToEachRange(action: (range: Range) => void) {
424424
const ranges = this.getRanges();
425425
assert(ranges.length);
426426
for (const range of ranges) {
427427
this.selectRange(range);
428-
action();
428+
action(range);
429429
}
430430
}
431431

@@ -1525,7 +1525,7 @@ Actual: ${stringify(fullActual)}`);
15251525
}
15261526
}
15271527

1528-
public verifyRenameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string) {
1528+
public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined): void {
15291529
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
15301530
if (!renameInfo.canRename) {
15311531
this.raiseError("Rename did not succeed");
@@ -1535,12 +1535,15 @@ Actual: ${stringify(fullActual)}`);
15351535
this.validate("fullDisplayName", fullDisplayName, renameInfo.fullDisplayName);
15361536
this.validate("kind", kind, renameInfo.kind);
15371537
this.validate("kindModifiers", kindModifiers, renameInfo.kindModifiers);
1538+
this.validate("fileToRename", fileToRename, renameInfo.fileToRename);
15381539

1539-
if (this.getRanges().length !== 1) {
1540-
this.raiseError("Expected a single range to be selected in the test file.");
1540+
if (!expectedRange) {
1541+
if (this.getRanges().length !== 1) {
1542+
this.raiseError("Expected a single range to be selected in the test file.");
1543+
}
1544+
expectedRange = this.getRanges()[0];
15411545
}
15421546

1543-
const expectedRange = this.getRanges()[0];
15441547
if (renameInfo.triggerSpan.start !== expectedRange.pos ||
15451548
ts.textSpanEnd(renameInfo.triggerSpan) !== expectedRange.end) {
15461549
this.raiseError("Expected triggerSpan [" + expectedRange.pos + "," + expectedRange.end + "). Got [" +
@@ -3977,7 +3980,7 @@ namespace FourSlashInterface {
39773980
this.state.goToRangeStart(range);
39783981
}
39793982

3980-
public eachRange(action: () => void) {
3983+
public eachRange(action: (range: FourSlash.Range) => void) {
39813984
this.state.goToEachRange(action);
39823985
}
39833986

@@ -4456,8 +4459,8 @@ namespace FourSlashInterface {
44564459
this.state.verifySemanticClassifications(classifications);
44574460
}
44584461

4459-
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string) {
4460-
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers);
4462+
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range) {
4463+
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange);
44614464
}
44624465

44634466
public renameInfoFailed(message?: string) {

src/server/protocol.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,12 @@ namespace ts.server.protocol {
10931093
*/
10941094
canRename: boolean;
10951095

1096+
/**
1097+
* File or directory to rename.
1098+
* If set, `getEditsForFileRename` should be called instead of `findRenameLocations`.
1099+
*/
1100+
fileToRename?: string;
1101+
10961102
/**
10971103
* Error message if item can not be renamed.
10981104
*/

src/server/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ namespace ts.server {
10961096
return projectInfo;
10971097
}
10981098

1099-
private getRenameInfo(args: protocol.FileLocationRequestArgs) {
1099+
private getRenameInfo(args: protocol.FileLocationRequestArgs): RenameInfo {
11001100
const { file, project } = this.getFileAndProject(args);
11011101
const position = this.getPositionInFile(args, file);
11021102
return project.getLanguageService().getRenameInfo(file, position);

src/services/rename.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ namespace ts.Rename {
2525
return undefined;
2626
}
2727

28-
// Can't rename a module name.
29-
if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) return undefined;
28+
if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) {
29+
return getRenameInfoForModule(node, sourceFile, symbol);
30+
}
3031

3132
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);
3233
const specifierName = (isImportOrExportSpecifierName(node) || isStringOrNumericLiteralLike(node) && node.parent.kind === SyntaxKind.ComputedPropertyName)
@@ -37,9 +38,28 @@ namespace ts.Rename {
3738
return getRenameInfoSuccess(displayName, fullDisplayName, kind, SymbolDisplay.getSymbolModifiers(symbol), node, sourceFile);
3839
}
3940

41+
function getRenameInfoForModule(node: StringLiteralLike, sourceFile: SourceFile, moduleSymbol: Symbol): RenameInfo | undefined {
42+
const moduleSourceFile = find(moduleSymbol.declarations, isSourceFile);
43+
if (!moduleSourceFile) return undefined;
44+
const withoutIndex = node.text.endsWith("/index") || node.text.endsWith("/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
45+
const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
46+
const kind = withoutIndex === undefined ? ScriptElementKind.moduleElement : ScriptElementKind.directory;
47+
return {
48+
canRename: true,
49+
fileToRename: name,
50+
kind,
51+
displayName: name,
52+
localizedErrorMessage: undefined,
53+
fullDisplayName: name,
54+
kindModifiers: ScriptElementKindModifier.none,
55+
triggerSpan: createTriggerSpanForNode(node, sourceFile),
56+
};
57+
}
58+
4059
function getRenameInfoSuccess(displayName: string, fullDisplayName: string, kind: ScriptElementKind, kindModifiers: string, node: Node, sourceFile: SourceFile): RenameInfo {
4160
return {
4261
canRename: true,
62+
fileToRename: undefined,
4363
kind,
4464
displayName,
4565
localizedErrorMessage: undefined,

src/services/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,11 @@ namespace ts {
784784

785785
export interface RenameInfo {
786786
canRename: boolean;
787+
/**
788+
* File or directory to rename.
789+
* If set, `getEditsForFileRename` should be called instead of `findRenameLocations`.
790+
*/
791+
fileToRename?: string;
787792
localizedErrorMessage?: string;
788793
displayName: string;
789794
fullDisplayName: string;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5101,6 +5101,11 @@ declare namespace ts {
51015101
}
51025102
interface RenameInfo {
51035103
canRename: boolean;
5104+
/**
5105+
* File or directory to rename.
5106+
* If set, `getEditsForFileRename` should be called instead of `findRenameLocations`.
5107+
*/
5108+
fileToRename?: string;
51045109
localizedErrorMessage?: string;
51055110
displayName: string;
51065111
fullDisplayName: string;
@@ -6422,6 +6427,11 @@ declare namespace ts.server.protocol {
64226427
* True if item can be renamed.
64236428
*/
64246429
canRename: boolean;
6430+
/**
6431+
* File or directory to rename.
6432+
* If set, `getEditsForFileRename` should be called instead of `findRenameLocations`.
6433+
*/
6434+
fileToRename?: string;
64256435
/**
64266436
* Error message if item can not be renamed.
64276437
*/

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5101,6 +5101,11 @@ declare namespace ts {
51015101
}
51025102
interface RenameInfo {
51035103
canRename: boolean;
5104+
/**
5105+
* File or directory to rename.
5106+
* If set, `getEditsForFileRename` should be called instead of `findRenameLocations`.
5107+
*/
5108+
fileToRename?: string;
51045109
localizedErrorMessage?: string;
51055110
displayName: string;
51065111
fullDisplayName: string;

tests/cases/fourslash/findAllRefs_importType_exportEquals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ verify.renameLocations(r1, [r1, r2]);
2727
verify.renameLocations(r2, [r0, r1, r2]);
2828
for (const range of [r3, r4]) {
2929
goTo.rangeStart(range);
30-
verify.renameInfoFailed();
30+
verify.renameInfoSucceeded(/*displayName*/ "/a.ts", /*fullDisplayName*/ "/a.ts", /*kind*/ "module", /*kindModifiers*/ "", /*fileToRename*/ "/a.ts", range);
3131
}

tests/cases/fourslash/fourslash.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ declare namespace FourSlashInterface {
129129
eachMarker(markers: ReadonlyArray<string>, action: (marker: Marker, index: number) => void): void;
130130
eachMarker(action: (marker: Marker, index: number) => void): void;
131131
rangeStart(range: Range): void;
132-
eachRange(action: () => void): void;
132+
eachRange(action: (range: Range) => void): void;
133133
bof(): void;
134134
eof(): void;
135135
implementation(): void;
@@ -315,7 +315,7 @@ declare namespace FourSlashInterface {
315315
text: string;
316316
textSpan?: TextSpan;
317317
}[]): void;
318-
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string): void;
318+
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range): void;
319319
renameInfoFailed(message?: string): void;
320320
renameLocations(startRanges: ArrayOrSingle<Range>, options: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: Range[] }): void;
321321

tests/cases/fourslash/renameImport.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
// @Filename: /a.ts
66
////export const x = 0;
77

8+
// @Filename: /dir/index.ts
9+
////export const x = 0;
10+
811
// @Filename: /b.ts
912
////import * as a from "[|./a|]";
10-
////import a2 = require("[|./a"|]);
13+
////import a2 = require("[|./a|]");
14+
////import * as dir from "[|{| "target": "dir" |}./dir|]";
15+
////import * as dir2 from "[|{| "target": "dir/index" |}./dir/index|]";
1116

1217
// @Filename: /c.js
1318
////const a = require("[|./a|]");
1419

1520
verify.noErrors();
16-
goTo.eachRange(() => { verify.renameInfoFailed(); });
21+
goTo.eachRange(range => {
22+
const target = range.marker && range.marker.data && range.marker.data.target;
23+
const name = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
24+
const kind = target === "dir" ? "directory" : "module";
25+
verify.renameInfoSucceeded(/*displayName*/ name, /*fullDisplayName*/ name, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ name, range);
26+
});

0 commit comments

Comments
 (0)