diff --git a/packages/language-service/src/expressions.ts b/packages/language-service/src/expressions.ts index 2baa061ea4..7eff222f58 100644 --- a/packages/language-service/src/expressions.ts +++ b/packages/language-service/src/expressions.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, AstPath as AstPathBase, ASTWithName, ASTWithSource, RecursiveAstVisitor} from '@angular/compiler'; +import {AST, AstPath as AstPathBase, ASTWithName, ASTWithSource, Interpolation, RecursiveAstVisitor} from '@angular/compiler'; import {AstType} from './expression_type'; import {BuiltinType, Span, Symbol, SymbolTable, TemplateSource} from './types'; -import {inSpan} from './utils'; +import {inSpan, isNarrower} from './utils'; type AstPath = AstPathBase; @@ -20,7 +20,10 @@ function findAstAt(ast: AST, position: number, excludeEmpty: boolean = false): A visit(ast: AST) { if ((!excludeEmpty || ast.sourceSpan.start < ast.sourceSpan.end) && inSpan(position, ast.sourceSpan)) { - path.push(ast); + const isNotNarrower = path.length && !isNarrower(ast.span, path[path.length - 1].span); + if (!isNotNarrower) { + path.push(ast); + } ast.visit(this); } } @@ -32,7 +35,14 @@ function findAstAt(ast: AST, position: number, excludeEmpty: boolean = false): A ast = ast.ast; } - visitor.visit(ast); + // `Interpolation` is useless here except the `expressions` of it. + if (ast instanceof Interpolation) { + ast = ast.expressions.filter((_ast: AST) => inSpan(position, _ast.sourceSpan))[0]; + } + + if (ast) { + visitor.visit(ast); + } return new AstPathBase(path, position); } diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index ccece9379b..c704ec78aa 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -832,6 +832,15 @@ describe('completions', () => { // should resolve to transform(value: number, prefix: number): number expectContain(c2, CompletionKind.METHOD, ['toFixed', 'toExponential']); }); + + it('should work in the conditional operator', () => { + mockHost.override(TEST_TEMPLATE, '{{ title ? title.~{cursor} }}'); + const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); + const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start); + expectContain(completions, CompletionKind.METHOD, [ + 'trim', + ]); + }); }); function expectContain(