From 58b147e4c449d08398417ebff9b34134ee9991d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Thu, 15 Mar 2018 10:00:21 +0800 Subject: [PATCH] add spelling suggestion support for module --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 1 + src/services/codefixes/fixSpelling.ts | 25 ++++++++++++++++--- .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + tests/cases/fourslash/codeFixSpelling5.ts | 10 ++++++++ 6 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSpelling5.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4e98e84e92f54..1a3bbab940fd4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -294,6 +294,7 @@ namespace ts { getAllPossiblePropertiesOfTypes, getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type), getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), + getSuggestionForNonexistentModule: (node, target) => getSuggestionForNonexistentModule(node, target), getBaseConstraintOfType, getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined, resolveName(name, location, meaning, excludeGlobals) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 235f5cab674ad..68f8e546571f7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2890,6 +2890,7 @@ namespace ts { getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 729f14e9ef946..e3f6a22c3ea49 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -4,12 +4,13 @@ namespace ts.codefix { const errorCodes = [ Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code, Diagnostics.Cannot_find_name_0_Did_you_mean_1.code, + Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2.code, ]; registerCodeFix({ errorCodes, getCodeActions(context) { const { sourceFile } = context; - const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); + const info = getInfo(sourceFile, context.span.start, context); if (!info) return undefined; const { node, suggestion } = info; const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion)); @@ -18,16 +19,17 @@ namespace ts.codefix { }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { - const info = getInfo(diag.file!, diag.start!, context.program.getTypeChecker()); + const info = getInfo(diag.file!, diag.start!, context); if (info) doChange(changes, context.sourceFile, info.node, info.suggestion); }), }); - function getInfo(sourceFile: SourceFile, pos: number, checker: TypeChecker): { node: Node, suggestion: string } | undefined { + function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBase): { node: Node, suggestion: string } | undefined { // This is the identifier of the misspelled word. eg: // this.speling = 1; // ^^^^^^^ const node = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); // TODO: GH#15852 + const checker = context.program.getTypeChecker(); let suggestion: string; if (isPropertyAccessExpression(node.parent) && node.parent.name === node) { @@ -35,6 +37,14 @@ namespace ts.codefix { const containingType = checker.getTypeAtLocation(node.parent.expression); suggestion = checker.getSuggestionForNonexistentProperty(node as Identifier, containingType); } + else if (isImportSpecifier(node.parent) && node.parent.name === node) { + Debug.assert(node.kind === SyntaxKind.Identifier); + const importDeclaration = findAncestor(node, isImportDeclaration); + const resolvedSourceFile = getResolvedSourceFileFromImportDeclaration(sourceFile, context, importDeclaration); + if (resolvedSourceFile && resolvedSourceFile.symbol) { + suggestion = checker.getSuggestionForNonexistentModule(node as Identifier, resolvedSourceFile.symbol); + } + } else { const meaning = getMeaningFromLocation(node); const name = getTextOfNode(node); @@ -62,4 +72,13 @@ namespace ts.codefix { } return flags; } + + function getResolvedSourceFileFromImportDeclaration (sourceFile: SourceFile, context: CodeFixContextBase, importDeclaration: ImportDeclaration): SourceFile | undefined { + if (!importDeclaration || !isStringLiteralLike(importDeclaration.moduleSpecifier)) return undefined; + + const resolvedModule = getResolvedModule(sourceFile, importDeclaration.moduleSpecifier.text); + if (!resolvedModule) return undefined; + + return context.program.getSourceFile(resolvedModule.resolvedFileName); + } } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4e3e01692f20e..2137e4a0ee650 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1820,6 +1820,7 @@ declare namespace ts { getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1031837234398..23bb01031d408 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1820,6 +1820,7 @@ declare namespace ts { getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined; getBaseConstraintOfType(type: Type): Type | undefined; getDefaultFromTypeParameter(type: Type): Type | undefined; } diff --git a/tests/cases/fourslash/codeFixSpelling5.ts b/tests/cases/fourslash/codeFixSpelling5.ts new file mode 100644 index 0000000000000..2ab34a44499f1 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpelling5.ts @@ -0,0 +1,10 @@ +/// + +// @Filename: f1.ts +////export const fooooooooo = 1; + +// @Filename: f2.ts +////import {[|fooooooooa|]} from "./f1"; + +goTo.file("f2.ts") +verify.rangeAfterCodeFix(`fooooooooo`); \ No newline at end of file