Skip to content

Commit c5948bf

Browse files
Merge pull request #29385 from uniqueiniquity/renameImportPref
Add user preference to opt-in to renaming import paths
2 parents 760b02b + 2c50ed3 commit c5948bf

File tree

15 files changed

+57
-30
lines changed

15 files changed

+57
-30
lines changed

src/harness/client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ namespace ts.server {
384384
return notImplemented();
385385
}
386386

387-
getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
387+
getRenameInfo(fileName: string, position: number, _options?: RenameInfoOptions, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
388+
// Not passing along 'options' because server should already have those from the 'configure' command
388389
const args: protocol.RenameRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), findInStrings, findInComments };
389390

390391
const request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
@@ -428,7 +429,7 @@ namespace ts.server {
428429
this.lastRenameEntry.inputs.position !== position ||
429430
this.lastRenameEntry.inputs.findInStrings !== findInStrings ||
430431
this.lastRenameEntry.inputs.findInComments !== findInComments) {
431-
this.getRenameInfo(fileName, position, findInStrings, findInComments);
432+
this.getRenameInfo(fileName, position, { allowRenameOfImportPath: true }, findInStrings, findInComments);
432433
}
433434

434435
return this.lastRenameEntry!.locations;

src/harness/fourslash.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,8 +1308,8 @@ Actual: ${stringify(fullActual)}`);
13081308
}
13091309
}
13101310

1311-
public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined): void {
1312-
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
1311+
public verifyRenameInfoSucceeded(displayName: string | undefined, fullDisplayName: string | undefined, kind: string | undefined, kindModifiers: string | undefined, fileToRename: string | undefined, expectedRange: Range | undefined, renameInfoOptions: ts.RenameInfoOptions | undefined): void {
1312+
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, renameInfoOptions || { allowRenameOfImportPath: true });
13131313
if (!renameInfo.canRename) {
13141314
throw this.raiseError("Rename did not succeed");
13151315
}
@@ -1334,8 +1334,9 @@ Actual: ${stringify(fullActual)}`);
13341334
}
13351335
}
13361336

1337-
public verifyRenameInfoFailed(message?: string) {
1338-
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
1337+
public verifyRenameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) {
1338+
allowRenameOfImportPath = allowRenameOfImportPath === undefined ? true : allowRenameOfImportPath;
1339+
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition, { allowRenameOfImportPath });
13391340
if (renameInfo.canRename) {
13401341
throw this.raiseError("Rename was expected to fail");
13411342
}
@@ -4091,12 +4092,12 @@ namespace FourSlashInterface {
40914092
this.state.verifySemanticClassifications(classifications);
40924093
}
40934094

4094-
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range) {
4095-
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange);
4095+
public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range, options?: ts.RenameInfoOptions) {
4096+
this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange, options);
40964097
}
40974098

4098-
public renameInfoFailed(message?: string) {
4099-
this.state.verifyRenameInfoFailed(message);
4099+
public renameInfoFailed(message?: string, allowRenameOfImportPath?: boolean) {
4100+
this.state.verifyRenameInfoFailed(message, allowRenameOfImportPath);
41004101
}
41014102

41024103
public renameLocations(startRanges: ArrayOrSingle<FourSlash.Range>, options: RenameLocationsOptions) {

src/harness/harnessLanguageService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,8 @@ namespace Harness.LanguageService {
469469
getSignatureHelpItems(fileName: string, position: number, options: ts.SignatureHelpItemsOptions | undefined): ts.SignatureHelpItems {
470470
return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position, options));
471471
}
472-
getRenameInfo(fileName: string, position: number): ts.RenameInfo {
473-
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position));
472+
getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo {
473+
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options));
474474
}
475475
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ts.RenameLocation[] {
476476
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments));

src/server/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,6 +2905,7 @@ namespace ts.server.protocol {
29052905
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
29062906
readonly allowTextChangesInNewFiles?: boolean;
29072907
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
2908+
readonly allowRenameOfImportPath?: boolean;
29082909
}
29092910

