Skip to content

Commit e02ef9f

Browse files
authored
Add quotes when renaming numerical indices (microsoft#53596)
1 parent 30fb9fa commit e02ef9f

16 files changed

+85
-21
lines changed

src/harness/client.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,15 +568,17 @@ export class SessionClient implements LanguageService {
568568
return notImplemented();
569569
}
570570

571-
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] {
571+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences | boolean | undefined): RenameLocation[] {
572572
if (!this.lastRenameEntry ||
573573
this.lastRenameEntry.inputs.fileName !== fileName ||
574574
this.lastRenameEntry.inputs.position !== position ||
575575
this.lastRenameEntry.inputs.findInStrings !== findInStrings ||
576576
this.lastRenameEntry.inputs.findInComments !== findInComments) {
577-
if (providePrefixAndSuffixTextForRename !== undefined) {
577+
const providePrefixAndSuffixTextForRename = typeof preferences === "boolean" ? preferences : preferences?.providePrefixAndSuffixTextForRename;
578+
const quotePreference = typeof preferences === "boolean" ? undefined : preferences?.quotePreference;
579+
if (providePrefixAndSuffixTextForRename !== undefined || quotePreference !== undefined) {
578580
// User preferences have to be set through the `Configure` command
579-
this.configure({ providePrefixAndSuffixTextForRename });
581+
this.configure({ providePrefixAndSuffixTextForRename, quotePreference });
580582
// Options argument is not used, so don't pass in options
581583
this.getRenameInfo(fileName, position, /*preferences*/{}, findInStrings, findInComments);
582584
// Restore previous user preferences

src/harness/fourslashImpl.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,13 +1802,18 @@ export class TestState {
18021802
isMarker(markerOrRange) ?
18031803
markerOrRange :
18041804
{ fileName: markerOrRange.fileName, position: markerOrRange.pos };
1805-
const { findInStrings = false, findInComments = false, providePrefixAndSuffixTextForRename = true } = options || {};
1805+
const {
1806+
findInStrings = false,
1807+
findInComments = false,
1808+
providePrefixAndSuffixTextForRename = true,
1809+
quotePreference = "double"
1810+
} = options || {};
18061811
const locations = this.languageService.findRenameLocations(
18071812
fileName,
18081813
position,
18091814
findInStrings,
18101815
findInComments,
1811-
providePrefixAndSuffixTextForRename,
1816+
{ providePrefixAndSuffixTextForRename, quotePreference },
18121817
);
18131818

18141819
if (!locations) {
@@ -1818,7 +1823,8 @@ export class TestState {
18181823
const renameOptions = options ?
18191824
(options.findInStrings !== undefined ? `// @findInStrings: ${findInStrings}\n` : "") +
18201825
(options.findInComments !== undefined ? `// @findInComments: ${findInComments}\n` : "") +
1821-
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") :
1826+
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") +
1827+
(options.quotePreference !== undefined ? `// @quotePreference: ${quotePreference}\n` : "") :
18221828
"";
18231829

18241830
return renameOptions + (renameOptions ? "\n" : "") + this.getBaselineForDocumentSpansWithFileContents(

src/harness/fourslashInterfaceImpl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,6 +1910,7 @@ export interface RenameOptions {
19101910
readonly findInStrings?: boolean;
19111911
readonly findInComments?: boolean;
19121912
readonly providePrefixAndSuffixTextForRename?: boolean;
1913+
readonly quotePreference?: "auto" | "double" | "single";
19131914
}
19141915
export type BaselineCommandWithMarkerOrRange = {
19151916
type: "findAllReferences" | "goToDefinition" | "getDefinitionAtPosition" | "goToSourceDefinition" | "goToType" | "goToImplementation";

src/harness/harnessLanguageService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,8 @@ class LanguageServiceShimProxy implements ts.LanguageService {
527527
getSmartSelectionRange(fileName: string, position: number): ts.SelectionRange {
528528
return unwrapJSONCallResult(this.shim.getSmartSelectionRange(fileName, position));
529529
}
530-
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
531-
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
530+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: ts.UserPreferences | boolean): ts.RenameLocation[] {
531+
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, preferences));
532532
}
533533
getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
534534
return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));

src/server/session.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ import {
136136
toFileNameLowerCase,
137137
tracing,
138138
unmangleScopedPackageName,
139-
UserPreferences,
140139
version,
141140
WithMetadata,
142141
} from "./_namespaces/ts";
@@ -497,14 +496,14 @@ function getRenameLocationsWorker(
497496
initialLocation: DocumentPosition,
498497
findInStrings: boolean,
499498
findInComments: boolean,
500-
{ providePrefixAndSuffixTextForRename }: UserPreferences
499+
preferences: protocol.UserPreferences
501500
): readonly RenameLocation[] {
502501
const perProjectResults = getPerProjectReferences(
503502
projects,
504503
defaultProject,
505504
initialLocation,
506505
/*isForRename*/ true,
507-
(project, position) => project.getLanguageService().findRenameLocations(position.fileName, position.pos, findInStrings, findInComments, providePrefixAndSuffixTextForRename),
506+
(project, position) => project.getLanguageService().findRenameLocations(position.fileName, position.pos, findInStrings, findInComments, preferences),
508507
(renameLocation, cb) => cb(documentSpanLocation(renameLocation)),
509508
);
510509

src/services/findAllReferences.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import {
7272
getNodeKind,
7373
getPropertySymbolFromBindingElement,
7474
getPropertySymbolsFromContextualType,
75+
getQuoteFromPreference,
7576
getReferencedFileLocation,
7677
getSuperContainer,
7778
getSymbolId,
@@ -151,6 +152,7 @@ import {
151152
isNamespaceExportDeclaration,
152153
isNewExpressionTarget,
153154
isNoSubstitutionTemplateLiteral,
155+
isNumericLiteral,
154156
isObjectBindingElementWithoutPropertyName,
155157
isObjectLiteralExpression,
156158
isObjectLiteralMethod,
@@ -205,6 +207,7 @@ import {
205207
PropertyAssignment,
206208
PropertyDeclaration,
207209
punctuationPart,
210+
QuotePreference,
208211
rangeIsOnSingleLine,
209212
ReferencedSymbol,
210213
ReferencedSymbolDefinitionInfo,
@@ -673,8 +676,8 @@ function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker,
673676
}
674677

675678
/** @internal */
676-
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean): RenameLocation {
677-
return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker)) };
679+
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean, quotePreference: QuotePreference): RenameLocation {
680+
return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker, quotePreference)) };
678681
}
679682

680683
function toReferencedSymbolEntry(entry: Entry, symbol: Symbol | undefined): ReferencedSymbolEntry {
@@ -716,7 +719,7 @@ function entryToDocumentSpan(entry: Entry): DocumentSpan {
716719
}
717720

718721
interface PrefixAndSuffix { readonly prefixText?: string; readonly suffixText?: string; }
719-
function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker): PrefixAndSuffix {
722+
function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker, quotePreference: QuotePreference): PrefixAndSuffix {
720723
if (entry.kind !== EntryKind.Span && isIdentifier(originalNode)) {
721724
const { node, kind } = entry;
722725
const parent = node.parent;
@@ -760,6 +763,12 @@ function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeC
760763
}
761764
}
762765

766+
// If the node is a numerical indexing literal, then add quotes around the property access.
767+
if (entry.kind !== EntryKind.Span && isNumericLiteral(entry.node) && isAccessExpression(entry.node.parent)) {
768+
const quote = getQuoteFromPreference(quotePreference);
769+
return { prefixText: quote, suffixText: quote };
770+
}
771+
763772
return emptyOptions;
764773
}
765774

