summaryrefslogtreecommitdiffstats
path: root/vendor/clap_complete/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/clap_complete/src')
-rw-r--r--vendor/clap_complete/src/dynamic.rs549
-rw-r--r--vendor/clap_complete/src/dynamic/completer.rs341
-rw-r--r--vendor/clap_complete/src/dynamic/mod.rs7
-rw-r--r--vendor/clap_complete/src/dynamic/shells/bash.rs121
-rw-r--r--vendor/clap_complete/src/dynamic/shells/fish.rs46
-rw-r--r--vendor/clap_complete/src/dynamic/shells/mod.rs82
-rw-r--r--vendor/clap_complete/src/dynamic/shells/shell.rs85
-rw-r--r--vendor/clap_complete/src/shells/bash.rs2
-rw-r--r--vendor/clap_complete/src/shells/fish.rs6
-rw-r--r--vendor/clap_complete/src/shells/powershell.rs4
10 files changed, 690 insertions, 553 deletions
diff --git a/vendor/clap_complete/src/dynamic.rs b/vendor/clap_complete/src/dynamic.rs
deleted file mode 100644
index f25b4a48d..000000000
--- a/vendor/clap_complete/src/dynamic.rs
+++ /dev/null
@@ -1,549 +0,0 @@
-//! Complete commands within shells
-
-/// Complete commands within bash
-pub mod bash {
- use std::ffi::OsStr;
- use std::ffi::OsString;
- use std::io::Write;
-
- use clap_lex::OsStrExt as _;
- use unicode_xid::UnicodeXID;
-
- #[derive(clap::Subcommand)]
- #[command(hide = true)]
- #[allow(missing_docs)]
- #[derive(Clone, Debug)]
- pub enum CompleteCommand {
- /// Register shell completions for this program
- Complete(CompleteArgs),
- }
-
- #[derive(clap::Args)]
- #[command(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
- #[allow(missing_docs)]
- #[derive(Clone, Debug)]
- pub struct CompleteArgs {
- /// Path to write completion-registration to
- #[arg(long, required = true)]
- register: Option<std::path::PathBuf>,
-
- #[arg(
- long,
- required = true,
- value_name = "COMP_CWORD",
- hide_short_help = true,
- group = "complete"
- )]
- index: Option<usize>,
-
- #[arg(long, hide_short_help = true, group = "complete")]
- ifs: Option<String>,
-
- #[arg(
- long = "type",
- required = true,
- hide_short_help = true,
- group = "complete"
- )]
- comp_type: Option<CompType>,
-
- #[arg(long, hide_short_help = true, group = "complete")]
- space: bool,
-
- #[arg(
- long,
- conflicts_with = "space",
- hide_short_help = true,
- group = "complete"
- )]
- no_space: bool,
-
- #[arg(raw = true, hide_short_help = true, group = "complete")]
- comp_words: Vec<OsString>,
- }
-
- impl CompleteCommand {
- /// Process the completion request
- pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
- self.try_complete(cmd).unwrap_or_else(|e| e.exit());
- std::process::exit(0)
- }
-
- /// Process the completion request
- pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
- debug!("CompleteCommand::try_complete: {self:?}");
- let CompleteCommand::Complete(args) = self;
- if let Some(out_path) = args.register.as_deref() {
- let mut buf = Vec::new();
- let name = cmd.get_name();
- let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
- register(name, [bin], bin, &Behavior::default(), &mut buf)?;
- if out_path == std::path::Path::new("-") {
- std::io::stdout().write_all(&buf)?;
- } else if out_path.is_dir() {
- let out_path = out_path.join(file_name(name));
- std::fs::write(out_path, buf)?;
- } else {
- std::fs::write(out_path, buf)?;
- }
- } else {
- let index = args.index.unwrap_or_default();
- let comp_type = args.comp_type.unwrap_or_default();
- let space = match (args.space, args.no_space) {
- (true, false) => Some(true),
- (false, true) => Some(false),
- (true, true) => {
- unreachable!("`--space` and `--no-space` set, clap should prevent this")
- }
- (false, false) => None,
- }
- .unwrap();
- let current_dir = std::env::current_dir().ok();
- let completions = complete(
- cmd,
- args.comp_words.clone(),
- index,
- comp_type,
- space,
- current_dir.as_deref(),
- )?;
-
- let mut buf = Vec::new();
- for (i, completion) in completions.iter().enumerate() {
- if i != 0 {
- write!(&mut buf, "{}", args.ifs.as_deref().unwrap_or("\n"))?;
- }
- write!(&mut buf, "{}", completion.to_string_lossy())?;
- }
- std::io::stdout().write_all(&buf)?;
- }
-
- Ok(())
- }
- }
-
- /// The recommended file name for the registration code
- pub fn file_name(name: &str) -> String {
- format!("{name}.bash")
- }
-
- /// Define the completion behavior
- pub enum Behavior {
- /// Bare bones behavior
- Minimal,
- /// Fallback to readline behavior when no matches are generated
- Readline,
- /// Customize bash's completion behavior
- Custom(String),
- }
-
- impl Default for Behavior {
- fn default() -> Self {
- Self::Readline
- }
- }
-
- /// Generate code to register the dynamic completion
- pub fn register(
- name: &str,
- executables: impl IntoIterator<Item = impl AsRef<str>>,
- completer: &str,
- behavior: &Behavior,
- buf: &mut dyn Write,
- ) -> Result<(), std::io::Error> {
- let escaped_name = name.replace('-', "_");
- debug_assert!(
- escaped_name.chars().all(|c| c.is_xid_continue()),
- "`name` must be an identifier, got `{escaped_name}`"
- );
- let mut upper_name = escaped_name.clone();
- upper_name.make_ascii_uppercase();
-
- let executables = executables
- .into_iter()
- .map(|s| shlex::quote(s.as_ref()).into_owned())
- .collect::<Vec<_>>()
- .join(" ");
-
- let options = match behavior {
- Behavior::Minimal => "-o nospace -o bashdefault",
- Behavior::Readline => "-o nospace -o default -o bashdefault",
- Behavior::Custom(c) => c.as_str(),
- };
-
- let completer = shlex::quote(completer);
-
- let script = r#"
-_clap_complete_NAME() {
- local IFS=$'\013'
- local SUPPRESS_SPACE=0
- if compopt +o nospace 2> /dev/null; then
- SUPPRESS_SPACE=1
- fi
- if [[ ${SUPPRESS_SPACE} == 1 ]]; then
- SPACE_ARG="--no-space"
- else
- SPACE_ARG="--space"
- fi
- COMPREPLY=( $("COMPLETER" complete --index ${COMP_CWORD} --type ${COMP_TYPE} ${SPACE_ARG} --ifs="$IFS" -- "${COMP_WORDS[@]}") )
- if [[ $? != 0 ]]; then
- unset COMPREPLY
- elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
- compopt -o nospace
- fi
-}
-complete OPTIONS -F _clap_complete_NAME EXECUTABLES
-"#
- .replace("NAME", &escaped_name)
- .replace("EXECUTABLES", &executables)
- .replace("OPTIONS", options)
- .replace("COMPLETER", &completer)
- .replace("UPPER", &upper_name);
-
- writeln!(buf, "{script}")?;
- Ok(())
- }
-
- /// Type of completion attempted that caused a completion function to be called
- #[derive(Copy, Clone, Debug, PartialEq, Eq)]
- #[non_exhaustive]
- pub enum CompType {
- /// Normal completion
- Normal,
- /// List completions after successive tabs
- Successive,
- /// List alternatives on partial word completion
- Alternatives,
- /// List completions if the word is not unmodified
- Unmodified,
- /// Menu completion
- Menu,
- }
-
- impl clap::ValueEnum for CompType {
- fn value_variants<'a>() -> &'a [Self] {
- &[
- Self::Normal,
- Self::Successive,
- Self::Alternatives,
- Self::Unmodified,
- Self::Menu,
- ]
- }
- fn to_possible_value(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
- match self {
- Self::Normal => {
- let value = "9";
- debug_assert_eq!(b'\t'.to_string(), value);
- Some(
- clap::builder::PossibleValue::new(value)
- .alias("normal")
- .help("Normal completion"),
- )
- }
- Self::Successive => {
- let value = "63";
- debug_assert_eq!(b'?'.to_string(), value);
- Some(
- clap::builder::PossibleValue::new(value)
- .alias("successive")
- .help("List completions after successive tabs"),
- )
- }
- Self::Alternatives => {
- let value = "33";
- debug_assert_eq!(b'!'.to_string(), value);
- Some(
- clap::builder::PossibleValue::new(value)
- .alias("alternatives")
- .help("List alternatives on partial word completion"),
- )
- }
- Self::Unmodified => {
- let value = "64";
- debug_assert_eq!(b'@'.to_string(), value);
- Some(
- clap::builder::PossibleValue::new(value)
- .alias("unmodified")
- .help("List completions if the word is not unmodified"),
- )
- }
- Self::Menu => {
- let value = "37";
- debug_assert_eq!(b'%'.to_string(), value);
- Some(
- clap::builder::PossibleValue::new(value)
- .alias("menu")
- .help("Menu completion"),
- )
- }
- }
- }
- }
-
- impl Default for CompType {
- fn default() -> Self {
- Self::Normal
- }
- }
-
- /// Complete the command specified
- pub fn complete(
- cmd: &mut clap::Command,
- args: Vec<std::ffi::OsString>,
- arg_index: usize,
- _comp_type: CompType,
- _trailing_space: bool,
- current_dir: Option<&std::path::Path>,
- ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
- cmd.build();
-
- let raw_args = clap_lex::RawArgs::new(args.into_iter());
- let mut cursor = raw_args.cursor();
- let mut target_cursor = raw_args.cursor();
- raw_args.seek(
- &mut target_cursor,
- clap_lex::SeekFrom::Start(arg_index as u64),
- );
- // As we loop, `cursor` will always be pointing to the next item
- raw_args.next_os(&mut target_cursor);
-
- // TODO: Multicall support
- if !cmd.is_no_binary_name_set() {
- raw_args.next_os(&mut cursor);
- }
-
- let mut current_cmd = &*cmd;
- let mut pos_index = 1;
- let mut is_escaped = false;
- while let Some(arg) = raw_args.next(&mut cursor) {
- if cursor == target_cursor {
- return complete_arg(&arg, current_cmd, current_dir, pos_index, is_escaped);
- }
-
- debug!("complete::next: Begin parsing '{:?}'", arg.to_value_os(),);
-
- if let Ok(value) = arg.to_value() {
- if let Some(next_cmd) = current_cmd.find_subcommand(value) {
- current_cmd = next_cmd;
- pos_index = 0;
- continue;
- }
- }
-
- if is_escaped {
- pos_index += 1;
- } else if arg.is_escape() {
- is_escaped = true;
- } else if let Some(_long) = arg.to_long() {
- } else if let Some(_short) = arg.to_short() {
- } else {
- pos_index += 1;
- }
- }
-
- Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "No completion generated",
- ))
- }
-
- fn complete_arg(
- arg: &clap_lex::ParsedArg<'_>,
- cmd: &clap::Command,
- current_dir: Option<&std::path::Path>,
- pos_index: usize,
- is_escaped: bool,
- ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
- debug!(
- "complete_arg: arg={:?}, cmd={:?}, current_dir={:?}, pos_index={}, is_escaped={}",
- arg,
- cmd.get_name(),
- current_dir,
- pos_index,
- is_escaped
- );
- let mut completions = Vec::new();
-
- if !is_escaped {
- if let Some((flag, value)) = arg.to_long() {
- if let Ok(flag) = flag {
- if let Some(value) = value {
- if let Some(arg) = cmd.get_arguments().find(|a| a.get_long() == Some(flag))
- {
- completions.extend(
- complete_arg_value(value.to_str().ok_or(value), arg, current_dir)
- .into_iter()
- .map(|os| {
- // HACK: Need better `OsStr` manipulation
- format!("--{}={}", flag, os.to_string_lossy()).into()
- }),
- )
- }
- } else {
- completions.extend(
- crate::generator::utils::longs_and_visible_aliases(cmd)
- .into_iter()
- .filter_map(|f| {
- f.starts_with(flag).then(|| format!("--{f}").into())
- }),
- );
- }
- }
- } else if arg.is_escape() || arg.is_stdio() || arg.is_empty() {
- // HACK: Assuming knowledge of is_escape / is_stdio
- completions.extend(
- crate::generator::utils::longs_and_visible_aliases(cmd)
- .into_iter()
- .map(|f| format!("--{f}").into()),
- );
- }
-
- if arg.is_empty() || arg.is_stdio() || arg.is_short() {
- // HACK: Assuming knowledge of is_stdio
- completions.extend(
- crate::generator::utils::shorts_and_visible_aliases(cmd)
- .into_iter()
- // HACK: Need better `OsStr` manipulation
- .map(|f| format!("{}{}", arg.to_value_os().to_string_lossy(), f).into()),
- );
- }
- }
-
- if let Some(positional) = cmd
- .get_positionals()
- .find(|p| p.get_index() == Some(pos_index))
- {
- completions.extend(complete_arg_value(arg.to_value(), positional, current_dir));
- }
-
- if let Ok(value) = arg.to_value() {
- completions.extend(complete_subcommand(value, cmd));
- }
-
- Ok(completions)
- }
-
- fn complete_arg_value(
- value: Result<&str, &OsStr>,
- arg: &clap::Arg,
- current_dir: Option<&std::path::Path>,
- ) -> Vec<OsString> {
- let mut values = Vec::new();
- debug!("complete_arg_value: arg={arg:?}, value={value:?}");
-
- if let Some(possible_values) = crate::generator::utils::possible_values(arg) {
- if let Ok(value) = value {
- values.extend(possible_values.into_iter().filter_map(|p| {
- let name = p.get_name();
- name.starts_with(value).then(|| name.into())
- }));
- }
- } else {
- let value_os = match value {
- Ok(value) => OsStr::new(value),
- Err(value_os) => value_os,
- };
- match arg.get_value_hint() {
- clap::ValueHint::Other => {
- // Should not complete
- }
- clap::ValueHint::Unknown | clap::ValueHint::AnyPath => {
- values.extend(complete_path(value_os, current_dir, |_| true));
- }
- clap::ValueHint::FilePath => {
- values.extend(complete_path(value_os, current_dir, |p| p.is_file()));
- }
- clap::ValueHint::DirPath => {
- values.extend(complete_path(value_os, current_dir, |p| p.is_dir()));
- }
- clap::ValueHint::ExecutablePath => {
- use is_executable::IsExecutable;
- values.extend(complete_path(value_os, current_dir, |p| p.is_executable()));
- }
- clap::ValueHint::CommandName
- | clap::ValueHint::CommandString
- | clap::ValueHint::CommandWithArguments
- | clap::ValueHint::Username
- | clap::ValueHint::Hostname
- | clap::ValueHint::Url
- | clap::ValueHint::EmailAddress => {
- // No completion implementation
- }
- _ => {
- // Safe-ish fallback
- values.extend(complete_path(value_os, current_dir, |_| true));
- }
- }
- values.sort();
- }
-
- values
- }
-
- fn complete_path(
- value_os: &OsStr,
- current_dir: Option<&std::path::Path>,
- is_wanted: impl Fn(&std::path::Path) -> bool,
- ) -> Vec<OsString> {
- let mut completions = Vec::new();
-
- let current_dir = match current_dir {
- Some(current_dir) => current_dir,
- None => {
- // Can't complete without a `current_dir`
- return Vec::new();
- }
- };
- let (existing, prefix) = value_os
- .split_once("\\")
- .unwrap_or((OsStr::new(""), value_os));
- let root = current_dir.join(existing);
- debug!("complete_path: root={root:?}, prefix={prefix:?}");
- let prefix = prefix.to_string_lossy();
-
- for entry in std::fs::read_dir(&root)
- .ok()
- .into_iter()
- .flatten()
- .filter_map(Result::ok)
- {
- let raw_file_name = OsString::from(entry.file_name());
- if !raw_file_name.starts_with(&prefix) {
- continue;
- }
-
- if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
- let path = entry.path();
- let mut suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
- suggestion.push(""); // Ensure trailing `/`
- completions.push(suggestion.as_os_str().to_owned());
- } else {
- let path = entry.path();
- if is_wanted(&path) {
- let suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
- completions.push(suggestion.as_os_str().to_owned());
- }
- }
- }
-
- completions
- }
-
- fn complete_subcommand(value: &str, cmd: &clap::Command) -> Vec<OsString> {
- debug!(
- "complete_subcommand: cmd={:?}, value={:?}",
- cmd.get_name(),
- value
- );
-
- let mut scs = crate::generator::utils::all_subcommands(cmd)
- .into_iter()
- .filter(|x| x.0.starts_with(value))
- .map(|x| OsString::from(&x.0))
- .collect::<Vec<_>>();
- scs.sort();
- scs.dedup();
- scs
- }
-}
diff --git a/vendor/clap_complete/src/dynamic/completer.rs b/vendor/clap_complete/src/dynamic/completer.rs
new file mode 100644
index 000000000..3813e910a
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/completer.rs
@@ -0,0 +1,341 @@
+use std::ffi::OsStr;
+use std::ffi::OsString;
+
+use clap::builder::StyledStr;
+use clap_lex::OsStrExt as _;
+
+/// Shell-specific completions
+pub trait Completer {
+ /// The recommended file name for the registration code
+ fn file_name(&self, name: &str) -> String;
+ /// Register for completions
+ fn write_registration(
+ &self,
+ name: &str,
+ bin: &str,
+ completer: &str,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error>;
+ /// Complete the command
+ fn write_complete(
+ &self,
+ cmd: &mut clap::Command,
+ args: Vec<std::ffi::OsString>,
+ current_dir: Option<&std::path::Path>,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error>;
+}
+
+/// Complete the command specified
+pub fn complete(
+ cmd: &mut clap::Command,
+ args: Vec<std::ffi::OsString>,
+ arg_index: usize,
+ current_dir: Option<&std::path::Path>,
+) -> Result<Vec<(std::ffi::OsString, Option<StyledStr>)>, std::io::Error> {
+ cmd.build();
+
+ let raw_args = clap_lex::RawArgs::new(args.into_iter());
+ let mut cursor = raw_args.cursor();
+ let mut target_cursor = raw_args.cursor();
+ raw_args.seek(
+ &mut target_cursor,
+ clap_lex::SeekFrom::Start(arg_index as u64),
+ );
+ // As we loop, `cursor` will always be pointing to the next item
+ raw_args.next_os(&mut target_cursor);
+
+ // TODO: Multicall support
+ if !cmd.is_no_binary_name_set() {
+ raw_args.next_os(&mut cursor);
+ }
+
+ let mut current_cmd = &*cmd;
+ let mut pos_index = 1;
+ let mut is_escaped = false;
+ while let Some(arg) = raw_args.next(&mut cursor) {
+ if cursor == target_cursor {
+ return complete_arg(&arg, current_cmd, current_dir, pos_index, is_escaped);
+ }
+
+ debug!("complete::next: Begin parsing '{:?}'", arg.to_value_os(),);
+
+ if let Ok(value) = arg.to_value() {
+ if let Some(next_cmd) = current_cmd.find_subcommand(value) {
+ current_cmd = next_cmd;
+ pos_index = 1;
+ continue;
+ }
+ }
+
+ if is_escaped {
+ pos_index += 1;
+ } else if arg.is_escape() {
+ is_escaped = true;
+ } else if let Some(_long) = arg.to_long() {
+ } else if let Some(_short) = arg.to_short() {
+ } else {
+ pos_index += 1;
+ }
+ }
+
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no completion generated",
+ ))
+}
+
+fn complete_arg(
+ arg: &clap_lex::ParsedArg<'_>,
+ cmd: &clap::Command,
+ current_dir: Option<&std::path::Path>,
+ pos_index: usize,
+ is_escaped: bool,
+) -> Result<Vec<(std::ffi::OsString, Option<StyledStr>)>, std::io::Error> {
+ debug!(
+ "complete_arg: arg={:?}, cmd={:?}, current_dir={:?}, pos_index={}, is_escaped={}",
+ arg,
+ cmd.get_name(),
+ current_dir,
+ pos_index,
+ is_escaped
+ );
+ let mut completions = Vec::new();
+
+ if !is_escaped {
+ if let Some((flag, value)) = arg.to_long() {
+ if let Ok(flag) = flag {
+ if let Some(value) = value {
+ if let Some(arg) = cmd.get_arguments().find(|a| a.get_long() == Some(flag)) {
+ completions.extend(
+ complete_arg_value(value.to_str().ok_or(value), arg, current_dir)
+ .into_iter()
+ .map(|(os, help)| {
+ // HACK: Need better `OsStr` manipulation
+ (format!("--{}={}", flag, os.to_string_lossy()).into(), help)
+ }),
+ )
+ }
+ } else {
+ completions.extend(longs_and_visible_aliases(cmd).into_iter().filter_map(
+ |(f, help)| f.starts_with(flag).then(|| (format!("--{f}").into(), help)),
+ ));
+ }
+ }
+ } else if arg.is_escape() || arg.is_stdio() || arg.is_empty() {
+ // HACK: Assuming knowledge of is_escape / is_stdio
+ completions.extend(
+ longs_and_visible_aliases(cmd)
+ .into_iter()
+ .map(|(f, help)| (format!("--{f}").into(), help)),
+ );
+ }
+
+ if arg.is_empty() || arg.is_stdio() || arg.is_short() {
+ let dash_or_arg = if arg.is_empty() {
+ "-".into()
+ } else {
+ arg.to_value_os().to_string_lossy()
+ };
+ // HACK: Assuming knowledge of is_stdio
+ completions.extend(
+ shorts_and_visible_aliases(cmd)
+ .into_iter()
+ // HACK: Need better `OsStr` manipulation
+ .map(|(f, help)| (format!("{}{}", dash_or_arg, f).into(), help)),
+ );
+ }
+ }
+
+ if let Some(positional) = cmd
+ .get_positionals()
+ .find(|p| p.get_index() == Some(pos_index))
+ {
+ completions.extend(complete_arg_value(arg.to_value(), positional, current_dir));
+ }
+
+ if let Ok(value) = arg.to_value() {
+ completions.extend(complete_subcommand(value, cmd));
+ }
+
+ Ok(completions)
+}
+
+fn complete_arg_value(
+ value: Result<&str, &OsStr>,
+ arg: &clap::Arg,
+ current_dir: Option<&std::path::Path>,
+) -> Vec<(OsString, Option<StyledStr>)> {
+ let mut values = Vec::new();
+ debug!("complete_arg_value: arg={arg:?}, value={value:?}");
+
+ if let Some(possible_values) = possible_values(arg) {
+ if let Ok(value) = value {
+ values.extend(possible_values.into_iter().filter_map(|p| {
+ let name = p.get_name();
+ name.starts_with(value)
+ .then(|| (name.into(), p.get_help().cloned()))
+ }));
+ }
+ } else {
+ let value_os = match value {
+ Ok(value) => OsStr::new(value),
+ Err(value_os) => value_os,
+ };
+ match arg.get_value_hint() {
+ clap::ValueHint::Other => {
+ // Should not complete
+ }
+ clap::ValueHint::Unknown | clap::ValueHint::AnyPath => {
+ values.extend(complete_path(value_os, current_dir, |_| true));
+ }
+ clap::ValueHint::FilePath => {
+ values.extend(complete_path(value_os, current_dir, |p| p.is_file()));
+ }
+ clap::ValueHint::DirPath => {
+ values.extend(complete_path(value_os, current_dir, |p| p.is_dir()));
+ }
+ clap::ValueHint::ExecutablePath => {
+ use is_executable::IsExecutable;
+ values.extend(complete_path(value_os, current_dir, |p| p.is_executable()));
+ }
+ clap::ValueHint::CommandName
+ | clap::ValueHint::CommandString
+ | clap::ValueHint::CommandWithArguments
+ | clap::ValueHint::Username
+ | clap::ValueHint::Hostname
+ | clap::ValueHint::Url
+ | clap::ValueHint::EmailAddress => {
+ // No completion implementation
+ }
+ _ => {
+ // Safe-ish fallback
+ values.extend(complete_path(value_os, current_dir, |_| true));
+ }
+ }
+ values.sort();
+ }
+
+ values
+}
+
+fn complete_path(
+ value_os: &OsStr,
+ current_dir: Option<&std::path::Path>,
+ is_wanted: impl Fn(&std::path::Path) -> bool,
+) -> Vec<(OsString, Option<StyledStr>)> {
+ let mut completions = Vec::new();
+
+ let current_dir = match current_dir {
+ Some(current_dir) => current_dir,
+ None => {
+ // Can't complete without a `current_dir`
+ return Vec::new();
+ }
+ };
+ let (existing, prefix) = value_os
+ .split_once("\\")
+ .unwrap_or((OsStr::new(""), value_os));
+ let root = current_dir.join(existing);
+ debug!("complete_path: root={root:?}, prefix={prefix:?}");
+ let prefix = prefix.to_string_lossy();
+
+ for entry in std::fs::read_dir(&root)
+ .ok()
+ .into_iter()
+ .flatten()
+ .filter_map(Result::ok)
+ {
+ let raw_file_name = entry.file_name();
+ if !raw_file_name.starts_with(&prefix) {
+ continue;
+ }
+
+ if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
+ let path = entry.path();
+ let mut suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
+ suggestion.push(""); // Ensure trailing `/`
+ completions.push((suggestion.as_os_str().to_owned(), None));
+ } else {
+ let path = entry.path();
+ if is_wanted(&path) {
+ let suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
+ completions.push((suggestion.as_os_str().to_owned(), None));
+ }
+ }
+ }
+
+ completions
+}
+
+fn complete_subcommand(value: &str, cmd: &clap::Command) -> Vec<(OsString, Option<StyledStr>)> {
+ debug!(
+ "complete_subcommand: cmd={:?}, value={:?}",
+ cmd.get_name(),
+ value
+ );
+
+ let mut scs = subcommands(cmd)
+ .into_iter()
+ .filter(|x| x.0.starts_with(value))
+ .map(|x| (OsString::from(&x.0), x.1))
+ .collect::<Vec<_>>();
+ scs.sort();
+ scs.dedup();
+ scs
+}
+
+/// Gets all the long options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `help` and `version` depending on the [`clap::Command`] settings.
+fn longs_and_visible_aliases(p: &clap::Command) -> Vec<(String, Option<StyledStr>)> {
+ debug!("longs: name={}", p.get_name());
+
+ p.get_arguments()
+ .filter_map(|a| {
+ a.get_long_and_visible_aliases().map(|longs| {
+ longs
+ .into_iter()
+ .map(|s| (s.to_string(), a.get_help().cloned()))
+ })
+ })
+ .flatten()
+ .collect()
+}
+
+/// Gets all the short options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `h` and `V` depending on the [`clap::Command`] settings.
+fn shorts_and_visible_aliases(p: &clap::Command) -> Vec<(char, Option<StyledStr>)> {
+ debug!("shorts: name={}", p.get_name());
+
+ p.get_arguments()
+ .filter_map(|a| {
+ a.get_short_and_visible_aliases()
+ .map(|shorts| shorts.into_iter().map(|s| (s, a.get_help().cloned())))
+ })
+ .flatten()
+ .collect()
+}
+
+/// Get the possible values for completion
+fn possible_values(a: &clap::Arg) -> Option<Vec<clap::builder::PossibleValue>> {
+ if !a.get_num_args().expect("built").takes_values() {
+ None
+ } else {
+ a.get_value_parser()
+ .possible_values()
+ .map(|pvs| pvs.collect())
+ }
+}
+
+/// Gets subcommands of [`clap::Command`] in the form of `("name", "bin_name")`.
+///
+/// Subcommand `rustup toolchain install` would be converted to
+/// `("install", "rustup toolchain install")`.
+fn subcommands(p: &clap::Command) -> Vec<(String, Option<StyledStr>)> {
+ debug!("subcommands: name={}", p.get_name());
+ debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
+
+ p.get_subcommands()
+ .map(|sc| (sc.get_name().to_string(), sc.get_about().cloned()))
+ .collect()
+}
diff --git a/vendor/clap_complete/src/dynamic/mod.rs b/vendor/clap_complete/src/dynamic/mod.rs
new file mode 100644
index 000000000..f7c985704
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/mod.rs
@@ -0,0 +1,7 @@
+//! Complete commands within shells
+
+mod completer;
+
+pub mod shells;
+
+pub use completer::*;
diff --git a/vendor/clap_complete/src/dynamic/shells/bash.rs b/vendor/clap_complete/src/dynamic/shells/bash.rs
new file mode 100644
index 000000000..43c128e5b
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/shells/bash.rs
@@ -0,0 +1,121 @@
+use unicode_xid::UnicodeXID as _;
+
+/// Bash completions
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Bash;
+
+impl crate::dynamic::Completer for Bash {
+ fn file_name(&self, name: &str) -> String {
+ format!("{name}.bash")
+ }
+ fn write_registration(
+ &self,
+ name: &str,
+ bin: &str,
+ completer: &str,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ let escaped_name = name.replace('-', "_");
+ debug_assert!(
+ escaped_name.chars().all(|c| c.is_xid_continue()),
+ "`name` must be an identifier, got `{escaped_name}`"
+ );
+ let mut upper_name = escaped_name.clone();
+ upper_name.make_ascii_uppercase();
+
+ let completer = shlex::quote(completer);
+
+ let script = r#"
+_clap_complete_NAME() {
+ export IFS=$'\013'
+ export _CLAP_COMPLETE_INDEX=${COMP_CWORD}
+ export _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
+ if compopt +o nospace 2> /dev/null; then
+ export _CLAP_COMPLETE_SPACE=false
+ else
+ export _CLAP_COMPLETE_SPACE=true
+ fi
+ COMPREPLY=( $("COMPLETER" complete --shell bash -- "${COMP_WORDS[@]}") )
+ if [[ $? != 0 ]]; then
+ unset COMPREPLY
+ elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
+ compopt -o nospace
+ fi
+}
+complete -o nospace -o bashdefault -F _clap_complete_NAME BIN
+"#
+ .replace("NAME", &escaped_name)
+ .replace("BIN", bin)
+ .replace("COMPLETER", &completer)
+ .replace("UPPER", &upper_name);
+
+ writeln!(buf, "{script}")?;
+ Ok(())
+ }
+ fn write_complete(
+ &self,
+ cmd: &mut clap::Command,
+ args: Vec<std::ffi::OsString>,
+ current_dir: Option<&std::path::Path>,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
+ .ok()
+ .and_then(|i| i.parse().ok())
+ .unwrap_or_default();
+ let _comp_type: CompType = std::env::var("_CLAP_COMPLETE_COMP_TYPE")
+ .ok()
+ .and_then(|i| i.parse().ok())
+ .unwrap_or_default();
+ let _space: Option<bool> = std::env::var("_CLAP_COMPLETE_SPACE")
+ .ok()
+ .and_then(|i| i.parse().ok());
+ let ifs: Option<String> = std::env::var("IFS").ok().and_then(|i| i.parse().ok());
+ let completions = crate::dynamic::complete(cmd, args, index, current_dir)?;
+
+ for (i, (completion, _)) in completions.iter().enumerate() {
+ if i != 0 {
+ write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
+ }
+ write!(buf, "{}", completion.to_string_lossy())?;
+ }
+ Ok(())
+ }
+}
+
+/// Type of completion attempted that caused a completion function to be called
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+enum CompType {
+ /// Normal completion
+ Normal,
+ /// List completions after successive tabs
+ Successive,
+ /// List alternatives on partial word completion
+ Alternatives,
+ /// List completions if the word is not unmodified
+ Unmodified,
+ /// Menu completion
+ Menu,
+}
+
+impl std::str::FromStr for CompType {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "9" => Ok(Self::Normal),
+ "63" => Ok(Self::Successive),
+ "33" => Ok(Self::Alternatives),
+ "64" => Ok(Self::Unmodified),
+ "37" => Ok(Self::Menu),
+ _ => Err(format!("unsupported COMP_TYPE `{}`", s)),
+ }
+ }
+}
+
+impl Default for CompType {
+ fn default() -> Self {
+ Self::Normal
+ }
+}
diff --git a/vendor/clap_complete/src/dynamic/shells/fish.rs b/vendor/clap_complete/src/dynamic/shells/fish.rs
new file mode 100644
index 000000000..9d7e8c684
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/shells/fish.rs
@@ -0,0 +1,46 @@
+/// Fish completions
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Fish;
+
+impl crate::dynamic::Completer for Fish {
+ fn file_name(&self, name: &str) -> String {
+ format!("{name}.fish")
+ }
+ fn write_registration(
+ &self,
+ _name: &str,
+ bin: &str,
+ completer: &str,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ let bin = shlex::quote(bin);
+ let completer = shlex::quote(completer);
+ writeln!(
+ buf,
+ r#"complete -x -c {bin} -a "("'{completer}'" complete --shell fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
+ )
+ }
+ fn write_complete(
+ &self,
+ cmd: &mut clap::Command,
+ args: Vec<std::ffi::OsString>,
+ current_dir: Option<&std::path::Path>,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ let index = args.len() - 1;
+ let completions = crate::dynamic::complete(cmd, args, index, current_dir)?;
+
+ for (completion, help) in completions {
+ write!(buf, "{}", completion.to_string_lossy())?;
+ if let Some(help) = help {
+ write!(
+ buf,
+ "\t{}",
+ help.to_string().lines().next().unwrap_or_default()
+ )?;
+ }
+ writeln!(buf)?;
+ }
+ Ok(())
+ }
+}
diff --git a/vendor/clap_complete/src/dynamic/shells/mod.rs b/vendor/clap_complete/src/dynamic/shells/mod.rs
new file mode 100644
index 000000000..54d23a3d4
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/shells/mod.rs
@@ -0,0 +1,82 @@
+//! Shell support
+
+mod bash;
+mod fish;
+mod shell;
+
+pub use bash::*;
+pub use fish::*;
+pub use shell::*;
+
+use std::ffi::OsString;
+use std::io::Write as _;
+
+use crate::dynamic::Completer as _;
+
+#[derive(clap::Subcommand)]
+#[allow(missing_docs)]
+#[derive(Clone, Debug)]
+pub enum CompleteCommand {
+ /// Register shell completions for this program
+ #[command(hide = true)]
+ Complete(CompleteArgs),
+}
+
+#[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)]
+pub struct CompleteArgs {
+ /// Specify shell to complete for
+ #[arg(long)]
+ shell: Shell,
+
+ /// Path to write completion-registration to
+ #[arg(long, required = true)]
+ register: Option<std::path::PathBuf>,
+
+ #[arg(raw = true, hide_short_help = true, group = "complete")]
+ comp_words: Vec<OsString>,
+}
+
+impl CompleteCommand {
+ /// Process the completion request
+ pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
+ self.try_complete(cmd).unwrap_or_else(|e| e.exit());
+ std::process::exit(0)
+ }
+
+ /// Process the completion request
+ pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
+ debug!("CompleteCommand::try_complete: {self:?}");
+ let CompleteCommand::Complete(args) = self;
+ if let Some(out_path) = args.register.as_deref() {
+ let mut buf = Vec::new();
+ let name = cmd.get_name();
+ let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
+ args.shell.write_registration(name, bin, bin, &mut buf)?;
+ if out_path == std::path::Path::new("-") {
+ std::io::stdout().write_all(&buf)?;
+ } else if out_path.is_dir() {
+ let out_path = out_path.join(args.shell.file_name(name));
+ std::fs::write(out_path, buf)?;
+ } else {
+ std::fs::write(out_path, buf)?;
+ }
+ } else {
+ let current_dir = std::env::current_dir().ok();
+
+ let mut buf = Vec::new();
+ args.shell.write_complete(
+ cmd,
+ args.comp_words.clone(),
+ current_dir.as_deref(),
+ &mut buf,
+ )?;
+ std::io::stdout().write_all(&buf)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/vendor/clap_complete/src/dynamic/shells/shell.rs b/vendor/clap_complete/src/dynamic/shells/shell.rs
new file mode 100644
index 000000000..a9f48cee9
--- /dev/null
+++ b/vendor/clap_complete/src/dynamic/shells/shell.rs
@@ -0,0 +1,85 @@
+use std::fmt::Display;
+use std::str::FromStr;
+
+use clap::builder::PossibleValue;
+use clap::ValueEnum;
+
+/// Shell with auto-generated completion script available.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum Shell {
+ /// Bourne Again SHell (bash)
+ Bash,
+ /// Friendly Interactive SHell (fish)
+ Fish,
+}
+
+impl Display for Shell {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.to_possible_value()
+ .expect("no values are skipped")
+ .get_name()
+ .fmt(f)
+ }
+}
+
+impl FromStr for Shell {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ for variant in Self::value_variants() {
+ if variant.to_possible_value().unwrap().matches(s, false) {
+ return Ok(*variant);
+ }
+ }
+ Err(format!("invalid variant: {s}"))
+ }
+}
+
+// Hand-rolled so it can work even when `derive` feature is disabled
+impl ValueEnum for Shell {
+ fn value_variants<'a>() -> &'a [Self] {
+ &[Shell::Bash, Shell::Fish]
+ }
+
+ fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
+ Some(match self {
+ Shell::Bash => PossibleValue::new("bash"),
+ Shell::Fish => PossibleValue::new("fish"),
+ })
+ }
+}
+
+impl Shell {
+ fn completer(&self) -> &dyn crate::dynamic::Completer {
+ match self {
+ Self::Bash => &super::Bash,
+ Self::Fish => &super::Fish,
+ }
+ }
+}
+
+impl crate::dynamic::Completer for Shell {
+ fn file_name(&self, name: &str) -> String {
+ self.completer().file_name(name)
+ }
+ fn write_registration(
+ &self,
+ name: &str,
+ bin: &str,
+ completer: &str,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ self.completer()
+ .write_registration(name, bin, completer, buf)
+ }
+ fn write_complete(
+ &self,
+ cmd: &mut clap::Command,
+ args: Vec<std::ffi::OsString>,
+ current_dir: Option<&std::path::Path>,
+ buf: &mut dyn std::io::Write,
+ ) -> Result<(), std::io::Error> {
+ self.completer().write_complete(cmd, args, current_dir, buf)
+ }
+}
diff --git a/vendor/clap_complete/src/shells/bash.rs b/vendor/clap_complete/src/shells/bash.rs
index 60e6b346c..2a97e1de2 100644
--- a/vendor/clap_complete/src/shells/bash.rs
+++ b/vendor/clap_complete/src/shells/bash.rs
@@ -58,7 +58,7 @@ impl Generator for Bash {
esac
}}
-complete -F _{name} -o bashdefault -o default {name}
+complete -F _{name} -o nosort -o bashdefault -o default {name}
",
name = bin_name,
cmd = bin_name.replace('-', "__"),
diff --git a/vendor/clap_complete/src/shells/fish.rs b/vendor/clap_complete/src/shells/fish.rs
index 5a069d35b..7dae5b6d6 100644
--- a/vendor/clap_complete/src/shells/fish.rs
+++ b/vendor/clap_complete/src/shells/fish.rs
@@ -168,10 +168,12 @@ fn value_completion(option: &Arg) -> String {
.filter_map(|value| if value.is_hide_set() {
None
} else {
+ // The help text after \t is wrapped in '' to make sure that the it is taken literally
+ // and there is no command substitution or variable expansion resulting in unexpected errors
Some(format!(
- "{}\t{}",
+ "{}\t'{}'",
escape_string(value.get_name(), true).as_str(),
- escape_string(&value.get_help().unwrap_or_default().to_string(), true)
+ escape_string(&value.get_help().unwrap_or_default().to_string(), false)
))
})
.collect::<Vec<_>>()
diff --git a/vendor/clap_complete/src/shells/powershell.rs b/vendor/clap_complete/src/shells/powershell.rs
index 417facf70..6b09b2e3a 100644
--- a/vendor/clap_complete/src/shells/powershell.rs
+++ b/vendor/clap_complete/src/shells/powershell.rs
@@ -124,7 +124,9 @@ fn generate_aliases(completions: &mut String, preamble: &String, arg: &Arg) {
for alias in aliases {
let _ = write!(
completions,
- "{preamble}'-{alias}', '{alias}', [CompletionResultType]::ParameterName, '{tooltip}')"
+ "{preamble}'-{alias}', '{alias}{}', [CompletionResultType]::ParameterName, '{tooltip}')",
+ // make PowerShell realize there is a difference between `-s` and `-S`
+ if alias.is_uppercase() { " " } else { "" },
);
}
}