Handles different references and renames involved with import export and import type node (#34813)

* Test

* Handle import type node when handling the namespace import and reexport
Fixes #33017

* Handle qualifier of the importTypeNode

* Handle export specifier

* Fix rename prefix when search for rename starts at qualifier in import type ndoe

* Fix rename of qualifier with importType node when invoked without provideSuffixAndPrefix option
This commit is contained in:
Sheetal Nandi
2019-12-11 15:11:27 -08:00
committed by GitHub
parent fc0f67dcfd
commit 1fd1b429f0
6 changed files with 70 additions and 9 deletions

View File

@@ -462,7 +462,7 @@ namespace FourSlashInterface {
this.state.verifyRangesWithSameTextAreRenameLocations(...texts);
}
public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[] }) {
public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[], providePrefixAndSuffixTextForRename?: boolean }) {
this.state.verifyRangesAreRenameLocations(options);
}
@@ -1602,4 +1602,4 @@ namespace FourSlashInterface {
readonly providePrefixAndSuffixTextForRename?: boolean;
};
export type RenameLocationOptions = FourSlash.Range | { readonly range: FourSlash.Range, readonly prefixText?: string, readonly suffixText?: string };
}
}

View File

@@ -380,7 +380,10 @@ namespace ts.FindAllReferences {
return contains(originalSymbol!.declarations, entry.node.parent) ? { prefixText: name + " as " } : emptyOptions;
}
else if (isExportSpecifier(entry.node.parent) && !entry.node.parent.propertyName) {
return originalNode === entry.node ? { prefixText: name + " as " } : { suffixText: " as " + name };
// If the symbol for the node is same as declared node symbol use prefix text
return originalNode === entry.node || checker.getSymbolAtLocation(originalNode) === checker.getSymbolAtLocation(entry.node) ?
{ prefixText: name + " as " } :
{ suffixText: " as " + name };
}
}
@@ -758,7 +761,6 @@ namespace ts.FindAllReferences {
// Compute the meaning from the location and the symbol it references
const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All;
const result: SymbolAndEntries[] = [];
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
@@ -1894,6 +1896,13 @@ namespace ts.FindAllReferences {
return fromRoot(symbol.flags & SymbolFlags.FunctionScopedVariable ? paramProps[1] : paramProps[0]);
}
const exportSpecifier = getDeclarationOfKind<ExportSpecifier>(symbol, SyntaxKind.ExportSpecifier);
const localSymbol = exportSpecifier && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
if (localSymbol) {
const res = cbSymbol(localSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.Node);
if (res) return res;
}
// symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property.
// Don't do this when populating search set for a rename when prefix and suffix text will be provided -- just rename the local.
if (!isForRenamePopulateSearchSymbolSet) {

View File

@@ -176,7 +176,9 @@ namespace ts.FindAllReferences {
const directImports = getDirectImports(moduleSymbol);
if (directImports) {
for (const directImport of directImports) {
addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport));
if (!isImportTypeNode(directImport)) {
addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport));
}
}
}
}
@@ -221,8 +223,9 @@ namespace ts.FindAllReferences {
if (decl.kind === SyntaxKind.ImportType) {
if (decl.qualifier) {
if (isIdentifier(decl.qualifier) && decl.qualifier.escapedText === symbolName(exportSymbol)) {
singleReferences.push(decl.qualifier);
const firstIdentifier = getFirstIdentifier(decl.qualifier);
if (firstIdentifier.escapedText === symbolName(exportSymbol)) {
singleReferences.push(firstIdentifier);
}
}
else if (exportKind === ExportKind.ExportEquals) {

View File

@@ -0,0 +1,49 @@
/// <reference path="fourslash.ts" />
// @Filename: /foo/types/types.ts
////[|export type [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 0 |}Full|] = { prop: string; };|]
// @Filename: /foo/types/index.ts
////[|import * as [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 2 |}foo|] from './types';|]
////[|export { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 4 |}foo|] };|]
// @Filename: /app.ts
////[|import { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 6 |}foo|] } from './foo/types';|]
////export type fullType = [|foo|].[|Full|];
////type namespaceImport = typeof import('./foo/types');
////type fullType2 = import('./foo/types').[|foo|].[|Full|];
verify.noErrors();
const [full0Def, full0, foo0Def, foo0, foo1Def, foo1, foo2Def, foo2, foo3, full1, foo4, full2] = test.ranges();
const fullRanges = [full0, full1, full2];
const full = {
definition: "type Full = {\n prop: string;\n}",
ranges: fullRanges
};
verify.referenceGroups(fullRanges, [full]);
verify.renameLocations(fullRanges, fullRanges);
const fooTypesRanges = [foo0, foo1];
const fooTypes = {
definition: "import foo",
ranges: fooTypesRanges
};
const fooAppRanges = [foo2, foo3];
const fooApp = {
definition: "import foo",
ranges: fooAppRanges
};
const exportFooRanges = [foo4];
const fooExport = {
definition: "export foo",
ranges: exportFooRanges
};
verify.referenceGroups(fooTypesRanges, [fooTypes, fooExport, fooApp]);
verify.referenceGroups(fooAppRanges, [fooApp, fooTypes, fooExport]);
verify.referenceGroups(exportFooRanges, [fooTypes, fooExport, fooApp]);
verify.renameLocations([foo0], [foo0, { range: foo1, suffixText: " as foo" }]);
verify.renameLocations([foo1, foo4], [foo2, foo3, foo4, { range: foo1, prefixText: "foo as " }]);
verify.renameLocations(fooAppRanges, [{ range: foo2, prefixText: "foo as " }, foo3]);
verify.rangesAreRenameLocations({ ranges: [foo2, foo3, foo4, foo0, foo1], providePrefixAndSuffixTextForRename: false });

View File

@@ -297,7 +297,7 @@ declare namespace FourSlashInterface {
singleReferenceGroup(definition: ReferencesDefinition, ranges?: Range[] | string): void;
rangesAreOccurrences(isWriteAccess?: boolean, ranges?: Range[]): void;
rangesWithSameTextAreRenameLocations(...texts: string[]): void;
rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] });
rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[], providePrefixAndSuffixTextForRename?: boolean });
findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void;
noSignatureHelp(...markers: (string | Marker)[]): void;
noSignatureHelpForTriggerReason(triggerReason: SignatureHelpTriggerReason, ...markers: (string | Marker)[]): void

View File

@@ -25,5 +25,5 @@ const classes = { definition: "class C", ranges: [r0] };
const bs = { definition: "(alias) class C\nexport C", ranges: [r1] };
const imports = { definition: "(alias) class C\nimport C", ranges: importRanges };
verify.referenceGroups(r0, [classes, bs, imports]);
verify.referenceGroups(r1, [bs, imports, classes]);
verify.referenceGroups(r1, [classes, bs, imports]);
verify.referenceGroups(importRanges, [imports, bs, classes]);