refactor(language-service): Consistent naming between ts and ng LanguageService (#34888)

This commit makes the Angular Language Service interface a strict subset
of TypeScript's Language Service by renaming all methods to be
consistent with TypeScript's.

The custom Angular `LanguageService` interface was needed before the
inception of TypeScript tsserver plugin, but is now obsolete since
Angular LS is a proper tsserver plugin.

This allows us to easily adapt to upstream TS changes in the future, and
also allows us to reuse all data types defined in TypeScript.

PR Close #34888
This commit is contained in:
Keen Yee Liau
2020-01-21 14:51:43 -08:00
committed by Matias Niemelä
parent c1823f83cc
commit 065c788875
8 changed files with 189 additions and 221 deletions

View File

@ -39,7 +39,7 @@ describe('diagnostics', () => {
it('should produce no diagnostics for test.ng', () => {
// there should not be any errors on existing external template
expect(ngLS.getDiagnostics('/app/test.ng')).toEqual([]);
expect(ngLS.getSemanticDiagnostics('/app/test.ng')).toEqual([]);
});
it('should not return TS and NG errors for existing files', () => {
@ -52,14 +52,14 @@ describe('diagnostics', () => {
expect(syntaxDiags).toEqual([]);
const semanticDiags = tsLS.getSemanticDiagnostics(file);
expect(semanticDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(file);
const ngDiags = ngLS.getSemanticDiagnostics(file);
expect(ngDiags).toEqual([]);
}
});
it('should report error for unexpected end of expression', () => {
const content = mockHost.override(TEST_TEMPLATE, `{{ 5 / }}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
const {messageText, start, length} = diags[0];
expect(messageText)
@ -73,7 +73,7 @@ describe('diagnostics', () => {
// https://github.com/angular/vscode-ng-language-service/issues/242
it('should support $any() type cast function', () => {
mockHost.override(TEST_TEMPLATE, `<div>{{$any(title).xyz}}</div>`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags).toEqual([]);
});
@ -84,7 +84,7 @@ describe('diagnostics', () => {
];
for (const template of templates) {
mockHost.override(TEST_TEMPLATE, template);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText).toBe('Unable to resolve signature for call of method $any');
}
@ -95,7 +95,7 @@ describe('diagnostics', () => {
<div *ngFor="let h of heroes | slice:0:1">
{{h.name}}
</div>`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags).toEqual([]);
});
@ -104,7 +104,7 @@ describe('diagnostics', () => {
<div *ngFor="let h of heroes | slice:0:1">
{{h.age}}
</div>`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toBe(`Identifier 'age' is not defined. 'Hero' does not contain such a member`);
@ -116,7 +116,7 @@ describe('diagnostics', () => {
<span (click)="greet()"></span>
</ng-template>
`);
const diagnostics = ngLS.getDiagnostics(TEST_TEMPLATE);
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diagnostics).toEqual([]);
});
@ -136,7 +136,7 @@ describe('diagnostics', () => {
{{ i === isFirst }}
</div>
`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText).toBe(`Expected the operants to be of similar type or any`);
});
@ -148,7 +148,7 @@ describe('diagnostics', () => {
{{ i < 2 }}
</div>
`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(0);
});
});
@ -157,7 +157,7 @@ describe('diagnostics', () => {
it('should work with numeric index signatures (arrays)', () => {
mockHost.override(TEST_TEMPLATE, `
{{heroes[0].badProperty}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
@ -167,7 +167,7 @@ describe('diagnostics', () => {
it('should work with index notation', () => {
mockHost.override(TEST_TEMPLATE, `
{{heroesByName['Jacky'].badProperty}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
@ -176,7 +176,7 @@ describe('diagnostics', () => {
it('should work with dot notation', () => {
mockHost.override(TEST_TEMPLATE, `
{{heroesByName.jacky.badProperty}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
@ -185,7 +185,7 @@ describe('diagnostics', () => {
it('should not produce errors with dot notation if stringIndexType is a primitive type',
() => {
mockHost.override(TEST_TEMPLATE, `{{primitiveIndexType.test}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(0);
});
});
@ -194,7 +194,7 @@ describe('diagnostics', () => {
it('should produce diagnostics for invalid tuple type property access', () => {
mockHost.override(TEST_TEMPLATE, `
{{tupleArray[1].badProperty}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
@ -203,7 +203,7 @@ describe('diagnostics', () => {
it('should not produce errors if tuple array index out of bound', () => {
mockHost.override(TEST_TEMPLATE, `
{{tupleArray[2].badProperty}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags).toEqual([]);
});
@ -211,13 +211,13 @@ describe('diagnostics', () => {
mockHost.override(TEST_TEMPLATE, `
<test-comp (test)="myClick.bind(this)">
</test-comp>`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags).toEqual([]);
});
describe('in expression-cases.ts', () => {
it('should report access to an unknown field', () => {
const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
expect(diags).toContain(
`Identifier 'foo' is not defined. ` +
`The component declaration, template variable declarations, ` +
@ -225,25 +225,25 @@ describe('diagnostics', () => {
});
it('should report access to an unknown sub-field', () => {
const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
expect(diags).toContain(
`Identifier 'nam' is not defined. 'Person' does not contain such a member`);
});
it('should report access to a private member', () => {
const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
expect(diags).toContain(`Identifier 'myField' refers to a private member of the component`);
});
it('should report numeric operator errors', () => {
const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
expect(diags).toContain('Expected a numeric type');
});
});
describe('in ng-for-cases.ts', () => {
it('should report an unknown field', () => {
const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(NG_FOR_CASES).map(d => d.messageText);
expect(diags).toContain(
`Identifier 'people_1' is not defined. ` +
`The component declaration, template variable declarations, ` +
@ -251,12 +251,12 @@ describe('diagnostics', () => {
});
it('should report an unknown context reference', () => {
const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(NG_FOR_CASES).map(d => d.messageText);
expect(diags).toContain(`The template context does not define a member called 'even_1'`);
});
it('should report an unknown value in a key expression', () => {
const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(NG_FOR_CASES).map(d => d.messageText);
expect(diags).toContain(
`Identifier 'trackBy_1' is not defined. ` +
`The component declaration, template variable declarations, ` +
@ -266,7 +266,7 @@ describe('diagnostics', () => {
describe('in ng-if-cases.ts', () => {
it('should report an implicit context reference', () => {
const diags = ngLS.getDiagnostics(NG_IF_CASES).map(d => d.messageText);
const diags = ngLS.getSemanticDiagnostics(NG_IF_CASES).map(d => d.messageText);
expect(diags).toContain(`The template context does not define a member called 'unknown'`);
});
});
@ -275,7 +275,7 @@ describe('diagnostics', () => {
it('should not report diagnostic on iteration of any', () => {
const fileName = '/app/test.ng';
mockHost.override(fileName, '<div *ngFor="let value of anyValue">{{value.someField}}</div>');
const diagnostics = ngLS.getDiagnostics(fileName);
const diagnostics = ngLS.getSemanticDiagnostics(fileName);
expect(diagnostics).toEqual([]);
});
@ -287,7 +287,7 @@ describe('diagnostics', () => {
</div>
</div>
`);
const diagnostics = ngLS.getDiagnostics(TEST_TEMPLATE);
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
@ -300,14 +300,14 @@ describe('diagnostics', () => {
it('should accept an event', () => {
const fileName = '/app/test.ng';
mockHost.override(fileName, '<div (click)="myClick($event)">Click me!</div>');
const diagnostics = ngLS.getDiagnostics(fileName);
const diagnostics = ngLS.getSemanticDiagnostics(fileName);
expect(diagnostics).toEqual([]);
});
it('should reject it when not in an event binding', () => {
const fileName = '/app/test.ng';
const content = mockHost.override(fileName, '<div [tabIndex]="$event"></div>');
const diagnostics = ngLS.getDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
@ -325,7 +325,7 @@ describe('diagnostics', () => {
template: '<div *ngFor></div> ~{after-div}'
})
export class MyComponent {}`);
expect(() => ngLS.getDiagnostics(fileName)).not.toThrow();
expect(() => ngLS.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should report a component not in a module', () => {
@ -334,7 +334,7 @@ describe('diagnostics', () => {
template: '<div></div>'
})
export class MyComponent {}`);
const diagnostics = ngLS.getDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
@ -356,7 +356,7 @@ describe('diagnostics', () => {
export class AppComponent {}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -366,7 +366,7 @@ describe('diagnostics', () => {
template: '<a *ngIf="(auth.isAdmin | async) || (event.leads && event.leads[(auth.uid | async)])"></a>'
})
export class MyComponent {}`);
expect(() => ngLS.getDiagnostics(fileName)).not.toThrow();
expect(() => ngLS.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should not throw using a directive with no value', () => {
@ -377,7 +377,7 @@ describe('diagnostics', () => {
export class MyComponent {
name = 'some name';
}`);
expect(() => ngLS.getDiagnostics(fileName)).not.toThrow();
expect(() => ngLS.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should report an error for invalid metadata', () => {
@ -395,7 +395,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT) !;
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
expect(ngDiags.length).toBe(1);
const {messageText, start, length} = ngDiags[0];
const keyword = `() => 'foo'`;
@ -417,7 +417,7 @@ describe('diagnostics', () => {
@Component({
template: ''
}) class`);
expect(() => ngLS.getDiagnostics(fileName)).not.toThrow();
expect(() => ngLS.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should not report an error for sub-types of string in non-strict mode', () => {
@ -435,7 +435,7 @@ describe('diagnostics', () => {
});
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -454,7 +454,7 @@ describe('diagnostics', () => {
});
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -468,7 +468,7 @@ describe('diagnostics', () => {
export class AppComponent {
onClick() { }
}`);
const diagnostics = ngLS.getDiagnostics(APP_COMPONENT) !;
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
const {messageText, start, length} = diagnostics[0];
expect(messageText).toBe('Unexpected callable expression. Expected a method call');
const keyword = `"onClick"`;
@ -492,7 +492,7 @@ describe('diagnostics', () => {
});
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -509,7 +509,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags.length).toBe(1);
const {messageText, start, length} = ngDiags[0];
expect(messageText).toBe(`The pipe 'dat' could not be found`);
@ -529,7 +529,7 @@ describe('diagnostics', () => {
export class AppComponent {}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -558,7 +558,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -576,7 +576,7 @@ describe('diagnostics', () => {
const msgText = ts.flattenDiagnosticMessageText(tsDiags[0].messageText, '\n');
expect(msgText).toBe(
`Type 'null[]' is not assignable to type 'Provider[]'.\n Type 'null' is not assignable to type 'Provider'.`);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags.length).toBe(1);
const {messageText, start, length} = ngDiags[0];
expect(messageText)
@ -604,7 +604,7 @@ describe('diagnostics', () => {
export class AppComponent {}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -626,7 +626,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -650,7 +650,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(APP_COMPONENT);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(ngDiags).toEqual([]);
});
@ -674,7 +674,7 @@ describe('diagnostics', () => {
});
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const diagnostic = ngLS.getDiagnostics(APP_COMPONENT);
const diagnostic = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(diagnostic).toEqual([]);
});
@ -710,7 +710,7 @@ describe('diagnostics', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
const diagnostics = ngLS.getDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeDefined();
@ -727,7 +727,7 @@ describe('diagnostics', () => {
})
export class MyComponent {}`);
const diagnostics = ngLS.getDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeUndefined();
@ -741,7 +741,7 @@ describe('diagnostics', () => {
selector: 'app-example',
})
export class AppComponent {}`);
const diags = ngLS.getDiagnostics(APP_COMPONENT);
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(diags.length).toBe(1);
const {file, messageText, start, length} = diags[0];
expect(file !.fileName).toBe(APP_COMPONENT);
@ -760,7 +760,7 @@ describe('diagnostics', () => {
templateUrl: './test.ng',
})
export class AppComponent {}`);
const diags = ngLS.getDiagnostics(APP_COMPONENT);
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(diags.length).toBe(1);
const {file, messageText, start, length} = diags[0];
expect(file !.fileName).toBe(APP_COMPONENT);
@ -779,7 +779,7 @@ describe('diagnostics', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
const diagnostics = ngLS.getDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeDefined();
@ -799,7 +799,7 @@ describe('diagnostics', () => {
})
export class AppComponent {}`);
const diagnostics = ngLS.getDiagnostics(APP_COMPONENT) !;
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
expect(diagnostics.length).toBe(0);
});
});
@ -812,7 +812,7 @@ describe('diagnostics', () => {
// Source span information is lost in the process.
const content = mockHost.override(
TEST_TEMPLATE, '\r\n<div>\r\n{{line0}}\r\n{{line1}}\r\n{{line2}}\r\n</div>');
const ngDiags = ngLS.getDiagnostics(TEST_TEMPLATE);
const ngDiags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(ngDiags.length).toBe(3);
for (let i = 0; i < 3; ++i) {
const {messageText, start, length} = ngDiags[i];
@ -831,7 +831,7 @@ describe('diagnostics', () => {
const fileName = mockHost.addCode(
'\n@Component({template:`\r\n\r\n{{line}}`})export class ComponentCRLF {}');
const content = mockHost.readFile(fileName) !;
const ngDiags = ngLS.getDiagnostics(fileName);
const ngDiags = ngLS.getSemanticDiagnostics(fileName);
expect(ngDiags.length).toBeGreaterThan(0);
const {messageText, start, length} = ngDiags[0];
expect(messageText)
@ -854,7 +854,7 @@ describe('diagnostics', () => {
`);
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(fileName);
const ngDiags = ngLS.getSemanticDiagnostics(fileName);
expect(ngDiags).toEqual([]);
});
});