Restores `jj show` support, broken by the 0.3.0 dispatch rewrite.
`jj show` accepts its revision as a positional `[REVSET]` argument
rather than a `-r` option, which the help-driven dispatch didn't
recognize — so bare `jjf` (which defaults to `show`) errored instead
of opening the picker. A revision named as a bare positional now also
bypasses the picker, like an explicit `-r`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 0.3.0 help-driven dispatch only scanned a leaf's `Options:` block
for a revset flag. `jj show` takes its revision as a positional
`[REVSET]` argument, not an option, so it looked like it accepted no
revset — and bare `jjf` (which defaults to `show`) errored with
"takes no revset flag" instead of opening the picker.
- Read the `Arguments:` block too: a positional `[REVSET]`/`[REVSETS]`
that exposes `-r` as an alias is recognized, and jjf injects
`-r <picked>` for it.
- A bare positional in the passthrough now counts as the user having
named the revision, so `jjf show @-` bypasses the picker like an
explicit `-r` would. Option values are stepped over (parsing every
`Options:` section, including globals) so they aren't mistaken for
the positional.
- The picker-bypass check also accepts the inline `--revision=<rev>` /
`--to=<rev>` form, not just space-separated flags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Subcommand dispatch is now help-driven instead of substring-matching
"REVSET", so multi-level subcommands (`tag set`, `bookmark move`, …)
and non-`-r` revset flags (`--to`) work, and pre-supplied revset
flags bypass the picker instead of being double-injected.
Two real failures motivated this:
- `jjf bm main` (where `bm` is a user alias for `bookmark move`) ran
`jj bm main -r <id>` — but bookmark move uses `--to`, not `-r`.
- `jjf tag set v0.2.0` errored with "doesn't take revisions" because
jjf only inspected `jj tag --help` (parent) instead of walking
into the `set` leaf, which does take `-r`.
Replace the boolean `supports_revisions()` (a substring match on
"REVSET") with `RevsetTarget { leaf, passthrough, flag }`:
- Walks multi-level subcommands by parsing each parent's `Commands:`
block — including `[aliases: …]` annotations, so `jjf tag s` walks
to `tag set`. Aliases like `bm` work transparently because `jj bm
--help` already emits the resolved leaf's help.
- Parses the leaf's `Options:` block and picks the revset flag in
priority order: `-r`/`--revision[s]` first, then `-t, --to`.
`--from` is intentionally not a fallback (different semantics from
"the revision the picker chose").
- Bypasses the picker entirely when the user already passed a revset
flag in their args, so `jjf describe -r @-` runs verbatim instead
of double-injecting `-r`.
`exec` and `command_line` now take `&RevsetTarget` and emit the
resolved flag; signatures thread through `tui.rs` and `main.rs`.
New unit tests cover the help parsers and command-line construction
against captured fixtures of `jj tag set --help`, `jj bookmark move
--help`, and `jj tag --help`.
Picker now renders jj's real graph (merge connectors and all). Cursor
and filter skip decorative connector rows; hint row drops trailing
key/label pairs to fit a single line on narrow terminals; cursor
arrow glyphs changed to ●──▶.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Custom oneline template (drops timestamp + commit-id hash from the
left pane); keeps change-id with shortest-unique-prefix highlighting,
bookmarks, conflict/empty labels, and the description first-line.
More room for the actually-useful columns.
- Drop the dim '── <change_id> ────' header above the preview; the
cursor row tee (├) now carries the 'this is what you're looking at'
signal.
- Box-char frame for the preview pane: top border (┌────) and left
border (│), with ├ replacing │ on the cursor row to draw the eye
from picker selection into preview content.
- Dynamic description clipping: the description fills whatever rows
are left after the file list (ellipsis on overflow). Replaces the
hard 3-line cap, which under-used vertical space for short file
lists and was buggy (didn't count blank lines within description).
- Bump VIEWPORT_MIN from 15 to 20 so the preview pane has enough
vertical room to be useful by default.
- Reverse-video caret in input row (was cyan █ block — collided with
the cyan ❯ prompt and read as 'unfocused-pane cursor')
- Synchronized-output brackets (DEC mode 2026) around each redraw —
eliminates tearing on Kitty/WezTerm/foot/recent iTerm; ignored elsewhere
- Two-tone hint row (keys at normal weight, labels dim) gives the eye
a scan anchor
- Dim header above preview pane (── <change_id> ────) anchors the right
column to the cursor row's revision
- Light-terminal detection via COLORFGBG: switch cursor-row highlight
from indexed-237 dark-gray bg to reverse-video on themes where the
dark bg would be invisible
- Shell wrappers echo the resolved 'jj …' command (dim '$' prefix)
before running it, so it's visible in scrollback
Previously, the original `jjf …` invocation only got dropped from
history on successful confirm. A cancelled or errored picker left a
useless `jjf …` entry behind. Now bash drops it unconditionally via
`_jjf_drop_last_history`; fish arms the postexec hook before the
`--emit` call so the deletion happens regardless of exit path.
When term_cols >= 80, right side shows jj show --summary of the
currently highlighted revision (header stripped — just description and
the M/A/D file list, with description capped at 3 lines). Per-row
preview is cached, so navigating back to previously-viewed revs is
instant. Bumps the viewport floor to 15 rows (max = full terminal) so
the preview has room to be useful.
Mirrors the original bash+fzf script's behavior — `jjf` alone becomes
a quick rev browser. Implemented in shell wrappers (so the --emit
history-rewrite path still applies); the binary itself still prints
usage on bare invocation, for explicit invocations.
Previously used jj's default revsets.log, which is intentionally narrow
for jj log's scrollback purposes (`@ | trunk() | ancestors(mutable, 2)`).
For a picker, we want every visible revision so older revs (for abandon,
rebase -d, etc.) are reachable. `--limit 1000` keeps the blast radius
bounded on huge repos; smart prefix matching keeps lookup cheap.
A TUI revision picker for jujutsu (jj). Wraps any jj subcommand with an
inline fuzzy-finder over jj log, then forwards the picked revision(s)
as -r '<id>' to the underlying jj invocation.
Features:
- Inline viewport (no full-screen alt-screen takeover)
- Smart prefix matching: typing the bold portion of a change ID jumps
to that revision instantly
- Multi-select via Tab; emitted as -r 'A|B|C' revset union
- Preflight detection of subcommands that don't accept -r
- Optional shell integration (jjf init <bash|zsh|fish|nu>) that
rewrites shell history so up-arrow recalls the resolved jj command
Hand-rolled ANSI rendering to /dev/tty; only crossterm, nucleo-matcher,
anyhow, and libc as runtime dependencies.