cabalist-parser 0.1.1

CST/AST parser for .cabal files with round-trip fidelity
Documentation
  • Coverage
  • 93.85%
    244 out of 260 items documented3 out of 81 items with examples
  • Size
  • Source code size: 423.74 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 16.05 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 47s Average build duration of successful builds.
  • all releases: 29s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • joshburgess/cabalist
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • joshburgess

Cabalist is a strongly opinionated toolkit for managing Haskell .cabal files. It provides an interactive TUI, a scriptable CLI, and an LSP server for editor integration — all backed by a parser with byte-identical round-trip fidelity.

The guiding principle: make pure-Cabal Haskell development as approachable as Stack, by eliminating the friction of .cabal file management while keeping the .cabal file as the source of truth.

What Cabalist Does

  • Manages dependencies — search Hackage, add packages with correct PVP bounds, detect outdated versions, visualize the dependency tree
  • Manages GHC extensions — browse 119 extensions with descriptions and safety notes, toggle them on/off
  • Lints your .cabal file — 16 opinionated, individually configurable checks that catch missing bounds, bad practices, and structural issues
  • Formats your .cabal file — round-trip safe formatting with optional alphabetical sorting
  • Builds your project — run cabal build, cabal test, and cabal clean with streaming output and error navigation
  • Initializes new projects — guided wizard with templates (library, application, library+exe, full)
  • Edits metadata — change name, version, license, synopsis, and all other top-level fields
  • Manages cabal.project — view and edit project-level configuration (compiler, index-state, constraints)
  • Integrates with editors — full LSP server with diagnostics, completions, hover, code actions, formatting, semantic tokens, inlay hints, goto definition, and rename

All of this while preserving your comments, formatting, and field ordering. Cabalist never rewrites what you didn't ask it to change.

Three Tools, One Codebase

Tool Use Case
cabalist Interactive TUI for day-to-day development
cabalist-cli Scriptable CLI for automation, CI, and quick edits from the terminal
cabalist-lsp LSP server for VS Code, Neovim, Emacs, and any LSP-compatible editor

Installation

Pre-built Binaries

Download from the Releases page.

Homebrew (macOS / Linux)

brew install joshburgess/tap/cabalist

Nix

nix run github:joshburgess/cabalist

From Source

Requires Rust 1.88+:

cargo install --path crates/cabalist-tui   # Interactive TUI
cargo install --path crates/cabalist-cli   # CLI tool
cargo install --path crates/cabalist-lsp   # LSP server

Quick Start

TUI

cd my-haskell-project
cabalist                  # Launch the interactive TUI

Use d for dependencies, e for extensions, b for build, m for metadata, p for cabal.project. Press ? for help anywhere.

CLI

Command Description
cabalist-cli check Lint your .cabal file
cabalist-cli add aeson Add a dependency with PVP bounds
cabalist-cli add text --version "^>=2.0" Add with specific version constraint
cabalist-cli remove old-package Remove a dependency
cabalist-cli extensions --toggle DerivingStrategies Toggle an extension
cabalist-cli set synopsis "My cool library" Set a metadata field
cabalist-cli fmt Format the .cabal file
cabalist-cli deps --outdated Check for outdated dependencies
cabalist-cli deps --tree Show dependency tree
cabalist-cli modules --scan Find .hs files not listed in .cabal
cabalist-cli build Run cabal build
cabalist-cli test Run cabal test
cabalist-cli info Show project summary
cabalist-cli init --type full Create a new project
cabalist-cli update-index Download/refresh Hackage index

Editor (LSP)

Install cabalist-lsp and configure your editor:

  • VS Code: See editors/vscode/ for the extension
  • Neovim: See editors/neovim/init.lua
  • Emacs: See editors/emacs/cabalist-lsp.el

The LSP provides: diagnostics (16 lints), completions (packages, extensions, fields), hover documentation, code actions (quick fixes), document symbols (outline), formatting, semantic tokens, inlay hints (latest versions), goto definition (import: to common stanza), and rename (common stanza refactoring).

Documentation