src/services/services.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ import {
105105
getNonAssignedNameOfDeclaration,
106106
getNormalizedAbsolutePath,
107107
getObjectFlags,
108+
getQuotePreference,
108109
getScriptKind,
109110
getSetExternalModuleIndicator,
110111
getSnapshotText,
@@ -2128,7 +2129,7 @@ export function createLanguageService(
21282129
return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
21292130
}
21302131

2131-
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined {
2132+
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: UserPreferences | boolean): RenameLocation[] | undefined {
21322133
synchronizeHostData();
21332134
const sourceFile = getValidSourceFile(fileName);
21342135
const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position));
@@ -2145,8 +2146,10 @@ export function createLanguageService(
21452146
});
21462147
}
21472148
else {
2149+
const quotePreference = getQuotePreference(sourceFile, preferences ?? emptyOptions);
2150+
const providePrefixAndSuffixTextForRename = typeof preferences === "boolean" ? preferences : preferences?.providePrefixAndSuffixTextForRename;
21482151
return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, use: FindAllReferences.FindReferencesUse.Rename },
2149-
(entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false));
2152+
(entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false, quotePreference));
21502153
}
21512154
}
21522155

src/services/shims.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export interface LanguageServiceShim extends Shim {
256256
* Returns a JSON-encoded value of the type:
257257
* { fileName: string, textSpan: { start: number, length: number } }[]
258258
*/
259-
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string;
259+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: UserPreferences | boolean): string;
260260

