Skip to content

Commit ee657f4

Browse files
beyangjeanp413
authored andcommitted
add doc/sourcedive.snb.md
1 parent 86a3678 commit ee657f4

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

doc/sourcedive.snb.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Interactive introduction to the codebase
2+
3+
This is a technical deep dive into how OpenVSCode Server turns VS Code into a web IDE, with interactive code search queries and snippets. This is [best viewed on Sourcegraph](https://sourcegraph.com/github.com/sourcegraph/openvscode-server/-/blob/doc/sourcedive.snb.md).
4+
5+
The code snippets in this file correspond to search queries and can be displayed by clicking the blue "Run search" button to the right of each query. For example, here is a snippet that shows off an instance of dependency injection within VS Code:
6+
7+
```sourcegraph
8+
patterntype:structural repo:^github\.com/gitpod-io/openvscode-server$@5c8a1f file:^src/vs/code/browser/workbench/workbench\.ts create(document.body, {:[1]})
9+
```
10+
11+
## Architectural overview
12+
13+
OpenVSCode Server is a fork of VS Code that extends the editor to be runnable in the browser, speaking to a web server that provides a remote dev environment.
14+
15+
Upstream VS Code consists of [layers](https://github.com/microsoft/vscode/wiki/Source-Code-Organization):
16+
17+
* `base`: Provides general utilities and user interface building blocks.
18+
* `platform`: Defines service injection support and the base services for VS Code.
19+
* `editor`: The "Monaco" editor is available as a separate downloadable component.
20+
* `workbench`: Hosts the "Monaco" editor and provides the framework for "viewlets" like the Explorer, Status Bar, or Menu Bar, leveraging Electron to implement the VS Code desktop application.
21+
* `code`: The entry point to the desktop app that stitches everything together, this includes the Electron main file and the CLI for example.
22+
23+
OpenVSCode Server adds an additional [`server` layer](https://github.com/gitpod-io/openvscode-server/tree/main/src/vs/server). The client side remains largely unchanged, save for the injection of RPC-based handlers for things like filesystem and terminal interactions, in place of local handlers. The `server` layer has 3 main components:
24+
25+
* Web-based workbench
26+
* Remote server
27+
* Remote CLI
28+
29+
The web-based workbench lives on the client side and is the place where the RPC-based dependencies are injected. VS Code's codebase is modular and makes heavy use of dependency injection, which makes it easier to substitute different implementations. The entrypoint into the web-based workbench is in [workbench.ts](https://sourcegraph.com/github.com/gitpod-io/openvscode-server/-/blob/src/vs/code/browser/workbench/workbench.ts). In that file, the `create` function creates the workbench using dependency injection:
30+
31+
```sourcegraph
32+
patterntype:structural repo:/gitpod-io/openvscode-server$@5c8a1f fork:yes file:server/browser/workbench/workbench.ts create(document.body, :[2])
33+
```
34+
35+
The workbench talks to the remote server via RPC. There are 2 RPC channels:
36+
37+
* The management connection handles filesystem and terminal requests
38+
* The extension connection creates the remote extension host process and handles extension-related requests
39+
40+
Let's take a look at how those connections are set up on the server side. The main entrypoint into the server lives in [`server.main.ts`](https://sourcegraph.com/github.com/gitpod-io/openvscode-server@5c8a1f/-/blob/src/vs/server/node/server.main.ts?L11):
41+
42+
```sourcegraph
43+
repo:^github\.com/gitpod-io/openvscode-server$@5c8a1f file:^src/vs/server/node/server\.main\.ts export async function main
44+
```
45+
46+
Of particular note in the `main` function is `channelServer`, which registers different service channels for handling different types of requests received from the client, such as logging, debugging, and filesystem requests.
47+
48+
49+
```sourcegraph
50+
repo:^github\.com/gitpod-io/openvscode-server$@5c8a1f file:^src/vs/server/node/server\.main\.ts const channelServer =
51+
```
52+
53+
These channels then relay the requests to the appropriate service implementations. Lower in the `main` function, a `ServiceCollection` instance is used as a dependency injection container that holds all the concrete implementations of the various service types:
54+
55+
```sourcegraph
56+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts Const services = new ServiceCollection()
57+
```
58+
59+
Then the whole bundle is wrapped by an HTTP server, which is the outermost container that handles requests from the client:
60+
61+
```sourcegraph
62+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts const server = http.createServer
63+
```
64+
65+
Some of the handler endpoints correspond to static resources:
66+
67+
```sourcegraph
68+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts if (pathname === '/vscode-remote-resource')
69+
```
70+
71+
Then there are the endpoints that upgrade a HTTP request to a WebSocket connection:
72+
73+
```sourcegraph
74+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts server.on('upgrade',
75+
```
76+
77+
The aforementioned management connection and extension connection use these WebSocket connections. The management connection connects `channelServer` with your editor window, including requests for handling terminal and fileystem requests.
78+
79+
On the extension side, there's a special protocol over WebSocket that initiates a handshake to set up the remote extension host process:
80+
81+
```sourcegraph
82+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts const controlListener = protocol.onControlMessage
83+
```
84+
85+
The extension connection will fork the extension host process and connect the user's editor window. Among other things, keystrokes are sent down this connection, as they may be relevant to extensions in use.
86+
87+
88+
## Startup
89+
90+
Now, let's walk through what happens at startup.
91+
92+
On server startup, first we create the channel server and register channels to handle RPC calls and events from the web workbench:
93+
94+
```sourcegraph
95+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 const channelServer = new IPCServer
96+
```
97+
98+
99+
Then we create the service collection (effectively the dependency injection container for service implementations, as described earlier) with all services required for the RPC channels:
100+
101+
```sourcegraph
102+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts services.set(IRawURITransformerFactory, rawURITransformerFactory)
103+
```
104+
105+
Then we instantiate these services and start the HTTP server:
106+
107+
```sourcegraph
108+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts const clients = new Map<string, Client>()
109+
```
110+
111+
When a user tries to access the server, the web workbench is served by HTTP listener:
112+
113+
```sourcegraph
114+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 file:^src/vs/server/node/server\.main\.ts return handleRoot(req, res, devMode ? options.mainDev ||
115+
```
116+
117+
The web workbench first loads 3rd party dependencies like xterm:
118+
119+
```sourcegraph
120+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 xterm': `${window.location.origin} file:workbench-dev.html
121+
```
122+
123+
...and then itself:
124+
125+
```sourcegraph
126+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 require(['vs/server/browser/workbench/workbench'],
127+
```
128+
129+
The web workbench uses dependency injection to configure how to establish WebSockets, load static resources, load webviews, and so on:
130+
131+
```sourcegraph
132+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 create(document.body, { file:src/vs/server/
133+
```
134+
135+
When the web workbench is created, it opens WebSocket connections to the server:
136+
137+
```sourcegraph
138+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 if (req.headers['upgrade'] !== 'websocket' || !req.url)
139+
```
140+
141+
There is one connection to the RPC channel server to notify about a new client:
142+
143+
```sourcegraph
144+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 onDidClientConnectEmitter.fire({ protocol, onDidClientDisconnect: onDidClientDisconnectEmitter.event })
145+
```
146+
147+
...and another for the extension host process which is running remote extensions:
148+
149+
```sourcegraph
150+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 const extensionHost = cp.fork(FileAccess.asFileUri
151+
```
152+
153+
When a user creates a new terminal the management connection is used to call the remote terminal channel:
154+
155+
```sourcegraph
156+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 return this.createProcess(context.remoteAuthority, args);
157+
```
158+
159+
...which delegates to pseudo terminal service:
160+
161+
```sourcegraph
162+
repo:^github\.com/gitpod-io/openvscode-server$@8b3e975 const persistentTerminalId = await this.ptyService.createProcess
163+
```
164+
165+
The terminal service then enacts the corresponding action and relays the response back through the request chain covered above.
166+
167+
## Diving in
168+
169+
This hopefully gives you a good overview of how OpenVSCode Server turns VS Code into a web-based IDE. The code is completely open-source and released by Gitpod. You can try out [Gitpod](https://www.gitpod.io/) as a service or dive into more of the [source code on Sourcegraph](https://sourcegraph.com/github.com/gitpod-io/openvscode-server).

0 commit comments

Comments
 (0)