summaryrefslogtreecommitdiff
path: root/src/dynamic/shells/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynamic/shells/mod.rs')
-rw-r--r--src/dynamic/shells/mod.rs82
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(())
+ }
+}