# \[ANN\] Fatou: a fast Julia language server, formatter, and linter (that doesn't need Julia to work)

**URL:** <https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553>\
**Category:** Tooling\
**Tags:** lint, languageserver, formatting, lsp\
**Created:** [July 31, 2026, 7:46pm UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553 "2026-07-31T19:46:25Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jolars](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jolars/32/15493_2.png) [@jolars](https://discourse.julialang.org/u/jolars)\
**Post date:** [July 31, 2026, 7:46pm UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/1 "2026-07-31T19:46:26Z")

</div>

Hi all,

I’m happy to announce **Fatou** : a language server, formatter, and linter for Julia, that doesn’t need to run Julia itself.

- Repo: [GitHub - jolars/fatou: Julia language server, formatter, and linter built in Rust · GitHub](https://github.com/jolars/fatou)
- Docs: [https://fatou.dev](https://fatou.dev)

## Motivation

I kept running into the same friction: the tools I use while editing (formatting on save, a few lint diagnostics, a language server that starts instantly) all pay Julia’s startup and first-call compilation cost. That’s completely fine for a long-lived session, but it’s noticeable on the command line and in CI, and it makes editor integration heavier than I wanted.

So Fatou takes the other approach and parses Julia directly. It’s built on the same architecture as rust-analyzer: a lossless [`rowan`](https://crates.io/crates/rowan) CST, [`salsa`](https://crates.io/crates/salsa) for incremental recomputation, and [`lsp-server`](https://crates.io/crates/lsp-server) for the LSP transport. The parser is developed against [JuliaSyntax.jl](https://github.com/JuliaLang/JuliaSyntax.jl) as a differential oracle, so parity with the reference parser is a primary goal.

The other big motivation is to have one unified tool that does formatting, linting, and language server duties, exactly like ruff does for Python. This is both leaner and more consistent than having three separate tools, and also avoids the problem of having formatter and linter disagreeing.

The name, for the curious comes from Pierre Fatou, whose Fatou set is the complement of the Julia set.

## What it Does Today

Three things, all from one binary:

```sh
fatou format <file.jl> # format to stdout (or stdin)
fatou lint --check <dir> # lint
fatou lsp # language server over stdio

```

- **Formatter** : an opinionated formatter with a small config surface (line width, indent width). I’ve been growing it construct by construct against hand-written fixtures rather than trying to match any existing style byte-for-byte. It’s probably closest in style to Runic, but is line-width aware and also  
borrows a lot of ideas from Air (a formatter for R).
- **Linter** : a growing set of built-in rules: unused bindings, unused/duplicate arguments, unused imports, undefined names, `break` outside a loop, assignment-in-condition, `== nothing` comparisons, include cycles and missing include files, call arity, redefined constants, and more. Some rules include autofixes.
- **Language server** : Fatou provides a full-fledged LSP implementation. Itt provides completion, hover, go-to-definition, find references and document highlights, rename (with prepare), document and workspace symbols, call hierarchy and type hierarchy, signature help, code actions, folding ranges, selection ranges, document links, and semantic tokens, alongside formatting (whole-document and range) and diagnostics (both push and pull).

## Getting It

It ships through several channels so you can use whatever fits:

- [crates.io](http://crates.io): `cargo install fatou`
- npm: `npm install -g fatou-cli` (bundles a prebuilt binary)
- PyPI: `uv tool install fatou` or `pipx install fatou`
- Prebuilt binaries on the [releases page](https://github.com/jolars/fatou/releases)
- **VS Code/Open VSX** : the **Fatou** extension ([Marketplace](https://marketplace.visualstudio.com/items?itemName=jolars.fatou), [Open VSX](https://open-vsx.org/extension/jolars/fatou))
- Neovim and other editors: [setup guide](https://fatou.dev/guide/editors.html)

For CI there’s [fatou-action](https://github.com/jolars/fatou-action) for GitHub Actions and [fatou-pre-commit](https://github.com/jolars/fatou-pre-commit) for pre-commit hooks.

## On Performance

Since it’s a compiled binary, the cold-start story is the clear win: no runtime to spin up before it formats a file, which matters most on the command line and in CI. For the warm start case (an editor or language server that stays alive) it’s still fast but the gap to the Julia-native solutions naturally shrinks. I have a benchmark page in the docs that compares against [Runic](https://github.com/fredrikekre/Runic.jl) and [JuliaFormatter](https://github.com/domluna/JuliaFormatter.jl):

> **[Performance - Fatou](https://fatou.dev/performance.html)**
>
> Language server, formatter, and linter for Julia

## Status

Fatou is still young, so:

- The parser is still stabilizing. It’s lossless and correct across a large corpus (including the JuliaSyntax.jl test suite, the Julia source tree, and a large set of real-world packages), but I expect there are some corner cases that will still trip it up. If you find one, please report it.
- The formatter’s style is opinionated and still stabilizing. If it formats something in a way that looks wrong to you, please let me know.
- The linter’s rule set is deliberately small and conservative for now. The goal is for a non-intrusive set of rules that are useful to a wide audience.

## Feedback Wanted

I’m very interested in feedback, especially on:

- files it fails to parse (a snippet or a link is perfect),
- formatting that comes out ugly or surprising, and
- lint rules you wish existed.

Issues and discussion are welcome on the [tracker](https://github.com/jolars/fatou/issues). I hope some of you find it useful!

---

<div class="post-metadata">

**Author:** ![sinisterMage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sinistermage/32/222897_2.png) [@sinisterMage](https://discourse.julialang.org/u/sinisterMage)\
**Post date:** [August 1, 2026, 6:09am UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/2 "2026-08-01T06:09:02Z")

</div>

very cool 😃  
i actually took a very similar approach to yours with a Julia package manager project i am working on (by that i mean the small binary that dosent pay startup costs). it reminds me in spirit of bunJS as well.  
anyways, cool project and i wish you luck with it!

---

<div class="post-metadata">

**Author:** ![HanD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hand/32/213908_2.png) [@HanD](https://discourse.julialang.org/u/HanD)\
**Post date:** [August 5, 2026, 4:36pm UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/3 "2026-08-05T16:36:38Z")

</div>

Oh, I’ve been waiting for this for so long! The “official” LanguageServer.jl is slow and has (had?) several showstoppers that prevented me from using it on real-life projects. I’ve moved to [JETLS.jl](https://github.com/aviatesk/JETLS.jl), which did its job but isn’t particularly fast, either. So I have high hopes and great expectations for Fatou!

I’ve tried the linter and formatter on a project where I have been using JETLS and Runic.jl for formatting, and it ran fast and smooth. Formatting is indeed very similar to Runic, with a few noteworthy differences:

```diff
- samples = load_samples_recursively(
- config.data;
- basedir = dirname(config_path),
- )
+ samples = load_samples_recursively(config.data; basedir = dirname(
+ config_path,
+ ))

```

```diff
- job_metadata = Dict(
- "load_feature_space" => true,
- "job_kwargs" => merge(
- as_dict(config.job_kwargs),
- Dict(
- "featureset_ids" =>
- filter(!isnothing, featureset_ids)
- ),
- ),
- )
+ job_metadata = Dict("load_feature_space" => true, "job_kwargs" => merge(
+ as_dict(config.job_kwargs),
+ Dict("featureset_ids" => filter(!isnothing, featureset_ids)),
+ ))

```

Red is Runic, and green is Fatou. I find Runic’s formatting more readable: if the whole expression doesn’t fit on a single line, then it’s one argument per line.

Keep up the good work, I will probably come back with more feedback later!

---

<div class="post-metadata">

**Author:** ![jolars](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jolars/32/15493_2.png) [@jolars](https://discourse.julialang.org/u/jolars)\
**Post date:** [August 5, 2026, 5:03pm UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/4 "2026-08-05T17:03:34Z")

</div>

Thanks! I agree, Runic is better here. This is a hugging rule that I’ve implemented that’s inspired by the Air formatter (for R), but isn’t working as intended. I’ll fix this ASAP. Thanks!

---

<div class="post-metadata">

**Author:** ![songjhaha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/songjhaha/32/42503_2.png) [@songjhaha](https://discourse.julialang.org/u/songjhaha)\
**Post date:** [August 6, 2026, 6:10am UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/5 "2026-08-06T06:10:23Z")

</div>

Thanks for this great work!

Finally I could have a standalone parser without julia runtime.

And It’s more reliable with some many test and behaiver better than tree-sitter

---

<div class="post-metadata">

**Author:** ![jolars](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jolars/32/15493_2.png) [@jolars](https://discourse.julialang.org/u/jolars)\
**Post date:** [August 6, 2026, 6:37am UTC](https://discourse.julialang.org/t/ann-fatou-a-fast-julia-language-server-formatter-and-linter-that-doesnt-need-julia-to-work/138553/7 "2026-08-06T06:37:06Z")

</div>

Alright, I’ve fixed this issue now! It should look more or less identical to the Runic output now (in the development version).