Guide What It Covers
Getting Started End-to-end walkthroughs for common scenarios
TUI Guide Every view, keybinding, and workflow in the interactive TUI
CLI Reference Every command, flag, and example for the CLI
Editor Setup VS Code, Neovim, and Emacs configuration
Opinions & Lints All 16 lints with rationale and configuration
Configuration cabalist.toml reference with all options
Keybindings Complete TUI keyboard reference
Cabal Spec Compliance Parser syntax support and test coverage

Architecture

Cabalist is a Rust workspace of 9 focused crates:

Crate Purpose
cabalist-parser CST/AST parser with byte-identical round-trip fidelity
cabalist-project Parser for cabal.project files
cabalist-ghc GHC extensions (200+), warnings, and version knowledge base
cabalist-hackage Hackage index, package search, PVP version bounds
cabalist-opinions 16 lints, defaults, templates, and configuration
cabalist-cabal Async subprocess interface to cabal CLI
cabalist-tui Interactive TUI (ratatui + crossterm)
cabalist-cli Non-interactive CLI (clap)
cabalist-lsp LSP server (tower-lsp)

Design Principles

  1. The .cabal file is the source of truth — always.
  2. Preserve what the user wrote — comments, formatting, and field ordering are never lost.
  3. Shell out to cabal for what it does well — solving, building, testing.
  4. Be strongly opinionated with transparent escape hatches — every lint and default is individually configurable.

Why Rust?

Cabalist is a tool for Haskell developers, written in Rust. This is a deliberate choice, not an accident.

Zero-dependency installation. Cabalist's goal is to make .cabal management as easy as Stack. Stack's magic is that it's a single binary you download and run. Cabalist achieves the same: brew install cabalist and you're done. A Haskell implementation would require a working GHC toolchain to build — creating a chicken-and-egg problem for the exact users (newcomers setting up their first project) that cabalist is designed to help.

Trivial cross-platform static binaries. Rust produces fully static, self-contained binaries for every major platform with a single cargo build --target. Our CI builds for Linux x64, macOS x64, macOS ARM, and Windows in a simple matrix. GHC can produce static binaries, but it requires musl libc on Linux, doesn't support fully static linking on macOS (Apple's linker requires dynamic system frameworks), and makes cross-compilation painful. For a tool whose purpose is lowering the barrier to entry, distribution friction matters.

The parser needed to not be the Cabal library. This sounds paradoxical, but: if you write a .cabal parser in Haskell, you'd naturally reach for Cabal-syntax — a 100k+ line library designed for evaluation and dependency solving, not round-trip editing. Cabalist's CST parser preserves every byte of the original source (comments, whitespace, field ordering) with byte-identical fidelity. This is a fundamentally different design than what Cabal-syntax provides, and building it from scratch in Rust made it natural to get the abstraction right without fighting an existing library's assumptions.

TUI and LSP ecosystem maturity. ratatui + crossterm is the most actively maintained TUI framework in any language, with excellent Windows terminal support. tower-lsp provides an async LSP server that starts in milliseconds — important for a tool that activates on every .cabal file open. Haskell's brick is excellent for TUIs but less actively developed, and Haskell LSP servers are known for slow startup due to GHC runtime initialization.

Rust's type system fits the problem. Ownership semantics naturally prevent use-after-free bugs in the CST arena (flat Vec<Node> with NodeId indices). No GC pressure on large parse trees. The async runtime (tokio) handles concurrent build subprocess streaming cleanly. These aren't things Haskell can't do — they're things Rust makes easy to get right by default.

What Haskell would have been better at. If cabalist needed to fully evaluate conditionals, resolve flags, or compute dependency solutions, the Cabal library would be the right tool. But cabalist intentionally doesn't do that — it shells out to cabal for solving and building, and works at the syntactic level where a purpose-built CST parser is the right abstraction.

The short version: cabalist is a developer tool, and the best developer tools minimize friction for their users. Rust minimizes friction at every point — installation, distribution, startup time, cross-platform support — in ways that directly serve cabalist's mission of making Haskell development more approachable.

Contributing

cargo build --workspace   # Build everything
cargo test --workspace    # Run all 690+ tests
cargo clippy --workspace  # Check for common issues

License

Dual-licensed under Apache 2.0 or MIT.