Repository: clap-rs/clap
Pull request #5642 (2024-08-09 19:03:23)

fix(complete)!: Audit the API
<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->



Patch:
diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs
index 563d7ec3..549cdc79 100644
--- a/clap_builder/src/builder/arg.rs
+++ b/clap_builder/src/builder/arg.rs
@@ -880,13 +880,6 @@ impl Arg {
         self.ext.set(tagged);
         self
     }
-
-    /// Remove an [`ArgExt`]
-    #[cfg(feature = "unstable-ext")]
-    pub fn remove<T: ArgExt + Extension>(mut self) -> Self {
-        self.ext.remove::<T>();
-        self
-    }
 }
 
 /// # Value Handling
@@ -4240,6 +4233,12 @@ impl Arg {
     pub fn get<T: ArgExt + Extension>(&self) -> Option<&T> {
         self.ext.get::<T>()
     }
+
+    /// Remove an [`ArgExt`]
+    #[cfg(feature = "unstable-ext")]
+    pub fn remove<T: ArgExt + Extension>(mut self) -> Option<T> {
+        self.ext.remove::<T>()
+    }
 }
 
 /// # Internally used only
diff --git a/clap_complete/src/dynamic/complete.rs b/clap_complete/src/dynamic/complete.rs
index e0ea4efc..6b5807f7 100644
--- a/clap_complete/src/dynamic/complete.rs
+++ b/clap_complete/src/dynamic/complete.rs
@@ -408,7 +408,7 @@ fn complete_custom_arg_value(
     debug!("complete_custom_arg_value: completer={completer:?}, value={value:?}");
 
     let mut values = Vec::new();
-    let custom_arg_values = completer.completions();
+    let custom_arg_values = completer.candidates();
     values.extend(custom_arg_values);
 
     values.retain(|comp| comp.get_content().starts_with(&value.to_string_lossy()));
diff --git a/clap_complete/src/dynamic/completer.rs b/clap_complete/src/dynamic/completer.rs
deleted file mode 100644
index 54715a91..00000000
--- a/clap_complete/src/dynamic/completer.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use std::ffi::OsString;
-
-/// 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 given command
-    fn write_complete(
-        &self,
-        cmd: &mut clap::Command,
-        args: Vec<OsString>,
-        current_dir: Option<&std::path::Path>,
-        buf: &mut dyn std::io::Write,
-    ) -> Result<(), std::io::Error>;
-}
diff --git a/clap_complete/src/dynamic/custom.rs b/clap_complete/src/dynamic/custom.rs
index 5a424550..2c03a765 100644
--- a/clap_complete/src/dynamic/custom.rs
+++ b/clap_complete/src/dynamic/custom.rs
@@ -34,9 +34,11 @@ impl ArgValueCompleter {
         Self(Arc::new(completer))
     }
 
+    /// All potential candidates for an argument.
+    ///
     /// See [`CompletionCandidate`] for more information.
-    pub fn completions(&self) -> Vec<CompletionCandidate> {
-        self.0.completions()
+    pub fn candidates(&self) -> Vec<CompletionCandidate> {
+        self.0.candidates()
     }
 }
 
