fix(language-service): Add directive selectors & banana-in-a-box to completions (#33311)

This commit refactors attribute completions and fixes two bugs:
1. selectors for directives are not provided
2. banana-in-a-box (two way binding) syntax are not provided

PR closes https://github.com/angular/vscode-ng-language-service/issues/358

PR Close #33311
This commit is contained in:
Keen Yee Liau
2019-10-21 18:49:32 -07:00
committed by Andrew Kushnir
parent c0b90c2010
commit 49eec5d872
4 changed files with 130 additions and 163 deletions

View File

@ -40,7 +40,7 @@ describe('completions', () => {
}
});
it('should be able to return element directives', () => {
it('should be able to return component directives', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'empty');
const completions = ngLS.getCompletionsAt(APP_COMPONENT, marker.start);
expectContain(completions, CompletionKind.COMPONENT, [
@ -51,6 +51,12 @@ describe('completions', () => {
]);
});
it('should be able to return attribute directives', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'h1-after-space');
const completions = ngLS.getCompletionsAt(APP_COMPONENT, marker.start);
expectContain(completions, CompletionKind.ATTRIBUTE, ['string-model', 'number-model']);
});
it('should be able to return angular pseudo elements', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'empty');
const completions = ngLS.getCompletionsAt(APP_COMPONENT, marker.start);
@ -279,7 +285,18 @@ describe('completions', () => {
expectContain(completions, CompletionKind.METHOD, ['modelChanged']);
});
it('should be able to complete a two-way binding', () => {
it('should be able to complete a the LHS of a two-way binding', () => {
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'two-way-binding-input');
const completions = ngLS.getCompletionsAt(PARSING_CASES, marker.start);
expectContain(completions, CompletionKind.ATTRIBUTE, [
'ngModel',
'[ngModel]',
'(ngModelChange)',
'[(ngModel)]',
]);
});
it('should be able to complete a the RHS of a two-way binding', () => {
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'two-way-binding-model');
const completions = ngLS.getCompletionsAt(PARSING_CASES, marker.start);
expectContain(completions, CompletionKind.PROPERTY, ['test']);
@ -288,7 +305,7 @@ describe('completions', () => {
it('should work with input and output', () => {
const m1 = mockHost.getLocationMarkerFor(PARSING_CASES, 'string-marker');
const c1 = ngLS.getCompletionsAt(PARSING_CASES, m1.start);
expectContain(c1, CompletionKind.ATTRIBUTE, ['[model]', '(model)']);
expectContain(c1, CompletionKind.ATTRIBUTE, ['[model]', '(modelChange)', '[(model)]']);
const m2 = mockHost.getLocationMarkerFor(PARSING_CASES, 'number-marker');
const c2 = ngLS.getCompletionsAt(PARSING_CASES, m2.start);
@ -347,7 +364,7 @@ describe('completions', () => {
function expectContain(
completions: ts.CompletionInfo | undefined, kind: CompletionKind, names: string[]) {
expect(completions).toBeDefined();
expect(completions !.entries).toEqual(jasmine.arrayContaining(names.map(name => {
return jasmine.objectContaining({name, kind});
}) as any));
for (const name of names) {
expect(completions !.entries).toContain(jasmine.objectContaining({ name, kind } as any));
}
}

View File

@ -69,7 +69,9 @@ export class EventBinding {
}
@Component({
template: '<h1 [(model)]="~{two-way-binding-model}test"></h1>',
template: `
<h1 [(model)]="~{two-way-binding-model}test"></h1>
<input ~{two-way-binding-input}></input>`,
})
export class TwoWayBinding {
test: string = 'test';
@ -80,7 +82,7 @@ export class TwoWayBinding {
})
export class StringModel {
@Input() model: string = 'model';
@Output() modelChanged: EventEmitter<string> = new EventEmitter();
@Output() modelChange: EventEmitter<string> = new EventEmitter();
}
@Directive({
@ -88,7 +90,7 @@ export class StringModel {
})
export class NumberModel {
@Input('inputAlias') model: number = 0;
@Output('outputAlias') modelChanged: EventEmitter<number> = new EventEmitter();
@Output('outputAlias') modelChange: EventEmitter<number> = new EventEmitter();
}
@Component({