Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 3 additions & 28 deletions packages/language-service/src/locate_symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down
42 changes: 39 additions & 3 deletions packages/language-service/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -247,3 +247,39 @@ export function getPathToNodeAtPosition(nodes: Node[], position: number): HtmlAs
visitAll(visitor, nodes);
return new AstPath<Node>(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);
}
}
}
}
}