Zqlu Ant Financial · Data Experience Technology team
VS Code is a new tool that combines the simplicity of a Code editor with the tools that application developers need in their develop-build-debug process. Code provides comprehensive editing and debugging support, an extensible model, and lightweight integration with existing tools.
This is the introduction on VSCode Github warehouse. Now, the number of VSCode Github stars has reached 47,000, and VSCode adopts Electron, The Monaco code editor is also used by Visual Studio Team Service (Visual Studio Online). In terms of language, VSCode uses TypeScript.
Before starting the analysis of THE source code of VSCode itself, first look at the dependence of VSCode Electron, understand the Electron can better understand the code organization and dependence of VSCode; The second is the dependency injection pattern used in VSCode source code.
Electron
Electron is a framework for developing desktop applications using HTML, JavaScript, and CSS frontend. We can take a look at the sample quick start application provided on Electron’s website:
Package. json is defined as follows, notice the main field and start script:
{
"name": "electron-quick-start"."version": "1.0.0"."description": "A minimal Electron application"."main": "main.js"."scripts": {
"start": "electron ."
},
"repository": "https://github.com/electron/electron-quick-start"."keywords": ["Electron"."quick"."start"."tutorial"."demo"]."author": "GitHub"."license": "CC0-1.0"."devDependencies": {
"electron": "~ 1.7.8"}}Copy the code
Then look at the main.js script:
const electron = require('electron');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const url = require('url');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({ width: 800.height: 600 });
// and load the index.html of the app.
mainWindow.loadURL(
url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:'.slashes: true,}));// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed'.function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed'.function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if(process.platform ! = ='darwin') { app.quit(); }}); app.on('activate'.function() {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) { createWindow(); }});// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Copy the code
As you can see, the main script mainly defines the application of several event handlers. In the ready event handler, a BrowseWindow object is created and the index.html page is loaded.
Renderer.js is loaded with the script tag in index.html:
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
<! -- All of the Node.js APIs are available in this renderer process. -->
We are using Node.js <script>document.write(process.versions.node)</script>,
Chromium <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
<script>
// You can also require other files to run in this process
require('./renderer.js')
</script>
</body>
</html>
Copy the code
At this point, the Electron quick launch instance application is complete, and after executing NPM start, you can see the contents of the index.html displayed on the screen.
The first thing we need to know about the two types of processes we will encounter in Electron above, and the differences between them, are called the main process and the renderer process.
Let’s start with the main and renderer definitions:
In Electron, the process where the main script in package.json runs is the main process, and the script running in the main process creates a Web page to display the user interface. A Electron application always has one and only one main process. Since Electron uses Chromium to display web pages, the multi-process architecture in Chromium is also used. Each Web page in Electron runs in its own rendering process. In normal browsers, Web pages usually run in a sandbox environment and are not allowed to touch native resources. However, Electron users can perform some low-level interactions with the operating system on the page with the support of the Node.js API.
The difference between the main and renderer processes:
The main process creates the page using BrowseWindow instances, and each BrowseWindow instance runs the page in its own rendering process. When a BrowseWindow instance is destroyed, the rendering process is terminated. The main process manages all the Web pages and their corresponding renderers. Each renderer process is independent and cares about the Web page it runs on.
Of interest to developers is what apis are available to scripts in the main and render processes respectively.
First, the Electron API provides a wealth of apis, some of which can only be used in the main process, some of which can only be used in the renderer process, and some of which can be used by both main and renderer processes.
Then for node.js apis, as well as third-party NPM packages, both the main process and the renderer process can be used directly.
Finally, since the rendering process runs in Chromium pages, there are also browser-provided apis such as DOM manipulation apis.
API | The main process | Rendering process |
---|---|---|
Electron API | Part of the | Part of the |
Node.js API/module | is | is |
The browser API | no | is |
After learning about Electron, we will see later which code in VSCode runs in the main process and which code runs in the renderer process.
Dependency injection
Dependency injection is a design pattern that may not be used much by front-end developers, but it is everywhere in VSCode’s source code, so here is a brief introduction. Let’s start with the definition of dependency injection:
In software engineering, dependency injection is a design pattern of objects that provide dependencies for a class of objects. The dependent object is called Service, and injection refers to passing the dependent object Service to the object that uses the Service (called Client), so that the Client does not need to actively establish (new) the dependent Service Service, and does not need to obtain the dependent Service through the factory mode.
In a typical dependency injection pattern, there are several types of roles:
- Objects that are relied upon and used, i.e
Service
- The client object that uses the service, i.e
Client
- The interface definition for the customer to use the service,
Interface
- Injector: Is responsible for creating and supplying service objects to clients, and usually also for creating Client objects
The most common form of dependency injection is constructor dependency injection: clients declare dependent services in constructor arguments, as shown in TypeScript code:
class Client { constructor(serviceA: ServiceA, serviceB: ServiceB) {this.servicea = serviceA;} // The Client can store the dependent Service in its own state: this.servicea = serviceA; this.serviceB = serviceB; }}Copy the code
In this mode, the Client does not need to construct the required Service object. One of the advantages of this mode is that the construction and behavior of the object are separated. After the interface is introduced, the dependency relationship between the Client and Service only needs the interface to define. The Client needs to rely on the service interface in the constructor parameters. Combined with the injector, the Client object can be more flexible and decouple.
Finally, in VSCode’s source code, most of the basic functions are implemented as service objects. The definition of a service is divided into two parts:
- Service interface
- Service identification: implemented through decorators in TypeScript
When a Client declares a dependent Service, it also declares it in the constructor argument as follows:
class Client { constructor( @IModelService modelService: IModelService, @optional(IEditorService) editorService: IEditorService, ) { // ... this.modelService = modelService; this.editorService = editorService; }}Copy the code
Here, the declared Client object depends on IModelService and IEditorService. The decorator @iModelService is the identifier of ModelService. The IModelService is just a TypeScript interface definition; @optional(IEditorService) is the EditorService identifier and is declared as an optional dependency by the optional decoration.
Finally, if the code is actually using the Client object, the instantiationService provided by the injector needs to instantiate the instance to the Client:
const myClient = instantiationService.createInstance(Client);
Copy the code
The source code organization
Now that we know about Electron and dependency injection, we can take a look at VSCode’s own source code organization.
VSCode Core
First of all, VSCode is made up of its core, which implements the basic code editor, and the built-in Extensions VSCode workbench. It also provides an extension API that allows built-in extensions and third-party extensions to extend the capabilities of VSCode Core.
Source code for Core is divided into the following directories:
src/vs/base
: Defines basic tool methods and basic DOM UI controlssrc/vs/code
The Monaco Editor code Editor: contains the Monaco Editor, which is packaged and published separately, and parts that are available only in VSCodesrc/vs/platform
: dependency injection implementation and the underlying Services used by VSCodesrc/vs/workbench
: an implementation of VSCode desktop application workbenchsrc/vs/code
: VSCode Entrance of the Electron application, including the main process script entrance of the Electron
Second, since VSCode relies on Electron, and we mentioned above that Electron has the main process and the render process, but they don’t have the API they can use, each directory in VSCode Core is organized according to the API they can use. Each subdirectory under Core is divided into the following categories according to the target environment in which the code is running:
common
: uses only JavaScript API source code, may run in any environmentbrowser
: You need to use the source code of the API provided by the browser, such as DOM manipulationnode
: Need to useNode.js
API source code providedelectron-browser
Need to use the source code of the Electron rendering process APIelectron-main
Need to use the source code of the Electron main process API
According to the above rules, source code in SRC/VS/Workbench /browser can only use the basic JavaScript API and the browser-provided API, And SRC/vs/workbench/electron – in the browser source code, you can use JavaScript API browser API, Node. Js API, and in the process of the electron rendering API.
VSCode Extensions
In the VSCode repository, in addition to the SRC/VS Core described above, there is a large chunk of the built-in vs Code extensions, whose source code is located within extensions.
First VSCode acts as a code editor, but has extensions for various code editing features such as syntax highlighting, completion hints, validation, etc. Therefore, a large part of VSCode’s built-in extensions are supported by various programming languages, such as: Extensions \ HTML, Extensions \javascript, extensions\ CPP, etc. Most language extensions have TextMate syntax definitions such as.tmtheme,.tmLanguage, etc.
Another type of built-in extension is the VSCode body extension, such as the VSCode default extensions/ Theme-defaults.
reference
- Electron applied structure
- Wiki: Code Organization
- Dependency Injection
- InversifyJS
This article briefly covers the Electron application structure, the dependency injection design pattern, and the VSCode source code and general organization before reading the source code.
Next preview: from the command line to enter code command to appear VSCode desktop application, VSCode is the execution process is what?
If you are interested in our team, you can follow our column, follow Github or send your resume to ‘tao.qit####alibaba-inc.com’.replace(‘####’, ‘@’)
Original address: github.com/ProtoTeam/b…