29102911
export interface CompilerOptions {

src/server/session.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,8 @@ namespace ts.server {
11771177
private getRenameInfo(args: protocol.FileLocationRequestArgs): RenameInfo {
11781178
const { file, project } = this.getFileAndProject(args);
11791179
const position = this.getPositionInFile(args, file);
1180-
return project.getLanguageService().getRenameInfo(file, position);
1180+
const preferences = this.getHostPreferences();
1181+
return project.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: preferences.allowRenameOfImportPath });
11811182
}
11821183

11831184
private getProjects(args: protocol.FileRequestArgs, getScriptInfoEnsuringProjectsUptoDate?: boolean, ignoreNoProjectError?: boolean): Projects {
@@ -1236,7 +1237,7 @@ namespace ts.server {
12361237
if (!simplifiedResult) return locations;
12371238

12381239
const defaultProject = this.getDefaultProject(args);
1239-
const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position), Debug.assertDefined(this.projectService.getScriptInfo(file)));
1240+
const renameInfo: protocol.RenameInfo = this.mapRenameInfo(defaultProject.getLanguageService().getRenameInfo(file, position, { allowRenameOfImportPath: this.getHostPreferences().allowRenameOfImportPath }), Debug.assertDefined(this.projectService.getScriptInfo(file)));
12401241
return { info: renameInfo, locs: this.toSpanGroups(locations) };
12411242
}
12421243

src/services/rename.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/* @internal */
22
namespace ts.Rename {
3-
export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number): RenameInfo {
3+
export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number, options?: RenameInfoOptions): RenameInfo {
44
const node = getTouchingPropertyName(sourceFile, position);
55
const renameInfo = node && nodeIsEligibleForRename(node)
6-
? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()))
6+
? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()), options)
77
: undefined;
88
return renameInfo || getRenameInfoError(Diagnostics.You_cannot_rename_this_element);
99
}
1010

11-
function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean): RenameInfo | undefined {
11+
function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean, options?: RenameInfoOptions): RenameInfo | undefined {
1212
const symbol = typeChecker.getSymbolAtLocation(node);
1313
if (!symbol) return;
1414
// Only allow a symbol to be renamed if it actually has at least one declaration.
@@ -26,7 +26,7 @@ namespace ts.Rename {
2626
}
2727

2828
if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) {
29-
return getRenameInfoForModule(node, sourceFile, symbol);
29+
return options && options.allowRenameOfImportPath ? getRenameInfoForModule(node, sourceFile, symbol) : undefined;
3030
}
3131

3232
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);

src/services/services.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,9 +2062,9 @@ namespace ts {
20622062
}
20632063
}
20642064

2065-
function getRenameInfo(fileName: string, position: number): RenameInfo {
2065+
function getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo {
20662066
synchronizeHostData();
2067-
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position);
2067+
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options);
20682068
}
20692069

