Language Server Protocol
Give a pause speedy Gonzales! Take a moment and imagine a world without intellisense, code completion, go-to definition or any other features you use today to develop production-ready code. It might send chills up the hands to the head that generates the amazing code you write. Well, don’t forget there were days when developers used notepads to write code, and command window to compile and run it. Well, we moved on to a new universe or probably a parallel universe as many sci-fi movies fantasize about these days. In this universe, you have IDE (Integrated Development Environment) which magically does all that for numerous languages. Have you wondered how IDE’s features have increased over the past 5 years? This is a phenomenon that was not witnessed in the past 20 years.
At the heart of this exponential growth has been the humble, oblivious element called Language Server Protocol. A standard that built consensus amongst the IDE builders to agree on how the rich features are supported in various IDEs for various languages. The parallel universe where developers are still probably developing using notepad will be wrangling with syntax, typos, a lot of notepads opened for understanding code and many such things. Challenges like these will curtail your speed of writing code, the code which is the juice of your creativity. As much as it is Science the real production-grade code that we witness is art the creative way of expressing the dry and boring business rules into structured code; whatever way it may be – Object or Function.
There is not much that we can voice over this topic. Organisations have built this, and we consume it as the law of the land. Nothing is stopping us from summarizing it isn’t it? JSON is at the epicentre of this brewing consensus. So, the entire protocol is defined as a typescript interface with the exchange of JSON messages between the server and the client. The server is a process that runs within the IDE. You might have noticed in the output messages and in the errors that might have popped up if something went wrong. The client is; no prize for guessing, it is the IDE. Interesting things happen when a code file is opened in the IDE. As a client the IDE manages the events associated with opening, editing and other actions with the document. For those events, IDE sends appropriate JSON messages to the language server. This chatter between the IDE and language server is defined in the protocol. Let us take this one step further by inspecting an implementation
The implementation
One of the best ways to understand more about Language Server is by reading its implementation. Given the nature of this topic, this will not be the daily type of activity for a majority of the developer population. However, reading an implementation is a good way to learn and be aware for a developer. We will be taking a look at an implementation for the Svelte language . You can take a look at many other such implementations here .
The launchpad for us
We can easily see that typescript is the language used to build the server. The first thing to do for opening the bonnet is by inspecting the package.json. Let us do that –
|
{ |
“name”: “svelte-language-server”, |
“version”: “0.14.0”, |
“description”: “A language server for Svelte”, |
“main”: “dist/src/index.js”, |
“typings“: “dist/src/index”, |
“scripts”: { |
… |
“build”: “tsc“, |
… |
}, |
“bin”: { |
“svelteserver“: “bin/server.js” |
}, |
… |
“devDependencies“: { |
… |
}, |
“dependencies”: { |
“@jridgewell/trace-mapping”: “^0.3.9”, |
“@vscode/emmet-helper”: “^2.8.4”, |
“chokidar“: “^3.4.1”, |
“estree-walker”: “^2.0.1”, |
“fast-glob”: “^3.2.7”, |
“lodash“: “^4.17.21”, |
“prettier”: “2.8.0”, |
“prettier-plugin-svelte”: “~2.8.0”, |
“svelte”: “^3.49.0”, |
“svelte–preprocess“: “~4.10.1”, |
“svelte2tsx”: “~0.5.0”, |
“typescript”: “*”, |
“vscode-css-languageservice“: “~5.1.0”, |
“vscode-html-languageservice“: “~4.1.0”, |
“vscode-languageserver“: “7.1.0-next.4”, |
“vscode–languageserver-protocol”: “3.16.0”, |
“vscode–languageserver-types”: “3.16.0”, |
“vscode-uri“: “~3.0.0” |
} |
} |
Notable things here are –
index.ts
It is the skinniest of all but serves the purpose. The interface exports the essential components that per the Language Server Protocol gets invoked by the IDE in its lifecycle events.
|
export * from ‘./server’; |
export { offsetAt } from ‘./lib/documents’; |
export { SvelteCheck, SvelteCheckOptions, SvelteCheckDiagnosticSource } from ‘./svelte-check’; |
|
It seems the interesting one will be the server.ts. Let us anchor ourselves here until next dispatch. We will see next how server exports the startServer and attaches event handlers for connection initialisation. It also orchestrates the call to other language servers that we saw a while earlier.