ChrisAmelia
Built a language server for learning purposes
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.
Trending in Discussions
Other Trending Topics
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #elixirconf-us
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









