diff options
Diffstat (limited to 'src/dynamic/shells/mod.rs')
-rw-r--r-- | src/dynamic/shells/mod.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/dynamic/shells/mod.rs b/src/dynamic/shells/mod.rs new file mode 100644 index 0000000..54d23a3 --- /dev/null +++ b/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(()) + } +} |