Skip to content

Commit

Permalink
feat: compgen delegate (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden authored Jul 20, 2023
1 parent d1d299d commit 9414c76
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 95 deletions.
11 changes: 11 additions & 0 deletions src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,17 @@ impl Command {
}
}

pub(crate) fn delegated(&self) -> bool {
self.subcommands.is_empty()
&& self.flag_option_params.is_empty()
&& self.positional_params.len() == 1
&& self
.positional_params
.first()
.map(|v| v.terminated)
.unwrap_or_default()
}

pub(crate) fn exist_main_fn(&self, cmd_paths: &[&str]) -> bool {
self.get_cmd_fn(cmd_paths)
.map(|v| v.ends_with("main"))
Expand Down
25 changes: 14 additions & 11 deletions src/compgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,21 @@ pub fn compgen(
(last_arg.to_string(), None)
}
};
let new_args: Vec<String> = args
.iter()
.enumerate()
.map(|(i, v)| {
if i == args.len() - 1 {
last.clone()
} else {
v.to_string()
}
})
.collect();
let cmd = Command::new(script_content)?;
let new_args: Vec<String> = if cmd.delegated() {
args.to_vec()
} else {
args.iter()
.enumerate()
.map(|(i, v)| {
if i == args.len() - 1 {
last.clone()
} else {
v.to_string()
}
})
.collect()
};
let matcher = Matcher::new(&cmd, &new_args);
let compgen_values = matcher.compgen(shell);
let mut default_nospace = unbalance.is_some();
Expand Down
194 changes: 110 additions & 84 deletions src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,112 +69,131 @@ impl<'a, 'b> Matcher<'a, 'b> {
let mut flag_option_args = vec![vec![]];
let mut positional_args = vec![];
let mut dashes = vec![];
let mut is_rest_args_positional = false;
let mut is_last_arg_option_assign = false;
let mut arg_comp = ArgComp::Any;
let mut choices_fns = HashSet::new();
let args_len = args.len();
if let Some(arg) = args.last() {
if arg.starts_with('-') {
arg_comp = ArgComp::FlagOrOption;
} else if !arg.is_empty() {
arg_comp = ArgComp::CommandOrPositional;
if root.delegated() {
positional_args = args.iter().skip(1).map(|v| v.as_str()).collect();
} else {
let mut is_rest_args_positional = false;
if let Some(arg) = args.last() {
if arg.starts_with('-') {
arg_comp = ArgComp::FlagOrOption;
} else if !arg.is_empty() {
arg_comp = ArgComp::CommandOrPositional;
}
}
}
while arg_index < args_len {
let cmd = cmds[cmd_level].1;
let arg = args[arg_index].as_str();
if arg == "--" {
dashes.push(positional_args.len());
if is_rest_args_positional {
while arg_index < args_len {
let cmd = cmds[cmd_level].1;
let arg = args[arg_index].as_str();
if arg == "--" {
dashes.push(positional_args.len());
if is_rest_args_positional {
add_positional_arg(
&mut positional_args,
arg,
&mut is_rest_args_positional,
cmd,
);
}
} else if is_rest_args_positional
|| !dashes.is_empty()
|| (cmd.no_flags_options_subcommands() && !KNOWN_OPTIONS.contains(&arg))
{
add_positional_arg(
&mut positional_args,
arg,
&mut is_rest_args_positional,
cmd,
);
}
} else if is_rest_args_positional
|| !dashes.is_empty()
|| (cmd.no_flags_options_subcommands() && !KNOWN_OPTIONS.contains(&arg))
{
add_positional_arg(&mut positional_args, arg, &mut is_rest_args_positional, cmd);
} else if arg.starts_with('-') {
if let Some((k, v)) = arg.split_once('=') {
let param = cmd.find_flag_option(k);
if arg_index == args_len - 1 {
if let Some(param) = param {
arg_comp = ArgComp::OptionValue(param.name.clone(), 0)
} else if arg.starts_with('-') {
if let Some((k, v)) = arg.split_once('=') {
let param = cmd.find_flag_option(k);
if arg_index == args_len - 1 {
if let Some(param) = param {
arg_comp = ArgComp::OptionValue(param.name.clone(), 0)
}
is_last_arg_option_assign = true;
}
is_last_arg_option_assign = true;
}
if let Some((choices_fn, validate)) = param.and_then(|v| v.choices_fn.as_ref())
{
if *validate {
choices_fns.insert(choices_fn.as_str());
if let Some((choices_fn, validate)) =
param.and_then(|v| v.choices_fn.as_ref())
{
if *validate {
choices_fns.insert(choices_fn.as_str());
}
}
}
flag_option_args[cmd_level].push((k, vec![v], param.map(|v| v.name.as_str())));
} else if let Some(param) = cmd.find_flag_option(arg) {
if let Some((choices_fn, validate)) = param.choices_fn.as_ref() {
if *validate {
choices_fns.insert(choices_fn.as_str());
flag_option_args[cmd_level].push((
k,
vec![v],
param.map(|v| v.name.as_str()),
));
} else if let Some(param) = cmd.find_flag_option(arg) {
if let Some((choices_fn, validate)) = param.choices_fn.as_ref() {
if *validate {
choices_fns.insert(choices_fn.as_str());
}
}
}
match_flag_option(
&mut flag_option_args[cmd_level],
args,
&mut arg_index,
param,
&mut arg_comp,
);
} else if let Some(mut list) = match_combine_shorts(cmd, arg) {
let name = list.pop().and_then(|v| v.2).unwrap();
let param = cmd.find_flag_option(name).unwrap();
if let Some((choices_fn, validate)) = param.choices_fn.as_ref() {
if *validate {
choices_fns.insert(choices_fn.as_str());
match_flag_option(
&mut flag_option_args[cmd_level],
args,
&mut arg_index,
param,
&mut arg_comp,
);
} else if let Some(mut list) = match_combine_shorts(cmd, arg) {
let name = list.pop().and_then(|v| v.2).unwrap();
let param = cmd.find_flag_option(name).unwrap();
if let Some((choices_fn, validate)) = param.choices_fn.as_ref() {
if *validate {
choices_fns.insert(choices_fn.as_str());
}
}
flag_option_args[cmd_level].extend(list);
match_flag_option(
&mut flag_option_args[cmd_level],
args,
&mut arg_index,
param,
&mut arg_comp,
);
} else {
flag_option_args[cmd_level].push((arg, vec![], None));
}
flag_option_args[cmd_level].extend(list);
match_flag_option(
&mut flag_option_args[cmd_level],
args,
&mut arg_index,
param,
&mut arg_comp,
);
} else if let Some(subcmd) = cmd.find_subcommand(arg) {
cmd_level += 1;
cmds.push((
arg,
subcmd,
subcmd.name.clone().unwrap_or_else(|| arg.to_string()),
arg_index,
));
flag_option_args.push(vec![]);
} else {
flag_option_args[cmd_level].push((arg, vec![], None));
add_positional_arg(
&mut positional_args,
arg,
&mut is_rest_args_positional,
cmd,
);
}
} else if let Some(subcmd) = cmd.find_subcommand(arg) {
cmd_level += 1;
cmds.push((
arg,
subcmd,
subcmd.name.clone().unwrap_or_else(|| arg.to_string()),
arg_index,
));
flag_option_args.push(vec![]);
} else {
add_positional_arg(&mut positional_args, arg, &mut is_rest_args_positional, cmd);
arg_index += 1;
}
arg_index += 1;
}

if is_rest_args_positional {
arg_comp = ArgComp::CommandOrPositional;
}
if is_rest_args_positional {
arg_comp = ArgComp::CommandOrPositional;
}

let last_cmd = cmds.last().unwrap().1;
choices_fns.extend(last_cmd.positional_params.iter().filter_map(|v| {
if let Some((choices_fn, validate)) = v.choices_fn.as_ref() {
if *validate {
return Some(choices_fn.as_str());
let last_cmd = cmds.last().unwrap().1;
choices_fns.extend(last_cmd.positional_params.iter().filter_map(|v| {
if let Some((choices_fn, validate)) = v.choices_fn.as_ref() {
if *validate {
return Some(choices_fn.as_str());
}
}
}
None
}));
None
}));
}
Self {
cmds,
args,
Expand Down Expand Up @@ -236,6 +255,13 @@ impl<'a, 'b> Matcher<'a, 'b> {
}
let level = self.cmds.len() - 1;
let mut last_cmd = self.cmds[level].1;
if last_cmd.delegated() {
return last_cmd
.positional_params
.first()
.map(comp_positional)
.unwrap_or_default();
}
let mut output = match &self.arg_comp {
ArgComp::FlagOrOption => {
let mut output = self.comp_flag_options();
Expand Down
17 changes: 17 additions & 0 deletions tests/compgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,23 @@ fn redirect_symbols() {
snapshot_compgen!(script, vec![vec!["prog", ">", "Argc"]], argc::Shell::Bash);
}

#[test]
fn delegated() {
let script = r###"
# @arg args~[`_choice_delegate`]
_choice_delegate() {
echo $1
}
"###;

snapshot_compgen!(
script,
vec![vec!["prog", "abc"], vec!["prog", "-a"], vec!["prog", "-"]],
argc::Shell::Bash
);
}

#[test]
fn mult_char() {
let script = r###"
Expand Down
14 changes: 14 additions & 0 deletions tests/snapshots/integration__compgen__delegated.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: tests/compgen.rs
expression: data
---
************ COMPGEN `prog abc` ************
abc

************ COMPGEN `prog -a` ************
-a

************ COMPGEN `prog -` ************
-


0 comments on commit 9414c76

Please sign in to comment.