Skip to content

Commit

Permalink
String literal rename support (#39298)
Browse files Browse the repository at this point in the history
* add basicly support for rename string literal type

* fix merge conflict

* fix some behavior

* Update package-lock.json

* Update package-lock.json

* do not break old behavior if not type checked

* fix cr issue

Co-authored-by: TypeScript Bot <[email protected]>
  • Loading branch information
Kingwl and typescript-bot authored Nov 6, 2020
1 parent ad2e2f8 commit ca7510e
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 7 deletions.
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'
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");

0 comments on commit ca7510e

Please sign in to comment.