From c5ce6fb6c0d249a00fe2bdf3fe8b8d50dd128d59 Mon Sep 17 00:00:00 2001 From: dawn <90008@gaze.systems> Date: Sat, 17 Jan 2026 15:40:07 +0300 Subject: [PATCH] fix issue with optional branches having wrong flag insert positions --- crates/command_parser/src/command.rs | 3 +++ crates/command_parser/src/lib.rs | 11 +++++++++-- crates/command_parser/src/tree.rs | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/crates/command_parser/src/command.rs b/crates/command_parser/src/command.rs index 83b57ae0..3bbf636d 100644 --- a/crates/command_parser/src/command.rs +++ b/crates/command_parser/src/command.rs @@ -1,6 +1,7 @@ use std::{ collections::HashSet, fmt::{Debug, Display}, + sync::Arc, }; use smol_str::SmolStr; @@ -17,6 +18,7 @@ pub struct Command { pub show_in_suggestions: bool, pub parse_flags_before: usize, pub hidden_flags: HashSet, + pub original: Option>, } impl Command { @@ -41,6 +43,7 @@ impl Command { parse_flags_before, tokens, hidden_flags: HashSet::new(), + original: None, } } diff --git a/crates/command_parser/src/lib.rs b/crates/command_parser/src/lib.rs index 18de3446..e1b5b79b 100644 --- a/crates/command_parser/src/lib.rs +++ b/crates/command_parser/src/lib.rs @@ -181,15 +181,18 @@ pub fn parse_command( let mut flags: HashMap> = HashMap::new(); let mut misplaced_flags: Vec = Vec::new(); let mut invalid_flags: Vec = Vec::new(); + for (token_idx, raw_flag) in raw_flags { let Some(matched_flag) = match_flag(command.flags.iter(), raw_flag.clone()) else { invalid_flags.push(raw_flag); continue; }; + if token_idx != command.parse_flags_before { misplaced_flags.push(raw_flag); continue; } + match matched_flag { // a flag was matched Ok((name, value)) => { @@ -216,6 +219,8 @@ pub fn parse_command( } } } + + let full_cmd = command.original.as_deref().unwrap_or(&command); if misplaced_flags.is_empty().not() { let mut error = format!( "Flag{} ", @@ -230,7 +235,8 @@ pub fn parse_command( write!( &mut error, " in command `{prefix}{input}` {} misplaced. Try reordering to match the command usage `{prefix}{command}`.", - (misplaced_flags.len() > 1).then_some("are").unwrap_or("is") + (misplaced_flags.len() > 1).then_some("are").unwrap_or("is"), + command = full_cmd ).expect("oom"); return Err(error); } @@ -250,7 +256,8 @@ pub fn parse_command( " {} seem to be applicable in this command (`{prefix}{command}`).", (invalid_flags.len() > 1) .then_some("don't") - .unwrap_or("doesn't") + .unwrap_or("doesn't"), + command = full_cmd ) .expect("oom"); return Err(error); diff --git a/crates/command_parser/src/tree.rs b/crates/command_parser/src/tree.rs index 42939a3b..84fcca7f 100644 --- a/crates/command_parser/src/tree.rs +++ b/crates/command_parser/src/tree.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use ordermap::OrderMap; use crate::{command::Command, token::Token}; @@ -27,10 +29,20 @@ impl TreeBranch { if matches!(token, Token::Parameter(ref param) if param.is_optional()) && index < command.tokens.len() - 1 { - current_branch.register_command(Command { - tokens: command.tokens[index + 1..].to_vec(), - ..command.clone() - }); + let mut new_command = command.clone(); + new_command.tokens = command.tokens[index + 1..].to_vec(); + new_command.original = command + .original + .clone() + .or_else(|| Some(Arc::new(command.clone()))); + + // if the optional parameter we're skipping is *before* the flag insertion point, + // we need to shift the index left by 1 to account for the removed token + if new_command.parse_flags_before > index { + new_command.parse_flags_before -= 1; + } + + current_branch.register_command(new_command); } // recursively get or create a sub-branch for each token current_branch = current_branch