152 lines
5.1 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler';
import {AstPath} from './ast_path';
import {inSpan, isNarrower, spanOf} from './utils';
export class TemplateAstPath extends AstPath<TemplateAst> {
constructor(ast: TemplateAst[], public position: number, allowWidening: boolean = false) {
super(buildTemplatePath(ast, position, allowWidening));
}
}
function buildTemplatePath(
ast: TemplateAst[], position: number, allowWidening: boolean = false): TemplateAst[] {
const visitor = new TemplateAstPathBuilder(position, allowWidening);
templateVisitAll(visitor, ast);
return visitor.getPath();
}
export class NullTemplateVisitor implements TemplateAstVisitor {
visitNgContent(ast: NgContentAst): void {}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst): void {}
visitElement(ast: ElementAst): void {}
visitReference(ast: ReferenceAst): void {}
visitVariable(ast: VariableAst): void {}
visitEvent(ast: BoundEventAst): void {}
visitElementProperty(ast: BoundElementPropertyAst): void {}
visitAttr(ast: AttrAst): void {}
visitBoundText(ast: BoundTextAst): void {}
visitText(ast: TextAst): void {}
visitDirective(ast: DirectiveAst): void {}
visitDirectiveProperty(ast: BoundDirectivePropertyAst): void {}
}
export class TemplateAstChildVisitor implements TemplateAstVisitor {
constructor(private visitor?: TemplateAstVisitor) {}
// Nodes with children
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
return this.visitChildren(context, visit => {
visit(ast.attrs);
visit(ast.references);
visit(ast.variables);
visit(ast.directives);
visit(ast.providers);
visit(ast.children);
});
}
visitElement(ast: ElementAst, context: any): any {
return this.visitChildren(context, visit => {
visit(ast.attrs);
visit(ast.inputs);
visit(ast.outputs);
visit(ast.references);
visit(ast.directives);
visit(ast.providers);
visit(ast.children);
});
}
visitDirective(ast: DirectiveAst, context: any): any {
return this.visitChildren(context, visit => {
visit(ast.inputs);
visit(ast.hostProperties);
visit(ast.hostEvents);
});
}
// Terminal nodes
visitNgContent(ast: NgContentAst, context: any): any {}
visitReference(ast: ReferenceAst, context: any): any {}
visitVariable(ast: VariableAst, context: any): any {}
visitEvent(ast: BoundEventAst, context: any): any {}
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {}
visitAttr(ast: AttrAst, context: any): any {}
visitBoundText(ast: BoundTextAst, context: any): any {}
visitText(ast: TextAst, context: any): any {}
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
protected visitChildren<T extends TemplateAst>(
context: any,
cb: (visit: (<V extends TemplateAst>(children: V[]|undefined) => void)) => void) {
const visitor = this.visitor || this;
let results: any[][] = [];
function visit<T extends TemplateAst>(children: T[] | undefined) {
if (children && children.length) results.push(templateVisitAll(visitor, children, context));
}
cb(visit);
return [].concat.apply([], results);
}
}
class TemplateAstPathBuilder extends TemplateAstChildVisitor {
private path: TemplateAst[] = [];
constructor(private position: number, private allowWidening: boolean) { super(); }
visit(ast: TemplateAst, context: any): any {
let span = spanOf(ast) !;
if (inSpan(this.position, span)) {
const len = this.path.length;
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]) !)) {
this.path.push(ast);
}
} else {
// Returning a value here will result in the children being skipped.
return true;
}
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
return this.visitChildren(context, visit => {
// Ignore reference, variable and providers
visit(ast.attrs);
visit(ast.directives);
visit(ast.children);
});
}
visitElement(ast: ElementAst, context: any): any {
return this.visitChildren(context, visit => {
// Ingnore providers
visit(ast.attrs);
visit(ast.inputs);
visit(ast.outputs);
visit(ast.references);
visit(ast.directives);
visit(ast.children);
});
}
visitDirective(ast: DirectiveAst, context: any): any {
// Ignore the host properties of a directive
const result = this.visitChildren(context, visit => { visit(ast.inputs); });
// We never care about the diretive itself, just its inputs.
if (this.path[this.path.length - 1] == ast) {
this.path.pop();
}
return result;
}
getPath() { return this.path; }
}