Skip to content

Commit b1dc80a

Browse files
christianalfoniCompuIveslbogdan
authored
WIP: Language servers for containers (codesandbox#3348)
* split ext-host bootstrappers into client and container * Default to stream * Make it a flag * added Websocket LSP on containers * run websocket thingy * correctly pass message back * Connect with pid to tsserver proxy * new hardcoded id * handle client side flipping of extension hosts, pids, endpoint etc. * change lsp endpoint * also handle STAGING env * hardcode stream * changed to socket-io * WIP - gotodefinition * add go-to-definition * fix mounting path for go-to-definition * same socket typing handling * single tsserver * - update websocket URL generation logic; - update typings mount. * Handle flipping the experiment * fix experiments endpoint * Fix types * properly flip state of containerLsp experiment * show warning and refresh when changing experiment * Add readonly * fix readonly * add message for experimental sandbox Co-authored-by: Ives van Hoorne <[email protected]> Co-authored-by: Bogdan Luca <[email protected]>
1 parent db8244e commit b1dc80a

File tree

18 files changed

+702
-191
lines changed

18 files changed

+702
-191
lines changed

packages/app/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"@svgr/core": "^2.4.1",
8383
"@typeform/embed": "^0.12.0",
8484
"@types/rc-slider": "^8.6.5",
85+
"@types/socket.io-client": "^1.4.32",
8586
"@vue/babel-preset-app": "^3.2.0",
8687
"airtable": "^0.5.8",
8788
"apollo-boost": "^0.4.4",
@@ -162,9 +163,9 @@
162163
"normalizr": "^3.2.3",
163164
"onigasm": "^2.2.1",
164165
"ot": "^0.0.15",
165-
"overmind": "^22.0.0-1580488381342",
166+
"overmind": "^22.0.3",
166167
"overmind-devtools": "^19.0.0",
167-
"overmind-react": "^23.0.0-1580488381342",
168+
"overmind-react": "^23.0.3",
168169
"phoenix": "^1.4.11",
169170
"postcss": "^7.0.26",
170171
"postcss-selector-parser": "^2.2.3",

packages/app/src/app/overmind/effects/api/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,9 @@ export default {
478478
})
479479
.then(data => data.template);
480480
},
481+
updateExperiments(experiments: { [key: string]: boolean }): Promise<void> {
482+
return api.post(`/users/experiments`, {
483+
experiments,
484+
});
485+
},
481486
};

packages/app/src/app/overmind/effects/vscode/extensionHostWorker/bootstrappers/ext-host.ts renamed to packages/app/src/app/overmind/effects/vscode/extensionHostWorker/bootstrappers/client-ext-host.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import VueWorker from 'worker-loader?publicPath=/&name=vue-worker.[hash:8].worke
1111
import { initializeAll } from '../common/global';
1212

