Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(language-service): correctly parse expressions in an attribute #34517

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 5 additions & 6 deletions packages/language-service/src/completions.ts
Expand Up @@ -436,12 +436,11 @@ class ExpressionVisitor extends NullTemplateVisitor {
if (ast.name.startsWith('*')) {
this.microSyntaxInAttributeValue(ast, binding);
} else {
// If the position is in the expression or after the key or there is no key,
// return the expression completions
const span = new ParseSpan(0, ast.value.length);
const offset = ast.sourceSpan.start.offset;
const receiver = new ImplicitReceiver(span, span.toAbsolute(offset));
const expressionAst = new PropertyRead(span, span.toAbsolute(offset), receiver, '');
// If the position is in the expression or after the key or there is no key, return the
// expression completions.
// The expression must be reparsed to get a valid AST rather than only template bindings.
const expressionAst = this.info.expressionParser.parseBinding(
ast.value, ast.sourceSpan.toString(), ast.sourceSpan.start.offset);
ayazhafiz marked this conversation as resolved.
Show resolved Hide resolved
this.addAttributeValuesToCompletions(expressionAst);
}
}
Expand Down
13 changes: 13 additions & 0 deletions packages/language-service/test/completions_spec.ts
Expand Up @@ -358,6 +358,12 @@ describe('completions', () => {
expectContain(completions, CompletionKind.PROPERTY, ['test']);
});

it('should be able to complete property read', () => {
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'data-binding-property-read');
ayazhafiz marked this conversation as resolved.
Show resolved Hide resolved
const completions = ngLS.getCompletionsAt(PARSING_CASES, marker.start);
expectContain(completions, CompletionKind.PROPERTY, ['key']);
});

it('should be able to complete an event', () => {
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'event-binding-model');
const completions = ngLS.getCompletionsAt(PARSING_CASES, marker.start);
Expand Down Expand Up @@ -459,6 +465,13 @@ describe('completions', () => {
expectContain(completions, CompletionKind.PROPERTY, ['name', 'testEvent']);
});

it('should get reference property completions in a data binding', () => {
const marker =
mockHost.getLocationMarkerFor(PARSING_CASES, 'event-binding-ref-property-read');
ayazhafiz marked this conversation as resolved.
Show resolved Hide resolved
const completions = ngLS.getCompletionsAt(PARSING_CASES, marker.start);
expectContain(completions, CompletionKind.PROPERTY, ['name', 'testEvent']);
});

// TODO: Enable when we have a flag that indicates the project targets the DOM
// it('should reference the element if no component', () => {
// const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'test-comp-after-div');
Expand Down
7 changes: 6 additions & 1 deletion packages/language-service/test/project/app/parsing-cases.ts
Expand Up @@ -53,10 +53,13 @@ export class AttributeBinding {
}

@Component({
template: '<h1 [model]="~{property-binding-model}test"></h1>',
template: `
<h1 [model]="~{property-binding-model}test"></h1>
<h1 [model]="obj.~{data-binding-property-read}"></h1>`,
})
export class PropertyBinding {
test: string = 'test';
obj = {key: 'value'};
}

@Component({
Expand Down Expand Up @@ -126,6 +129,8 @@ export class AsyncForUsingComponent {
{{test1.~{test-comp-after-test}name}}
{{div.~{test-comp-after-div}.innerText}}
</test-comp>

<div (click)="test1.~{event-binding-ref-property-read}"></div>
</div>
<test-comp #test2></test-comp>`,
})
Expand Down