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:
parent
cb142b6df9
commit
d7ea389c84
|
@ -308,7 +308,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
|
||||||
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
||||||
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
||||||
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
||||||
return getExpressionScope(dinfo, templatePath, false);
|
return getExpressionScope(dinfo, templatePath);
|
||||||
});
|
});
|
||||||
if (templatePath.tail instanceof AttrAst ||
|
if (templatePath.tail instanceof AttrAst ||
|
||||||
templatePath.tail instanceof BoundElementPropertyAst ||
|
templatePath.tail instanceof BoundElementPropertyAst ||
|
||||||
|
@ -398,8 +398,7 @@ function interpolationCompletions(info: AstResult, position: number): ng.Complet
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const visitor = new ExpressionVisitor(
|
const visitor = new ExpressionVisitor(
|
||||||
info, position,
|
info, position, () => getExpressionScope(diagnosticInfoFromTemplateInfo(info), templatePath));
|
||||||
() => getExpressionScope(diagnosticInfoFromTemplateInfo(info), templatePath, false));
|
|
||||||
templatePath.tail.visit(visitor, null);
|
templatePath.tail.visit(visitor, null);
|
||||||
return visitor.results;
|
return visitor.results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@ export interface DiagnosticTemplateInfo {
|
||||||
|
|
||||||
export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): Diagnostic[] {
|
export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): Diagnostic[] {
|
||||||
const visitor = new ExpressionDiagnosticsVisitor(
|
const visitor = new ExpressionDiagnosticsVisitor(
|
||||||
info, (path: TemplateAstPath, includeEvent: boolean) =>
|
info, (path: TemplateAstPath) => getExpressionScope(info, path));
|
||||||
getExpressionScope(info, path, includeEvent));
|
|
||||||
templateVisitAll(visitor, info.templateAst);
|
templateVisitAll(visitor, info.templateAst);
|
||||||
return visitor.diagnostics;
|
return visitor.diagnostics;
|
||||||
}
|
}
|
||||||
|
@ -194,9 +193,9 @@ function refinedVariableType(
|
||||||
return query.getBuiltinType(BuiltinType.Any);
|
return query.getBuiltinType(BuiltinType.Any);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEventDeclaration(info: DiagnosticTemplateInfo, includeEvent?: boolean) {
|
function getEventDeclaration(info: DiagnosticTemplateInfo, path: TemplateAstPath) {
|
||||||
let result: SymbolDeclaration[] = [];
|
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>
|
// TODO: Determine the type of the event parameter based on the Observable<T> or EventEmitter<T>
|
||||||
// of the event.
|
// of the event.
|
||||||
result = [{name: '$event', kind: 'variable', type: info.query.getBuiltinType(BuiltinType.Any)}];
|
result = [{name: '$event', kind: 'variable', type: info.query.getBuiltinType(BuiltinType.Any)}];
|
||||||
|
@ -205,11 +204,11 @@ function getEventDeclaration(info: DiagnosticTemplateInfo, includeEvent?: boolea
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExpressionScope(
|
export function getExpressionScope(
|
||||||
info: DiagnosticTemplateInfo, path: TemplateAstPath, includeEvent: boolean): SymbolTable {
|
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
|
||||||
let result = info.members;
|
let result = info.members;
|
||||||
const references = getReferences(info);
|
const references = getReferences(info);
|
||||||
const variables = getVarDeclarations(info, path);
|
const variables = getVarDeclarations(info, path);
|
||||||
const events = getEventDeclaration(info, includeEvent);
|
const events = getEventDeclaration(info, path);
|
||||||
if (references.length || variables.length || events.length) {
|
if (references.length || variables.length || events.length) {
|
||||||
const referenceTable = info.query.createSymbolTable(references);
|
const referenceTable = info.query.createSymbolTable(references);
|
||||||
const variableTable = info.query.createSymbolTable(variables);
|
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});
|
{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 {
|
function hasTemplateReference(type: CompileTypeMetadata): boolean {
|
||||||
|
|
|
@ -37,7 +37,7 @@ export function locateSymbol(info: AstResult, position: number): SymbolInfo|unde
|
||||||
if (attribute) {
|
if (attribute) {
|
||||||
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
||||||
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
||||||
const scope = getExpressionScope(dinfo, path, inEvent);
|
const scope = getExpressionScope(dinfo, path);
|
||||||
if (attribute.valueSpan) {
|
if (attribute.valueSpan) {
|
||||||
const result = getExpressionSymbol(scope, ast, templatePosition, info.template.query);
|
const result = getExpressionSymbol(scope, ast, templatePosition, info.template.query);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -113,7 +113,7 @@ export function locateSymbol(info: AstResult, position: number): SymbolInfo|unde
|
||||||
const expressionPosition = templatePosition - ast.sourceSpan.start.offset;
|
const expressionPosition = templatePosition - ast.sourceSpan.start.offset;
|
||||||
if (inSpan(expressionPosition, ast.value.span)) {
|
if (inSpan(expressionPosition, ast.value.span)) {
|
||||||
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
||||||
const scope = getExpressionScope(dinfo, path, /* includeEvent */ false);
|
const scope = getExpressionScope(dinfo, path);
|
||||||
const result =
|
const result =
|
||||||
getExpressionSymbol(scope, ast.value, templatePosition, info.template.query);
|
getExpressionSymbol(scope, ast.value, templatePosition, info.template.query);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
|
@ -744,6 +744,15 @@ describe('completions', () => {
|
||||||
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
|
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
|
||||||
expectContain(completions, CompletionKind.PROPERTY, ['title']);
|
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(
|
function expectContain(
|
||||||
|
|
Loading…
Reference in New Issue