Educational project
I was always intrigued by how IDEs provide smart completions when you press certain keys, how the diagnostics hit right, and all the mechanics they provide behind the scenes.
So I decided to dive into the process behind it: the Language Server Protocol. I also wanted to learn Elixir and ended up building an educational language server in Elixir.
While this is educational, I have implemented some basic functionalities regarding English writing:
- Shows the definition of the word under the cursor by calling
vim.lsp.buf.hover()(default toKin Neovim). - Sends an error diagnostic if the document contains
VS Code. - Suggests a list of words for completion.
- Suggests some code actions: replace
VS CodewithNeovimor censor it withVS C*de.
Any feedback, especially on the Elixir part, is welcome. Iβm looking to get better in that language.
Architecture
- There are two
GenServer:InputServerandLSPServer. They are both initialized in the moduleApplicationby aSupervisor.
ββββββββββββββββ
βββββΊβ InputServer β
β ββββββββββββββββ
β
β
β
βββββββββββββββ initialize β
β Application ββββββββββββββββ€
β Supervisor β β
βββββββββββββββ β
β
β
β βββββββββββββ
βββββΊβ LSPServer β
βββββββββββββ
- This is where the life cycle starts:
InputServerlistens onstdio.
2.1.InputServersends that message toLSPServer.
2.2.LSPServerprocesses it and sends back a result toInputServer.
message
ββββββββ βββββββββββββββ
βclientββββββββββΊβ InputServer β
ββββββββ βββββ¬βββββ²βββββ
β β result
β β
β β
convey β β
ββββΌβββββ΄ββββ
β LSPServer β
βββββββββββββ
InputServersends the result back to the client and then loops back to listening onstdio.
ready to listen
ββββββββββββ
β β
β β
βΌ β
βββββββββββββββββ β
ββββββββββ result β βββββββββ
β client ββββββββββββββββββββ€ InputServer β
ββββββββββ β β
βββββββββββββββββ
Notes
I have encountered two challenges that I havenβt solved in the Elixir way.
When and how to properly shut down the application?
I have set two System.stop() at two different occasions, which I found redundant:
- when the client sends the
shutdownrequest, - when
:eofis received after listening withIO.read(:stdio, :line).
Process still runs in the background if a crash happens
In the life cycle of InputServer, if a crash happens (example String.split(nil, "\n")),
the process is still alive even after having closed Neovim, probably a zombie?
The only fix I have found was finding the source of the issue by checking the logs and correcting it.






















