From 82db4a41f6f160fe4becbc5847c3d76a4cacc1d1 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 19 Nov 2020 17:10:46 +0800 Subject: [PATCH] Allow qualified name for convert namespace import --- src/services/refactors/convertImport.ts | 25 ++++++++++++------- ...ConvertImport_namespaceToNamedWithTypes.ts | 16 ++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/cases/fourslash/refactorConvertImport_namespaceToNamedWithTypes.ts diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 1ad43126d5d20..5dec7fcf7368f 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -73,34 +73,33 @@ namespace ts.refactor { function doChangeNamespaceToNamed(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamespaceImport, allowSyntheticDefaultImports: boolean): void { let usedAsNamespaceOrDefault = false; - const nodesToReplace: PropertyAccessExpression[] = []; + const nodesToReplace: (PropertyAccessExpression | QualifiedName)[] = []; const conflictingNames = new Map(); FindAllReferences.Core.eachSymbolReferenceInFile(toConvert.name, checker, sourceFile, id => { - if (!isPropertyAccessExpression(id.parent)) { + if (!isPropertyAccessOrQualifiedName(id.parent)) { usedAsNamespaceOrDefault = true; } else { - const parent = cast(id.parent, isPropertyAccessExpression); - const exportName = parent.name.text; + const exportName = getRightOfPropertyAccessOrQualifiedName(id.parent).text; if (checker.resolveName(exportName, id, SymbolFlags.All, /*excludeGlobals*/ true)) { conflictingNames.set(exportName, true); } - Debug.assert(parent.expression === id, "Parent expression should match id"); - nodesToReplace.push(parent); + Debug.assert(getLeftOfPropertyAccessOrQualifiedName(id.parent) === id, "Parent expression should match id"); + nodesToReplace.push(id.parent); } }); // We may need to change `mod.x` to `_x` to avoid a name conflict. const exportNameToImportName = new Map(); - for (const propertyAccess of nodesToReplace) { - const exportName = propertyAccess.name.text; + for (const propertyAccessOrQualifiedName of nodesToReplace) { + const exportName = getRightOfPropertyAccessOrQualifiedName(propertyAccessOrQualifiedName).text; let importName = exportNameToImportName.get(exportName); if (importName === undefined) { exportNameToImportName.set(exportName, importName = conflictingNames.has(exportName) ? getUniqueName(exportName, sourceFile) : exportName); } - changes.replaceNode(sourceFile, propertyAccess, factory.createIdentifier(importName)); + changes.replaceNode(sourceFile, propertyAccessOrQualifiedName, factory.createIdentifier(importName)); } const importSpecifiers: ImportSpecifier[] = []; @@ -118,6 +117,14 @@ namespace ts.refactor { } } + function getRightOfPropertyAccessOrQualifiedName(propertyAccessOrQualifiedName: PropertyAccessExpression | QualifiedName) { + return isPropertyAccessExpression(propertyAccessOrQualifiedName) ? propertyAccessOrQualifiedName.name : propertyAccessOrQualifiedName.right; + } + + function getLeftOfPropertyAccessOrQualifiedName(propertyAccessOrQualifiedName: PropertyAccessExpression | QualifiedName) { + return isPropertyAccessExpression(propertyAccessOrQualifiedName) ? propertyAccessOrQualifiedName.expression : propertyAccessOrQualifiedName.left; + } + function doChangeNamedToNamespace(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamedImports): void { const importDecl = toConvert.parent.parent; const { moduleSpecifier } = importDecl; diff --git a/tests/cases/fourslash/refactorConvertImport_namespaceToNamedWithTypes.ts b/tests/cases/fourslash/refactorConvertImport_namespaceToNamedWithTypes.ts new file mode 100644 index 0000000000000..b0fbcfe411c17 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namespaceToNamedWithTypes.ts @@ -0,0 +1,16 @@ +/// + +/////*a*/import * as m from "m";/*b*/ +////declare const a: m.aa; +////declare const b: m.b; + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert namespace import to named imports", + actionDescription: "Convert namespace import to named imports", + newContent: +`import { aa, b as b_1 } from "m"; +declare const a: aa; +declare const b: b_1;`, +});