diff options
Diffstat (limited to 'vendor/clap_complete/src/dynamic.rs')
-rw-r--r-- | vendor/clap_complete/src/dynamic.rs | 549 |
1 files changed, 0 insertions, 549 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 - } -} |