CSV mode: csvlens and tidy-viewer-core as alternatives to xan

Open
#291 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
rust
Domain
cli, frontend

Research direction

Start with the CSV-mode spike in #288 and compare the two approaches described here: a foreground handoff to csvlens and an in-pager mode using the csv crate with tidy-viewer-core formatting. The issue does not name implementation files or tests; first inspect the pager’s rendering, input, and effect flow and check the dependencies’ APIs and packaging constraints. Done means returning a concrete comparison and recommendation that addresses the listed trade-offs.

Written by the indexing model from the issue text.

Description

area:pager needs-design type:feat
Problem / motivation

Companion to #288 (CSV mode in the pager — evaluate xan as the engine). That
issue lands on "xan is a binary-only crate, so it's an upstream ask, a vendor,
or roll-our-own." Before committing to any of those, the two obvious
alternatives deserve the same look — and both turn out to answer the question
better than xan does, in opposite ways.

Filing separately so the spike in #288 has something concrete to compare
against rather than a one-line "worth a look" bullet.

Proposed solution

Evaluate YS-L/csvlens and
alexhallam/tv (tidy-viewer) as the CSV
engine. Summary of what's actually in the manifests:

xan (#288) csvlens tidy-viewer-core
Library target ❌ bin only ✅ src/lib.rs, documented ✅ purpose-built core crate
On crates.io ✅ ✅ 0.15.1 ❌ publish = false
License Unlicense OR MIT MIT Unlicense OR MIT
ratatui 0.29 0.30 (our version) n/a (emits strings)
crossterm n/a 0.29 (our version) n/a
Direct deps ~90 ~16 5
Maintained active active (2026-07) last push 2025-08
Shape CLI subcommands self-driving TUI pure formatter

csvlens — the best feature answer, the wrong architecture.

It is genuinely a library (src/lib.rs opens with "This crate allows you to
use csvlens as a library"), MIT, and its stack is an exact match for ours:
ratatui 0.30, crossterm 0.29, ansi-to-tui 8, arboard 3 — all versions spyc
already carries. Feature-wise it's everything #288 asks for and more: find,
sort, column filter, wrap modes, sticky header, --watch live reload, themes.

The catch is the public API:

pub fn run_csvlens(args: &[&str]) -> Result<Option<String>>;
pub fn run_csvlens_with_options(opts: CsvlensOptions) -> Result<Option<String>>;

That's it (plus errors and a cfg-gated bench_api). It takes over the
terminal, runs its own event loop, and returns the selected cell
— the
internals that would actually render into a PagerView (view, wrap, ui,
csv) are all private mod, not pub mod. So it's not a renderer we can call
from a pure &self draw pass; it's an application. That collides head-on with
the MVU invariants — one update entry, one message channel, Effect as the
sole executor — and it reads from /dev/tty directly (crossterm's
use-dev-tty), which has to be reasoned about against our input-reader thread
and the pty pane.

Which leaves two shapes for it:

  1. Foreground handoff — treat it exactly like the v → $EDITOR path:
    suspend the loop, hand over the terminal, take back the selected cell on
    exit. Cheap to build, enormous feature win, and the returned cell is a nice
    hook (yank it, filter on it). But it's a modal escape hatch, not "an entry
    into the file": its keymap, not ours; no gf, no buffer history, no vsplit
    preview, no theme integration.
  2. Upstream ask to make the rendering internals public — same shape of
    problem as xan's missing [lib], just a smaller ask.

Also worth weighing: arrow 59 is a direct dependency, which is a lot of crate
family to take on for a viewer, though default-features = false +
features = ["csv"] trims it. The cli and clipboard features are both
optional and we'd drop both.

tidy-viewer-core — the best architectural fit, with a packaging problem.

tv is a workspace, and tidy-viewer-core is a deliberately extracted library
("shared between the CLI and Python bindings"). It is tiny — five
dependencies (lazy_static, regex, unicode-truncate, unicode-width,
itertools) — and it is pure formatting logic with exactly the signature shape
our renderers want:

format_strings(&data, min_col_width, max_col_width, sig_figs, preserve_scientific, max_decimal_width) -> Vec<String>
calculate_column_width(&formatted, min, max) -> usize
is_na(&str) -> bool

No terminal, no event loop, no ratatui — Vec<String> in, Vec<String> out.
That drops straight into model + &Theme -> Vec<Line> with no adaptation, and
it brings the one thing neither xan nor csvlens gives us for free: datatype
inference with significant-figure numeric formatting, which is most of what
makes tv's output readable.

Blockers:

  • publish = false — it is not on crates.io. A git dependency is not an
    option: crates.io rejects published crates that carry them, and spyc ships
    via cargo install spyc. So this is vendor-with-attribution (license
    permits) or an upstream ask to publish the core crate.
  • Repo last pushed 2025-08; the CLI crate carries deprecated/unmaintained
    deps (structopt, atty → RUSTSEC-2021-0145, crossterm 0.22, arrow/parquet
    56). None of that is in core, so the blast radius is limited if we only
    take core — but it is a maintenance signal, and audit.yml would see it if
    we ever pulled the workspace.
  • unicode-width 0.1 vs our 0.2 — a duplicate in the tree, and width
    agreement is load-bearing for us (see the grapheme-cluster note in
    Cargo.toml).

Where this points. These two aren't really competing — they're
complementary, and the spike in #288 should probably come back with both:

  • A. Foreground handoff to csvlens as the power view (an o-style verb,
    or its own key), for when you want to search/sort/filter a CSV — accepting
    that it's a separate application with its own keymap.
  • B. An in-pager CSV mode for Enter, built on the csv crate plus
    tidy-viewer-core's formatting (vendored or upstream-published), keeping
    gf / buffer history / vsplit preview / our theme.

B is the one that closes #288's actual complaint. A is a large win for
comparatively little work and could land first.

Alternatives considered
  • xan — #288. Binary-only, ~90 direct deps; strictly worse on both axes
    than the two above, though its flatten view is still nice prior art.
  • visidata — Python, so out of scope as a library; worth reading purely
    for UX prior art on column navigation.
  • Roll our own end to end (csv crate + our own width/format logic) —
    still viable, and note that option B above is only a short step from it:
    tidy-viewer-core is small enough that "vendor it" and "write it" are closer
    in cost than they look. The significant-figure formatting is the part worth
    not reinventing.
Dominant language
Rust
Stars
17
Forks
2
Avg merge
35m
Merged PRs (30d)
413

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Tripstack-Corp/spyc

All issues in Tripstack-Corp/spyc

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.