diff --git a/integration/language_service_plugin/goldens/definition.json b/integration/language_service_plugin/goldens/definition.json
new file mode 100644
index 0000000000..8d7bf0cc93
--- /dev/null
+++ b/integration/language_service_plugin/goldens/definition.json
@@ -0,0 +1,20 @@
+{
+ "seq": 0,
+ "type": "response",
+ "command": "definition",
+ "request_seq": 2,
+ "success": true,
+ "body": [
+ {
+ "file": "${PWD}/project/app/app.component.ts",
+ "start": {
+ "line": 7,
+ "offset": 30
+ },
+ "end": {
+ "line": 7,
+ "offset": 47
+ }
+ }
+ ]
+}
diff --git a/integration/language_service_plugin/goldens/quickinfo.json b/integration/language_service_plugin/goldens/quickinfo.json
new file mode 100644
index 0000000000..66a9771dcf
--- /dev/null
+++ b/integration/language_service_plugin/goldens/quickinfo.json
@@ -0,0 +1,22 @@
+{
+ "seq": 0,
+ "type": "response",
+ "command": "quickinfo",
+ "request_seq": 2,
+ "success": true,
+ "body": {
+ "kind": "angular",
+ "kindModifiers": "what does this do?",
+ "start": {
+ "line": 5,
+ "offset": 26
+ },
+ "end": {
+ "line": 5,
+ "offset": 30
+ },
+ "displayString": "property name of AppComponent",
+ "documentation": "",
+ "tags": []
+ }
+}
\ No newline at end of file
diff --git a/integration/language_service_plugin/matcher.ts b/integration/language_service_plugin/matcher.ts
index b46edbf3bc..fc4b4b3180 100644
--- a/integration/language_service_plugin/matcher.ts
+++ b/integration/language_service_plugin/matcher.ts
@@ -1,4 +1,4 @@
-import { writeFileSync } from 'fs';
+import { writeFileSync, readFileSync } from 'fs';
const goldens: string[] = process.argv.slice(2);
@@ -6,16 +6,18 @@ export const goldenMatcher: jasmine.CustomMatcherFactories = {
toMatchGolden(util: jasmine.MatchersUtil): jasmine.CustomMatcher {
return {
compare(actual: {command: string}, golden: string): jasmine.CustomMatcherResult {
- const expected = require(`./goldens/${golden}`);
- const pass = util.equals(actual, expected);
- if (!pass && goldens.indexOf(golden) >= 0) {
+ if (goldens.includes(golden)) {
console.error(`Writing golden file ${golden}`);
writeFileSync(`./goldens/${golden}`, JSON.stringify(actual, null, 2));
return { pass : true };
}
+ const content = readFileSync(`./goldens/${golden}`, 'utf-8');
+ const expected = JSON.parse(content.replace("${PWD}", process.env.PWD!));
+ const pass = util.equals(actual, expected);
return {
pass,
- message: `Expected response for '${actual.command}' to match golden file ${golden}.\n` +
+ message: `Expected ${JSON.stringify(actual, null, 2)} to match golden ` +
+ `${JSON.stringify(expected, null, 2)}.\n` +
`To generate new golden file, run "yarn golden ${golden}".`,
};
}
diff --git a/integration/language_service_plugin/package.json b/integration/language_service_plugin/package.json
index c5036b18a1..3fe1900776 100644
--- a/integration/language_service_plugin/package.json
+++ b/integration/language_service_plugin/package.json
@@ -13,7 +13,7 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"cleanup": "rm -rf ti-*.log tsserver.log",
- "golden": "node generate.js",
+ "golden": "yarn build && node generate.js",
"test": "yarn cleanup && yarn build && jasmine test.js"
}
}
diff --git a/integration/language_service_plugin/test.ts b/integration/language_service_plugin/test.ts
index 0147ed1ebb..0875a7f1d3 100644
--- a/integration/language_service_plugin/test.ts
+++ b/integration/language_service_plugin/test.ts
@@ -50,7 +50,6 @@ describe('Angular Language Service', () => {
// https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1055
client.sendRequest('open', {
file: `${PWD}/project/app/app.module.ts`,
- fileContent: ""
});
// Server does not send response to geterr request
// https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1770
@@ -77,7 +76,6 @@ describe('Angular Language Service', () => {
client.sendRequest('open', {
file: `${PWD}/project/app/app.component.ts`,
- fileContent: "import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n template: `
Hello {{name}}
`,\n})\nexport class AppComponent { name = 'Angular'; }\n"
});
client.sendRequest('geterr', {
@@ -101,4 +99,44 @@ describe('Angular Language Service', () => {
});
expect(response).toMatchGolden('completionInfo.json');
});
+
+ it('should perform quickinfo', async () => {
+ client.sendRequest('open', {
+ file: `${PWD}/project/app/app.component.ts`,
+ });
+
+ const resp1 = await client.sendRequest('reload', {
+ file: `${PWD}/project/app/app.component.ts`,
+ tmpFile: `${PWD}/project/app/app.component.ts`,
+ }) as any;
+ expect(resp1.command).toBe('reload');
+ expect(resp1.success).toBe(true);
+
+ const resp2 = await client.sendRequest('quickinfo', {
+ file: `${PWD}/project/app/app.component.ts`,
+ line: 5,
+ offset: 28,
+ });
+ expect(resp2).toMatchGolden('quickinfo.json');
+ });
+
+ it('should perform definition', async () => {
+ client.sendRequest('open', {
+ file: `${PWD}/project/app/app.component.ts`,
+ });
+
+ const resp1 = await client.sendRequest('reload', {
+ file: `${PWD}/project/app/app.component.ts`,
+ tmpFile: `${PWD}/project/app/app.component.ts`,
+ }) as any;
+ expect(resp1.command).toBe('reload');
+ expect(resp1.success).toBe(true);
+
+ const resp2 = await client.sendRequest('definition', {
+ file: `${PWD}/project/app/app.component.ts`,
+ line: 5,
+ offset: 28,
+ });
+ expect(resp2).toMatchGolden('definition.json');
+ });
});
diff --git a/integration/language_service_plugin/tsclient.ts b/integration/language_service_plugin/tsclient.ts
index 05dd3c0e0d..b35356a824 100644
--- a/integration/language_service_plugin/tsclient.ts
+++ b/integration/language_service_plugin/tsclient.ts
@@ -15,51 +15,56 @@ export class Client {
listen() {
this.server.stdout.on('data', (data: Buffer) => {
this.data = this.data ? Buffer.concat([this.data, data]) : data;
+ // tsserver could batch multiple responses together so we have to go
+ // through the entire buffer to keep looking for messages.
const CONTENT_LENGTH = 'Content-Length: '
- const index = this.data.indexOf(CONTENT_LENGTH);
- if (index < 0) {
- return;
- }
- let start = index + CONTENT_LENGTH.length;
- let end = this.data.indexOf('\r\n', start);
- if (end < start) {
- return;
- }
- const contentLengthStr = this.data.slice(start, end).toString();
- const contentLength = Number(contentLengthStr);
- if (isNaN(contentLength) || contentLength < 0) {
- return;
- }
- start = end + 4;
- end = start + contentLength;
- if (end > this.data.length) {
- return;
- }
- const content = this.data.slice(start, end).toString();
- this.data = this.data.slice(end);
- try {
- const payload = JSON.parse(content);
- if (payload.type === "event") {
+ do {
+ const index = this.data.indexOf(CONTENT_LENGTH);
+ if (index < 0) {
return;
}
- this.responseEmitter.emit('response', payload);
- }
- catch (error) {
- this.responseEmitter.emit('error', error);
- }
+ let start = index + CONTENT_LENGTH.length;
+ let end = this.data.indexOf('\r\n', start);
+ if (end < start) {
+ return;
+ }
+ const contentLengthStr = this.data.slice(start, end).toString();
+ const contentLength = Number(contentLengthStr);
+ if (isNaN(contentLength) || contentLength < 0) {
+ return;
+ }
+ start = end + 4;
+ end = start + contentLength;
+ if (end > this.data.length) {
+ return;
+ }
+ const content = this.data.slice(start, end).toString();
+ this.data = this.data.slice(end);
+ try {
+ const payload = JSON.parse(content);
+ if (payload.type === "response") {
+ const seq = `${payload.request_seq}`;
+ this.responseEmitter.emit(seq, payload);
+ }
+ }
+ catch (error) {
+ this.responseEmitter.emit('error', error);
+ }
+ } while (this.data.length > 0)
});
}
async send(type: string, command: string, params: {}) {
+ const seq = this.id++;
const request = {
- seq: this.id++,
+ seq,
type,
command,
arguments: params
};
this.server.stdin.write(JSON.stringify(request) + '\r\n');
return new Promise((resolve, reject) => {
- this.responseEmitter.once('response', resolve);
+ this.responseEmitter.once(`${seq}`, resolve);
this.responseEmitter.once('error', reject);
});
}
diff --git a/integration/language_service_plugin/tsconfig.json b/integration/language_service_plugin/tsconfig.json
index 9730ac2fe3..bb0a08cec4 100644
--- a/integration/language_service_plugin/tsconfig.json
+++ b/integration/language_service_plugin/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
- "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
+ "target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */