mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 06:44:16 +00:00
Merge pull request #5665 from epage/cmd
fix(complete)!: Put CompleteCommand behind unstable-command
This commit is contained in:
commit
2633817b19
12 changed files with 75 additions and 68 deletions
|
@ -52,12 +52,13 @@ automod = "1.0.14"
|
|||
|
||||
[[example]]
|
||||
name = "dynamic"
|
||||
required-features = ["unstable-dynamic"]
|
||||
required-features = ["unstable-dynamic", "unstable-command"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
unstable-doc = ["unstable-dynamic"] # for docs.rs
|
||||
unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:unicode-xid", "clap/derive", "dep:is_executable", "dep:pathdiff", "clap/unstable-ext"]
|
||||
unstable-doc = ["unstable-dynamic", "unstable-command"] # for docs.rs
|
||||
unstable-dynamic = ["dep:clap_lex", "dep:is_executable", "dep:pathdiff", "clap/unstable-ext"]
|
||||
unstable-command = ["unstable-dynamic", "dep:shlex", "dep:unicode-xid", "clap/derive", "dep:is_executable", "dep:pathdiff", "clap/unstable-ext"]
|
||||
debug = ["clap/debug"]
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clap::builder::PossibleValue;
|
||||
#[cfg(feature = "unstable-dynamic")]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
use clap::{FromArgMatches, Subcommand};
|
||||
use clap_complete::{generate, Generator, Shell};
|
||||
|
||||
|
@ -12,7 +12,7 @@ fn main() {
|
|||
return;
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-dynamic")]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
if let Ok(completions) = clap_complete::dynamic::CompleteCommand::from_arg_matches(&matches) {
|
||||
completions.complete(&mut cli());
|
||||
return;
|
||||
|
@ -195,7 +195,7 @@ fn cli() -> clap::Command {
|
|||
.value_hint(clap::ValueHint::EmailAddress),
|
||||
]),
|
||||
]);
|
||||
#[cfg(feature = "unstable-dynamic")]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
let cli = clap_complete::dynamic::CompleteCommand::augment_subcommands(cli);
|
||||
cli
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use clap::builder::StyledStr;
|
|||
|
||||
/// A shell-agnostic completion candidate
|
||||
///
|
||||
/// [`ShellCompleter`][crate::dynamic::shells::ShellCompleter] will adapt what it can to the
|
||||
/// [`Shell`][crate::dynamic::shells::Shell] will adapt what it can to the
|
||||
/// current shell.
|
||||
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CompletionCandidate {
|
||||
|
|
|
@ -10,7 +10,7 @@ use super::CompletionCandidate;
|
|||
///
|
||||
/// For integration with clap and shells, see [`CompleteCommand`][crate::dynamic::shells::CompleteCommand].
|
||||
///
|
||||
/// This is generally called by a [`ShellCompleter`][crate::dynamic::shells::ShellCompleter] which
|
||||
/// This is generally called by a [`Shell`][crate::dynamic::shells::Shell] which
|
||||
/// handles the shell-specific logic.
|
||||
pub fn complete(
|
||||
cmd: &mut clap::Command,
|
||||
|
|
|
@ -19,5 +19,7 @@ pub use custom::ArgValueCompleter;
|
|||
pub use custom::CustomCompleter;
|
||||
|
||||
// These live in `shells` because they are tightly coupled with the `ShellCompleter`s
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub use shells::CompleteArgs;
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub use shells::CompleteCommand;
|
||||
|
|
|
@ -76,6 +76,7 @@ use super::Shell;
|
|||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[command(about = None, long_about = None)]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub enum CompleteCommand {
|
||||
/// Register shell completions for this program
|
||||
#[command(hide = true)]
|
||||
|
@ -173,6 +174,7 @@ impl CompleteCommand {
|
|||
/// ```
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(about = None, long_about = None)]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub struct CompleteArgs {
|
||||
/// Specify shell to complete for
|
||||
#[arg(value_name = "NAME")]
|
||||
|
@ -229,15 +231,16 @@ impl CompleteArgs {
|
|||
/// This will generally be called by [`CompleteCommand`] or [`CompleteArgs`].
|
||||
///
|
||||
/// This handles adapting between the shell and [`completer`][crate::dynamic::complete()].
|
||||
/// A `ShellCompleter` can choose how much of that lives within the registration script and or
|
||||
/// lives in [`ShellCompleter::write_complete`].
|
||||
pub trait ShellCompleter {
|
||||
/// A `CommandCompleter` can choose how much of that lives within the registration script and or
|
||||
/// lives in [`CommandCompleter::write_complete`].
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub trait CommandCompleter {
|
||||
/// The recommended file name for the registration code
|
||||
fn file_name(&self, name: &str) -> String;
|
||||
/// Register for completions
|
||||
///
|
||||
/// Write the `buf` the logic needed for calling into `<cmd> complete`, passing needed
|
||||
/// arguments to [`ShellCompleter::write_complete`] through the environment.
|
||||
/// arguments to [`CommandCompleter::write_complete`] through the environment.
|
||||
fn write_registration(
|
||||
&self,
|
||||
name: &str,
|
||||
|
@ -247,7 +250,7 @@ pub trait ShellCompleter {
|
|||
) -> Result<(), std::io::Error>;
|
||||
/// Complete the given command
|
||||
///
|
||||
/// Adapt information from arguments and [`ShellCompleter::write_registration`]-defined env
|
||||
/// Adapt information from arguments and [`CommandCompleter::write_registration`]-defined env
|
||||
/// variables to what is needed for [`completer`][crate::dynamic::complete()].
|
||||
///
|
||||
/// Write out the [`CompletionCandidate`][crate::dynamic::CompletionCandidate]s in a way the shell will understand.
|
||||
|
@ -260,7 +263,41 @@ pub trait ShellCompleter {
|
|||
) -> Result<(), std::io::Error>;
|
||||
}
|
||||
|
||||
impl ShellCompleter for super::Bash {
|
||||
impl CommandCompleter for Shell {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
shell_completer(self).file_name(name)
|
||||
}
|
||||
fn write_registration(
|
||||
&self,
|
||||
name: &str,
|
||||
bin: &str,
|
||||
completer: &str,
|
||||
buf: &mut dyn std::io::Write,
|
||||
) -> Result<(), std::io::Error> {
|
||||
shell_completer(self).write_registration(name, bin, completer, buf)
|
||||
}
|
||||
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> {
|
||||
shell_completer(self).write_complete(cmd, args, current_dir, buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_completer(shell: &Shell) -> &dyn CommandCompleter {
|
||||
match shell {
|
||||
Shell::Bash => &super::Bash,
|
||||
Shell::Elvish => &super::Elvish,
|
||||
Shell::Fish => &super::Fish,
|
||||
Shell::Powershell => &super::Powershell,
|
||||
Shell::Zsh => &super::Zsh,
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandCompleter for super::Bash {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
format!("{name}.bash")
|
||||
}
|
||||
|
@ -380,7 +417,7 @@ impl Default for CompType {
|
|||
Self::Normal
|
||||
}
|
||||
}
|
||||
impl ShellCompleter for super::Elvish {
|
||||
impl CommandCompleter for super::Elvish {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
format!("{name}.elv")
|
||||
}
|
||||
|
@ -436,7 +473,7 @@ set edit:completion:arg-completer[BIN] = { |@words|
|
|||
}
|
||||
}
|
||||
|
||||
impl ShellCompleter for super::Fish {
|
||||
impl CommandCompleter for super::Fish {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
format!("{name}.fish")
|
||||
}
|
||||
|
@ -481,7 +518,7 @@ impl ShellCompleter for super::Fish {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShellCompleter for super::Powershell {
|
||||
impl CommandCompleter for super::Powershell {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
format!("{name}.ps1")
|
||||
}
|
||||
|
@ -547,7 +584,7 @@ Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{
|
|||
}
|
||||
}
|
||||
|
||||
impl ShellCompleter for super::Zsh {
|
||||
impl CommandCompleter for super::Zsh {
|
||||
fn file_name(&self, name: &str) -> String {
|
||||
format!("{name}.zsh")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Shell completion support, see [`CompleteCommand`] for more details
|
||||
|
||||
#[cfg(feature = "unstable-command")]
|
||||
mod command;
|
||||
|
||||
#[cfg(feature = "unstable-command")]
|
||||
pub use command::*;
|
||||
|
||||
use std::fmt::Display;
|
||||
|
@ -132,59 +134,22 @@ impl ValueEnum for Shell {
|
|||
}
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
fn completer(&self) -> &dyn ShellCompleter {
|
||||
match self {
|
||||
Self::Bash => &Bash,
|
||||
Self::Elvish => &Elvish,
|
||||
Self::Fish => &Fish,
|
||||
Self::Powershell => &Powershell,
|
||||
Self::Zsh => &Zsh,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellCompleter 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`ShellCompleter`] for Bash
|
||||
/// Bash completion adapter
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Bash;
|
||||
|
||||
/// A [`ShellCompleter`] for Elvish
|
||||
/// Elvish completion adapter
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Elvish;
|
||||
|
||||
/// A [`ShellCompleter`] for Fish
|
||||
/// Fish completion adapter
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Fish;
|
||||
|
||||
/// A [`ShellCompleter`] for Powershell
|
||||
/// Powershell completion adapter
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Powershell;
|
||||
|
||||
/// A [`ShellCompleter`] for zsh
|
||||
/// Zsh completion adapter
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Zsh;
|
||||
|
|
|
@ -111,10 +111,10 @@ fn value_terminator() {
|
|||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-dynamic")]
|
||||
#[cfg(feature = "unstable-command")]
|
||||
#[test]
|
||||
fn register_minimal() {
|
||||
use clap_complete::dynamic::shells::ShellCompleter as _;
|
||||
use clap_complete::dynamic::shells::CommandCompleter as _;
|
||||
|
||||
let name = "my-app";
|
||||
let bin = name;
|
||||
|
|
|
@ -321,6 +321,7 @@ pub(crate) fn register_example<R: completest::RuntimeBuilder>(context: &str, nam
|
|||
// Unconditionally include to avoid completion file tests failing based on the how
|
||||
// `cargo test` is invoked
|
||||
"--features=unstable-dynamic",
|
||||
"--features=unstable-command",
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -379,6 +380,7 @@ where
|
|||
// Unconditionally include to avoid completion file tests failing based on the how
|
||||
// `cargo test` is invoked
|
||||
"--features=unstable-dynamic",
|
||||
"--features=unstable-command",
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -175,14 +175,14 @@ value value
|
|||
assert_data_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
#[test]
|
||||
fn register_dynamic() {
|
||||
common::register_example::<completest_pty::ElvishRuntimeBuilder>("dynamic", "exhaustive");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
fn complete_dynamic() {
|
||||
if !common::has_command("elvish") {
|
||||
return;
|
||||
|
|
|
@ -165,14 +165,14 @@ bash (bash (shell)) fish (fish shell) zsh (zsh shell)"#;
|
|||
assert_data_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
#[test]
|
||||
fn register_dynamic() {
|
||||
common::register_example::<completest_pty::FishRuntimeBuilder>("dynamic", "exhaustive");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
fn complete_dynamic() {
|
||||
if !common::has_command("fish") {
|
||||
return;
|
||||
|
|
|
@ -163,14 +163,14 @@ pacman action alias value quote hint last --
|
|||
assert_data_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
#[test]
|
||||
fn register_dynamic() {
|
||||
common::register_example::<completest_pty::ZshRuntimeBuilder>("dynamic", "exhaustive");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, feature = "unstable-dynamic"))]
|
||||
#[cfg(all(unix, feature = "unstable-command"))]
|
||||
fn complete_dynamic() {
|
||||
if !common::has_command("zsh") {
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue