Hi everyone,
I want to share an experimental Crystal project I have been working on:
The short version: it started as an attempt to build a better Crystal LSP, and
over time grew into a larger compiler experiment with a new frontend, HIR/MIR
pipeline, LLVM backend, and bootstrap work.
This is not an official Crystal replacement, and the compiler/codegen side is
still beta. The LSP server is currently the most usable and stable part.
Why I started with LSP
My original motivation was developer experience. Crystal is a great language,
but for larger projects the editor experience can still be rough: slow first
responses, incomplete navigation, hover/definition gaps, semantic coloring
issues, and expensive re-analysis after small changes.
So I started with the LSP problem:
-
fast hover and go-to-definition;
-
better source-backed signatures;
-
semantic tokens that work on real Crystal/stdlib code;
-
completion/signature help without loading too much of the dependency graph;
-
document symbols, folding, formatting, rename, references, inlay hints, and
call hierarchy;
- VS Code integration that can run side by side with the existing
crystal
compiler, using crystal2 tool lsp or a configured server path.
The LSP now has a focused regression suite. The latest local gate is:
spec/lsp: 266 examples, 0 failures
It has also been tested on real projects, including Crystal V2 itself and
DiamondDB.
What is different from existing Crystal LSPs?
The main goal is not just “another LSP server”, but a compiler-backed language
server with aggressive caching and fast foreground paths.
Some design points:
- Lazy foreground work. Opening a file should not eagerly build every index
if the first user action only needs hover, definition, or document symbols.
- AST/project/prelude caches. The server keeps reusable summaries and avoids
redoing unchanged work where possible.
- Fast navigation paths. Many hover/definition requests avoid dependency
graph loading and use source-backed method/type information directly.
- Semantic token optimization. Large semantic-token responses are cached,
persisted for unchanged disk-backed files, and support LSP full/delta
responses.
- Real-world edge cases. Recent fixes cover Crystal operator methods,
generated numeric conversions, macro calls, lib fun receivers, scoped aliases,
qualified receivers, callable parameters, and semantic coloring inside
case/when branches.
- VS Code configurability. The extension no longer hardcodes a repo-local
server path. It can discover crystal2, crystal_v2, or crystal_v2_lsp,
and the path/args can be overridden in settings.
This is still young software, but the current LSP is usable enough that I would
like other Crystal users to try it and report concrete failures.
Why did this turn into code generation?
While building a good LSP, I kept running into the same underlying need: the
language server wants compiler-quality parsing, name resolution, type
information, macro awareness, and stable source mapping.
At that point it became natural to ask: if the frontend and semantic model are
being rebuilt anyway, can we also experiment with a compiler architecture that
is easier to cache, inspect, test, and eventually bootstrap?
That led to the current compiler pipeline:
Crystal source
-> parser / arena AST
-> HIR
-> MIR
-> LLVM IR
-> native binary
The bigger long-term idea is a compiler architecture with:
-
better incremental and cached analysis;
-
explicit IR boundaries for debugging;
-
source/HIR/MIR/LLVM equivalence checks across bootstrap stages;
-
method-level reachability analysis;
-
room for memory-management experiments such as stack/slab/ARC/GC strategy
selection;
- a shared compiler core that can power both codegen and the LSP.
Current compiler/codegen status
The honest status: the compiler is not ready as a production replacement.
What works:
-
the V2 compiler can be built from the current Crystal compiler;
-
stage1 can build a generated stage2 compiler;
-
many focused no-prelude/codegen/bootstrap regressions pass;
-
the repository has HIR/MIR/LLVM infrastructure and many regression guards;
-
there is a spec-first contract layer under
docs/specs/for bootstrap
invariants.
What does not work yet:
-
generated stage2 is still unstable on broader full-prelude compilation;
-
clean
stage1 -> stage2 -> stage3bootstrap is still in progress; -
codegen has known bugs and should be treated as experimental;
-
it is not a drop-in replacement for the official Crystal compiler.
So my recommendation today is:
-
try the LSP if you want editor tooling;
-
look at the compiler/codegen side if you are interested in compiler
internals, experiments, or helping with bootstrap bugs.
Things I would like Crystal V2 to eventually enable
Some of these are already partly implemented; some are still design goals:
-
very fast editor feedback on large Crystal files;
-
reliable source-backed hover/definition/signature help;
-
better semantic coloring for Crystal-specific constructs and operators;
-
incremental compilation-style dependency tracking;
-
inspectable HIR/MIR/LLVM outputs for debugging compiler bugs;
-
a bootstrap corridor with normalized semantic equivalence checks:
original -> stage1 -> stage2 -> stage3 -> ...;
-
room to experiment with memory strategies beyond today’s default model;
-
better tooling hooks around formatting, symbols, call hierarchy, and later
debugging support.
How to try it
Repository:
Build the LSP server:
git clone https://github.com/skuznetsov/crystal_lsp
cd crystal_lsp
./build_lsp.sh
The VS Code extension is in:
vscode-extension/
It can use:
crystal2 tool lsp
or a manually configured server path:
{
"crystalv2.lsp.serverPath": "/path/to/crystal2",
"crystalv2.lsp.serverArgs": ["tool", "lsp"]
}
Useful docs:
-
README: crystal_lsp/README.md at main · skuznetsov/crystal_lsp · GitHub
-
VS Code extension: crystal_lsp/vscode-extension at main · skuznetsov/crystal_lsp · GitHub
-
Bootstrap/spec contracts: crystal_lsp/docs/specs at main · skuznetsov/crystal_lsp · GitHub
-
Architecture overview: crystal_lsp/docs/architecture_overview.md at main · skuznetsov/crystal_lsp · GitHub
-
Codegen architecture: crystal_lsp/docs/codegen_architecture.md at main · skuznetsov/crystal_lsp · GitHub
Feedback wanted
The most useful feedback would be:
- LSP bugs on real Crystal projects, especially hover/definition/completion
misses;
-
slow files or slow request traces;
-
semantic coloring gaps;
-
crashes with a small reproducer;
-
codegen/bootstrap contributors who enjoy reducing compiler edge cases.
If you try it, please include:
-
OS and Crystal version;
-
how you launched the server;
-
the smallest
.crsnippet or repo/file where it fails; -
what the current Crystal tooling does versus Crystal V2.
Again: this is experimental, and the compiler side is not production-ready.
But the LSP is now stable enough that I think it is worth broader testing.