20702070
function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings): RefactorContext {

src/services/shims.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ namespace ts {
164164
* Returns a JSON-encoded value of the type:
165165
* { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
166166
*/
167-
getRenameInfo(fileName: string, position: number): string;
167+
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string;
168168

169169
/**
170170
* Returns a JSON-encoded value of the type:
@@ -831,10 +831,10 @@ namespace ts {
831831
);
832832
}
833833

834-
public getRenameInfo(fileName: string, position: number): string {
834+
public getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string {
835835
return this.forwardJSONCall(
836836
`getRenameInfo('${fileName}', ${position})`,
837-
() => this.languageService.getRenameInfo(fileName, position)
837+
() => this.languageService.getRenameInfo(fileName, position, options)
838838
);
839839
}
840840

src/services/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ namespace ts {
294294

295295
getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined;
296296

297-
getRenameInfo(fileName: string, position: number): RenameInfo;
297+
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
298298
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray<RenameLocation> | undefined;
299299

300300
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
@@ -848,6 +848,10 @@ namespace ts {
848848
localizedErrorMessage: string;
849849
}
850850

851+
export interface RenameInfoOptions {
852+
readonly allowRenameOfImportPath?: boolean;
853+
}
854+
851855
export interface SignatureHelpParameter {
852856
name: string;
853857
documentation: SymbolDisplayPart[];

src/testRunner/unittests/tsserver/rename.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@ namespace ts.projectSystem {
77
const session = createSession(createServerHost([aTs, bTs]));
88
openFilesForSession([bTs], session);
99

10-
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
11-
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
10+
const response1 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
11+
assert.deepEqual<protocol.RenameResponseBody | undefined>(response1, {
12+
info: {
13+
canRename: false,
14+
localizedErrorMessage: "You cannot rename this element."
15+
},
16+
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
17+
});
18+
19+
session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: true } });
20+
const response2 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
21+
assert.deepEqual<protocol.RenameResponseBody | undefined>(response2, {
1222
info: {
1323
canRename: true,
1424
fileToRename: aTs.path,

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4706,7 +4706,7 @@ declare namespace ts {
47064706
getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan | undefined;
47074707
getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined;
47084708
getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined;
4709-
getRenameInfo(fileName: string, position: number): RenameInfo;
4709+
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
47104710
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray<RenameLocation> | undefined;
47114711
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
47124712
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
@@ -5150,6 +5150,9 @@ declare namespace ts {
51505150
canRename: false;
51515151
localizedErrorMessage: string;
51525152
}
5153+
interface RenameInfoOptions {
5154+
readonly allowRenameOfImportPath?: boolean;
5155+
}
51535156
interface SignatureHelpParameter {
51545157
name: string;
51555158
documentation: SymbolDisplayPart[];
@@ -7923,6 +7926,7 @@ declare namespace ts.server.protocol {
79237926
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
79247927
readonly allowTextChangesInNewFiles?: boolean;
79257928
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
7929+
readonly allowRenameOfImportPath?: boolean;
79267930
}
79277931
interface CompilerOptions {
79287932
allowJs?: boolean;

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4706,7 +4706,7 @@ declare namespace ts {
47064706
getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan | undefined;
47074707
getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined;
47084708
getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined;
4709-
getRenameInfo(fileName: string, position: number): RenameInfo;
4709+
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
47104710
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReadonlyArray<RenameLocation> | undefined;
47114711
getDefinitionAtPosition(fileName: string, position: number): ReadonlyArray<DefinitionInfo> | undefined;
47124712
getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined;
@@ -5150,6 +5150,9 @@ declare namespace ts {
51505150
canRename: false;
51515151
localizedErrorMessage: string;
51525152
}
5153+
interface RenameInfoOptions {
5154+
readonly allowRenameOfImportPath?: boolean;
5155+
}
51535156
interface SignatureHelpParameter {
51545157
name: string;
51555158
documentation: SymbolDisplayPart[];

tests/cases/fourslash/findAllRefs_importType_exportEquals.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ verify.renameLocations(r2, [r0, r1, r2]);
2727
for (const range of [r3b, r4b]) {
2828
goTo.rangeStart(range);
2929
verify.renameInfoSucceeded(/*displayName*/ "/a.ts", /*fullDisplayName*/ "/a.ts", /*kind*/ "module", /*kindModifiers*/ "", /*fileToRename*/ "/a.ts", range);
30+
verify.renameInfoFailed("You cannot rename this element.", /*allowRenameOfImportPath*/ false);
3031
}

tests/cases/fourslash/fourslash.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ declare namespace FourSlashInterface {
282282
text: string;
283283
textSpan?: TextSpan;
284284
}[]): void;
285-
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range): void;
286-
renameInfoFailed(message?: string): void;
285+
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range, allowRenameOfImportPath?: boolean): void;
286+
renameInfoFailed(message?: string, allowRenameOfImportPath?: boolean): void;
287287
renameLocations(startRanges: ArrayOrSingle<Range>, options: RenameLocationsOptions): void;
288288

289289
/** Verify the quick info available at the current marker. */

tests/cases/fourslash/renameImport.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ goTo.eachRange(range => {
2727
const name = target === "dir" ? "/dir" : target === "dir/index" ? "/dir/index.ts" : "/a.ts";
2828
const kind = target === "dir" ? "directory" : "module";
2929
verify.renameInfoSucceeded(/*displayName*/ name, /*fullDisplayName*/ name, /*kind*/ kind, /*kindModifiers*/ "", /*fileToRename*/ name, range);
30+
verify.renameInfoFailed("You cannot rename this element.", /*allowRenameOfImportPath*/ false);
3031
});
3132

3233
goTo.marker("global");

0 commit comments

Comments
 (0)