@@ -55,14 +57,14 @@ pub trait CustomCompleter: Send + Sync {
     /// All potential candidates for an argument.
     ///
     /// See [`CompletionCandidate`] for more information.
-    fn completions(&self) -> Vec<CompletionCandidate>;
+    fn candidates(&self) -> Vec<CompletionCandidate>;
 }
 
 impl<F> CustomCompleter for F
 where
     F: Fn() -> Vec<CompletionCandidate> + Send + Sync,
 {
-    fn completions(&self) -> Vec<CompletionCandidate> {
+    fn candidates(&self) -> Vec<CompletionCandidate> {
         self()
     }
 }
diff --git a/clap_complete/src/dynamic/mod.rs b/clap_complete/src/dynamic/mod.rs
index 60b57bcf..0809c04e 100644
--- a/clap_complete/src/dynamic/mod.rs
+++ b/clap_complete/src/dynamic/mod.rs
@@ -4,13 +4,11 @@
 
 mod candidate;
 mod complete;
-mod completer;
 mod custom;
 
 pub mod shells;
 
 pub use candidate::CompletionCandidate;
 pub use complete::complete;
-pub use completer::Completer;
 pub use custom::ArgValueCompleter;
 pub use custom::CustomCompleter;
diff --git a/clap_complete/src/dynamic/shells/bash.rs b/clap_complete/src/dynamic/shells/bash.rs
index ca6d791f..637f620a 100644
--- a/clap_complete/src/dynamic/shells/bash.rs
+++ b/clap_complete/src/dynamic/shells/bash.rs
@@ -4,7 +4,7 @@ use unicode_xid::UnicodeXID as _;
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct Bash;
 
-impl crate::dynamic::Completer for Bash {
+impl crate::dynamic::shells::ShellCompleter for Bash {
     fn file_name(&self, name: &str) -> String {
         format!("{name}.bash")
     }
diff --git a/clap_complete/src/dynamic/shells/elvish.rs b/clap_complete/src/dynamic/shells/elvish.rs
index 52767a65..f9c03488 100644
--- a/clap_complete/src/dynamic/shells/elvish.rs
+++ b/clap_complete/src/dynamic/shells/elvish.rs
@@ -2,7 +2,7 @@
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct Elvish;
 
-impl crate::dynamic::Completer for Elvish {
+impl crate::dynamic::shells::ShellCompleter for Elvish {
     fn file_name(&self, name: &str) -> String {
         format!("{name}.elv")
     }
diff --git a/clap_complete/src/dynamic/shells/fish.rs b/clap_complete/src/dynamic/shells/fish.rs
index c3ae1dba..86587d42 100644
--- a/clap_complete/src/dynamic/shells/fish.rs
+++ b/clap_complete/src/dynamic/shells/fish.rs
@@ -2,7 +2,7 @@
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct Fish;
 
-impl crate::dynamic::Completer for Fish {
+impl crate::dynamic::shells::ShellCompleter for Fish {
     fn file_name(&self, name: &str) -> String {
         format!("{name}.fish")
     }
diff --git a/clap_complete/src/dynamic/shells/mod.rs b/clap_complete/src/dynamic/shells/mod.rs
index 1d6fc3c0..a1cca607 100644
--- a/clap_complete/src/dynamic/shells/mod.rs
+++ b/clap_complete/src/dynamic/shells/mod.rs
@@ -15,8 +15,6 @@ pub use zsh::*;
 use std::ffi::OsString;
 use std::io::Write as _;
 
-use crate::dynamic::Completer as _;
-
 /// A subcommand definition to `flatten` into your CLI
 ///
 /// This provides a one-stop solution for integrating completions into your CLI
@@ -162,3 +160,25 @@ impl CompleteCommand {
         Ok(())
     }
 }
+
+/// Shell-specific completions
+pub trait ShellCompleter {
+    /// 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 given command
+    fn write_complete(
+        &self,
+        cmd: &mut clap::Command,
+        args: Vec<OsString>,
+        current_dir: Option<&std::path::Path>,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error>;
+}
diff --git a/clap_complete/src/dynamic/shells/shell.rs b/clap_complete/src/dynamic/shells/shell.rs
index dfc091c3..b3bd28c7 100644
--- a/clap_complete/src/dynamic/shells/shell.rs
+++ b/clap_complete/src/dynamic/shells/shell.rs
@@ -57,7 +57,7 @@ impl ValueEnum for Shell {
 }
 
 impl Shell {
-    fn completer(&self) -> &dyn crate::dynamic::Completer {
+    fn completer(&self) -> &dyn crate::dynamic::shells::ShellCompleter {
         match self {
             Self::Bash => &super::Bash,
             Self::Fish => &super::Fish,
@@ -67,7 +67,7 @@ impl Shell {
     }
 }
 
-impl crate::dynamic::Completer for Shell {
+impl crate::dynamic::shells::ShellCompleter for Shell {
     fn file_name(&self, name: &str) -> String {
         self.completer().file_name(name)
     }
diff --git a/clap_complete/src/dynamic/shells/zsh.rs b/clap_complete/src/dynamic/shells/zsh.rs
index 326d29ce..a3724a69 100644
--- a/clap_complete/src/dynamic/shells/zsh.rs
+++ b/clap_complete/src/dynamic/shells/zsh.rs
@@ -2,7 +2,7 @@
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub struct Zsh;
 
-impl crate::dynamic::Completer for Zsh {
+impl crate::dynamic::shells::ShellCompleter for Zsh {
     fn file_name(&self, name: &str) -> String {
         format!("{name}.zsh")
     }


Test patch:
diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs
index 61eccb0d..cc19243c 100644
--- a/clap_complete/tests/testsuite/bash.rs
+++ b/clap_complete/tests/testsuite/bash.rs
@@ -114,7 +114,7 @@ fn value_terminator() {
 #[cfg(feature = "unstable-dynamic")]
 #[test]
 fn register_minimal() {
-    use clap_complete::dynamic::Completer;
+    use clap_complete::dynamic::shells::ShellCompleter as _;
 
     let name = "my-app";
     let bin = name;
diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs
index 31214fa1..63f59a87 100644
--- a/clap_complete/tests/testsuite/dynamic.rs
+++ b/clap_complete/tests/testsuite/dynamic.rs
@@ -597,7 +597,7 @@ fn suggest_custom_arg_value() {
     struct MyCustomCompleter {}
 
     impl CustomCompleter for MyCustomCompleter {
-        fn completions(&self) -> Vec<CompletionCandidate> {
+        fn candidates(&self) -> Vec<CompletionCandidate> {
             vec![
                 CompletionCandidate::new("custom1"),
                 CompletionCandidate::new("custom2"),