Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String literal rename support #39298

Merged
merged 13 commits into from
Nov 6, 2020
22 changes: 17 additions & 5 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace ts.FindAllReferences {
| { readonly type: DefinitionKind.Label; readonly node: Identifier }
| { readonly type: DefinitionKind.Keyword; readonly node: Node }
| { readonly type: DefinitionKind.This; readonly node: Node }
| { readonly type: DefinitionKind.String; readonly node: StringLiteral };
| { readonly type: DefinitionKind.String; readonly node: StringLiteralLike };

export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal }
export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal;
Expand Down Expand Up @@ -621,7 +621,7 @@ namespace ts.FindAllReferences {
// Could not find a symbol e.g. unknown identifier
if (!symbol) {
// String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial.
return !options.implementations && isStringLiteral(node) ? getReferencesForStringLiteral(node, sourceFiles, cancellationToken) : undefined;
return !options.implementations && isStringLiteralLike(node) ? getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken) : undefined;
}

if (symbol.escapedName === InternalSymbolName.ExportEquals) {
Expand Down Expand Up @@ -1889,11 +1889,23 @@ namespace ts.FindAllReferences {
}];
}

function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] {
function getReferencesForStringLiteral(node: StringLiteralLike, sourceFiles: readonly SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): SymbolAndEntries[] {
const type = getContextualTypeOrAncestorTypeNodeType(node, checker);
const references = flatMap(sourceFiles, sourceFile => {
cancellationToken.throwIfCancellationRequested();
return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, node.text), ref =>
isStringLiteral(ref) && ref.text === node.text ? nodeEntry(ref, EntryKind.StringLiteral) : undefined);
return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, node.text), ref => {
if (isStringLiteralLike(ref) && ref.text === node.text) {
if (type) {
const refType = getContextualTypeOrAncestorTypeNodeType(ref, checker);
if (type !== checker.getStringType() && type === refType) {
return nodeEntry(ref, EntryKind.StringLiteral);
}
}
else {
return nodeEntry(ref, EntryKind.StringLiteral);
}
}
});
});

return [{
Expand Down
10 changes: 9 additions & 1 deletion src/services/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ namespace ts.Rename {
function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean, options?: RenameInfoOptions): RenameInfo | undefined {
const symbol = typeChecker.getSymbolAtLocation(node);
if (!symbol) {
if (isLabelName(node)) {
if (isStringLiteralLike(node)) {
const type = getContextualTypeOrAncestorTypeNodeType(node, typeChecker);
if (type && ((type.flags & TypeFlags.StringLiteral) || (
(type.flags & TypeFlags.Union) && every((type as UnionType).types, type => !!(type.flags & TypeFlags.StringLiteral))
))) {
return getRenameInfoSuccess(node.text, node.text, ScriptElementKind.string, "", node, sourceFile);
}
}
else if (isLabelName(node)) {
const name = getTextOfNode(node);
return getRenameInfoSuccess(name, name, ScriptElementKind.label, ScriptElementKindModifier.none, node, sourceFile);
}
Expand Down
21 changes: 21 additions & 0 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,27 @@ namespace ts {
}
}

function getAncestorTypeNode(node: Node) {
let lastTypeNode: TypeNode | undefined;
findAncestor(node, a => {
if (isTypeNode(a)) {
lastTypeNode = a;
}
return !isQualifiedName(a.parent) && !isTypeNode(a.parent) && !isTypeElement(a.parent);
});
return lastTypeNode;
}

export function getContextualTypeOrAncestorTypeNodeType(node: Expression, checker: TypeChecker) {
const contextualType = checker.getContextualType(node);
if (contextualType) {
return contextualType;
}

const ancestorTypeNode = getAncestorTypeNode(node);
return ancestorTypeNode && checker.getTypeAtLocation(ancestorTypeNode);
}

function getAdjustedLocationForDeclaration(node: Node, forRename: boolean) {
if (!forRename) {
switch (node.kind) {
Expand Down
11 changes: 11 additions & 0 deletions tests/cases/fourslash/renameStringLiteralOk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path='fourslash.ts'/>

//// interface Foo {
//// f: '[|foo|]' | 'bar'
//// }
//// const d: 'foo' = 'foo'
//// declare const f: Foo
//// f.f = '[|foo|]'
//// f.f = `[|foo|]`

verify.rangesWithSameTextAreRenameLocations("foo");
18 changes: 18 additions & 0 deletions tests/cases/fourslash/renameStringLiteralOk1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>

//// declare function f(): '[|foo|]' | 'bar'
//// class Foo {
//// f = f()
//// }
//// const d: 'foo' = 'foo'
//// declare const ff: Foo
//// ff.f = '[|foo|]'

verify.rangesWithSameTextAreRenameLocations("foo");

interface Foo {
f: 'foo' | 'bar'
}
const d: 'foo' = 'foo'
declare const f: Foo
f.f = 'foo'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f.f = 'foo'
f.f = 'foo'

add new line

2 changes: 1 addition & 1 deletion tests/cases/fourslash/renameStringLiteralTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
////
////animate({ deltaX: 100, deltaY: 100, easing: "[|ease-in-out|]" });

goTo.eachRange(() => { verify.renameInfoFailed(); });
verify.rangesWithSameTextAreRenameLocations("ease-in-out");