Skip to content

Commit

Permalink
Merge pull request #18 from rsteube/positional-completion
Browse files Browse the repository at this point in the history
added positional completion
  • Loading branch information
rsteube authored Oct 23, 2022
2 parents c6d7dbc + 97cac0f commit 5edbd63
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 24 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ keywords = ["cli", "completion", "clap", "carapace"]
[dependencies]
clap = { version = "4.0.0", default-features = false, features = ["std"] }
clap_complete = { version = "4.0.0" }
indexmap = {version = "1.9.1", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
39 changes: 29 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,28 @@ fn main() {
.value_parser(["one", "two", "three"]),
)
.subcommand(
Command::new("subcommand").about("example subcommand").arg(
Arg::new("command")
.long("command")
.short('c')
.help("execute command")
.value_hint(ValueHint::CommandName),
),
Command::new("subcommand")
.about("example subcommand")
.arg(
Arg::new("command")
.long("command")
.short('c')
.help("execute command")
.value_hint(ValueHint::CommandName),
)
.arg(
Arg::new("pos1")
.value_parser(["four", "five", "six"])
.value_hint(ValueHint::DirPath),
)
.arg(
Arg::new("posAny")
.num_args(1..)
.value_hint(ValueHint::Hostname),
),
);

generate(Spec, &mut cmd, "myapp", &mut io::stdout());
generate(Spec, &mut cmd, "example", &mut io::stdout());
}
```

Expand All @@ -55,9 +67,9 @@ aliases:
- alias2
description: example command
flags:
-h, --help: show help
--optional?: optional argument
-v=: takes argument
--optional?: optional argument
-h, --help: show help
completion:
flag:
optional:
Expand All @@ -76,4 +88,11 @@ commands:
command:
- $_os.PathExecutables
- $files
positional:
- - $directories
- four
- five
- six
positionalany:
- $_net.Hosts
```
28 changes: 20 additions & 8 deletions examples/carapace_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ fn main() {
.value_parser(["one", "two", "three"]),
)
.subcommand(
Command::new("subcommand").about("example subcommand").arg(
Arg::new("command")
.long("command")
.short('c')
.help("execute command")
.value_hint(ValueHint::CommandName),
),
Command::new("subcommand")
.about("example subcommand")
.arg(
Arg::new("command")
.long("command")
.short('c')
.help("execute command")
.value_hint(ValueHint::CommandName),
)
.arg(
Arg::new("pos1")
.value_parser(["four", "five", "six"])
.value_hint(ValueHint::DirPath),
)
.arg(
Arg::new("posAny")
.num_args(1..)
.value_hint(ValueHint::Hostname),
),
);

generate(Spec, &mut cmd, "myapp", &mut io::stdout());
generate(Spec, &mut cmd, "example", &mut io::stdout());
}
62 changes: 56 additions & 6 deletions src/carapace_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use clap::{
ValueHint::{self, *},
};
use clap_complete::*;
use indexmap::IndexMap as Map;
use serde::Serialize;
use std::collections::HashMap as Map;

#[derive(Default, Serialize)]
pub struct Command {
Expand Down Expand Up @@ -68,6 +68,8 @@ fn command_for(cmd: &clap::Command) -> Command {
flags: flags_for(cmd),
completion: Completion {
flag: flag_completions_for(cmd),
positional: positional_completion_for(cmd),
positionalany: positionalany_completion_for(cmd),
..Default::default()
},
commands: cmd.get_subcommands().map(command_for).collect(),
Expand All @@ -77,13 +79,19 @@ fn command_for(cmd: &clap::Command) -> Command {

fn flags_for(cmd: &clap::Command) -> Map<String, String> {
let mut m = Map::new();
for option in cmd
let mut options = cmd
.get_opts()
.filter(|o| !o.is_positional())
.map(|x| x.to_owned())
.chain(generator::utils::flags(cmd))
.collect::<Vec<Arg>>()
{
.collect::<Vec<Arg>>();
options.sort_by_key(|o| {
o.get_long()
.unwrap_or(&o.get_short().unwrap_or_default().to_string())
.to_owned()
});

for option in options {
let signature = if let Some(long) = option.get_long() {
if let Some(short) = option.get_short() {
format!("-{}, --{}", short, long)
Expand All @@ -101,16 +109,58 @@ fn flags_for(cmd: &clap::Command) -> Map<String, String> {
m
}

fn positionalany_completion_for(cmd: &clap::Command) -> Vec<String> {
let mut positionals = cmd.get_positionals().collect::<Vec<&Arg>>();
positionals.sort_by_key(|a| a.get_index());
if let Some(last) = positionals.last() {
if last.get_num_args().unwrap_or_default().max_values() == usize::MAX {
// TODO different way to detect unboundend?
return action_for(last.get_value_hint())
.into_iter()
.chain(values_for(last))
.collect::<Vec<String>>();
}
}
vec![]
}

fn positional_completion_for(cmd: &clap::Command) -> Vec<Vec<String>> {
let mut positionals = cmd.get_positionals().collect::<Vec<&Arg>>();
positionals.sort_by_key(|a| a.get_index());
positionals
.into_iter()
.filter(|p| p.get_num_args().unwrap_or_default().max_values() != usize::MAX) // filter last vararg pos (added to positionalany)
.map(|p| {
action_for(p.get_value_hint())
.into_iter()
.chain(values_for(p))
.collect::<Vec<String>>()
})
.collect()
}

fn flag_completions_for(cmd: &clap::Command) -> Map<String, Vec<String>> {
let mut m = Map::new();
for option in cmd.get_opts().filter(|o| !o.is_positional()) {
let mut options = cmd
.get_opts()
.filter(|o| !o.is_positional())
.map(|x| x.to_owned())
.chain(generator::utils::flags(cmd))
.collect::<Vec<Arg>>();
options.sort_by_key(|o| {
o.get_long()
.unwrap_or(&o.get_short().unwrap_or_default().to_string())
.to_owned()
});

for option in options {
let name = option
.get_long()
.unwrap_or(&option.get_short().unwrap_or_default().to_string())
.to_owned();
let action = action_for(option.get_value_hint())
.into_iter()
.chain(values_for(option))
.chain(values_for(&option))
.collect::<Vec<String>>();

if !action.is_empty() {
Expand Down

0 comments on commit 5edbd63

Please sign in to comment.