From 90b303faa35e45f580494985ed940642592224f4 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 27 Jan 2020 13:03:28 -0800 Subject: [PATCH] refactor(language-service): make findOutputBinding a utility function (#34997) This commit makes `findOutputBinding` a utility function for the language service, which will be used by the `expression_diagnostics` module in #34570. Keeping the function in `locate_symbol` results in a circular dependency between `expression_diagnostics` and `locate_symbol`. PR Close #34997 --- .../language-service/src/locate_symbol.ts | 31 ++------------ packages/language-service/src/utils.ts | 42 +++++++++++++++++-- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/language-service/src/locate_symbol.ts b/packages/language-service/src/locate_symbol.ts index f91a315166..1ff01cb19d 100644 --- a/packages/language-service/src/locate_symbol.ts +++ b/packages/language-service/src/locate_symbol.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, Attribute, BoundDirectivePropertyAst, BoundEventAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference} from '@angular/compiler'; +import {Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference} from '@angular/compiler'; import * as tss from 'typescript/lib/tsserverlibrary'; + import {AstResult} from './common'; import {getExpressionScope} from './expression_diagnostics'; import {getExpressionSymbol} from './expressions'; import {Definition, DirectiveKind, Span, Symbol} from './types'; -import {diagnosticInfoFromTemplateInfo, findTemplateAstAt, getPathToNodeAtPosition, inSpan, isNarrower, offsetSpan, spanOf} from './utils'; +import {diagnosticInfoFromTemplateInfo, findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils'; export interface SymbolInfo { symbol: Symbol; @@ -286,32 +287,6 @@ function findInputBinding(info: AstResult, name: string, directiveAst: Directive } } -function findOutputBinding(info: AstResult, path: TemplateAstPath, binding: BoundEventAst): Symbol| - undefined { - const element = path.first(ElementAst); - if (element) { - for (const directive of element.directives) { - const invertedOutputs = invertMap(directive.directive.outputs); - const fieldName = invertedOutputs[binding.name]; - if (fieldName) { - const classSymbol = info.template.query.getTypeSymbol(directive.directive.type.reference); - if (classSymbol) { - return classSymbol.members().get(fieldName); - } - } - } - } -} - -function invertMap(obj: {[name: string]: string}): {[name: string]: string} { - const result: {[name: string]: string} = {}; - for (const name of Object.keys(obj)) { - const v = obj[name]; - result[v] = name; - } - return result; -} - /** * Wrap a symbol and change its kind to component. */ diff --git a/packages/language-service/src/utils.ts b/packages/language-service/src/utils.ts index 59d1f0fad4..6d89d70d56 100644 --- a/packages/language-service/src/utils.ts +++ b/packages/language-service/src/utils.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AstPath, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, identifierName, templateVisitAll, visitAll} from '@angular/compiler'; +import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, identifierName, templateVisitAll, visitAll} from '@angular/compiler'; import * as ts from 'typescript'; import {AstResult, SelectorInfo} from './common'; import {DiagnosticTemplateInfo} from './expression_diagnostics'; -import {Span} from './types'; +import {Span, Symbol} from './types'; export interface SpanHolder { sourceSpan: ParseSourceSpan; @@ -102,7 +102,7 @@ export function diagnosticInfoFromTemplateInfo(info: AstResult): DiagnosticTempl export function findTemplateAstAt(ast: TemplateAst[], position: number): TemplateAstPath { const path: TemplateAst[] = []; const visitor = new class extends RecursiveTemplateAstVisitor { - visit(ast: TemplateAst, context: any): any { + visit(ast: TemplateAst): any { let span = spanOf(ast); if (inSpan(position, span)) { const len = path.length; @@ -247,3 +247,39 @@ export function getPathToNodeAtPosition(nodes: Node[], position: number): HtmlAs visitAll(visitor, nodes); return new AstPath(path, position); } + + +/** + * Inverts an object's key-value pairs. + */ +export function invertMap(obj: {[name: string]: string}): {[name: string]: string} { + const result: {[name: string]: string} = {}; + for (const name of Object.keys(obj)) { + const v = obj[name]; + result[v] = name; + } + return result; +} + + +/** + * Finds the directive member providing a template output binding, if one exists. + * @param info aggregate template AST information + * @param path narrowing + */ +export function findOutputBinding( + info: AstResult, path: TemplateAstPath, binding: BoundEventAst): Symbol|undefined { + const element = path.first(ElementAst); + if (element) { + for (const directive of element.directives) { + const invertedOutputs = invertMap(directive.directive.outputs); + const fieldName = invertedOutputs[binding.name]; + if (fieldName) { + const classSymbol = info.template.query.getTypeSymbol(directive.directive.type.reference); + if (classSymbol) { + return classSymbol.members().get(fieldName); + } + } + } + } +}