1313
childProcess.addDefaultForkHandler(DefaultWorkLoader);
14+
1415
childProcess.addForkHandler(
1516
'/extensions/node_modules/typescript/lib/tsserver.js',
1617
TSWorker
@@ -26,10 +27,10 @@ childProcess.addForkHandler(
2627

2728
initializeAll().then(() => {
2829
// Preload the TS worker for fast init
29-
childProcess.preloadWorker(
30+
childProcess.preloadForkHandler(
3031
'/extensions/node_modules/typescript/lib/tsserver.js'
3132
);
3233

3334
// eslint-disable-next-line
34-
require('../workers/ext-host-worker');
35+
import('../workers/ext-host-worker');
3536
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as childProcess from 'node-services/lib/child_process';
2+
// @ts-ignore
3+
import DefaultWorkLoader from 'worker-loader?publicPath=/&name=dynamic-worker.[hash:8].worker.js!./generic-1';
4+
// @ts-ignore
5+
import SvelteWorker from 'worker-loader?publicPath=/&name=svelte-worker.[hash:8].worker.js!./svelte-worker';
6+
// @ts-ignore
7+
import VueWorker from 'worker-loader?publicPath=/&name=vue-worker.[hash:8].worker.js!./vue-worker';
8+
9+
import { initializeAll } from '../common/global';
10+
import { WebsocketLSP } from '../services/WebsocketLSP';
11+
12+
childProcess.addDefaultForkHandler(DefaultWorkLoader);
13+
14+
childProcess.addForkHandler(
15+
'/extensions/node_modules/typescript/lib/tsserver.js',
16+
() => new WebsocketLSP()
17+
);
18+
childProcess.addForkHandler(
19+
'/extensions/octref.vetur.0.16.2/server/dist/vueServerMain.js',
20+
VueWorker
21+
);
22+
childProcess.addForkHandler(
23+
'/extensions/jamesbirtles.svelte-vscode-0.7.1/node_modules/svelte-language-server/bin/server.js',
24+
SvelteWorker
25+
);
26+
27+
initializeAll().then(() => {
28+
// Preload the TS worker for fast init
29+
childProcess.preloadForkHandler(
30+
'/extensions/node_modules/typescript/lib/tsserver.js'
31+
);
32+
33+
// eslint-disable-next-line
34+
import('../workers/ext-host-worker');
35+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { commonPostMessage } from '@codesandbox/common/lib/utils/global';
2+
import {
3+
IForkHandler,
4+
IForkHandlerCallback,
5+
} from 'node-services/lib/child_process';
6+
import io from 'socket.io-client';
7+
8+
// We want to make sure that our pids are consistent.
9+
// By counting pids based on endpoint we should consistently
10+
// pass the correct pid number related to the language server,
11+
// though it does require VSCode to always fire up the language
12+
// servers in the same order. The pid count decreases as
13+
// the WebsocketLSP is disposed
14+
const pidCountByEndpoint: {
15+
[endpoint: string]: number;
16+
} = {};
17+
18+
export class WebsocketLSP implements IForkHandler {
19+
io: typeof io.Socket;
20+
listeners = new Set<IForkHandlerCallback>();
21+
22+
messagesQueue: any[] = [];
23+
endpoint: string;
24+
25+
constructor() {
26+
const instance = this;
27+
self.addEventListener('message', function listener(message) {
28+
if (message.data.$type === 'respond_lsp_endpoint') {
29+
self.removeEventListener('message', listener);
30+
instance.connect(message.data.$data);
31+
}
32+
});
33+
commonPostMessage({
34+
$type: 'request_lsp_endpoint',
35+
});
36+
}
37+
38+
private connect(endpoint: string) {
39+
if (!(endpoint in pidCountByEndpoint)) {
40+
pidCountByEndpoint[endpoint] = 0;
41+
}
42+
43+
this.endpoint = endpoint;
44+
this.io = io(
45+
endpoint + `?type=language-server&pid=${pidCountByEndpoint[endpoint]++}`
46+
);
47+
this.io.on('connect', () => {
48+
this.messagesQueue.forEach(message => {
49+
this.postMessage(message);
50+
});
51+
});
52+
this.io.on('connect_error', error => {
53+
// eslint-disable-next-line
54+
console.log('WEBSOCKET_LSP - ERROR', error);
55+
});
56+
this.io.on('disconnect', reason => {
57+
console.error('WEBSOCKET_LSP - CLOSE', reason);
58+
});
59+
60+
this.io.on('language-server', data => {
61+
/*
62+
const json = event.data.split('\n').find(line => line[0] === '{');
63+
console.log('OUT', JSON.stringify(JSON.parse(json), null, 2));
64+
*/
65+
this.listeners.forEach(listener => {
66+
listener({
67+
data: {
68+
$data: `Content-Length: ${data.length + 1}\r\n\r\n${data}\n`,
69+
$type: 'stdout',
70+
},
71+
});
72+
});
73+
});
74+
}
75+
76+
postMessage(message) {
77+
if (message.$type === 'input-write') {
78+
if (this.io.connected && message.$data) {
79+
/*
80+
console.log('IN', JSON.stringify(JSON.parse(message.$data), null, 2));
81+
*/
82+
this.io.emit('language-server', message.$data);
83+
} else if (message.$data) {
84+
this.messagesQueue.push(message);
85+
}
86+
}
87+
}
88+
89+
// Since setting up the connection is ASYNC, we rather create a list
90+
// of listeners. They all use "message" event anyways
91+
addEventListener(_: string, callback: IForkHandlerCallback) {
92+
this.listeners.add(callback);
93+
}
94+
95+
removeEventListener(_: string, callback: IForkHandlerCallback) {
96+
this.listeners.delete(callback);
97+
}
98+
99+
terminate() {
100+
// eslint-disable-next-line
101+
console.log('WEBSOCKET_LSP - TERMINATE');
102+
pidCountByEndpoint[this.endpoint]--;
103+
this.io.close();
104+
}
105+
}

packages/app/src/app/overmind/effects/vscode/extensionHostWorker/workers/ext-host-worker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const ctx: any = self;
1717

1818
self.addEventListener('message', async e => {
1919
const { data } = e;
20+
2021
if (data.$type === 'worker-manager') {
2122
if (data.$event === 'init') {
2223
debug('Initializing BrowserFS');

0 commit comments

Comments
 (0)