Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/@angular/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
TEMPLATE_TRANSFORMS,
CompilerConfig,
RenderTypes,
InterpolationConfig,
UrlResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
createOfflineCompileUrlResolver,
Expand Down
2 changes: 1 addition & 1 deletion modules/@angular/compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {assertionsEnabled} from '../src/facade/lang';

export * from './template_ast';
export {TEMPLATE_TRANSFORMS} from './template_parser';
export {CompilerConfig, RenderTypes} from './config';
export {CompilerConfig, RenderTypes, InterpolationConfig} from './config';
export * from './compile_metadata';
export * from './offline_compiler';
export {RuntimeCompiler} from './runtime_compiler';
Expand Down
24 changes: 15 additions & 9 deletions modules/@angular/compiler/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import {isBlank} from '../src/facade/lang';
import {isBlank, RegExpWrapper} from '../src/facade/lang';
import {unimplemented} from '../src/facade/exceptions';
import {Identifiers} from './identifiers';
import {CompileIdentifierMetadata} from './compile_metadata';
import {ViewEncapsulation} from '@angular/core';

export class CompilerConfig {
public renderTypes: RenderTypes;
public interpolateRegexp: RegExp;
public interpolation: InterpolationConfig;
public defaultEncapsulation: ViewEncapsulation;

constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
public useJit: boolean, renderTypes: RenderTypes = null,
interpolateRegexp: RegExp = null, defaultEncapsulation: ViewEncapsulation = null) {
public useJit: boolean, renderTypes: RenderTypes = null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we are adding more options, I'm wondering if we should switch to named params. Probably out of scope of this PR though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I leave that as TODO comment?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create an other GH Issue

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done #8933

interpolation: InterpolationConfig = null,
defaultEncapsulation: ViewEncapsulation = null) {
if (isBlank(renderTypes)) {
renderTypes = new DefaultRenderTypes();
}
this.renderTypes = renderTypes;
if (isBlank(interpolateRegexp)) {
interpolateRegexp = DEFAULT_INTERPOLATE_REGEXP;
if (isBlank(interpolation)) {
interpolation = DEFAULT_INTERPOLATION_CONFIG;
}
this.interpolateRegexp = interpolateRegexp;
this.interpolation = interpolation;
if (isBlank(defaultEncapsulation)) {
defaultEncapsulation = ViewEncapsulation.Emulated;
}
Expand Down Expand Up @@ -51,6 +52,11 @@ export class DefaultRenderTypes implements RenderTypes {
}

/**
* A regexp pattern used to interpolate in default.
* Interpolation configuration for the parser.
* Can be replaced to customize the start/end symbols.
*/
export var DEFAULT_INTERPOLATE_REGEXP = /\{\{([\s\S]*?)\}\}/g;
export class InterpolationConfig {
constructor(public startSymbol: string, public endSymbol: string) {}
}

const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I use const here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

36 changes: 27 additions & 9 deletions modules/@angular/compiler/src/expression_parser/parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {Injectable, Inject} from '@angular/core';
import {isBlank, isPresent, StringWrapper} from '../../src/facade/lang';
import {
isBlank,
isPresent,
StringWrapper,
RegExpWrapper,
escapeRegExp
} from '../../src/facade/lang';
import {BaseException} from '../../src/facade/exceptions';
import {ListWrapper} from '../../src/facade/collection';
import {
Expand Down Expand Up @@ -46,7 +52,7 @@ import {
AstVisitor,
Quote
} from './ast';
import {CompilerConfig} from '../config';
import {CompilerConfig, InterpolationConfig} from '../config';


var _implicitReceiver = new ImplicitReceiver();
Expand All @@ -65,12 +71,21 @@ export class TemplateBindingParseResult {
constructor(public templateBindings: TemplateBinding[], public warnings: string[]) {}
}

function _createInterpolateRegExp(config: InterpolationConfig): RegExp {
var regexp = escapeRegExp(config.startSymbol) + '([\\s\\S]*?)' + escapeRegExp(config.endSymbol);
return RegExpWrapper.create(regexp, 'g');
}

@Injectable()
export class Parser {
/** @internal */
private _interpolateRegExp: RegExp;
constructor(/** @internal */
public _lexer: Lexer,
/** @internal */
public _config: CompilerConfig) {}
private _config: CompilerConfig) {
this._interpolateRegExp = _createInterpolateRegExp(_config.interpolation);
}

parseAction(input: string, location: any): ASTWithSource {
this._checkNoInterpolation(input, location);
Expand Down Expand Up @@ -138,7 +153,7 @@ export class Parser {
}

splitInterpolation(input: string, location: string): SplitInterpolation {
var parts = StringWrapper.split(input, this._config.interpolateRegexp);
var parts = StringWrapper.split(input, this._interpolateRegExp);
if (parts.length <= 1) {
return null;
}
Expand Down Expand Up @@ -188,18 +203,21 @@ export class Parser {
}

private _checkNoInterpolation(input: string, location: any): void {
var parts = StringWrapper.split(input, this._config.interpolateRegexp);
var parts = StringWrapper.split(input, this._interpolateRegExp);
if (parts.length > 1) {
throw new ParseException('Got interpolation ({{}}) where expression was expected', input,
`at column ${this._findInterpolationErrorColumn(parts, 1)} in`,
location);
throw new ParseException(
`Got interpolation (${this._config.interpolation.startSymbol}${this._config.interpolation.endSymbol}) where expression was expected`,
input, `at column ${this._findInterpolationErrorColumn(parts, 1)} in`, location);
}
}

private _findInterpolationErrorColumn(parts: string[], partInErrIdx: number): number {
var errLocation = '';
for (var j = 0; j < partInErrIdx; j++) {
errLocation += j % 2 === 0 ? parts[j] : `{{${parts[j]}}}`;
errLocation +=
j % 2 === 0 ?
parts[j] :
`${this._config.interpolation.startSymbol}${parts[j]}${this._config.interpolation.endSymbol}`;
}

return errLocation.length;
Expand Down
9 changes: 6 additions & 3 deletions modules/@angular/compiler/src/i18n/i18n_html_parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {CompilerConfig} from "../config";
import {HtmlParser, HtmlParseTreeResult} from "../html_parser";
import {ParseSourceSpan, ParseError} from "../parse_util";
import {
Expand Down Expand Up @@ -115,10 +116,12 @@ let _PLACEHOLDER_EXPANDED_REGEXP = /<ph(\s)+name=("(\w)+")><\/ph>/gi;
*/
export class I18nHtmlParser implements HtmlParser {
errors: ParseError[];
;

constructor(private _htmlParser: HtmlParser, private _parser: Parser,
constructor(private _config: CompilerConfig, private _htmlParser: HtmlParser, private _parser: Parser,
private _messagesContent: string, private _messages: {[key: string]: HtmlAst[]},
private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {
}

parse(sourceContent: string, sourceUrl: string,
parseExpansionForms: boolean = false): HtmlParseTreeResult {
Expand Down Expand Up @@ -354,7 +357,7 @@ export class I18nHtmlParser implements HtmlParser {
private _convertIntoExpression(name: string, expMap: Map<string, string>,
sourceSpan: ParseSourceSpan) {
if (expMap.has(name)) {
return `{{${expMap.get(name)}}}`;
return `${this._config.interpolation.startSymbol}${expMap.get(name)}${this._config.interpolation.endSymbol}`;
} else {
throw new I18nError(sourceSpan, `Invalid interpolation name '${name}'`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {ddescribe, describe, it, xit, iit, expect, beforeEach} from '@angular/co
import {isBlank, isPresent} from '../../src/facade/lang';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {Unparser} from './unparser';
import {CompilerConfig} from '@angular/compiler';
import {CompilerConfig, InterpolationConfig} from '@angular/compiler';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {BindingPipe, LiteralPrimitive, AST} from '@angular/compiler/src/expression_parser/ast';

Expand Down Expand Up @@ -455,7 +455,7 @@ export function main() {
});

it('should support custom interpolation regexp', () => {
var customParser = new Parser(new Lexer(), new CompilerConfig(true, true, true, null, /<<([\s\S]*?)>>/g));
var customParser = new Parser(new Lexer(), new CompilerConfig(true, true, true, null, new InterpolationConfig('<<', '>>')));
var ast = (customParser.parseInterpolation('<< a >>', null) as any).ast;
expect(ast.strings).toEqual(['', '']);
expect(ast.expressions.length).toEqual(1);
Expand Down
6 changes: 3 additions & 3 deletions modules/@angular/compiler/test/i18n/i18n_html_parser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ export function main() {
describe('I18nHtmlParser', () => {
function parse(template: string, messages: {[key: string]: string}, implicitTags: string[] = [],
implicitAttrs: {[k: string]: string[]} = {}): HtmlParseTreeResult {
var parser = new Parser(new Lexer(), new CompilerConfig(true, true, true));
var config = new CompilerConfig(true, true, true);
var parser = new Parser(new Lexer(), config);
let htmlParser = new HtmlParser();

let msgs = '';
StringMapWrapper.forEach(messages, (v, k) => msgs += `<msg id="${k}">${v}</msg>`);
let res = deserializeXmb(`<message-bundle>${msgs}</message-bundle>`, 'someUrl');

return new I18nHtmlParser(htmlParser, parser, res.content, res.messages, implicitTags,
implicitAttrs)
return new I18nHtmlParser(config, htmlParser, parser, res.content, res.messages, implicitTags, implicitAttrs)
.parse(template, "someurl", true);
}

Expand Down
4 changes: 4 additions & 0 deletions modules/@angular/facade/src/lang.dart
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,7 @@ num bitWiseAnd(List values) {
String escape(String s) {
return Uri.encodeComponent(s);
}

String escapeRegExp(String s) {
return s.replaceAllMapped(new RegExp(r'([.*+?^=!:${}()|[\]\/\\])'), (Match m) => '\\${m[1]}');
}
4 changes: 4 additions & 0 deletions modules/@angular/facade/src/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,7 @@ export function bitWiseAnd(values: number[]): number {
export function escape(s: string): string {
return _global.encodeURI(s);
}

export function escapeRegExp(s: string): string {
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
8 changes: 6 additions & 2 deletions tools/public_api_guard/public_api_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1246,10 +1246,10 @@ const COMPILER = [
'CompileTypeMetadata.type:CompileTypeMetadata',
'CompileTypeMetadata.value:any',
'CompilerConfig',
'CompilerConfig.constructor(genDebugInfo:boolean, logBindingUpdate:boolean, useJit:boolean, renderTypes:RenderTypes=null, interpolateRegexp:RegExp=null, defaultEncapsulation:ViewEncapsulation=null)',
'CompilerConfig.constructor(genDebugInfo:boolean, logBindingUpdate:boolean, useJit:boolean, renderTypes:RenderTypes=null, interpolation:InterpolationConfig=null, defaultEncapsulation:ViewEncapsulation=null)',
'CompilerConfig.defaultEncapsulation:ViewEncapsulation',
'CompilerConfig.genDebugInfo:boolean',
'CompilerConfig.interpolateRegexp:RegExp',
'CompilerConfig.interpolation:InterpolationConfig',
'CompilerConfig.logBindingUpdate:boolean',
'CompilerConfig.renderTypes:RenderTypes',
'CompilerConfig.useJit:boolean',
Expand Down Expand Up @@ -1295,6 +1295,10 @@ const COMPILER = [
'EmbeddedTemplateAst.sourceSpan:ParseSourceSpan',
'EmbeddedTemplateAst.variables:VariableAst[]',
'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any',
'InterpolationConfig',
'InterpolationConfig.constructor(startSymbol:string, endSymbol:string)',
'InterpolationConfig.endSymbol:string',
'InterpolationConfig.startSymbol:string',
'NgContentAst',
'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'NgContentAst.index:number',
Expand Down