fix(compiler): interpolation expressions report the correct offset (#12125)
This commit is contained in:
parent
f4566f8128
commit
d641c36a45
|
@ -17,7 +17,7 @@ import {EOF, Lexer, Token, TokenType, isIdentifier, isQuote} from './lexer';
|
||||||
|
|
||||||
|
|
||||||
export class SplitInterpolation {
|
export class SplitInterpolation {
|
||||||
constructor(public strings: string[], public expressions: string[]) {}
|
constructor(public strings: string[], public expressions: string[], public offsets: number[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TemplateBindingParseResult {
|
export class TemplateBindingParseResult {
|
||||||
|
@ -41,8 +41,12 @@ export class Parser {
|
||||||
input: string, location: any,
|
input: string, location: any,
|
||||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
||||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||||
|
const sourceToLex = this._stripComments(input);
|
||||||
const tokens = this._lexer.tokenize(this._stripComments(input));
|
const tokens = this._lexer.tokenize(this._stripComments(input));
|
||||||
const ast = new _ParseAST(input, location, tokens, true, this.errors).parseChain();
|
const ast = new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, true, this.errors,
|
||||||
|
input.length - sourceToLex.length)
|
||||||
|
.parseChain();
|
||||||
return new ASTWithSource(ast, input, location, this.errors);
|
return new ASTWithSource(ast, input, location, this.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +83,12 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(input));
|
const sourceToLex = this._stripComments(input);
|
||||||
return new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
const tokens = this._lexer.tokenize(sourceToLex);
|
||||||
|
return new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||||
|
input.length - sourceToLex.length)
|
||||||
|
.parseChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseQuote(input: string, location: any): AST {
|
private _parseQuote(input: string, location: any): AST {
|
||||||
|
@ -95,7 +103,8 @@ export class Parser {
|
||||||
|
|
||||||
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
return new _ParseAST(input, location, tokens, false, this.errors).parseTemplateBindings();
|
return new _ParseAST(input, location, tokens, input.length, false, this.errors, 0)
|
||||||
|
.parseTemplateBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInterpolation(
|
parseInterpolation(
|
||||||
|
@ -107,8 +116,13 @@ export class Parser {
|
||||||
let expressions: AST[] = [];
|
let expressions: AST[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < split.expressions.length; ++i) {
|
for (let i = 0; i < split.expressions.length; ++i) {
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
const expressionText = split.expressions[i];
|
||||||
var ast = new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
const sourceToLex = this._stripComments(expressionText);
|
||||||
|
const tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
||||||
|
const ast = new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||||
|
split.offsets[i] + (expressionText.length - sourceToLex.length))
|
||||||
|
.parseChain();
|
||||||
expressions.push(ast);
|
expressions.push(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +142,19 @@ export class Parser {
|
||||||
}
|
}
|
||||||
const strings: string[] = [];
|
const strings: string[] = [];
|
||||||
const expressions: string[] = [];
|
const expressions: string[] = [];
|
||||||
|
const offsets: number[] = [];
|
||||||
|
let offset = 0;
|
||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
var part: string = parts[i];
|
var part: string = parts[i];
|
||||||
if (i % 2 === 0) {
|
if (i % 2 === 0) {
|
||||||
// fixed string
|
// fixed string
|
||||||
strings.push(part);
|
strings.push(part);
|
||||||
|
offset += part.length;
|
||||||
} else if (part.trim().length > 0) {
|
} else if (part.trim().length > 0) {
|
||||||
|
offset += interpolationConfig.start.length;
|
||||||
expressions.push(part);
|
expressions.push(part);
|
||||||
|
offsets.push(offset);
|
||||||
|
offset += part.length + interpolationConfig.end.length;
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
'Blank expressions are not allowed in interpolated strings', input,
|
'Blank expressions are not allowed in interpolated strings', input,
|
||||||
|
@ -143,7 +162,7 @@ export class Parser {
|
||||||
location);
|
location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new SplitInterpolation(strings, expressions);
|
return new SplitInterpolation(strings, expressions, offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
||||||
|
@ -208,8 +227,9 @@ export class _ParseAST {
|
||||||
index: number = 0;
|
index: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public input: string, public location: any, public tokens: any[], public parseAction: boolean,
|
public input: string, public location: any, public tokens: Token[],
|
||||||
private errors: ParserError[]) {}
|
public inputLength: number, public parseAction: boolean, private errors: ParserError[],
|
||||||
|
private offset: number) {}
|
||||||
|
|
||||||
peek(offset: number): Token {
|
peek(offset: number): Token {
|
||||||
var i = this.index + offset;
|
var i = this.index + offset;
|
||||||
|
@ -219,7 +239,8 @@ export class _ParseAST {
|
||||||
get next(): Token { return this.peek(0); }
|
get next(): Token { return this.peek(0); }
|
||||||
|
|
||||||
get inputIndex(): number {
|
get inputIndex(): number {
|
||||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
return (this.index < this.tokens.length) ? this.next.index + this.offset :
|
||||||
|
this.inputLength + this.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
span(start: number) { return new ParseSpan(start, this.inputIndex); }
|
span(start: number) { return new ParseSpan(start, this.inputIndex); }
|
||||||
|
@ -311,7 +332,7 @@ export class _ParseAST {
|
||||||
while (this.optionalCharacter(chars.$COLON)) {
|
while (this.optionalCharacter(chars.$COLON)) {
|
||||||
args.push(this.parseExpression());
|
args.push(this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new BindingPipe(this.span(result.span.start), result, name, args);
|
result = new BindingPipe(this.span(result.span.start - this.offset), result, name, args);
|
||||||
} while (this.optionalOperator('|'));
|
} while (this.optionalOperator('|'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding} from '@angular/compiler/src/expression_parser/ast';
|
import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding} from '@angular/compiler/src/expression_parser/ast';
|
||||||
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
|
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
|
||||||
import {Parser, TemplateBindingParseResult} from '@angular/compiler/src/expression_parser/parser';
|
import {Parser, SplitInterpolation, TemplateBindingParseResult} from '@angular/compiler/src/expression_parser/parser';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||||
|
@ -39,6 +39,10 @@ export function main() {
|
||||||
return createParser().parseInterpolation(text, location);
|
return createParser().parseInterpolation(text, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitInterpolation(text: string, location: any = null): SplitInterpolation {
|
||||||
|
return createParser().splitInterpolation(text, location);
|
||||||
|
}
|
||||||
|
|
||||||
function parseSimpleBinding(text: string, location: any = null): ASTWithSource {
|
function parseSimpleBinding(text: string, location: any = null): ASTWithSource {
|
||||||
return createParser().parseSimpleBinding(text, location);
|
return createParser().parseSimpleBinding(text, location);
|
||||||
}
|
}
|
||||||
|
@ -539,5 +543,18 @@ export function main() {
|
||||||
it('should be able to recover from a missing selector in a array literal',
|
it('should be able to recover from a missing selector in a array literal',
|
||||||
() => recover('[[a.], b, c]'));
|
() => recover('[[a.], b, c]'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('offsets', () => {
|
||||||
|
it('should retain the offsets of an interpolation', () => {
|
||||||
|
const interpolations = splitInterpolation('{{a}} {{b}} {{c}}');
|
||||||
|
expect(interpolations.offsets).toEqual([2, 9, 16]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retain the offsets into the expression AST of interpolations', () => {
|
||||||
|
const source = parseInterpolation('{{a}} {{b}} {{c}}');
|
||||||
|
const interpolation = source.ast as Interpolation;
|
||||||
|
expect(interpolation.expressions.map(e => e.span.start)).toEqual([2, 9, 16]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue