diff options
Diffstat (limited to 'vendor/clap_complete/src/dynamic/shells')
-rw-r--r-- | vendor/clap_complete/src/dynamic/shells/bash.rs | 121 | ||||
-rw-r--r-- | vendor/clap_complete/src/dynamic/shells/fish.rs | 46 | ||||
-rw-r--r-- | vendor/clap_complete/src/dynamic/shells/mod.rs | 82 | ||||
-rw-r--r-- | vendor/clap_complete/src/dynamic/shells/shell.rs | 85 |
4 files changed, 334 insertions, 0 deletions
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) + } +} |