Your 2,400-line lib.rs should not be a wall you keep scrolling past. Now your editor offers to split it for you.
Today we released SplitRS 0.3.1 — and with it splitrs-lsp, a Language Server Protocol implementation that brings SplitRS’s AST-based refactoring directly into your editor.
If you have not met SplitRS yet: it is a production-ready Rust refactoring tool that takes a large source file — the kind that grows past 1,000 lines until nobody wants to touch it — and splits it into well-organized, compilable modules. It parses with syn, clusters related methods by call graph, infers the right visibility (pub(super), pub(crate), or pub), and generates the use statements and mod.rs for you. It already handles generics, async, Arc/Mutex, trait impls, and nested types. We built it because we needed it: SplitRS has been the workhorse behind keeping the COOLJAPAN codebase under the 2,000-line-per-file policy, and it cut its teeth refactoring tens of thousands of lines of real production Rust.
No language runtime. No external tool to install per editor. No Python sidecar. SplitRS is pure Rust — syn for parsing, prettyplease for formatting — and it compiles to a single static binary. splitrs-lsp speaks LSP over stdio and runs alongside rust-analyzer, not instead of it.
Why 0.3.1 matters
The CLI has always worked. But refactoring is a thing you do while reading code, and the friction of dropping to a terminal, typing a path, and re-opening files broke that flow. 0.3.1 closes the gap:
- In-editor diagnostics. When a file exceeds your
.splitrs.tomlmax_linesthreshold, the server emits a diagnostic (source: "splitrs") right where you are reading. Turn onsplit_impl_blocksand you also get per-impl-block diagnostics when an impl runs pastmax_impl_lines. - A one-action split. The code action
Refactor with splitrs(kindREFACTOR_EXTRACT) runs the full splitter and applies the result as aWorkspaceEdit— your editor writes the new modules. - Metrics on hover. Hover at the top of any Rust file and the server reports lines of code, method count, largest impl block, and average cyclomatic complexity — so you know before you split whether it is worth it.
- Config that follows you. The server watches
.splitrs.tomlviadid_change_watched_filesand hot-reloads it, so tighteningmax_linesre-evaluates open buffers immediately.
This release also sharpens the core engine: trait implementations for the same type are now batched into a single shared module instead of scattering one file per trait, line-count estimates are computed from prettyplease-formatted output (no more drift between the preview and the files actually written), and when the output target is the crate root, SplitRS writes mod.rs rather than overwriting your lib.rs. Duplicate std::collections imports across split modules are deduplicated.
Technical Deep Dive: how the LSP fits the pipeline
SplitRS’s analysis pipeline is unchanged — splitrs-lsp is a thin, correct front-end onto it:
- AST parsing (
syn). The server parses the open document the same way the CLI parses a file argument. FileAnalyzer+ metrics. The sameFileAnalyzerthat drives the CLI produces the LoC / method / complexity numbers surfaced on hover, so the editor and the command line agree.- Module generation. When you invoke the code action, the server runs the standard generation path — method clustering, import synthesis, visibility inference — and serializes the result into a
WorkspaceEditrather than writing to disk directly. - Config layer (
.splitrs.toml). A singleConfigis shared: the watcher reloads it on change, the diagnostics readmax_lines/max_impl_linesfrom it, and CLI runs honor the same file.
splitrs-lsp ships as a second [[bin]] gated behind the lsp feature, which is on by default — so a plain cargo install splitrs gives you both binaries. The feature pulls in tower-lsp, a small tokio runtime, and dashmap for document state; if you want the language server without the CLI surface, build --no-default-features --features lsp.
Getting Started
Install both the CLI and the language server:
cargo install splitrs
Split a file from the command line (still the fastest path for one-off jobs):
splitrs --input src/large_file.rs --output src/large_file/ --split-impl-blocks --max-impl-lines 200
Then wire the language server into your editor. Helix (languages.toml):
[[language]]
name = "rust"
language-servers = ["rust-analyzer", "splitrs-lsp"]
[language-server.splitrs-lsp]
command = "splitrs-lsp"
Neovim:
vim.lsp.start({ name = 'splitrs-lsp', cmd = { 'splitrs-lsp' } })
Drop a .splitrs.toml in your project root so the server knows your thresholds:
[splitrs]
max_lines = 1000 # diagnostic when a file exceeds this
max_impl_lines = 300 # diagnostic on oversized impl blocks
split_impl_blocks = true
Now open a large file: hover the top for metrics, watch for the diagnostic, and trigger Refactor with splitrs when you are ready.
What’s New in 0.3.1
splitrs-lsplanguage server (featurelsp, default-on): atower-lspserver over stdio with diagnostics, theRefactor with splitrscode action, top-of-file hover metrics, and.splitrs.tomlhot reload.- Per-impl-block diagnostics when
split_impl_blocks = trueand an impl exceedsmax_impl_lines. - Trait implementation batching: trait impls for the same type are grouped into one shared module instead of one file per trait.
- Accurate line-count estimation via
prettyplease-formatted output — estimates now match the files actually written. lib.rspreservation: targeting the crate root writesmod.rsinstead of overwriting an existinglib.rs.- Import deduplication for
std::collections::*across split modules. - New optional dependencies behind
lsp:tower-lsp 0.20,tokio 1.52,dashmap 6.1.
Tips
- Install just the server in CI or minimal images.
cargo install splitrs --no-default-features --features lspgives yousplitrs-lspwithout the CLI surface. - Let
.splitrs.tomlbe your single source of truth. The same file drives editor diagnostics and CLI runs — setmax_lines/max_impl_linesonce and both agree. Edit it live; the server hot-reloads. - Read the hover before you split. The average-complexity number tells you whether a long file is genuinely tangled or just long-but-flat; a flat data module may not benefit from splitting.
- Keep
--split-impl-blocksfor the big ones. A 2,000-line file is usually one giant impl block —--max-impl-lines 200turns it into focused method-group modules instead of one slightly-smaller file. - Trust the preview now. Because line counts come from
prettyplease,--dry-run(-n) reports the module sizes you will actually get.
This is the foundation
SplitRS keeps the COOLJAPAN ecosystem maintainable from the inside. It is a pure-Rust development tool — syn and prettyplease, nothing more — built and used by the same team behind the rest of the stack, where the under-2,000-lines-per-file rule is policy rather than aspiration. 0.3.1 is the point where that discipline stops being a terminal chore and starts living in your editor.
Repository: https://github.com/cool-japan/splitrs
Star the repo if you have ever scrolled past a file that should have been ten files. Pure Rust refactoring is here — fast, safe, and right inside your editor.
— KitaSan at COOLJAPAN OÜ April 25, 2026