fix(language-service): wrong completions in conditional operator (#37505)

In `a ? b.~{cursor}`, the LS will provide the symbols in the scope of the current template, because the `path.tail` is `falseExp` whose value is `EmptyExpr`, and the span of `falseExp` is wider than the `trueExp`, so the value of `path` should be narrowed.

PR Close #37505
This commit is contained in:
ivanwonder 2020-06-09 17:42:42 +08:00 committed by Misko Hevery
parent adc9d5cdcb
commit e99bcbb4d4
2 changed files with 23 additions and 4 deletions

View File

@ -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<AST>;
@ -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<AST>(path, position);
}

View File

@ -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(