JesseHerrick

JesseHerrick

Dexter - A fast, full-featured Elixir LSP optimized for large codebases

Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It supports a slew of LSP features like go-to-definition, go-to-references, hover docs, near instant formatting, renaming of modules, functions, and variables across the codebase, and more.

Remote’s Elixir codebase is enormous (over 57k files) so we have run into every bottleneck you can imagine with the existing LSPs. Expert works great on smaller codebases, but it can’t yet handle codebases of our size. On our codebase, we’re usually not even able to get it to finish indexing.

Dexter started as a CLI tool I wrote for myself to improve my search keybindings for jumping around files due to not having a working LSP. I quickly realized that I had built the foundations for an LSP without the server. So I added an LSP server in front and Dexter was born. I released Dexter internally at Remote last week and the feedback has been very positive, so I wanted to open source it to help others with similar issues and give back to the community.

Dexter takes a different approach than the other Elixir LSPs. Rather than attempting to compile the codebase and glean info from the compiled code and parsed AST, Dexter simply parses the code of your project and dependencies directly. I was surprised by how fast and accurate this approach can be.

Everything in Dexter is built for speed without sacrificing correctness. There are limitations to the pure text-based approach, but we’re able to work around those with clever parsing and deferring any complicated macro/import injection tracing to runtime.

I know that the Elixir community has consolidated around Expert, which I think has been a great move. Dexter takes a different approach to the compilation-based LSPs to solve the specific pains we’ve had at Remote with such a large Elixir codebase, so I imagine it will be very helpful for those working on large codebases (although it works great on small codebases too!). While not everything is applicable, I’d imagine that there are some optimizations that we did in Dexter that could be similarly considered in Expert. But that being said, we’re really enjoying using Dexter at Remote and plan to continue maintaining and improving it into the future.

Give it a try and let me know what you think!

To install (assuming you have SQLite installed, if not install it first):

mise plugin add dexter https://github.com/remoteoss/dexter.git && mise use -g dexter@latest

And then just configure your editor, either with the VS Code/Cursor extension, NeoVim, or your other favorite editor.

If you have any thoughts, feature requests, or bug reports, I’d love to hear them. Likewise, contributions are absolutely welcome to the project.


Features

  • Fast indexing — cold index completes in ~11s on a 57k-file Elixir monorepo, ~100ms on Oban, ~300ms on the Elixir standard library (measured on an M1 MacBook Pro). After your first index, incremental indexing makes sure that you never have to reindex the whole codebase again.
  • Go-to-definition — jump to any module, function, type, or variable definition. Resolves aliases, imports, defdelegate chains, use injections, and the Elixir stdlib. Handles all definition forms: def, defp, defmacro, defprotocol, defimpl, defstruct, and more.
  • Go-to-references — find all usages of a function or module across the codebase, including through import, use chains, and defdelegate.
  • Hover documentation@doc, @moduledoc, @typedoc, and @spec annotations rendered as Markdown when you hover over a symbol.
  • Autocompletion — modules, functions, types, and variables with full snippet support. Resolves through aliases, imports, use injections, and the Elixir stdlib. Works for qualified calls (MyApp.Repo.|), bare function calls, and module prefixes.
  • Rename — rename modules, functions, and variables with automatic file renaming when the convention is followed.
  • No compilation required — the index is built by parsing source files directly, not by compiling your project. Dexter works immediately on any codebase, even ones that don’t compile.
  • Monorepo and umbrella support — a single index at the repository root covers all apps and shared libraries. Go-to-definition, find references, and rename work cross-project out of the box.
  • Format on save — formats .ex, .exs, and .heex files on save via a persistent Elixir process. Near-instant after the first save. Formatter plugins (Styler, Phoenix.LiveView.HTMLFormatter) are loaded from your project’s _build — no install needed. Syntax errors are surfaced as diagnostics.
  • Elixir stdlib indexing — jump to Enum, String, Mix, and other bundled modules by indexing your local Elixir installation sources.
  • Signature help — parameter hints as you type function calls.
  • Workspace symbols — search for any module or function across the entire codebase.
  • Call hierarchy — navigate incoming and outgoing calls.
  • Code actions — add missing aliases with a single action.
  • Document symbols — outline view of all functions and modules in the current file.
  • Document highlight — highlight all occurrences of the symbol under the cursor.
  • Variable support — go-to-definition, rename, and completion for local variables via tree-sitter, with correct scoping across case, with, for, and other block constructs.
  • Git branch switch detection — automatically reindexes when you switch branches.

Checkout the GitHub Repo for more details:

https://github.com/remoteoss/dexter

Most Liked

lpil

lpil

Creator of Gleam

Nice work!

One thing I’ve not noticed anyone mention yet (unless I missed it!) is the security benefits of this approach.

Elixir compilation means that executing arbitrary Elixir code, and that can do anything at all. Previously if we wanted to audit a version of a package before adding it to our project (as we should!) we would have to browse the code without any LSP assistance, as doing so would execute not-yet-trusted code. Now with Dexter we can have a proper and modern development environment while exploring new code. Bravo!

hauleth

hauleth

For anyone interested - Nix derivation:

{
  buildGoModule,
  fetchFromGitHub,
  installShellFiles,
  lib,
  stdenv,
}:
let
  name = "dexter";
  version = "0.5.3";
  src = fetchFromGitHub {
    owner = "remoteoss";
    repo = name;
    tag = "v${version}";
    hash = "sha256-8JjxR7Q+4OgBSIgODxIEU/0mC+bPp9Nz7uCAjfn4HiY=";
  };
in
buildGoModule {
  pname = name;

  inherit version src;

  nativeBuildInputs = [ installShellFiles ];

  proxyVendor = true;

  postInstall = ''
    mv $out/bin/cmd $out/bin/dexter
  ''
  + (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
    installShellCompletion --cmd dexter \
      --bash <($out/bin/dexter completion bash) \
      --fish <($out/bin/dexter completion fish) \
      --zsh <($out/bin/dexter completion zsh)
  '');

  vendorHash = "sha256-1mJ4HdDCsZl/g8F+L+NrW2ACuiHe2aSheJO/1XfKAb4=";

  meta = {
    description = "Fast implementation of Elixir language server in Go";
    mainProgram = "dexter";
    homepage = "https://github.com/remoteoss/dexter";
    license = lib.licenses.mit;
    platforms = lib.platforms.all;
  };
}
egze

egze

Dexter is so fast! At first I was like: huh, it indexed the project already? It must be broken!

But no, it just works. Try it!

Where Next?

Popular in Announcing Top

OvermindDL1
I created a new library (rather I pulled out a couple files from my big project), it manages an operating system PID file for the BEAM. ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
mspanc
I am pleased to announce an initial release of the Membrane Framework - an Elixir-based framework with special focus on processing multim...
New
alisinabh
Hey everyone i’ve developed a library for Jalaali calendar for elixir which supports converting Gregorian dates to Jalaali and vice vers...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
kip
ex_cldr provides localisation and internationalisation support based upon the data from the Unicode CLDR project. Unicode released CLDR ...
407 12840 120
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
Crowdhailer
Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure). 1.0-rc.1 is now available. To use it re...
New
markmark206
simple_feature_flags is a tiny package that lets you turn features on or off based on which environment (e.g. localhost, staging, product...
New
OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13966 106
New

Other popular topics Top

albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43622 214
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement