diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index 5fc14c855d..1f1a35a058 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -12,17 +12,17 @@ describe('type check blocks diagnostics', () => { describe('parse spans', () => { it('should annotate binary ops', () => { expect(tcbWithSpans('{{ a + b }}')) - .toContain('"" + (((ctx).a /*3,5*/) + ((ctx).b /*7,9*/) /*3,9*/);'); + .toContain('"" + (((ctx).a /*3,4*/) + ((ctx).b /*7,8*/) /*3,8*/);'); }); it('should annotate conditions', () => { expect(tcbWithSpans('{{ a ? b : c }}')) - .toContain('((ctx).a /*3,5*/ ? (ctx).b /*7,9*/ : (ctx).c /*11,13*/) /*3,13*/;'); + .toContain('((ctx).a /*3,4*/ ? (ctx).b /*7,8*/ : (ctx).c /*11,12*/) /*3,12*/;'); }); it('should annotate interpolations', () => { expect(tcbWithSpans('{{ hello }} {{ world }}')) - .toContain('"" + (ctx).hello /*3,9*/ + (ctx).world /*15,21*/;'); + .toContain('"" + (ctx).hello /*3,8*/ + (ctx).world /*15,20*/;'); }); it('should annotate literal map expressions', () => { @@ -35,46 +35,46 @@ describe('type check blocks diagnostics', () => { it('should annotate literal array expressions', () => { const TEMPLATE = '{{ [a, b] }}'; - expect(tcbWithSpans(TEMPLATE)).toContain('[(ctx).a /*4,5*/, (ctx).b /*7,8*/] /*3,10*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('[(ctx).a /*4,5*/, (ctx).b /*7,8*/] /*3,9*/;'); }); it('should annotate literals', () => { const TEMPLATE = '{{ 123 }}'; - expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,7*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,6*/;'); }); it('should annotate non-null assertions', () => { const TEMPLATE = `{{ a! }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/)! /*3,6*/);'); + expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/)! /*3,5*/);'); }); it('should annotate prefix not', () => { const TEMPLATE = `{{ !a }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('!((ctx).a /*4,6*/) /*3,6*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('!((ctx).a /*4,5*/) /*3,5*/;'); }); it('should annotate method calls', () => { const TEMPLATE = `{{ method(a, b) }}`; expect(tcbWithSpans(TEMPLATE)) - .toContain('(ctx).method((ctx).a /*10,11*/, (ctx).b /*13,14*/) /*3,16*/;'); + .toContain('(ctx).method((ctx).a /*10,11*/, (ctx).b /*13,14*/) /*3,15*/;'); }); it('should annotate method calls of variables', () => { const TEMPLATE = `{{ method(a, b) }}`; expect(tcbWithSpans(TEMPLATE)) - .toContain('(_t2 /*27,40*/).method((ctx).a /*34,35*/, (ctx).b /*37,38*/) /*27,40*/;'); + .toContain('(_t2 /*27,39*/).method((ctx).a /*34,35*/, (ctx).b /*37,38*/) /*27,39*/;'); }); it('should annotate function calls', () => { const TEMPLATE = `{{ method(a)(b, c) }}`; expect(tcbWithSpans(TEMPLATE)) .toContain( - '((ctx).method((ctx).a /*10,11*/) /*3,12*/)((ctx).b /*13,14*/, (ctx).c /*16,17*/) /*3,19*/;'); + '((ctx).method((ctx).a /*10,11*/) /*3,12*/)((ctx).b /*13,14*/, (ctx).c /*16,17*/) /*3,18*/;'); }); it('should annotate property access', () => { const TEMPLATE = `{{ a.b.c }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/).b /*3,6*/).c /*3,9*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/).b /*3,6*/).c /*3,8*/;'); }); it('should annotate property writes', () => { @@ -85,7 +85,7 @@ describe('type check blocks diagnostics', () => { it('should annotate keyed property access', () => { const TEMPLATE = `{{ a[b] }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*3,4*/)[(ctx).b /*5,6*/] /*3,8*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*3,4*/)[(ctx).b /*5,6*/] /*3,7*/;'); }); it('should annotate keyed property writes', () => { @@ -97,19 +97,19 @@ describe('type check blocks diagnostics', () => { it('should annotate safe property access', () => { const TEMPLATE = `{{ a?.b }}`; expect(tcbWithSpans(TEMPLATE)) - .toContain('((null as any) ? ((ctx).a /*3,4*/)!.b : undefined) /*3,8*/'); + .toContain('((null as any) ? ((ctx).a /*3,4*/)!.b : undefined) /*3,7*/'); }); it('should annotate safe method calls', () => { const TEMPLATE = `{{ a?.method(b) }}`; expect(tcbWithSpans(TEMPLATE)) .toContain( - '((null as any) ? ((ctx).a /*3,4*/)!.method((ctx).b /*13,14*/) : undefined) /*3,16*/'); + '((null as any) ? ((ctx).a /*3,4*/)!.method((ctx).b /*13,14*/) : undefined) /*3,15*/'); }); it('should annotate $any casts', () => { const TEMPLATE = `{{ $any(a) }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*8,9*/ as any) /*3,11*/;'); + expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*8,9*/ as any) /*3,10*/;'); }); it('should annotate chained expressions', () => { @@ -127,17 +127,17 @@ describe('type check blocks diagnostics', () => { }]; const block = tcbWithSpans(TEMPLATE, PIPES); expect(block).toContain( - '(null as TestPipe).transform((ctx).a /*3,5*/, (ctx).b /*12,14*/) /*3,14*/;'); + '(null as TestPipe).transform((ctx).a /*3,4*/, (ctx).b /*12,13*/) /*3,13*/;'); }); describe('attaching multiple comments for multiple references', () => { it('should be correct for element refs', () => { const TEMPLATE = `{{ a || a }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('((_t1 /*19,21*/) || (_t1 /*24,26*/) /*19,26*/);'); + expect(tcbWithSpans(TEMPLATE)).toContain('((_t1 /*19,20*/) || (_t1 /*24,25*/) /*19,25*/);'); }); it('should be correct for template vars', () => { const TEMPLATE = `{{ a || a }}`; - expect(tcbWithSpans(TEMPLATE)).toContain('((_t2 /*26,28*/) || (_t2 /*31,33*/) /*26,33*/);'); + expect(tcbWithSpans(TEMPLATE)).toContain('((_t2 /*26,27*/) || (_t2 /*31,32*/) /*26,32*/);'); }); it('should be correct for directive refs', () => { const DIRECTIVES: TestDeclaration[] = [{ @@ -148,7 +148,7 @@ describe('type check blocks diagnostics', () => { }]; const TEMPLATE = `{{ a || a }}`; expect(tcbWithSpans(TEMPLATE, DIRECTIVES)) - .toContain('((_t2 /*23,25*/) || (_t2 /*28,30*/) /*23,30*/);'); + .toContain('((_t2 /*23,24*/) || (_t2 /*28,29*/) /*23,29*/);'); }); }); }); diff --git a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts index d7679f3001..5c51defba1 100644 --- a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts @@ -176,9 +176,9 @@ runInEachFileSystem((os) => { expect(mappings).toContain( {source: 'items.push(', generated: 'ctx.items.push(', sourceUrl: '../test.ts'}); expect(mappings).toContain( - {source: `'item' `, generated: `"item"`, sourceUrl: '../test.ts'}); + {source: `'item'`, generated: `"item"`, sourceUrl: '../test.ts'}); expect(mappings).toContain({ - source: '+ items.length)', + source: ' + items.length)', generated: ' + ctx.items.length)', sourceUrl: '../test.ts' }); diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index bf8da66c8f..cb5f2dba08 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -312,7 +312,14 @@ export class _ParseAST { */ get currentAbsoluteOffset(): number { return this.absoluteOffset + this.inputIndex; } - span(start: number) { return new ParseSpan(start, this.inputIndex); } + span(start: number) { + // `end` is either the + // - end index of the current token + // - start of the first token (this can happen e.g. when creating an implicit receiver) + const curToken = this.peek(-1); + const end = this.index > 0 ? curToken.end + this.offset : this.inputIndex; + return new ParseSpan(start, end); + } sourceSpan(start: number): AbsoluteSourceSpan { const serial = `${start}@${this.inputIndex}`; @@ -740,7 +747,6 @@ export class _ParseAST { const value = this.parseConditional(); return new PropertyWrite(this.span(start), this.sourceSpan(start), receiver, id, value); } else { - const span = this.span(start); return new PropertyRead(this.span(start), this.sourceSpan(start), receiver, id); } } @@ -887,11 +893,7 @@ export class _ParseAST { return null; } const ast = this.parsePipe(); // example: "condition | async" - const {start} = ast.span; - // Getting the end of the last token removes trailing whitespace. - // If ast has the correct end span then no need to peek at last token. - // TODO(ayazhafiz): Remove this in https://github.com/angular/angular/pull/34690 - const {end} = this.peek(-1); + const {start, end} = ast.span; const value = this.input.substring(start, end); return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors); } diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index c03aebbd99..69fb2a358c 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -516,7 +516,7 @@ describe('parser', () => { const bindings = parseTemplateBindings(attr); expect(humanizeSpans(bindings, attr)).toEqual([ // source span, key span, value span - ['ngIf="cond | pipe ', 'ngIf', 'cond | pipe '], + ['ngIf="cond | pipe ', 'ngIf', 'cond | pipe'], ['ngIf="cond | pipe as foo, ', 'foo', 'ngIf'], ['let x; ', 'x', null], ['ngIf as y', 'y', 'ngIf'], @@ -531,7 +531,7 @@ describe('parser', () => { // source span, key span, value span ['ngFor="', 'ngFor', null], ['let item; ', 'item', null], - ['of items | slice:0:1 ', 'of', 'items | slice:0:1 '], + ['of items | slice:0:1 ', 'of', 'items | slice:0:1'], ['of items | slice:0:1 as collection, ', 'collection', 'of'], ['trackBy: func; ', 'trackBy', 'func'], ['index as i', 'i', 'index'], @@ -545,7 +545,7 @@ describe('parser', () => { // source span, key span, value span ['ngFor="', 'ngFor', null], ['let item, ', 'item', null], - ['of: [1,2,3] | pipe ', 'of', '[1,2,3] | pipe '], + ['of: [1,2,3] | pipe ', 'of', '[1,2,3] | pipe'], ['of: [1,2,3] | pipe as items; ', 'items', 'of'], ['let i=index, ', 'i', 'index'], ['count as len, ', 'len', 'count'], diff --git a/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts b/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts index 6641e16108..e9c2a7a248 100644 --- a/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts +++ b/packages/compiler/test/render3/r3_ast_absolute_span_spec.ts @@ -60,9 +60,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in a binary expression', () => { expect(humanizeExpressionSource(parse('
{{1 + 2}}
').nodes)) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a binary expression. Look into fixing this. - ['1', new AbsoluteSourceSpan(7, 9)], + ['1', new AbsoluteSourceSpan(7, 8)], ['2', new AbsoluteSourceSpan(11, 12)], ])); }); @@ -78,10 +76,8 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in a conditional', () => { expect(humanizeExpressionSource(parse('
{{bool ? 1 : 0}}
').nodes)) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a conditional expression. Look into fixing this. - ['bool', new AbsoluteSourceSpan(7, 12)], - ['1', new AbsoluteSourceSpan(14, 16)], + ['bool', new AbsoluteSourceSpan(7, 11)], + ['1', new AbsoluteSourceSpan(14, 15)], ['0', new AbsoluteSourceSpan(18, 19)], ])); }); @@ -133,9 +129,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in an interpolation', () => { expect(humanizeExpressionSource(parse('
{{1 + 2}}
').nodes)) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a conditional expression. Look into fixing this. - ['1', new AbsoluteSourceSpan(7, 9)], + ['1', new AbsoluteSourceSpan(7, 8)], ['2', new AbsoluteSourceSpan(11, 12)], ])); }); @@ -197,9 +191,7 @@ describe('expression AST absolute source spans', () => { describe('literal map', () => { it('should provide absolute offsets of a literal map', () => { expect(humanizeExpressionSource(parse('
{{ {a: 0} }}
').nodes)).toContain([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a literal map. Look into fixing this. - '{a: 0}', new AbsoluteSourceSpan(8, 15) + '{a: 0}', new AbsoluteSourceSpan(8, 14) ]); }); @@ -248,9 +240,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets expressions in a pipe', () => { expect(humanizeExpressionSource(parse('
{{prop | pipe}}
').nodes)).toContain([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a pipe. Look into fixing this. - 'prop', new AbsoluteSourceSpan(7, 12) + 'prop', new AbsoluteSourceSpan(7, 11) ]); }); }); diff --git a/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts b/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts index 4e922720bb..ca2b1b3ad8 100644 --- a/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts +++ b/packages/compiler/test/template_parser/template_parser_absolute_span_spec.ts @@ -94,9 +94,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in a binary expression', () => { expect(humanizeExpressionSource(parse('
{{1 + 2}}
'))) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a binary expression. Look into fixing this. - ['1', new AbsoluteSourceSpan(7, 9)], + ['1', new AbsoluteSourceSpan(7, 8)], ['2', new AbsoluteSourceSpan(11, 12)], ])); }); @@ -112,10 +110,8 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in a conditional', () => { expect(humanizeExpressionSource(parse('
{{bool ? 1 : 0}}
'))) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a conditional expression. Look into fixing this. - ['bool', new AbsoluteSourceSpan(7, 12)], - ['1', new AbsoluteSourceSpan(14, 16)], + ['bool', new AbsoluteSourceSpan(7, 11)], + ['1', new AbsoluteSourceSpan(14, 15)], ['0', new AbsoluteSourceSpan(18, 19)], ])); }); @@ -167,9 +163,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets of expressions in an interpolation', () => { expect(humanizeExpressionSource(parse('
{{1 + 2}}
'))) .toEqual(jasmine.arrayContaining([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a conditional expression. Look into fixing this. - ['1', new AbsoluteSourceSpan(7, 9)], + ['1', new AbsoluteSourceSpan(7, 8)], ['2', new AbsoluteSourceSpan(11, 12)], ])); }); @@ -231,9 +225,7 @@ describe('expression AST absolute source spans', () => { describe('literal map', () => { it('should provide absolute offsets of a literal map', () => { expect(humanizeExpressionSource(parse('
{{ {a: 0} }}
'))).toContain([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a literal map. Look into fixing this. - '{a: 0}', new AbsoluteSourceSpan(8, 15) + '{a: 0}', new AbsoluteSourceSpan(8, 14) ]); }); @@ -287,11 +279,7 @@ describe('expression AST absolute source spans', () => { it('should provide absolute offsets expressions in a pipe', () => { expect(humanizeExpressionSource(parse('
{{prop | test}}
', [], [testPipe]))) - .toContain([ - // TODO(ayazhafiz): The expression parser includes an extra whitespace on a expressions - // with trailing whitespace in a pipe. Look into fixing this. - 'prop', new AbsoluteSourceSpan(7, 12) - ]); + .toContain(['prop', new AbsoluteSourceSpan(7, 11)]); }); }); diff --git a/packages/language-service/src/completions.ts b/packages/language-service/src/completions.ts index 300aa60808..0a01ebf5dd 100644 --- a/packages/language-service/src/completions.ts +++ b/packages/language-service/src/completions.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ASTWithSource, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding, getHtmlTagDefinition} from '@angular/compiler'; +import {AST, AbsoluteSourceSpan, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding, getHtmlTagDefinition} from '@angular/compiler'; import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars'; import {AstResult} from './common'; @@ -575,21 +575,21 @@ class ExpressionVisitor extends NullTemplateVisitor { } } } - } - else if (inSpan(valueRelativePosition, binding.value?.ast.span)) { - this.processExpressionCompletions(binding.value !.ast); - return; - } - - // If the expression is incomplete, for example *ngFor="let x of |" - // binding.expression is null. We could still try to provide suggestions - // by looking for symbols that are in scope. - const KW_OF = ' of '; - const ofLocation = attr.value.indexOf(KW_OF); - if (ofLocation > 0 && valueRelativePosition >= ofLocation + KW_OF.length) { - const expressionAst = this.info.expressionParser.parseBinding( - attr.value, attr.sourceSpan.toString(), attr.sourceSpan.start.offset); - this.processExpressionCompletions(expressionAst); + } else if (binding instanceof ExpressionBinding) { + if (inSpan(this.position, binding.value?.ast.sourceSpan)) { + this.processExpressionCompletions(binding.value !.ast); + return; + } else if (!binding.value && this.position > binding.key.span.end) { + // No expression is defined for the value of the key expression binding, but the cursor is + // in a location where the expression would be defined. This can happen in a case like + // let i of | + // ^-- cursor + // In this case, backfill the value to be an empty expression and retrieve completions. + this.processExpressionCompletions(new EmptyExpr( + new ParseSpan(valueRelativePosition, valueRelativePosition), + new AbsoluteSourceSpan(this.position, this.position))); + return; + } } } } diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index 33cab0f1c0..ed8f3e081f 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -310,15 +310,30 @@ describe('completions', () => { expect(completions).toBeUndefined(); }); - it('should include field reference', () => { - mockHost.override(TEST_TEMPLATE, `
`); - const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); - const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); - expectContain(completions, CompletionKind.PROPERTY, ['title', 'heroes', 'league']); - // the symbol 'x' declared in *ngFor is also in scope. This asserts that - // we are actually taking the AST into account and not just referring to - // the symbol table of the Component. - expectContain(completions, CompletionKind.VARIABLE, ['x']); + describe('template binding: key expression', () => { + it('should complete the RHS of a template key expression without an expression value', () => { + mockHost.override( + TEST_TEMPLATE, `
`); // value is undefined + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PROPERTY, ['title', 'heroes', 'league']); + // the symbol 'x' declared in *ngFor is also in scope. This asserts that + // we are actually taking the AST into account and not just referring to + // the symbol table of the Component. + expectContain(completions, CompletionKind.VARIABLE, ['x']); + }); + + it('should complete the RHS of a template key expression with an expression value', () => { + mockHost.override( + TEST_TEMPLATE, `
`); // value is defined + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.PROPERTY, ['title', 'heroes', 'league']); + // the symbol 'x' declared in *ngFor is also in scope. This asserts that + // we are actually taking the AST into account and not just referring to + // the symbol table of the Component. + expectContain(completions, CompletionKind.VARIABLE, ['x']); + }); }); it('should include expression completions', () => { diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index 7a5b03f84c..dd51b1d13f 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -328,7 +328,7 @@ describe('diagnostics', () => { it('report an unknown field in $implicit context', () => { mockHost.override(TEST_TEMPLATE, `
- {{ ~{start-emb}myVar.missingField ~{end-emb}}} + {{ ~{start-emb}myVar.missingField~{end-emb} }}
`); const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE); @@ -346,7 +346,7 @@ describe('diagnostics', () => { it('report an unknown field in non implicit context', () => { mockHost.override(TEST_TEMPLATE, `
- {{ ~{start-emb}myVar.missingField ~{end-emb}}} + {{ ~{start-emb}myVar.missingField~{end-emb} }}
`); const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);