feat(html_parser): change HtmlElementAst to store both the start and the end positions
This commit is contained in:
parent
a1880c3576
commit
17c8ec8a5d
@ -19,7 +19,8 @@ export class HtmlAttrAst implements HtmlAst {
|
|||||||
|
|
||||||
export class HtmlElementAst implements HtmlAst {
|
export class HtmlElementAst implements HtmlAst {
|
||||||
constructor(public name: string, public attrs: HtmlAttrAst[], public children: HtmlAst[],
|
constructor(public name: string, public attrs: HtmlAttrAst[], public children: HtmlAst[],
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan,
|
||||||
|
public endSourceSpan: ParseSourceSpan) {}
|
||||||
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
|
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,11 +154,12 @@ class TreeBuilder {
|
|||||||
selfClosing = false;
|
selfClosing = false;
|
||||||
}
|
}
|
||||||
var end = this.peek.sourceSpan.start;
|
var end = this.peek.sourceSpan.start;
|
||||||
var el = new HtmlElementAst(fullName, attrs, [],
|
let span = new ParseSourceSpan(startTagToken.sourceSpan.start, end);
|
||||||
new ParseSourceSpan(startTagToken.sourceSpan.start, end));
|
var el = new HtmlElementAst(fullName, attrs, [], span, span, null);
|
||||||
this._pushElement(el);
|
this._pushElement(el);
|
||||||
if (selfClosing) {
|
if (selfClosing) {
|
||||||
this._popElement(fullName);
|
this._popElement(fullName);
|
||||||
|
el.endSourceSpan = span;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +174,8 @@ class TreeBuilder {
|
|||||||
var tagDef = getHtmlTagDefinition(el.name);
|
var tagDef = getHtmlTagDefinition(el.name);
|
||||||
var parentEl = this._getParentElement();
|
var parentEl = this._getParentElement();
|
||||||
if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) {
|
if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) {
|
||||||
var newParent = new HtmlElementAst(tagDef.parentToAdd, [], [el], el.sourceSpan);
|
var newParent = new HtmlElementAst(tagDef.parentToAdd, [], [el], el.sourceSpan,
|
||||||
|
el.startSourceSpan, el.endSourceSpan);
|
||||||
this._addToParent(newParent);
|
this._addToParent(newParent);
|
||||||
this.elementStack.push(newParent);
|
this.elementStack.push(newParent);
|
||||||
this.elementStack.push(el);
|
this.elementStack.push(el);
|
||||||
@ -187,6 +189,8 @@ class TreeBuilder {
|
|||||||
var fullName =
|
var fullName =
|
||||||
getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
|
getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
|
||||||
|
|
||||||
|
this._getParentElement().endSourceSpan = endTagToken.sourceSpan;
|
||||||
|
|
||||||
if (getHtmlTagDefinition(fullName).isVoid) {
|
if (getHtmlTagDefinition(fullName).isVoid) {
|
||||||
this.errors.push(
|
this.errors.push(
|
||||||
HtmlTreeError.create(fullName, endTagToken.sourceSpan,
|
HtmlTreeError.create(fullName, endTagToken.sourceSpan,
|
||||||
|
@ -50,7 +50,8 @@ export class LegacyHtmlAstTransformer implements HtmlAstVisitor {
|
|||||||
this.visitingTemplateEl = ast.name.toLowerCase() == 'template';
|
this.visitingTemplateEl = ast.name.toLowerCase() == 'template';
|
||||||
let attrs = ast.attrs.map(attr => attr.visit(this, null));
|
let attrs = ast.attrs.map(attr => attr.visit(this, null));
|
||||||
let children = ast.children.map(child => child.visit(this, null));
|
let children = ast.children.map(child => child.visit(this, null));
|
||||||
return new HtmlElementAst(ast.name, attrs, children, ast.sourceSpan);
|
return new HtmlElementAst(ast.name, attrs, children, ast.sourceSpan, ast.startSourceSpan,
|
||||||
|
ast.endSourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAttr(originalAst: HtmlAttrAst, context: any): HtmlAttrAst {
|
visitAttr(originalAst: HtmlAttrAst, context: any): HtmlAttrAst {
|
||||||
|
78
modules/angular2/test/compiler/html_ast_spec_utils.ts
Normal file
78
modules/angular2/test/compiler/html_ast_spec_utils.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import {HtmlParser, HtmlParseTreeResult, HtmlTreeError} from 'angular2/src/compiler/html_parser';
|
||||||
|
import {
|
||||||
|
HtmlAst,
|
||||||
|
HtmlAstVisitor,
|
||||||
|
HtmlElementAst,
|
||||||
|
HtmlAttrAst,
|
||||||
|
HtmlTextAst,
|
||||||
|
HtmlCommentAst,
|
||||||
|
htmlVisitAll
|
||||||
|
} from 'angular2/src/compiler/html_ast';
|
||||||
|
import {ParseError, ParseLocation} from 'angular2/src/compiler/parse_util';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
|
||||||
|
export function humanizeDom(parseResult: HtmlParseTreeResult): any[] {
|
||||||
|
if (parseResult.errors.length > 0) {
|
||||||
|
var errorString = parseResult.errors.join('\n');
|
||||||
|
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var humanizer = new _Humanizer(false);
|
||||||
|
htmlVisitAll(humanizer, parseResult.rootNodes);
|
||||||
|
return humanizer.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function humanizeDomSourceSpans(parseResult: HtmlParseTreeResult): any[] {
|
||||||
|
if (parseResult.errors.length > 0) {
|
||||||
|
var errorString = parseResult.errors.join('\n');
|
||||||
|
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var humanizer = new _Humanizer(true);
|
||||||
|
htmlVisitAll(humanizer, parseResult.rootNodes);
|
||||||
|
return humanizer.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function humanizeLineColumn(location: ParseLocation): string {
|
||||||
|
return `${location.line}:${location.col}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Humanizer implements HtmlAstVisitor {
|
||||||
|
result: any[] = [];
|
||||||
|
elDepth: number = 0;
|
||||||
|
|
||||||
|
constructor(private includeSourceSpan: boolean){};
|
||||||
|
|
||||||
|
visitElement(ast: HtmlElementAst, context: any): any {
|
||||||
|
var res = this._appendContext(ast, [HtmlElementAst, ast.name, this.elDepth++]);
|
||||||
|
this.result.push(res);
|
||||||
|
htmlVisitAll(this, ast.attrs);
|
||||||
|
htmlVisitAll(this, ast.children);
|
||||||
|
this.elDepth--;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAttr(ast: HtmlAttrAst, context: any): any {
|
||||||
|
var res = this._appendContext(ast, [HtmlAttrAst, ast.name, ast.value]);
|
||||||
|
this.result.push(res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitText(ast: HtmlTextAst, context: any): any {
|
||||||
|
var res = this._appendContext(ast, [HtmlTextAst, ast.value, this.elDepth]);
|
||||||
|
this.result.push(res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitComment(ast: HtmlCommentAst, context: any): any {
|
||||||
|
var res = this._appendContext(ast, [HtmlCommentAst, ast.value, this.elDepth]);
|
||||||
|
this.result.push(res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _appendContext(ast: HtmlAst, input: any[]): any[] {
|
||||||
|
if (!this.includeSourceSpan) return input;
|
||||||
|
input.push(ast.sourceSpan.toString());
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,8 @@ import {
|
|||||||
HtmlCommentAst,
|
HtmlCommentAst,
|
||||||
htmlVisitAll
|
htmlVisitAll
|
||||||
} from 'angular2/src/compiler/html_ast';
|
} from 'angular2/src/compiler/html_ast';
|
||||||
import {ParseError, ParseLocation, ParseSourceSpan} from 'angular2/src/compiler/parse_util';
|
import {ParseError, ParseLocation} from 'angular2/src/compiler/parse_util';
|
||||||
|
import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './html_ast_spec_utils';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('HtmlParser', () => {
|
describe('HtmlParser', () => {
|
||||||
@ -51,6 +50,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('elements', () => {
|
describe('elements', () => {
|
||||||
it('should parse root level elements', () => {
|
it('should parse root level elements', () => {
|
||||||
expect(humanizeDom(parser.parse('<div></div>', 'TestComp')))
|
expect(humanizeDom(parser.parse('<div></div>', 'TestComp')))
|
||||||
@ -253,6 +253,16 @@ export function main() {
|
|||||||
[HtmlTextAst, '\na\n', 1, '\na\n'],
|
[HtmlTextAst, '\na\n', 1, '\na\n'],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set the start and end source spans', () => {
|
||||||
|
let node = <HtmlElementAst>parser.parse('<div>a</div>', 'TestComp').rootNodes[0];
|
||||||
|
|
||||||
|
expect(node.startSourceSpan.start.offset).toEqual(0);
|
||||||
|
expect(node.startSourceSpan.end.offset).toEqual(5);
|
||||||
|
|
||||||
|
expect(node.endSourceSpan.start.offset).toEqual(6);
|
||||||
|
expect(node.endSourceSpan.end.offset).toEqual(12);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
@ -299,33 +309,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanizeDom(parseResult: HtmlParseTreeResult): any[] {
|
export function humanizeErrors(errors: ParseError[]): any[] {
|
||||||
if (parseResult.errors.length > 0) {
|
|
||||||
var errorString = parseResult.errors.join('\n');
|
|
||||||
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
var humanizer = new Humanizer(false);
|
|
||||||
htmlVisitAll(humanizer, parseResult.rootNodes);
|
|
||||||
return humanizer.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function humanizeDomSourceSpans(parseResult: HtmlParseTreeResult): any[] {
|
|
||||||
if (parseResult.errors.length > 0) {
|
|
||||||
var errorString = parseResult.errors.join('\n');
|
|
||||||
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
var humanizer = new Humanizer(true);
|
|
||||||
htmlVisitAll(humanizer, parseResult.rootNodes);
|
|
||||||
return humanizer.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function humanizeLineColumn(location: ParseLocation): string {
|
|
||||||
return `${location.line}:${location.col}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function humanizeErrors(errors: ParseError[]): any[] {
|
|
||||||
return errors.map(error => {
|
return errors.map(error => {
|
||||||
if (error instanceof HtmlTreeError) {
|
if (error instanceof HtmlTreeError) {
|
||||||
// Parser errors
|
// Parser errors
|
||||||
@ -334,44 +318,4 @@ function humanizeErrors(errors: ParseError[]): any[] {
|
|||||||
// Tokenizer errors
|
// Tokenizer errors
|
||||||
return [(<any>error).tokenType, error.msg, humanizeLineColumn(error.span.start)];
|
return [(<any>error).tokenType, error.msg, humanizeLineColumn(error.span.start)];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class Humanizer implements HtmlAstVisitor {
|
|
||||||
result: any[] = [];
|
|
||||||
elDepth: number = 0;
|
|
||||||
|
|
||||||
constructor(private includeSourceSpan: boolean){};
|
|
||||||
|
|
||||||
visitElement(ast: HtmlElementAst, context: any): any {
|
|
||||||
var res = this._appendContext(ast, [HtmlElementAst, ast.name, this.elDepth++]);
|
|
||||||
this.result.push(res);
|
|
||||||
htmlVisitAll(this, ast.attrs);
|
|
||||||
htmlVisitAll(this, ast.children);
|
|
||||||
this.elDepth--;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitAttr(ast: HtmlAttrAst, context: any): any {
|
|
||||||
var res = this._appendContext(ast, [HtmlAttrAst, ast.name, ast.value]);
|
|
||||||
this.result.push(res);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitText(ast: HtmlTextAst, context: any): any {
|
|
||||||
var res = this._appendContext(ast, [HtmlTextAst, ast.value, this.elDepth]);
|
|
||||||
this.result.push(res);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitComment(ast: HtmlCommentAst, context: any): any {
|
|
||||||
var res = this._appendContext(ast, [HtmlCommentAst, ast.value, this.elDepth]);
|
|
||||||
this.result.push(res);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _appendContext(ast: HtmlAst, input: any[]): any[] {
|
|
||||||
if (!this.includeSourceSpan) return input;
|
|
||||||
input.push(ast.sourceSpan.toString());
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user