261261
/**
262262
* Returns a JSON-encoded value of the type:
@@ -952,10 +952,10 @@ class LanguageServiceShimObject extends ShimBase implements LanguageServiceShim
952952
);
953953
}
954954

955-
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
955+
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): string {
956956
return this.forwardJSONCall(
957-
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
958-
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
957+
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments})`,
958+
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, preferences)
959959
);
960960
}
961961

src/services/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ export interface LanguageService {
575575
/** @deprecated Use the signature with `UserPreferences` instead. */
576576
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
577577

578+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): readonly RenameLocation[] | undefined;
579+
/** @deprecated Pass `providePrefixAndSuffixTextForRename` as part of a `UserPreferences` parameter. */
578580
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): readonly RenameLocation[] | undefined;
579581

580582
getSmartSelectionRange(fileName: string, position: number): SelectionRange;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10095,6 +10095,8 @@ declare namespace ts {
1009510095
getRenameInfo(fileName: string, position: number, preferences: UserPreferences): RenameInfo;
1009610096
/** @deprecated Use the signature with `UserPreferences` instead. */
1009710097
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
10098+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): readonly RenameLocation[] | undefined;
10099+
/** @deprecated Pass `providePrefixAndSuffixTextForRename` as part of a `UserPreferences` parameter. */
1009810100
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): readonly RenameLocation[] | undefined;
1009910101
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
1010010102
getDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6153,6 +6153,8 @@ declare namespace ts {
61536153
getRenameInfo(fileName: string, position: number, preferences: UserPreferences): RenameInfo;
61546154
/** @deprecated Use the signature with `UserPreferences` instead. */
61556155
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): RenameInfo;
6156+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences: UserPreferences): readonly RenameLocation[] | undefined;
6157+
/** @deprecated Pass `providePrefixAndSuffixTextForRename` as part of a `UserPreferences` parameter. */
61566158
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): readonly RenameLocation[] | undefined;
61576159
getSmartSelectionRange(fileName: string, position: number): SelectionRange;
61586160
getDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// === findRenameLocations ===
2+
// === /tests/cases/fourslash/renameNumericalIndex.ts ===
3+
// const foo = { /*RENAME*/<|[|0RENAME|]: true|> };
4+
// foo[/*START PREFIX*/"[|0RENAME|]"/*END SUFFIX*/];
5+
6+
7+
8+
// === findRenameLocations ===
9+
// === /tests/cases/fourslash/renameNumericalIndex.ts ===
10+
// const foo = { <|[|0RENAME|]: true|> };
11+
// foo[/*START PREFIX*/"/*RENAME*/[|0RENAME|]"/*END SUFFIX*/];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// === findRenameLocations ===
2+
// @quotePreference: single
3+
4+
// === /tests/cases/fourslash/renameNumericalIndexSingleQuoted.ts ===
5+
// const foo = { /*RENAME*/<|[|0RENAME|]: true|> };
6+
// foo[/*START PREFIX*/'[|0RENAME|]'/*END SUFFIX*/];
7+
8+
9+
10+
// === findRenameLocations ===
11+
// @quotePreference: single
12+
13+
// === /tests/cases/fourslash/renameNumericalIndexSingleQuoted.ts ===
14+
// const foo = { <|[|0RENAME|]: true|> };
15+
// foo[/*START PREFIX*/'/*RENAME*/[|0RENAME|]'/*END SUFFIX*/];

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ declare namespace FourSlashInterface {
839839
readonly providePrefixAndSuffixTextForRename?: boolean;
840840
};
841841

842-
type RenameOptions = { readonly findInStrings?: boolean, readonly findInComments?: boolean, readonly providePrefixAndSuffixTextForRename?: boolean };
842+
type RenameOptions = { readonly findInStrings?: boolean, readonly findInComments?: boolean, readonly providePrefixAndSuffixTextForRename?: boolean, readonly quotePreference?: "auto" | "double" | "single" };
843843
type RenameLocationOptions = Range | { readonly range: Range, readonly prefixText?: string, readonly suffixText?: string };
844844
type DiagnosticIgnoredInterpolations = { template: string }
845845
type BaselineCommand = {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const foo = { [|0|]: true };
4+
////foo[[|0|]];
5+
6+
verify.baselineRenameAtRangesWithText("0");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const foo = { [|0|]: true };
4+
////foo[[|0|]];
5+
6+
verify.baselineRenameAtRangesWithText("0", { quotePreference: "single" });

0 commit comments

Comments
 (0)