mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-23 10:29:01 -06:00
String literal rename support (#39298)
* 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 <typescriptbot@microsoft.com>
This commit is contained in:
parent
ad2e2f80c1
commit
ca7510ea05
@ -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;
|
||||
@ -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) {
|
||||
@ -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 [{
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
11
tests/cases/fourslash/renameStringLiteralOk.ts
Normal file
11
tests/cases/fourslash/renameStringLiteralOk.ts
Normal file
@ -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
tests/cases/fourslash/renameStringLiteralOk1.ts
Normal file
18
tests/cases/fourslash/renameStringLiteralOk1.ts
Normal file
@ -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'
|
||||
@ -11,4 +11,4 @@
|
||||
////
|
||||
////animate({ deltaX: 100, deltaY: 100, easing: "[|ease-in-out|]" });
|
||||
|
||||
goTo.eachRange(() => { verify.renameInfoFailed(); });
|
||||
verify.rangesWithSameTextAreRenameLocations("ease-in-out");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user