summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 22:06:16 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 22:06:16 +0000
commit40c82bbbef9536ef86cb84fb53950ca655de90f2 (patch)
tree1d693e1cdf2ace296ebacf893aa7293deafa8a62 /src
parentf0eba62f7c65672e37713530fe123fe47fb93921 (diff)
parentfc66cf0e5917c166989360408d265133020fd510 (diff)
downloadclap_complete-40c82bbbef9536ef86cb84fb53950ca655de90f2.tar.gz
Snap for 11399400 from fc66cf0e5917c166989360408d265133020fd510 to sdk-release
Change-Id: Ic464c4b478f1283d22c7157d6bb386ca4f8ff5d0
Diffstat (limited to 'src')
-rw-r--r--src/dynamic/completer.rs4
-rw-r--r--src/dynamic/mod.rs2
-rw-r--r--src/dynamic/shells/bash.rs2
-rw-r--r--src/dynamic/shells/fish.rs2
-rw-r--r--src/dynamic/shells/mod.rs6
-rw-r--r--src/dynamic/shells/shell.rs2
-rw-r--r--src/generator/mod.rs4
-rw-r--r--src/generator/utils.rs2
-rw-r--r--src/lib.rs1
-rw-r--r--src/shells/bash.rs72
10 files changed, 67 insertions, 30 deletions
diff --git a/src/dynamic/completer.rs b/src/dynamic/completer.rs
index 8c8cb93..232dd4b 100644
--- a/src/dynamic/completer.rs
+++ b/src/dynamic/completer.rs
@@ -16,7 +16,7 @@ pub trait Completer {
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error>;
- /// Complete the command
+ /// Complete the given command
fn write_complete(
&self,
cmd: &mut clap::Command,
@@ -26,7 +26,7 @@ pub trait Completer {
) -> Result<(), std::io::Error>;
}
-/// Complete the command specified
+/// Complete the given command
pub fn complete(
cmd: &mut clap::Command,
args: Vec<std::ffi::OsString>,
diff --git a/src/dynamic/mod.rs b/src/dynamic/mod.rs
index f7c9857..ed72565 100644
--- a/src/dynamic/mod.rs
+++ b/src/dynamic/mod.rs
@@ -1,4 +1,6 @@
//! Complete commands within shells
+//!
+//! For quick-start, see [`shells::CompleteCommand`]
mod completer;
diff --git a/src/dynamic/shells/bash.rs b/src/dynamic/shells/bash.rs
index 43c128e..1308e67 100644
--- a/src/dynamic/shells/bash.rs
+++ b/src/dynamic/shells/bash.rs
@@ -1,6 +1,6 @@
use unicode_xid::UnicodeXID as _;
-/// Bash completions
+/// Completion support for Bash
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Bash;
diff --git a/src/dynamic/shells/fish.rs b/src/dynamic/shells/fish.rs
index 9d7e8c6..cfb9519 100644
--- a/src/dynamic/shells/fish.rs
+++ b/src/dynamic/shells/fish.rs
@@ -1,4 +1,4 @@
-/// Fish completions
+/// Completion support for Fish
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Fish;
diff --git a/src/dynamic/shells/mod.rs b/src/dynamic/shells/mod.rs
index 54d23a3..6cfed97 100644
--- a/src/dynamic/shells/mod.rs
+++ b/src/dynamic/shells/mod.rs
@@ -13,20 +13,26 @@ use std::io::Write as _;
use crate::dynamic::Completer as _;
+/// A subcommand definition to `flatten` into your CLI
+///
+/// This provides a one-stop solution for integrating completions into your CLI
#[derive(clap::Subcommand)]
#[allow(missing_docs)]
#[derive(Clone, Debug)]
+#[command(about = None, long_about = None)]
pub enum CompleteCommand {
/// Register shell completions for this program
#[command(hide = true)]
Complete(CompleteArgs),
}
+/// Generally used via [`CompleteCommand`]
#[derive(clap::Args)]
#[command(arg_required_else_help = true)]
#[command(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
#[allow(missing_docs)]
#[derive(Clone, Debug)]
+#[command(about = None, long_about = None)]
pub struct CompleteArgs {
/// Specify shell to complete for
#[arg(long)]
diff --git a/src/dynamic/shells/shell.rs b/src/dynamic/shells/shell.rs
index a9f48ce..c36d2a2 100644
--- a/src/dynamic/shells/shell.rs
+++ b/src/dynamic/shells/shell.rs
@@ -4,7 +4,7 @@ use std::str::FromStr;
use clap::builder::PossibleValue;
use clap::ValueEnum;
-/// Shell with auto-generated completion script available.
+/// Completion support for built-in shells
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
diff --git a/src/generator/mod.rs b/src/generator/mod.rs
index a371f68..415913e 100644
--- a/src/generator/mod.rs
+++ b/src/generator/mod.rs
@@ -36,7 +36,7 @@ pub trait Generator {
/// ```
fn file_name(&self, name: &str) -> String;
- /// Generates output out of [`clap::Command`](Command).
+ /// Generates output out of [`clap::Command`].
///
/// # Panics
///
@@ -44,7 +44,7 @@ pub trait Generator {
///
/// # Examples
///
- /// The following example generator displays the [`clap::Command`](Command)
+ /// The following example generator displays the [`clap::Command`]
/// as if it is printed using [`std::println`].
///
/// ```
diff --git a/src/generator/utils.rs b/src/generator/utils.rs
index ca76d18..6ea10d2 100644
--- a/src/generator/utils.rs
+++ b/src/generator/utils.rs
@@ -109,7 +109,7 @@ pub fn longs_and_visible_aliases(p: &Command) -> Vec<String> {
.collect()
}
-/// Gets all the flags of a [`clap::Command`](Command).
+/// Gets all the flags of a [`clap::Command`].
/// Includes `help` and `version` depending on the [`clap::Command`] settings.
pub fn flags(p: &Command) -> Vec<Arg> {
debug!("flags: name={}", p.get_name());
diff --git a/src/lib.rs b/src/lib.rs
index a442882..18fb282 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@
#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
#![doc = include_str!("../README.md")]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![allow(clippy::needless_doctest_main)]
diff --git a/src/shells/bash.rs b/src/shells/bash.rs
index 2a97e1d..f390853 100644
--- a/src/shells/bash.rs
+++ b/src/shells/bash.rs
@@ -18,6 +18,8 @@ impl Generator for Bash {
.get_bin_name()
.expect("crate::generate should have set the bin_name");
+ let fn_name = bin_name.replace('-', "__");
+
w!(
buf,
format!(
@@ -58,13 +60,17 @@ impl Generator for Bash {
esac
}}
-complete -F _{name} -o nosort -o bashdefault -o default {name}
+if [[ \"${{BASH_VERSINFO[0]}}\" -eq 4 && \"${{BASH_VERSINFO[1]}}\" -ge 4 || \"${{BASH_VERSINFO[0]}}\" -gt 4 ]]; then
+ complete -F _{name} -o nosort -o bashdefault -o default {name}
+else
+ complete -F _{name} -o bashdefault -o default {name}
+fi
",
name = bin_name,
- cmd = bin_name.replace('-', "__"),
+ cmd = fn_name,
name_opts = all_options_for_path(cmd, bin_name),
name_opts_details = option_details_for_path(cmd, bin_name),
- subcmds = all_subcommands(cmd),
+ subcmds = all_subcommands(cmd, &fn_name),
subcmd_details = subcommand_details(cmd)
)
.as_bytes()
@@ -72,7 +78,7 @@ complete -F _{name} -o nosort -o bashdefault -o default {name}
}
}
-fn all_subcommands(cmd: &Command) -> String {
+fn all_subcommands(cmd: &Command, parent_fn_name: &str) -> String {
debug!("all_subcommands");
fn add_command(
@@ -102,9 +108,8 @@ fn all_subcommands(cmd: &Command) -> String {
}
}
let mut subcmds = vec![];
- let fn_name = cmd.get_name().replace('-', "__");
for subcmd in cmd.get_subcommands() {
- add_command(&fn_name, subcmd, &mut subcmds);
+ add_command(parent_fn_name, subcmd, &mut subcmds);
}
subcmds.sort();
@@ -164,29 +169,50 @@ fn option_details_for_path(cmd: &Command, path: &str) -> String {
let mut opts = vec![String::new()];
for o in p.get_opts() {
+ let compopt = match o.get_value_hint() {
+ ValueHint::FilePath => Some("compopt -o filenames"),
+ ValueHint::DirPath => Some("compopt -o plusdirs"),
+ ValueHint::Other => Some("compopt -o nospace"),
+ _ => None,
+ };
+
if let Some(longs) = o.get_long_and_visible_aliases() {
opts.extend(longs.iter().map(|long| {
- format!(
- "--{})
- COMPREPLY=({})
- return 0
- ;;",
- long,
- vals_for(o)
- )
+ let mut v = vec![
+ format!("--{})", long),
+ format!("COMPREPLY=({})", vals_for(o)),
+ ];
+
+ if let Some(copt) = compopt {
+ v.extend([
+ r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
+ format!(" {}", copt),
+ "fi".to_string(),
+ ]);
+ }
+
+ v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
+ v.join("\n ")
}));
}
if let Some(shorts) = o.get_short_and_visible_aliases() {
opts.extend(shorts.iter().map(|short| {
- format!(
- "-{})
- COMPREPLY=({})
- return 0
- ;;",
- short,
- vals_for(o)
- )
+ let mut v = vec![
+ format!("-{})", short),
+ format!("COMPREPLY=({})", vals_for(o)),
+ ];
+
+ if let Some(copt) = compopt {
+ v.extend([
+ r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
+ format!(" {}", copt),
+ "fi".to_string(),
+ ]);
+ }
+
+ v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
+ v.join("\n ")
}));
}
}
@@ -206,6 +232,8 @@ fn vals_for(o: &Arg) -> String {
.collect::<Vec<_>>()
.join(" ")
)
+ } else if o.get_value_hint() == ValueHint::DirPath {
+ String::from("") // should be empty to avoid duplicate candidates
} else if o.get_value_hint() == ValueHint::Other {
String::from("\"${cur}\"")
} else {