fix issue with optional branches having wrong flag insert positions

This commit is contained in:
dawn 2026-01-17 15:40:07 +03:00
parent e2b354aae1
commit c5ce6fb6c0
No known key found for this signature in database
3 changed files with 28 additions and 6 deletions

View file

@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashSet, collections::HashSet,
fmt::{Debug, Display}, fmt::{Debug, Display},
sync::Arc,
}; };
use smol_str::SmolStr; use smol_str::SmolStr;
@ -17,6 +18,7 @@ pub struct Command {
pub show_in_suggestions: bool, pub show_in_suggestions: bool,
pub parse_flags_before: usize, pub parse_flags_before: usize,
pub hidden_flags: HashSet<SmolStr>, pub hidden_flags: HashSet<SmolStr>,
pub original: Option<Arc<Command>>,
} }
impl Command { impl Command {
@ -41,6 +43,7 @@ impl Command {
parse_flags_before, parse_flags_before,
tokens, tokens,
hidden_flags: HashSet::new(), hidden_flags: HashSet::new(),
original: None,
} }
} }

View file

@ -181,15 +181,18 @@ pub fn parse_command(
let mut flags: HashMap<String, Option<ParameterValue>> = HashMap::new(); let mut flags: HashMap<String, Option<ParameterValue>> = HashMap::new();
let mut misplaced_flags: Vec<MatchedFlag> = Vec::new(); let mut misplaced_flags: Vec<MatchedFlag> = Vec::new();
let mut invalid_flags: Vec<MatchedFlag> = Vec::new(); let mut invalid_flags: Vec<MatchedFlag> = Vec::new();
for (token_idx, raw_flag) in raw_flags { for (token_idx, raw_flag) in raw_flags {
let Some(matched_flag) = match_flag(command.flags.iter(), raw_flag.clone()) else { let Some(matched_flag) = match_flag(command.flags.iter(), raw_flag.clone()) else {
invalid_flags.push(raw_flag); invalid_flags.push(raw_flag);
continue; continue;
}; };
if token_idx != command.parse_flags_before { if token_idx != command.parse_flags_before {
misplaced_flags.push(raw_flag); misplaced_flags.push(raw_flag);
continue; continue;
} }
match matched_flag { match matched_flag {
// a flag was matched // a flag was matched
Ok((name, value)) => { 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() { if misplaced_flags.is_empty().not() {
let mut error = format!( let mut error = format!(
"Flag{} ", "Flag{} ",
@ -230,7 +235,8 @@ pub fn parse_command(
write!( write!(
&mut error, &mut error,
" in command `{prefix}{input}` {} misplaced. Try reordering to match the command usage `{prefix}{command}`.", " 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"); ).expect("oom");
return Err(error); return Err(error);
} }
@ -250,7 +256,8 @@ pub fn parse_command(
" {} seem to be applicable in this command (`{prefix}{command}`).", " {} seem to be applicable in this command (`{prefix}{command}`).",
(invalid_flags.len() > 1) (invalid_flags.len() > 1)
.then_some("don't") .then_some("don't")
.unwrap_or("doesn't") .unwrap_or("doesn't"),
command = full_cmd
) )
.expect("oom"); .expect("oom");
return Err(error); return Err(error);

View file

@ -1,3 +1,5 @@
use std::sync::Arc;
use ordermap::OrderMap; use ordermap::OrderMap;
use crate::{command::Command, token::Token}; use crate::{command::Command, token::Token};
@ -27,10 +29,20 @@ impl TreeBranch {
if matches!(token, Token::Parameter(ref param) if param.is_optional()) if matches!(token, Token::Parameter(ref param) if param.is_optional())
&& index < command.tokens.len() - 1 && index < command.tokens.len() - 1
{ {
current_branch.register_command(Command { let mut new_command = command.clone();
tokens: command.tokens[index + 1..].to_vec(), new_command.tokens = command.tokens[index + 1..].to_vec();
..command.clone() 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 // recursively get or create a sub-branch for each token
current_branch = current_branch current_branch = current_branch