feat(language-service): provide completion for $event variable (#34566)

This commit adds a completion for the `$event` variable in bound event
expressions.

This is the first in a series of PRs to support completions for
properties on `$event` variables (https://github.com/angular/vscode-ng-language-service/issues/531).

PR Close #34566
This commit is contained in:
ayazhafiz 2019-12-26 08:44:14 -06:00 committed by atscott
parent cb142b6df9
commit d7ea389c84
4 changed files with 18 additions and 16 deletions

View File

@ -308,7 +308,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
const dinfo = diagnosticInfoFromTemplateInfo(info);
return getExpressionScope(dinfo, templatePath, false);
return getExpressionScope(dinfo, templatePath);
});
if (templatePath.tail instanceof AttrAst ||
templatePath.tail instanceof BoundElementPropertyAst ||
@ -398,8 +398,7 @@ function interpolationCompletions(info: AstResult, position: number): ng.Complet
return [];
}
const visitor = new ExpressionVisitor(
info, position,
() => getExpressionScope(diagnosticInfoFromTemplateInfo(info), templatePath, false));
info, position, () => getExpressionScope(diagnosticInfoFromTemplateInfo(info), templatePath));
templatePath.tail.visit(visitor, null);
return visitor.results;
}

View File

@ -25,8 +25,7 @@ export interface DiagnosticTemplateInfo {
export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): Diagnostic[] {
const visitor = new ExpressionDiagnosticsVisitor(
info, (path: TemplateAstPath, includeEvent: boolean) =>
getExpressionScope(info, path, includeEvent));
info, (path: TemplateAstPath) => getExpressionScope(info, path));
templateVisitAll(visitor, info.templateAst);
return visitor.diagnostics;
}
@ -194,9 +193,9 @@ function refinedVariableType(
return query.getBuiltinType(BuiltinType.Any);
}
function getEventDeclaration(info: DiagnosticTemplateInfo, includeEvent?: boolean) {
function getEventDeclaration(info: DiagnosticTemplateInfo, path: TemplateAstPath) {
let result: SymbolDeclaration[] = [];
if (includeEvent) {
if (path.tail instanceof BoundEventAst) {
// TODO: Determine the type of the event parameter based on the Observable<T> or EventEmitter<T>
// of the event.
result = [{name: '$event', kind: 'variable', type: info.query.getBuiltinType(BuiltinType.Any)}];
@ -205,11 +204,11 @@ function getEventDeclaration(info: DiagnosticTemplateInfo, includeEvent?: boolea
}
export function getExpressionScope(
info: DiagnosticTemplateInfo, path: TemplateAstPath, includeEvent: boolean): SymbolTable {
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
let result = info.members;
const references = getReferences(info);
const variables = getVarDeclarations(info, path);
const events = getEventDeclaration(info, includeEvent);
const events = getEventDeclaration(info, path);
if (references.length || variables.length || events.length) {
const referenceTable = info.query.createSymbolTable(references);
const variableTable = info.query.createSymbolTable(variables);
@ -334,11 +333,6 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
{span: offsetSpan(span, this.info.offset), kind: ts.DiagnosticCategory.Error, message});
}
}
private reportWarning(message: string, span: Span) {
this.diagnostics.push(
{span: offsetSpan(span, this.info.offset), kind: ts.DiagnosticCategory.Warning, message});
}
}
function hasTemplateReference(type: CompileTypeMetadata): boolean {

View File

@ -37,7 +37,7 @@ export function locateSymbol(info: AstResult, position: number): SymbolInfo|unde
if (attribute) {
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
const dinfo = diagnosticInfoFromTemplateInfo(info);
const scope = getExpressionScope(dinfo, path, inEvent);
const scope = getExpressionScope(dinfo, path);
if (attribute.valueSpan) {
const result = getExpressionSymbol(scope, ast, templatePosition, info.template.query);
if (result) {
@ -113,7 +113,7 @@ export function locateSymbol(info: AstResult, position: number): SymbolInfo|unde
const expressionPosition = templatePosition - ast.sourceSpan.start.offset;
if (inSpan(expressionPosition, ast.value.span)) {
const dinfo = diagnosticInfoFromTemplateInfo(info);
const scope = getExpressionScope(dinfo, path, /* includeEvent */ false);
const scope = getExpressionScope(dinfo, path);
const result =
getExpressionSymbol(scope, ast.value, templatePosition, info.template.query);
if (result) {

View File

@ -744,6 +744,15 @@ describe('completions', () => {
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
expectContain(completions, CompletionKind.PROPERTY, ['title']);
});
describe('$event completions', () => {
it('should suggest $event in event bindings', () => {
mockHost.override(TEST_TEMPLATE, `<div (click)="myClick(~{cursor});"></div>`);
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
expectContain(completions, CompletionKind.VARIABLE, ['$event']);
});
});
});
function expectContain(