Get down into Visual studio code
The following article wants to get down into Visual studio code and its source code.
In hip-hop music, the term Get down came from Grandmaster Flash. He was able to identify a ten-second drum beat that he would manually edit from one drum solo to another, across songs and genres — extending the beat, so that eventually an MC could rap over it. He says: “I came up with a system to manually take a drum break — a drum solo in most records were like ten seconds — so I had to come up with a way to take duplicate copies of a record and seamlessly take ten seconds and make it ten minutes. Once I was able to do this, I changed the way DJs played records.”. This is the origin of the Get down.
Well, this article is not about the get down, but like the get down, it tries to analyze, and go deep into someone else product, Visual studio code, in order to improve our daily code. By understanding practices and patterns behind a large-scale application like the popular open-source editor by Microsoft.
Overview on the project
As said before, Visual studio code is an open-source framework, powered by Microsoft, you can find the code on Github:
git clone https://github.com/Microsoft/vscode.git
The repository also provide a cool tutorial in order to setup dev environment on you machine and start to contribute on the project. By the way, if you take a look to the tags of the repository, you can see that there are two main technologies that stand behind Visual studio code: Typescript and Electron.
Typescript
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. I have already spoke about typescript in these articles: Introducing Typescript, Introducing Typescript: Language features, SOLID principles using Typescript, Inversion of control and Dependency injection in Typescript.
Typescript is very useful applied to large and distributed codebases, Visual studio code has based all its code on it.
Electron
Electron can essentially run you HTML, JS, CSS applications as client applications. It builds cross platform desktop apps and it works as a bridge between OS inputs and outputs and your application and provide an uniform layer.
Structure of the project
Visual Studio code was born for extendibility. A lot of features, for example languages, are usually supported and threat as extensions. However, the source code also implements a core
part, which contains all the core APIs of the editor.
The core
is partitioned into the following layers:
base
: provides general utilities used across others layers;platform
: defines service injection support and the common services for Visual studio code;editor
: wraps the code editor of Visual studio code, which is called “Monaco”;languages
: as said before, for historical reasons, not all languages are implemented as extensions (yet);workbench
: hosts the “Monaco” editor and provides some core components, such as: explorer, status Bar, or menu bar;
Base layer
Provides general utilities and UI building block. The base
layer is split into some sublayers: browser
, common
, node
, parts
, test
, worker
;
All the source code inside that layer is designed in order to help the development process, let’s focus on some code inside that layer, for example the collections.ts file:
As you can see, it provides some abstract interfaces they act like dictionaries, and extension methods, such as: forEach<T>
, groupBy<T>
which query collections. All the components inside that layer are used across services and this is the reason why they are designed to be abstract as much as possible.
Editor layer
It contains the definition of the code editor, which is called Monaco. The Monaco Editor is generated straight from Visual studio code’s sources with some shims around services the code needs to make it run in a web browser outside of its home.
It is another open source project of Microsoft, you can clone the repo from here:
git clone https://github.com/Microsoft/monaco-editor.git
Workbench layer
The workbench layer hosts Monaco and integrate all the editor components inside Visual studio code. Let’s take a quick look to the IHistoryService
interface:
It provides methods in order to navigate through the history of files. The interface is also declared as decorator on line 11
:
export const IHistoryService = createDecorator<IHistoryService>('historyService');
So, it can be use as dependency in the constructor injection. We will speak more in detail about the dependency injection system of Visual studio code in the next subsection.
Promises
The Visual studio code API represents asynchronous operations with promises. Promises handling is independent of a specific promise library. Promises are expressed in the API by the Thenable
-type. Thenable
represents the common denominator which is the then method.
Here is the definition of the Thenable
interface:
Thenable
simply wraps any kind of type in a promise, and provides then method in order to handle the promise. When the use of a promise is optional, the API indicates this by returning or
-types:
provideNumber(): any | Thenable<any>
Disposable pattern
The Visual studio code API uses the dispose pattern in order to avoid resource waste. The dispose pattern is primarily used in languages whose runtime environment have automatic garbage collection. Although, the Typescript will not implement the disposable pattern as out-of-box feature, see: issues/16459, Visual studio code implements this feature on definition type level, in order to represent a type which can release resources, such as events and timers:
Dependency injection services-based
The code is organised around services of which most are defined in the platform
layer. Services get to its clients via constructor injection.
As proposed for ES7, declaring a service dependency happens by adding a corresponding decoration to a constructor argument:
A service is defined by two parts:
- the interface of a service
- service identifier;
Let’s see an concrete example of service definition taken from platform
layer:
As you can see, the previous code define an interface which describes the IClipboardService
. It also creates the decorator, it will be used as parameter into the constructor injection. On the other side, the interface should be implemented by a concrete class:
Finally, the service can be used in the following way:
Dependency injection workflow
Visual studio code doesn’t depend on any kind of 3rd party plugin for dependency injection. Almost the entire logic is inside the platform
layer.
Let’s go in deep into the service instantiation implementation. We can find that kind of logic inside the instantiation.ts
file:
There are 3 key entities inside the instantiation.ts
file:
ServiceIdentifier<T>
: describe an instance of a generic service;createDecorator<T>(serviceId:string)
: provides an unique entry point to initialize new services;storeServiceDependency
: provides a way to store the instances of the services;
The createDecorator<T>
function accepts an serviceId
as unique argument: if the service is present and already initialized, then it return the service; On the other side, if the service is not present, then it creates a new instance and stores it inside the the initialized services collection by using the storeServiceDependency
.
Final thought
In conclusion, the article aim is get down into Visual studio code. Open source projects are a great opportunity to understand what stand behind large-scale applications, and analyze them is a great starting point in order to start contributing and being part of open source communities.
Here are some references about these topic:
https://code.visualstudio.com/docs/extensionAPI/patterns-and-principles
https://github.com/samueleresca/decoration-ioc (Made by joelday)
https://github.com/Microsoft/monaco-editor
Cover photo: SEATTLE PUBLIC MARKET BY CARL FUNSETH
Cheers 🙂