diff --git a/modules/angular2/src/compiler/html_lexer.ts b/modules/angular2/src/compiler/html_lexer.ts index b346ae45b2..d3a90d74aa 100644 --- a/modules/angular2/src/compiler/html_lexer.ts +++ b/modules/angular2/src/compiler/html_lexer.ts @@ -34,8 +34,8 @@ export class HtmlToken { } export class HtmlTokenError extends ParseError { - constructor(errorMsg: string, public tokenType: HtmlTokenType, location: ParseLocation) { - super(location, errorMsg); + constructor(errorMsg: string, public tokenType: HtmlTokenType, span: ParseSourceSpan) { + super(span, errorMsg); } } @@ -125,7 +125,8 @@ class _HtmlTokenizer { private _processCarriageReturns(content: string): string { // http://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream - // In order to keep the original position in the source, we can not pre-process it. + // In order to keep the original position in the source, we can not + // pre-process it. // Instead CRs are processed right before instantiating the tokens. return StringWrapper.replaceAll(content, CR_OR_CRLF_REGEXP, '\n'); } @@ -168,6 +169,16 @@ class _HtmlTokenizer { return new ParseLocation(this.file, this.index, this.line, this.column); } + private _getSpan(start?: ParseLocation, end?: ParseLocation): ParseSourceSpan { + if (isBlank(start)) { + start = this._getLocation(); + } + if (isBlank(end)) { + end = this._getLocation(); + } + return new ParseSourceSpan(start, end); + } + private _beginToken(type: HtmlTokenType, start: ParseLocation = null) { if (isBlank(start)) { start = this._getLocation(); @@ -188,8 +199,8 @@ class _HtmlTokenizer { return token; } - private _createError(msg: string, position: ParseLocation): ControlFlowError { - var error = new HtmlTokenError(msg, this.currentTokenType, position); + private _createError(msg: string, span: ParseSourceSpan): ControlFlowError { + var error = new HtmlTokenError(msg, this.currentTokenType, span); this.currentTokenStart = null; this.currentTokenType = null; return new ControlFlowError(error); @@ -197,7 +208,7 @@ class _HtmlTokenizer { private _advance() { if (this.index >= this.length) { - throw this._createError(unexpectedCharacterErrorMsg($EOF), this._getLocation()); + throw this._createError(unexpectedCharacterErrorMsg($EOF), this._getSpan()); } if (this.peek === $LF) { this.line++; @@ -228,7 +239,8 @@ class _HtmlTokenizer { private _requireCharCode(charCode: number) { var location = this._getLocation(); if (!this._attemptCharCode(charCode)) { - throw this._createError(unexpectedCharacterErrorMsg(this.peek), location); + throw this._createError(unexpectedCharacterErrorMsg(this.peek), + this._getSpan(location, location)); } } @@ -253,7 +265,7 @@ class _HtmlTokenizer { private _requireStr(chars: string) { var location = this._getLocation(); if (!this._attemptStr(chars)) { - throw this._createError(unexpectedCharacterErrorMsg(this.peek), location); + throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan(location)); } } @@ -267,7 +279,7 @@ class _HtmlTokenizer { var start = this._getLocation(); this._attemptCharCodeUntilFn(predicate); if (this.index - start.offset < len) { - throw this._createError(unexpectedCharacterErrorMsg(this.peek), start); + throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan(start, start)); } } @@ -295,7 +307,7 @@ class _HtmlTokenizer { let numberStart = this._getLocation().offset; this._attemptCharCodeUntilFn(isDigitEntityEnd); if (this.peek != $SEMICOLON) { - throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getLocation()); + throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan()); } this._advance(); let strNum = this.input.substring(numberStart, this.index - 1); @@ -304,7 +316,7 @@ class _HtmlTokenizer { return StringWrapper.fromCharCode(charCode); } catch (e) { let entity = this.input.substring(start.offset + 1, this.index - 1); - throw this._createError(unknownEntityErrorMsg(entity), start); + throw this._createError(unknownEntityErrorMsg(entity), this._getSpan(start)); } } else { let startPosition = this._savePosition(); @@ -317,7 +329,7 @@ class _HtmlTokenizer { let name = this.input.substring(start.offset + 1, this.index - 1); let char = NAMED_ENTITIES[name]; if (isBlank(char)) { - throw this._createError(unknownEntityErrorMsg(name), start); + throw this._createError(unknownEntityErrorMsg(name), this._getSpan(start)); } return char; } @@ -394,7 +406,7 @@ class _HtmlTokenizer { let lowercaseTagName; try { if (!isAsciiLetter(this.peek)) { - throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getLocation()); + throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan()); } var nameStart = this.index; this._consumeTagOpenStart(start); diff --git a/modules/angular2/src/compiler/html_parser.ts b/modules/angular2/src/compiler/html_parser.ts index 301f1a3ad7..f7ed4461b4 100644 --- a/modules/angular2/src/compiler/html_parser.ts +++ b/modules/angular2/src/compiler/html_parser.ts @@ -19,13 +19,11 @@ import {ParseError, ParseLocation, ParseSourceSpan} from './parse_util'; import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix, mergeNsAndName} from './html_tags'; export class HtmlTreeError extends ParseError { - static create(elementName: string, location: ParseLocation, msg: string): HtmlTreeError { - return new HtmlTreeError(elementName, location, msg); + static create(elementName: string, span: ParseSourceSpan, msg: string): HtmlTreeError { + return new HtmlTreeError(elementName, span, msg); } - constructor(public elementName: string, location: ParseLocation, msg: string) { - super(location, msg); - } + constructor(public elementName: string, span: ParseSourceSpan, msg: string) { super(span, msg); } } export class HtmlParseTreeResult { @@ -146,7 +144,7 @@ class TreeBuilder { selfClosing = true; if (getNsPrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) { this.errors.push(HtmlTreeError.create( - fullName, startTagToken.sourceSpan.start, + fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`)); } } else if (this.peek.type === HtmlTokenType.TAG_OPEN_END) { @@ -189,10 +187,10 @@ class TreeBuilder { if (getHtmlTagDefinition(fullName).isVoid) { this.errors.push( - HtmlTreeError.create(fullName, endTagToken.sourceSpan.start, + HtmlTreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`)); } else if (!this._popElement(fullName)) { - this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan.start, + this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan, `Unexpected closing tag "${endTagToken.parts[1]}"`)); } } diff --git a/modules/angular2/src/compiler/parse_util.ts b/modules/angular2/src/compiler/parse_util.ts index 6fc087ebc8..a1070f2e68 100644 --- a/modules/angular2/src/compiler/parse_util.ts +++ b/modules/angular2/src/compiler/parse_util.ts @@ -9,12 +9,20 @@ export class ParseSourceFile { constructor(public content: string, public url: string) {} } -export abstract class ParseError { - constructor(public location: ParseLocation, public msg: string) {} +export class ParseSourceSpan { + constructor(public start: ParseLocation, public end: ParseLocation) {} toString(): string { - var source = this.location.file.content; - var ctxStart = this.location.offset; + return this.start.file.content.substring(this.start.offset, this.end.offset); + } +} + +export abstract class ParseError { + constructor(public span: ParseSourceSpan, public msg: string) {} + + toString(): string { + var source = this.span.start.file.content; + var ctxStart = this.span.start.offset; if (ctxStart > source.length - 1) { ctxStart = source.length - 1; } @@ -44,17 +52,9 @@ export abstract class ParseError { } } - let context = source.substring(ctxStart, this.location.offset) + '[ERROR ->]' + - source.substring(this.location.offset, ctxEnd + 1); + let context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' + + source.substring(this.span.start.offset, ctxEnd + 1); - return `${this.msg} ("${context}"): ${this.location}`; - } -} - -export class ParseSourceSpan { - constructor(public start: ParseLocation, public end: ParseLocation) {} - - toString(): string { - return this.start.file.content.substring(this.start.offset, this.end.offset); + return `${this.msg} ("${context}"): ${this.span.start}`; } } diff --git a/modules/angular2/src/compiler/template_parser.ts b/modules/angular2/src/compiler/template_parser.ts index f7796caaff..d6107d8341 100644 --- a/modules/angular2/src/compiler/template_parser.ts +++ b/modules/angular2/src/compiler/template_parser.ts @@ -79,7 +79,7 @@ var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0]; export const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms')); export class TemplateParseError extends ParseError { - constructor(message: string, location: ParseLocation) { super(location, message); } + constructor(message: string, span: ParseSourceSpan) { super(span, message); } } @Injectable() @@ -128,7 +128,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { } private _reportError(message: string, sourceSpan: ParseSourceSpan) { - this.errors.push(new TemplateParseError(message, sourceSpan.start)); + this.errors.push(new TemplateParseError(message, sourceSpan)); } private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { diff --git a/modules/angular2/test/compiler/html_lexer_spec.ts b/modules/angular2/test/compiler/html_lexer_spec.ts index 0ca26328e1..a4c32142a8 100644 --- a/modules/angular2/test/compiler/html_lexer_spec.ts +++ b/modules/angular2/test/compiler/html_lexer_spec.ts @@ -581,7 +581,8 @@ export function main() { let src = "111\n222\n333\nE\n444\n555\n666\n"; let file = new ParseSourceFile(src, 'file://'); let location = new ParseLocation(file, 12, 123, 456); - let error = new HtmlTokenError('**ERROR**', null, location); + let span = new ParseSourceSpan(location, location); + let error = new HtmlTokenError('**ERROR**', null, span); expect(error.toString()) .toEqual(`**ERROR** ("\n222\n333\n[ERROR ->]E\n444\n555\n"): file://@123:456`); }); @@ -631,7 +632,9 @@ function tokenizeAndHumanizeLineColumn(input: string): any[] { function tokenizeAndHumanizeErrors(input: string): any[] { return tokenizeHtml(input, 'someUrl') - .errors.map( - tokenError => - [tokenError.tokenType, tokenError.msg, humanizeLineColumn(tokenError.location)]); + .errors.map(tokenError => [ + tokenError.tokenType, + tokenError.msg, + humanizeLineColumn(tokenError.span.start) + ]); } diff --git a/modules/angular2/test/compiler/html_parser_spec.ts b/modules/angular2/test/compiler/html_parser_spec.ts index d2b2ce31c9..05bc86c67c 100644 --- a/modules/angular2/test/compiler/html_parser_spec.ts +++ b/modules/angular2/test/compiler/html_parser_spec.ts @@ -328,10 +328,10 @@ function humanizeErrors(errors: ParseError[]): any[] { return errors.map(error => { if (error instanceof HtmlTreeError) { // Parser errors - return [error.elementName, error.msg, humanizeLineColumn(error.location)]; + return [error.elementName, error.msg, humanizeLineColumn(error.span.start)]; } // Tokenizer errors - return [(error).tokenType, error.msg, humanizeLineColumn(error.location)]; + return [(error).tokenType, error.msg, humanizeLineColumn(error.span.start)]; }); }