From 39e4b1f9e304351f6e9e081a20fb7a7f637589b1 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Mon, 10 Jul 2017 14:16:31 -0700 Subject: [PATCH] Code fix to remove unused import should preserve default import --- src/services/codefixes/fixUnusedIdentifier.ts | 41 +++++++++++-------- tests/cases/fourslash/unusedImports13FS.ts | 12 ++++++ 2 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/unusedImports13FS.ts diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index cbe2ba5b1c..51c8a59627 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -87,9 +87,7 @@ namespace ts.codefix { case SyntaxKind.ImportSpecifier: const namedImports = parent.parent; if (namedImports.elements.length === 1) { - // Only 1 import and it is unused. So the entire declaration should be removed. - const importSpec = getAncestor(identifier, SyntaxKind.ImportDeclaration); - return [deleteNode(importSpec)]; + return deleteNamedImportBinding(namedImports); } else { // delete import specifier @@ -100,7 +98,7 @@ namespace ts.codefix { // or "'import {a, b as ns} from './file'" case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *' const importClause = parent; - if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'| + if (!importClause.namedBindings) { // |import d from './file'| const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration); return [deleteNode(importDecl)]; } @@ -118,25 +116,34 @@ namespace ts.codefix { } case SyntaxKind.NamespaceImport: - const namespaceImport = parent; - if (namespaceImport.name === identifier && !(namespaceImport.parent).name) { - const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration); - return [deleteNode(importDecl)]; - } - else { - const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1, /*includeJsDocComment*/ false); - if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { - const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart); - return [deleteRange({ pos: startPosition, end: namespaceImport.end })]; - } - return [deleteRange(namespaceImport)]; - } + return deleteNamedImportBinding(parent); default: return [deleteDefault()]; } } + function deleteNamedImportBinding(namedBindings: NamedImportBindings): CodeAction[] | undefined { + if ((namedBindings.parent).name) { + // Delete named imports while preserving the default import + // import d|, * as ns| from './file' + // import d|, { a }| from './file' + const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false); + if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { + const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart); + return [deleteRange({ pos: startPosition, end: namedBindings.end })]; + } + return undefined; + } + else { + // Delete the entire import declaration + // |import * as ns from './file'| + // |import { a } from './file'| + const importDecl = getAncestor(namedBindings, SyntaxKind.ImportDeclaration); + return [deleteNode(importDecl)]; + } + } + // token.parent is a variableDeclaration function deleteVariableDeclarationOrPrefixWithUnderscore(identifier: Identifier, varDecl: ts.VariableDeclaration): CodeAction[] | undefined { switch (varDecl.parent.parent.kind) { diff --git a/tests/cases/fourslash/unusedImports13FS.ts b/tests/cases/fourslash/unusedImports13FS.ts new file mode 100644 index 0000000000..4e19f4d7f1 --- /dev/null +++ b/tests/cases/fourslash/unusedImports13FS.ts @@ -0,0 +1,12 @@ +/// + +// @noUnusedLocals: true +// @Filename: file2.ts +//// [| import A, { x } from './a'; |] +//// console.log(A); + +// @Filename: file1.ts +//// export default 10; +//// export var x = 10; + +verify.rangeAfterCodeFix("import A from './a';"); \ No newline at end of file