Skip to content

Make formatting work, and say which formatter is behind it - #115

Merged
thomasjm merged 34 commits into
mainfrom
formatters
Oct 9, 2026
Merged

thomasjm merged 34 commits into
mainfrom
formatters

Conversation

@thomasjm

@thomasjm thomasjm commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Went through every language server to see whether formatting works and what backs it.

Fixed where it didn't:

  • Rust: rust-analyzer shells out to rustfmt from PATH, which wasn't there, so every format request failed with Failed to spawn "rustfmt". Added it (19 MB; rustc/cargo were already in the closure).
  • Ruby: Solargraph gates formatting behind an initialization option that defaults off. Turned on via kernels.ruby.lsp.solargraph.formatting; it formats with rubocop, already a gem dependency with a config in this repo.

Added a setting where there's more than one formatter to choose from, all of them already built into the server except Python's:

Setting Values Default
kernels.go.lsp.gopls.formatter gofmt, gofumpt gofmt
kernels.haskell.lsp.haskell-language-server.formattingProvider ormolu, fourmolu, stylish-haskell, none ormolu
kernels.python3.lsp.python-lsp-server.formatter autopep8, yapf, black, ruff, none autopep8
exporters.typst.lsp.tinymist.formatter typstyle, typstfmt, none typstyle

clangd, clojure-lsp, LanguageServer.jl, R's languageserver and typescript-language-server needed nothing.

The feature matrix's Formatting column is derived, not hand-written: each language server declares passthru.formatters, and the column counts as supported only when the server advertises formatting and a formatter is named — which is exactly the bash/shfmt failure mode. The formatter's name shows in the SVG cell's tooltip and in the table in FEATURE-MATRIX.md.

Every formatter has a test that formats a misformatted document over LSP and checks the result.

Four languages reach their server through a notebook proxy, so those also get the same test against a main.ipynb cell — the only case where the server sees a bare .ipynb URI. Rust passes. Go passes for a cell of plain statements and is pending for a cell with an import. C++ and Haskell are pending: cpp-notebook-language-server forwards the notebook's URI so clangd answers "trying to format non-added document", and HLS rejects the extension outright ("ormolu does not support .ipynb filetypes"). plans/notebook-formatting-in-proxies.md has the details. The other nine languages set a non-empty notebook_suffix, so codedown hands their server main.ipynb.rb and friends — the same case the raw-file test already covers.

Rust also bumps rust-notebook-language-server to 0.2.5.0, which fixes formatting inside notebook cells (it was forwarding textDocument/formatting with the notebook's URI instead of the shadow file's).

Getting the R language server to run at all

R had no LSP test before this, and four separate things stopped one from working:

  • It needs a /bin/sh at startup (processx's .onLoad calls system(), which goes through popen).
  • parallel::detectCores() shells out to grep "^processor" /proc/cpuinfo | wc -l, and the hermetic PATH had no grep. Without it detectCores returns NA and the server dies sizing its session pool.
  • Test directory names came from show code, so they contained backslashes; R.cache rewrites those to forward slashes and then can't write styler's cache. Formatting test names no longer embed the code.
  • languageserver answers shutdown with result: [] where the spec says null. lsp-test couldn't decode it, the thread reading the server's output died, and the session hung in teardown forever. Fixed in lsp-test (lsp-test: don't let an undecodable message or a silent server hang the session lsp#1): undecodable messages are skipped and the shutdown wait is bounded.

The result: [] is a server bug either way, so languageserver now builds from codedownio/languageserver — upstream master plus the one-line fix. That also moves us off the 0.3.16 pin this repo has carried since 2024. The same branch is what goes upstream as a PR; the src here goes back to REditorSupport once it lands.

Unmerged pin

tests/stack.yaml points at codedownio/lsp-test-helpers branch kernel-piggyback-and-binsh (the /bin/sh), which needs merging and the pin moved.

Unrelated change in the regenerated matrix

Go's Semantic highlighting flips to "not available". gopls 0.22 leaves its semanticTokens setting off by default; the stored probe data was stale.

@thomasjm thomasjm changed the title Make formatting actually work, and say which formatter is behind it Make formatting work, and say which formatter is behind it Oct 9, 2026
@thomasjm
thomasjm merged commit 1843266 into main Oct 9, 2026
61 of 62 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant