fix(compiler): do not lex `}}` when interpolation is disabled (#13531)

* doc(compiler): fix the ICU expander API docs

* test(compiler): add lexer and parser specs

* fix(compiler): do not lex `}}` when interpolation is disabled

fix #13525
This commit is contained in:
Victor Berchet 2016-12-16 15:33:16 -08:00 committed by Chuck Jazdzewski
parent a23fa94ca8
commit e78508507d
5 changed files with 61 additions and 10 deletions

View File

@ -30,9 +30,9 @@ const PLURAL_CASES: string[] = ['zero', 'one', 'two', 'few', 'many', 'other'];
* *
* ``` * ```
* <ng-container [ngPlural]="messages.length"> * <ng-container [ngPlural]="messages.length">
* <template ngPluralCase="=0">zero</ng-container> * <template ngPluralCase="=0">zero</template>
* <template ngPluralCase="=1">one</ng-container> * <template ngPluralCase="=1">one</template>
* <template ngPluralCase="other">more than one</ng-container> * <template ngPluralCase="other">more than one</template>
* </ng-container> * </ng-container>
* ``` * ```
*/ */

View File

@ -133,7 +133,7 @@ class _Tokenizer {
} else { } else {
this._consumeTagOpen(start); this._consumeTagOpen(start);
} }
} else if (!this._tokenizeIcu || !this._tokenizeExpansionForm()) { } else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
this._consumeText(); this._consumeText();
} }
} catch (e) { } catch (e) {
@ -586,8 +586,8 @@ class _Tokenizer {
parts.push(this._interpolationConfig.start); parts.push(this._interpolationConfig.start);
this._inInterpolation = true; this._inInterpolation = true;
} else if ( } else if (
this._interpolationConfig && this._attemptStr(this._interpolationConfig.end) && this._interpolationConfig && this._inInterpolation &&
this._inInterpolation) { this._attemptStr(this._interpolationConfig.end)) {
parts.push(this._interpolationConfig.end); parts.push(this._interpolationConfig.end);
this._inInterpolation = false; this._inInterpolation = false;
} else { } else {

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {beforeEach, describe, expect, it} from '../../../core/testing/testing_internal';
import * as html from '../../src/ml_parser/ast'; import * as html from '../../src/ml_parser/ast';
import {HtmlParser, ParseTreeResult, TreeError} from '../../src/ml_parser/html_parser'; import {HtmlParser, ParseTreeResult, TreeError} from '../../src/ml_parser/html_parser';
import {TokenType} from '../../src/ml_parser/lexer'; import {TokenType} from '../../src/ml_parser/lexer';
@ -304,6 +303,18 @@ export function main() {
]))).toEqual([[html.Text, 'One {{message}}', 0]]); ]))).toEqual([[html.Text, 'One {{message}}', 0]]);
}); });
it('should parse out expansion forms', () => {
const parsed =
parser.parse(`<div><span>{a, plural, =0 {b}}</span></div>`, 'TestComp', true);
expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0],
[html.Element, 'span', 1],
[html.Expansion, 'a', 'plural', 2],
[html.ExpansionCase, '=0', 3],
]);
});
it('should parse out nested expansion forms', () => { it('should parse out nested expansion forms', () => {
const parsed = parser.parse( const parsed = parser.parse(
`{messages.length, plural, =0 { {p.gender, select, male {m}} }}`, 'TestComp', true); `{messages.length, plural, =0 { {p.gender, select, male {m}} }}`, 'TestComp', true);

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {describe, expect, it} from '../../../core/testing/testing_internal';
import * as html from '../../src/ml_parser/ast'; import * as html from '../../src/ml_parser/ast';
import {HtmlParser} from '../../src/ml_parser/html_parser'; import {HtmlParser} from '../../src/ml_parser/html_parser';
import {ExpansionResult, expandNodes} from '../../src/ml_parser/icu_ast_expander'; import {ExpansionResult, expandNodes} from '../../src/ml_parser/icu_ast_expander';
@ -96,6 +95,20 @@ export function main() {
]); ]);
}); });
it('should parse an expansion form as a tag single child', () => {
const res = expand(`<div><span>{a, b, =4 {c}}</span></div>`);
expect(humanizeNodes(res.nodes)).toEqual([
[html.Element, 'div', 0],
[html.Element, 'span', 1],
[html.Element, 'ng-container', 2],
[html.Attribute, '[ngSwitch]', 'a'],
[html.Element, 'template', 3],
[html.Attribute, 'ngSwitchCase', '=4'],
[html.Text, 'c', 4],
]);
});
describe('errors', () => { describe('errors', () => {
it('should error on unknown plural cases', () => { it('should error on unknown plural cases', () => {
expect(humanizeErrors(expand('{n, plural, unknown {-}}').errors)).toEqual([ expect(humanizeErrors(expand('{n, plural, unknown {-}}').errors)).toEqual([

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {describe, expect, it} from '../../../core/testing/testing_internal';
import {getHtmlTagDefinition} from '../../src/ml_parser/html_tags'; import {getHtmlTagDefinition} from '../../src/ml_parser/html_tags';
import {InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import {InterpolationConfig} from '../../src/ml_parser/interpolation_config';
import * as lex from '../../src/ml_parser/lexer'; import * as lex from '../../src/ml_parser/lexer';
@ -524,7 +523,15 @@ export function main() {
]); ]);
}); });
it('should treat expansion form as text when they are not parsed', () => {
expect(tokenizeAndHumanizeParts('<span>{a, b, =4 {c}}</span>', false)).toEqual([
[lex.TokenType.TAG_OPEN_START, null, 'span'],
[lex.TokenType.TAG_OPEN_END],
[lex.TokenType.TEXT, '{a, b, =4 {c}}'],
[lex.TokenType.TAG_CLOSE, null, 'span'],
[lex.TokenType.EOF],
]);
});
}); });
describe('raw text', () => { describe('raw text', () => {
@ -672,6 +679,26 @@ export function main() {
]); ]);
}); });
it('should parse an expansion form as a tag single child', () => {
expect(tokenizeAndHumanizeParts('<div><span>{a, b, =4 {c}}</span></div>', true)).toEqual([
[lex.TokenType.TAG_OPEN_START, null, 'div'],
[lex.TokenType.TAG_OPEN_END],
[lex.TokenType.TAG_OPEN_START, null, 'span'],
[lex.TokenType.TAG_OPEN_END],
[lex.TokenType.EXPANSION_FORM_START],
[lex.TokenType.RAW_TEXT, 'a'],
[lex.TokenType.RAW_TEXT, 'b'],
[lex.TokenType.EXPANSION_CASE_VALUE, '=4'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
[lex.TokenType.TEXT, 'c'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.TAG_CLOSE, null, 'span'],
[lex.TokenType.TAG_CLOSE, null, 'div'],
[lex.TokenType.EOF],
]);
});
it('should parse an expansion forms with elements in it', () => { it('should parse an expansion forms with elements in it', () => {
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four <b>a</b>}}', true)).toEqual([ expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four <b>a</b>}}', true)).toEqual([
[lex.TokenType.EXPANSION_FORM_START], [lex.TokenType.EXPANSION_FORM_START],