feat(dispatch): walk subcommands and detect revset flag from help
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`.
This commit is contained in:
3 files changed
+471
-50
No files matched your search
+6
-9
@@ -278,8 +278,7 @@ fn score_row(
|
||||
|
||||
pub fn run(
|
||||
rows: Vec<Row>,
|
||||
subcommand: &str,
|
||||
passthrough: &[String],
|
||||
target: &jj::RevsetTarget,
|
||||
) -> Result<Option<Vec<String>>> {
|
||||
install_panic_hook();
|
||||
let mut tty = OpenOptions::new()
|
||||
@@ -295,7 +294,7 @@ pub fn run(
|
||||
tty.write_all(HIDE_CURSOR).context("hide cursor")?;
|
||||
tty.flush().ok();
|
||||
|
||||
let result = run_loop(&mut tty, rows, subcommand, passthrough);
|
||||
let result = run_loop(&mut tty, rows, target);
|
||||
|
||||
let _ = tty.write_all(SHOW_CURSOR);
|
||||
let _ = tty.write_all(b"\x1b[?1049l");
|
||||
@@ -320,15 +319,14 @@ fn install_panic_hook() {
|
||||
fn run_loop(
|
||||
tty: &mut File,
|
||||
rows: Vec<Row>,
|
||||
subcommand: &str,
|
||||
passthrough: &[String],
|
||||
target: &jj::RevsetTarget,
|
||||
) -> Result<Option<Vec<String>>> {
|
||||
let mut app = App::new(rows);
|
||||
let (mut term_cols, mut term_rows) = crossterm::terminal::size().unwrap_or((80, 24));
|
||||
// Chrome rows: input(1) + cmd_preview(1) + hint(1) = 3.
|
||||
app.last_height = (term_rows as usize).saturating_sub(3);
|
||||
loop {
|
||||
render(tty, &mut app, term_rows, term_cols, subcommand, passthrough)?;
|
||||
render(tty, &mut app, term_rows, term_cols, target)?;
|
||||
if !event::poll(Duration::from_millis(250)).context("event poll failed")? {
|
||||
continue;
|
||||
}
|
||||
@@ -454,8 +452,7 @@ fn render(
|
||||
app: &mut App,
|
||||
term_rows: u16,
|
||||
term_cols: u16,
|
||||
subcommand: &str,
|
||||
passthrough: &[String],
|
||||
target: &jj::RevsetTarget,
|
||||
) -> Result<()> {
|
||||
let viewport_y: u16 = 0;
|
||||
let viewport_height = term_rows;
|
||||
@@ -601,7 +598,7 @@ fn render(
|
||||
let cmd_y = viewport_y + 1 + log_height as u16 + 1;
|
||||
write!(buf, "\x1b[{};1H", cmd_y)?;
|
||||
buf.extend_from_slice(ERASE_TO_EOL);
|
||||
let cmd = jj::command_line(subcommand, passthrough, &app.pending_ids());
|
||||
let cmd = jj::command_line(target, &app.pending_ids());
|
||||
let cmd_max = (term_cols as usize).saturating_sub(2); // for "▶ "
|
||||
let cmd_visible: String = if cmd.chars().count() <= cmd_max {
|
||||
cmd
|
||||
|
||||
Reference in new issue
Block a user