Merge pull request #5705 from epage/command

fix(complete)!: Remove CompleteCommand
This commit is contained in:
Ed Page 2024-08-28 21:47:36 -05:00 committed by GitHub
commit 50f7646cf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 34 additions and 1191 deletions

1
Cargo.lock generated
View file

@ -485,7 +485,6 @@ dependencies = [
"shlex",
"snapbox",
"trycmd",
"unicode-xid",
]
[[package]]

View file

@ -38,7 +38,6 @@ clap = { path = "../", version = "4.5.15", default-features = false, features =
clap_lex = { path = "../clap_lex", version = "0.7.0", optional = true }
is_executable = { version = "1.0.1", optional = true }
shlex = { version = "1.3.0", optional = true }
unicode-xid = { version = "0.2.2", optional = true }
[dev-dependencies]
snapbox = { version = "0.6.0", features = ["diff", "dir", "examples"] }
@ -51,13 +50,12 @@ automod = "1.0.14"
[[example]]
name = "dynamic"
required-features = ["unstable-dynamic", "unstable-command"]
required-features = ["unstable-dynamic"]
[features]
default = []
unstable-doc = ["unstable-dynamic", "unstable-command"] # for docs.rs
unstable-doc = ["unstable-dynamic"] # for docs.rs
unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:is_executable", "clap/unstable-ext"]
unstable-command = ["unstable-dynamic", "dep:unicode-xid", "clap/derive", "dep:is_executable", "clap/unstable-ext"]
debug = ["clap/debug"]
[lints]

View file

@ -1,8 +1,5 @@
use clap::FromArgMatches;
use clap::Subcommand;
fn command() -> clap::Command {
let cmd = clap::Command::new("dynamic")
clap::Command::new("dynamic")
.arg(
clap::Arg::new("input")
.long("input")
@ -15,8 +12,7 @@ fn command() -> clap::Command {
.short('F')
.value_parser(["json", "yaml", "toml"]),
)
.args_conflicts_with_subcommands(true);
clap_complete::CompleteCommand::augment_subcommands(cmd)
.args_conflicts_with_subcommands(true)
}
fn main() {
@ -24,11 +20,7 @@ fn main() {
let cmd = command();
let matches = cmd.get_matches();
if let Ok(completions) = clap_complete::CompleteCommand::from_arg_matches(&matches) {
completions.complete(&mut command());
} else {
println!("{matches:#?}");
}
println!("{matches:#?}");
}
#[test]

View file

@ -1,6 +1,4 @@
use clap::builder::PossibleValue;
#[cfg(feature = "unstable-command")]
use clap::{FromArgMatches, Subcommand};
use clap_complete::{generate, Generator, Shell};
fn main() {
@ -15,12 +13,6 @@ fn main() {
return;
}
#[cfg(feature = "unstable-command")]
if let Ok(completions) = clap_complete::CompleteCommand::from_arg_matches(&matches) {
completions.complete(&mut cli());
return;
};
println!("{:?}", matches);
}
@ -30,7 +22,7 @@ fn print_completions<G: Generator>(gen: G, cmd: &mut clap::Command) {
#[allow(clippy::let_and_return)]
fn cli() -> clap::Command {
let cli = clap::Command::new("exhaustive")
clap::Command::new("exhaustive")
.version("3.0")
.propagate_version(true)
.args([
@ -197,8 +189,5 @@ fn cli() -> clap::Command {
.long("email")
.value_hint(clap::ValueHint::EmailAddress),
]),
]);
#[cfg(feature = "unstable-command")]
let cli = clap_complete::CompleteCommand::augment_subcommands(cli);
cli
])
}

View file

@ -1,253 +0,0 @@
//! [`<bin> complete`][CompleteCommand] completion integration
//!
//! - If you aren't using a subcommand, see [`CompleteCommand`]
//! - If you are using subcommands, see [`CompleteArgs`]
//!
//! To source your completions:
//!
//! **WARNING:** We recommend re-sourcing your completions on upgrade.
//! These completions work by generating shell code that calls into `your_program` while completing.
//! That interface is unstable and a mismatch between the shell code and `your_program` may result
//! in either invalid completions or no completions being generated.
//! For this reason, we recommend generating the shell code anew on shell startup so that it is
//! "self-correcting" on shell launch, rather than writing the generated completions to a file.
//!
//! Bash
//! ```bash
//! echo "source <(your_program complete bash)" >> ~/.bashrc
//! ```
//!
//! Elvish
//! ```elvish
//! echo "eval (your_program complete elvish)" >> ~/.elvish/rc.elv
//! ```
//!
//! Fish
//! ```fish
//! echo "source (your_program complete fish | psub)" >> ~/.config/fish/config.fish
//! ```
//!
//! Powershell
//! ```powershell
//! echo "your_program complete powershell | Invoke-Expression" >> $PROFILE
//! ```
//!
//! Zsh
//! ```zsh
//! echo "source <(your_program complete zsh)" >> ~/.zshrc
//! ```
mod shells;
use std::ffi::OsString;
use std::io::Write as _;
pub use shells::*;
/// A completion subcommand to add to your CLI
///
/// To customize completions, see
/// - [`ValueHint`][crate::ValueHint]
/// - [`ValueEnum`][clap::ValueEnum]
/// - [`ArgValueCandidates`][crate::ArgValueCandidates]
/// - [`ArgValueCompleter`][crate::ArgValueCompleter]
///
/// **Warning:** `stdout` should not be written to before [`CompleteCommand::complete`] has had a
/// chance to run.
///
/// # Examples
///
/// To integrate completions into an application without subcommands:
/// ```no_run
/// // src/main.rs
/// use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
/// use clap_complete::CompleteCommand;
///
/// #[derive(Parser, Debug)]
/// #[clap(name = "dynamic", about = "A dynamic command line tool")]
/// struct Cli {
/// /// The subcommand to run complete
/// #[command(subcommand)]
/// complete: Option<CompleteCommand>,
///
/// /// Input file path
/// #[clap(short, long, value_hint = clap::ValueHint::FilePath)]
/// input: Option<String>,
/// /// Output format
/// #[clap(short = 'F', long, value_parser = ["json", "yaml", "toml"])]
/// format: Option<String>,
/// }
///
/// fn main() {
/// let cli = Cli::parse();
/// if let Some(completions) = cli.complete {
/// completions.complete(&mut Cli::command());
/// }
///
/// // normal logic continues...
/// }
///```
#[derive(clap::Subcommand)]
#[allow(missing_docs)]
#[derive(Clone, Debug)]
#[command(about = None, long_about = None)]
pub enum CompleteCommand {
/// Register shell completions for this program
#[command(hide = true)]
Complete(CompleteArgs),
}
impl CompleteCommand {
/// Process the completion request and exit
///
/// **Warning:** `stdout` should not be written to before this has had a
/// chance to run.
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
///
/// **Warning:** `stdout` should not be written to before or after this has run.
pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
debug!("CompleteCommand::try_complete: {self:?}");
let CompleteCommand::Complete(args) = self;
args.try_complete(cmd)
}
}
/// A completion subcommand to add to your CLI
///
/// To customize completions, see
/// - [`ValueHint`][crate::ValueHint]
/// - [`ValueEnum`][clap::ValueEnum]
/// - [`ArgValueCandidates`][crate::ArgValueCandidates]
/// - [`ArgValueCompleter`][crate::ArgValueCompleter]
///
/// **Warning:** `stdout` should not be written to before [`CompleteArgs::complete`] has had a
/// chance to run.
///
/// # Examples
///
/// To integrate completions into an application without subcommands:
/// ```no_run
/// // src/main.rs
/// use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
/// use clap_complete::CompleteArgs;
///
/// #[derive(Parser, Debug)]
/// #[clap(name = "dynamic", about = "A dynamic command line tool")]
/// struct Cli {
/// #[command(subcommand)]
/// complete: Command,
/// }
///
/// #[derive(Subcommand, Debug)]
/// enum Command {
/// Complete(CompleteArgs),
/// Print,
/// }
///
/// fn main() {
/// let cli = Cli::parse();
/// match cli.complete {
/// Command::Complete(completions) => {
/// completions.complete(&mut Cli::command());
/// },
/// Command::Print => {
/// println!("Hello world!");
/// }
/// }
/// }
///```
#[derive(clap::Args, Clone, Debug)]
#[command(about = None, long_about = None)]
pub struct CompleteArgs {
/// Specify shell to complete for
#[arg(value_name = "NAME")]
shell: Option<Shell>,
#[arg(raw = true, value_name = "ARG", hide = true)]
comp_words: Option<Vec<OsString>>,
}
impl CompleteArgs {
/// Process the completion request and exit
///
/// **Warning:** `stdout` should not be written to before this has had a
/// chance to run.
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
///
/// **Warning:** `stdout` should not be written to before or after this has run.
pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
debug!("CompleteCommand::try_complete: {self:?}");
let shell = self.shell.or_else(Shell::from_env).ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::Other,
"unknown shell, please specify the name of your shell",
)
})?;
if let Some(comp_words) = self.comp_words.as_ref() {
let current_dir = std::env::current_dir().ok();
let mut buf = Vec::new();
shell.write_complete(cmd, comp_words.clone(), current_dir.as_deref(), &mut buf)?;
std::io::stdout().write_all(&buf)?;
} else {
let name = cmd.get_name();
let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
let mut buf = Vec::new();
shell.write_registration(name, bin, bin, &mut buf)?;
std::io::stdout().write_all(&buf)?;
}
Ok(())
}
}
/// Shell-integration for completions
///
/// This will generally be called by [`CompleteCommand`] or [`CompleteArgs`].
///
/// This handles adapting between the shell and [`completer`][crate::engine::complete()].
/// A `CommandCompleter` can choose how much of that lives within the registration script and or
/// lives in [`CommandCompleter::write_complete`].
pub trait CommandCompleter {
/// Register for completions
///
/// Write the `buf` the logic needed for calling into `<cmd> complete`, passing needed
/// arguments to [`CommandCompleter::write_complete`] through the environment.
///
/// **WARNING:** There are no stability guarantees between the call to
/// [`CommandCompleter::write_complete`] that this generates and actually calling [`CommandCompleter::write_complete`].
/// Caching the results of this call may result in invalid or no completions to be generated.
fn write_registration(
&self,
name: &str,
bin: &str,
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error>;
/// Complete the given command
///
/// Adapt information from arguments and [`CommandCompleter::write_registration`]-defined env
/// variables to what is needed for [`completer`][crate::engine::complete()].
///
/// Write out the [`CompletionCandidate`][crate::engine::CompletionCandidate]s in a way the shell will understand.
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>;
}

View file

@ -1,529 +0,0 @@
use std::ffi::OsString;
use std::fmt::Display;
use std::str::FromStr;
use clap::builder::PossibleValue;
use clap::ValueEnum;
use unicode_xid::UnicodeXID as _;
use super::CommandCompleter;
/// Completion support for built-in shells
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
/// Bourne Again `SHell` (bash)
Bash,
/// Elvish shell
Elvish,
/// Friendly Interactive `SHell` (fish)
Fish,
/// `PowerShell`
Powershell,
/// Z `SHell` (zsh)
Zsh,
}
impl Shell {
/// Parse a shell from a path to the executable for the shell
///
/// # Examples
///
/// ```
/// use clap_complete::shells::Shell;
///
/// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash));
/// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh));
/// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None);
/// ```
pub fn from_shell_path(path: impl AsRef<std::path::Path>) -> Option<Shell> {
parse_shell_from_path(path.as_ref())
}
/// Determine the user's current shell from the environment
///
/// This will read the SHELL environment variable and try to determine which shell is in use
/// from that.
///
/// If SHELL is not set, then on windows, it will default to powershell, and on
/// other operating systems it will return `None`.
///
/// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
/// types, then return `None`.
///
/// # Example:
///
/// ```no_run
/// # use clap::Command;
/// use clap_complete::{generate, shells::Shell};
/// # fn build_cli() -> Command {
/// # Command::new("compl")
/// # }
/// let shell = Shell::from_env();
/// println!("{shell:?}");
/// ```
pub fn from_env() -> Option<Shell> {
if let Some(env_shell) = std::env::var_os("SHELL") {
Shell::from_shell_path(env_shell)
} else {
None
}
}
fn completer(&self) -> &dyn CommandCompleter {
match self {
Self::Bash => &Bash,
Self::Elvish => &Elvish,
Self::Fish => &Fish,
Self::Powershell => &Powershell,
Self::Zsh => &Zsh,
}
}
}
impl CommandCompleter for Shell {
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<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)
}
}
// use a separate function to avoid having to monomorphize the entire function due
// to from_shell_path being generic
fn parse_shell_from_path(path: &std::path::Path) -> Option<Shell> {
let name = path.file_stem()?.to_str()?;
match name {
"bash" => Some(Shell::Bash),
"elvish" => Some(Shell::Elvish),
"fish" => Some(Shell::Fish),
"powershell" | "powershell_ise" => Some(Shell::Powershell),
"zsh" => Some(Shell::Zsh),
_ => None,
}
}
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::Elvish,
Shell::Fish,
Shell::Powershell,
Shell::Zsh,
]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(match self {
Shell::Bash => PossibleValue::new("bash"),
Shell::Elvish => PossibleValue::new("elvish"),
Shell::Fish => PossibleValue::new("fish"),
Shell::Powershell => PossibleValue::new("powershell"),
Shell::Zsh => PossibleValue::new("zsh"),
})
}
}
/// Bash completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Bash;
impl CommandCompleter for 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::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
let script = r#"
_clap_complete_NAME() {
local IFS=$'\013'
local _CLAP_COMPLETE_INDEX=${COMP_CWORD}
local _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
if compopt +o nospace 2> /dev/null; then
local _CLAP_COMPLETE_SPACE=false
else
local _CLAP_COMPLETE_SPACE=true
fi
COMPREPLY=( $( \
IFS="$IFS" \
_CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
_CLAP_COMPLETE_COMP_TYPE="$_CLAP_COMPLETE_COMP_TYPE" \
_CLAP_COMPLETE_SPACE="$_CLAP_COMPLETE_SPACE" \
"COMPLETER" complete bash -- "${COMP_WORDS[@]}" \
) )
if [[ $? != 0 ]]; then
unset COMPREPLY
elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
compopt -o nospace
fi
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -o nospace -o bashdefault -o nosort -F _clap_complete_NAME BIN
else
complete -o nospace -o bashdefault -F _clap_complete_NAME BIN
fi
"#
.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<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::engine::complete(cmd, args, index, current_dir)?;
for (i, candidate) in completions.iter().enumerate() {
if i != 0 {
write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
}
write!(buf, "{}", candidate.get_value().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 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
}
}
/// Elvish completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Elvish;
impl CommandCompleter for Elvish {
fn write_registration(
&self,
_name: &str,
bin: &str,
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error> {
let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
let completer =
shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
let script = r#"
set edit:completion:arg-completer[BIN] = { |@words|
set E:_CLAP_IFS = "\n"
var index = (count $words)
set index = (- $index 1)
set E:_CLAP_COMPLETE_INDEX = (to-string $index)
put (COMPLETER complete elvish -- $@words) | to-lines
}
"#
.replace("COMPLETER", &completer)
.replace("BIN", &bin);
writeln!(buf, "{script}")?;
Ok(())
}
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> {
let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
.ok()
.and_then(|i| i.parse().ok())
.unwrap_or_default();
let ifs: Option<String> = std::env::var("_CLAP_IFS").ok().and_then(|i| i.parse().ok());
let completions = crate::engine::complete(cmd, args, index, current_dir)?;
for (i, candidate) in completions.iter().enumerate() {
if i != 0 {
write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
}
write!(buf, "{}", candidate.get_value().to_string_lossy())?;
}
Ok(())
}
}
/// Fish completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Fish;
impl CommandCompleter for Fish {
fn write_registration(
&self,
_name: &str,
bin: &str,
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error> {
let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
let completer =
shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
writeln!(
buf,
r#"complete -x -c {bin} -a "("'{completer}'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
)
}
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> {
let index = args.len() - 1;
let completions = crate::engine::complete(cmd, args, index, current_dir)?;
for candidate in completions {
write!(buf, "{}", candidate.get_value().to_string_lossy())?;
if let Some(help) = candidate.get_help() {
write!(
buf,
"\t{}",
help.to_string().lines().next().unwrap_or_default()
)?;
}
writeln!(buf)?;
}
Ok(())
}
}
/// Powershell completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Powershell;
impl CommandCompleter for Powershell {
fn write_registration(
&self,
_name: &str,
bin: &str,
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error> {
let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
let completer =
shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
writeln!(
buf,
r#"
Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{
param($wordToComplete, $commandAst, $cursorPosition)
$results = Invoke-Expression "&{completer} complete powershell -- $($commandAst.ToString())";
$results | ForEach-Object {{
$split = $_.Split("`t");
$cmd = $split[0];
if ($split.Length -eq 2) {{
$help = $split[1];
}}
else {{
$help = $split[0];
}}
[System.Management.Automation.CompletionResult]::new($cmd, $cmd, 'ParameterValue', $help)
}}
}};
"#
)
}
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> {
let index = args.len() - 1;
let completions = crate::engine::complete(cmd, args, index, current_dir)?;
for candidate in completions {
write!(buf, "{}", candidate.get_value().to_string_lossy())?;
if let Some(help) = candidate.get_help() {
write!(
buf,
"\t{}",
help.to_string().lines().next().unwrap_or_default()
)?;
}
writeln!(buf)?;
}
Ok(())
}
}
/// Zsh completion adapter
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Zsh;
impl CommandCompleter for Zsh {
fn write_registration(
&self,
_name: &str,
bin: &str,
completer: &str,
buf: &mut dyn std::io::Write,
) -> Result<(), std::io::Error> {
let bin = shlex::try_quote(bin).unwrap_or(std::borrow::Cow::Borrowed(bin));
let completer =
shlex::try_quote(completer).unwrap_or(std::borrow::Cow::Borrowed(completer));
let script = r#"#compdef BIN
function _clap_dynamic_completer() {
local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
local _CLAP_IFS=$'\n'
local completions=("${(@f)$( \
_CLAP_IFS="$_CLAP_IFS" \
_CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
COMPLETER complete zsh -- ${words} 2>/dev/null \
)}")
if [[ -n $completions ]]; then
compadd -a completions
fi
}
compdef _clap_dynamic_completer BIN"#
.replace("COMPLETER", &completer)
.replace("BIN", &bin);
writeln!(buf, "{script}")?;
Ok(())
}
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> {
let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
.ok()
.and_then(|i| i.parse().ok())
.unwrap_or_default();
let ifs: Option<String> = std::env::var("_CLAP_IFS").ok().and_then(|i| i.parse().ok());
// If the current word is empty, add an empty string to the args
let mut args = args.clone();
if args.len() == index {
args.push("".into());
}
let completions = crate::engine::complete(cmd, args, index, current_dir)?;
for (i, candidate) in completions.iter().enumerate() {
if i != 0 {
write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
}
write!(buf, "{}", candidate.get_value().to_string_lossy())?;
}
Ok(())
}
}

View file

@ -66,18 +66,12 @@ const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a
mod macros;
pub mod aot;
#[cfg(feature = "unstable-command")]
pub mod command;
#[cfg(feature = "unstable-dynamic")]
pub mod engine;
#[cfg(feature = "unstable-dynamic")]
pub mod env;
pub use clap::ValueHint;
#[cfg(feature = "unstable-command")]
pub use command::CompleteArgs;
#[cfg(feature = "unstable-command")]
pub use command::CompleteCommand;
#[doc(inline)]
#[cfg(feature = "unstable-dynamic")]
pub use engine::ArgValueCandidates;

View file

@ -1,32 +0,0 @@
PS1='% '
. /etc/bash_completion
_clap_complete_exhaustive() {
local IFS=$'\013'
local _CLAP_COMPLETE_INDEX=${COMP_CWORD}
local _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
if compopt +o nospace 2> /dev/null; then
local _CLAP_COMPLETE_SPACE=false
else
local _CLAP_COMPLETE_SPACE=true
fi
COMPREPLY=( $( \
IFS="$IFS" \
_CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
_CLAP_COMPLETE_COMP_TYPE="$_CLAP_COMPLETE_COMP_TYPE" \
_CLAP_COMPLETE_SPACE="$_CLAP_COMPLETE_SPACE" \
"exhaustive" complete bash -- "${COMP_WORDS[@]}" \
) )
if [[ $? != 0 ]]; then
unset COMPREPLY
elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
compopt -o nospace
fi
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -o nospace -o bashdefault -o nosort -F _clap_complete_exhaustive exhaustive
else
complete -o nospace -o bashdefault -F _clap_complete_exhaustive exhaustive
fi

View file

@ -1,14 +0,0 @@
set edit:rprompt = (constantly "")
set edit:prompt = (constantly "% ")
set edit:completion:arg-completer[exhaustive] = { |@words|
set E:_CLAP_IFS = "\n"
var index = (count $words)
set index = (- $index 1)
set E:_CLAP_COMPLETE_INDEX = (to-string $index)
put (exhaustive complete elvish -- $@words) | to-lines
}

View file

@ -1 +0,0 @@
complete -x -c exhaustive -a "("'exhaustive'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"

View file

@ -1,7 +0,0 @@
set -U fish_greeting ""
set -U fish_autosuggestion_enabled 0
function fish_title
end
function fish_prompt
printf '%% '
end;

View file

@ -1,5 +0,0 @@
fpath=($fpath $ZDOTDIR/zsh)
autoload -U +X compinit && compinit -u # bypass compaudit security checking
precmd_functions="" # avoid the prompt being overwritten
PS1='%% '
PROMPT='%% '

View file

@ -1,17 +0,0 @@
#compdef exhaustive
function _clap_dynamic_completer() {
local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
local _CLAP_IFS=$'\n'
local completions=("${(@f)$( \
_CLAP_IFS="$_CLAP_IFS" \
_CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
exhaustive complete zsh -- ${words} 2>/dev/null \
)}")
if [[ -n $completions ]]; then
compadd -a completions
fi
}
compdef _clap_dynamic_completer exhaustive

View file

@ -20,9 +20,6 @@ _exhaustive() {
exhaustive,alias)
cmd="exhaustive__alias"
;;
exhaustive,complete)
cmd="exhaustive__complete"
;;
exhaustive,help)
cmd="exhaustive__help"
;;
@ -47,9 +44,6 @@ _exhaustive() {
exhaustive__help,alias)
cmd="exhaustive__help__alias"
;;
exhaustive__help,complete)
cmd="exhaustive__help__complete"
;;
exhaustive__help,help)
cmd="exhaustive__help__help"
;;
@ -168,7 +162,7 @@ _exhaustive() {
case "${cmd}" in
exhaustive)
opts="-h -V --global --generate --help --version action quote value pacman last alias hint complete help"
opts="-h -V --global --generate --help --version action quote value pacman last alias hint help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -237,22 +231,8 @@ _exhaustive() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
exhaustive__complete)
opts="-h -V --global --help --version bash elvish fish powershell zsh [ARG]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
exhaustive__help)
opts="action quote value pacman last alias hint complete help"
opts="action quote value pacman last alias hint help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -293,20 +273,6 @@ _exhaustive() {
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
exhaustive__help__complete)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
exhaustive__help__help)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then

View file

@ -33,7 +33,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
cand last 'last'
cand alias 'alias'
cand hint 'hint'
cand complete 'Register shell completions for this program'
cand help 'Print this message or the help of the given subcommand(s)'
}
&'exhaustive;action'= {
@ -238,13 +237,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
cand -V 'Print version'
cand --version 'Print version'
}
&'exhaustive;complete'= {
cand --global 'everywhere'
cand -h 'Print help'
cand --help 'Print help'
cand -V 'Print version'
cand --version 'Print version'
}
&'exhaustive;help'= {
cand action 'action'
cand quote 'quote'
@ -253,7 +245,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
cand last 'last'
cand alias 'alias'
cand hint 'hint'
cand complete 'Register shell completions for this program'
cand help 'Print this message or the help of the given subcommand(s)'
}
&'exhaustive;help;action'= {
@ -297,8 +288,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
}
&'exhaustive;help;hint'= {
}
&'exhaustive;help;complete'= {
}
&'exhaustive;help;help'= {
}
]

View file

@ -35,7 +35,6 @@ complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "pacman"
complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "last"
complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "alias"
complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "hint"
complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "complete" -d 'Register shell completions for this program'
complete -c exhaustive -n "__fish_exhaustive_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l set -d 'value' -r
complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l choice -d 'enum' -r -f -a "{first\t'',second\t''}"
@ -136,18 +135,14 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l email -r
complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l global -d 'everywhere'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s h -l help -d 'Print help'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s V -l version -d 'Print version'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l global -d 'everywhere'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s h -l help -d 'Print help'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s V -l version -d 'Print version'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "action"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "quote"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "value"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "pacman"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "last"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "alias"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "hint"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "complete" -d 'Register shell completions for this program'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint complete help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "action"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "quote"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "value"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "pacman"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "last"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "alias"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "hint"
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and not __fish_seen_subcommand_from action quote value pacman last alias hint help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-single-quotes" -d 'Can be \'always\', \'auto\', or \'never\''
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-double-quotes" -d 'Can be "always", "auto", or "never"'
complete -c exhaustive -n "__fish_exhaustive_using_subcommand help; and __fish_seen_subcommand_from quote" -f -a "cmd-backticks" -d 'For more information see `echo test`'

View file

@ -323,17 +323,6 @@ _arguments "${_arguments_options[@]}" : \
'*::command_with_args:_cmdambivalent' \
&& ret=0
;;
(complete)
_arguments "${_arguments_options[@]}" : \
'--global[everywhere]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
'::shell -- Specify shell to complete for:(bash elvish fish powershell zsh)' \
'*::comp_words:' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
":: :_exhaustive__help_commands" \
@ -434,10 +423,6 @@ _arguments "${_arguments_options[@]}" : \
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(complete)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
&& ret=0
@ -461,7 +446,6 @@ _exhaustive_commands() {
'last:' \
'alias:' \
'hint:' \
'complete:Register shell completions for this program' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'exhaustive commands' commands "$@"
@ -476,11 +460,6 @@ _exhaustive__alias_commands() {
local commands; commands=()
_describe -t commands 'exhaustive alias commands' commands "$@"
}
(( $+functions[_exhaustive__complete_commands] )) ||
_exhaustive__complete_commands() {
local commands; commands=()
_describe -t commands 'exhaustive complete commands' commands "$@"
}
(( $+functions[_exhaustive__help_commands] )) ||
_exhaustive__help_commands() {
local commands; commands=(
@ -491,7 +470,6 @@ _exhaustive__help_commands() {
'last:' \
'alias:' \
'hint:' \
'complete:Register shell completions for this program' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'exhaustive help commands' commands "$@"
@ -506,11 +484,6 @@ _exhaustive__help__alias_commands() {
local commands; commands=()
_describe -t commands 'exhaustive help alias commands' commands "$@"
}
(( $+functions[_exhaustive__help__complete_commands] )) ||
_exhaustive__help__complete_commands() {
local commands; commands=()
_describe -t commands 'exhaustive help complete commands' commands "$@"
}
(( $+functions[_exhaustive__help__help_commands] )) ||
_exhaustive__help__help_commands() {
local commands; commands=()

View file

@ -111,24 +111,6 @@ fn value_terminator() {
);
}
#[cfg(feature = "unstable-command")]
#[test]
fn register_minimal() {
use clap_complete::command::CommandCompleter as _;
let name = "my-app";
let bin = name;
let completer = name;
let mut buf = Vec::new();
clap_complete::command::Bash
.write_registration(name, bin, completer, &mut buf)
.unwrap();
snapbox::Assert::new()
.action_env("SNAPSHOTS")
.eq(buf, snapbox::file!["../snapshots/register_minimal.bash"]);
}
#[test]
fn two_multi_valued_arguments() {
let name = "my-app";
@ -171,15 +153,17 @@ fn complete() {
common::load_runtime::<completest_pty::BashRuntimeBuilder>("static", "exhaustive");
let input = "exhaustive \t\t";
let expected = r#"%
-h --global --help action value last hint help
-V --generate --version quote pacman alias complete "#;
let expected = snapbox::str![[r#"
%
-h --global --help action value last hint
-V --generate --version quote pacman alias help
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
// Issue 5239 (https://github.com/clap-rs/clap/issues/5239)
let input = "exhaustive hint --file test\t";
let expected = "exhaustive hint --file test % exhaustive hint --file tests/";
let expected = snapbox::str!["exhaustive hint --file test % exhaustive hint --file tests/"];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
@ -285,41 +269,3 @@ fn complete_dynamic_env() {
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn register_dynamic_command() {
common::register_example::<completest_pty::BashRuntimeBuilder>("dynamic-command", "exhaustive");
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn complete_dynamic_command() {
if !common::has_command("bash") {
return;
}
let term = completest::Term::new();
let mut runtime =
common::load_runtime::<completest_pty::BashRuntimeBuilder>("dynamic-command", "exhaustive");
let input = "exhaustive \t\t";
let expected = snapbox::str![[r#"
%
--global --help -h action help last quote
--generate --version -V alias hint pacman value
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#"
%
--single-quotes --brackets --help cmd-backslash cmd-expansions
--double-quotes --expansions --version cmd-backticks cmd-single-quotes
--backticks --choice -h cmd-brackets escape-help
--backslash --global -V cmd-double-quotes help
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}

View file

@ -321,7 +321,6 @@ 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();
@ -381,7 +380,6 @@ where
// Unconditionally include to avoid completion file tests failing based on the how
// `cargo test` is invoked
"--features=unstable-dynamic",
"--features=unstable-command",
],
)
.unwrap();

View file

@ -163,7 +163,6 @@ fn complete() {
-h Print help
action action
alias alias
complete Register shell completions for this program
help Print this message or the help of the given subcommand(s)
hint hint
last last
@ -214,48 +213,3 @@ fn complete_dynamic_env() {
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn register_dynamic_command() {
common::register_example::<completest_pty::ElvishRuntimeBuilder>(
"dynamic-command",
"exhaustive",
);
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn complete_dynamic_command() {
if !common::has_command("elvish") {
return;
}
let term = completest::Term::new();
let mut runtime = common::load_runtime::<completest_pty::ElvishRuntimeBuilder>(
"dynamic-command",
"exhaustive",
);
let input = "exhaustive \t";
let expected = snapbox::str![[r#"
% exhaustive --generate
COMPLETING argument
--generate --help -V action help last quote
--global --version -h alias hint pacman value
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
let input = "exhaustive quote \t";
let expected = snapbox::str![[r#"
% exhaustive quote --backslash
COMPLETING argument
--backslash --double-quotes --single-quotes cmd-backslash cmd-expansions
--backticks --expansions --version cmd-backticks cmd-single-quotes
--brackets --global -V cmd-brackets escape-help
--choice --help -h cmd-double-quotes help
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}

View file

@ -152,16 +152,20 @@ fn complete() {
common::load_runtime::<completest_pty::FishRuntimeBuilder>("static", "exhaustive");
let input = "exhaustive \t";
let expected = r#"% exhaustive
action complete (Register shell completions for this program) hint pacman value
alias help (Print this message or the help of the given subcommand(s)) last quote "#;
let expected = snapbox::str![[r#"
% exhaustive
action help (Print this message or the help of the given subcommand(s)) last quote
alias hint pacman value
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
let input = "exhaustive quote --choice \t";
let actual = runtime.complete(input, &term).unwrap();
let expected = r#"% exhaustive quote --choice
bash (bash (shell)) fish (fish shell) zsh (zsh shell)"#;
let expected = snapbox::str![[r#"
% exhaustive quote --choice
bash (bash (shell)) fish (fish shell) zsh (zsh shell)
"#]];
assert_data_eq!(actual, expected);
}
@ -221,60 +225,3 @@ help (Print this message or the help of the given subcommand(s))
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn register_dynamic_command() {
common::register_example::<completest_pty::FishRuntimeBuilder>("dynamic-command", "exhaustive");
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn complete_dynamic_command() {
if !common::has_command("fish") {
return;
}
let term = completest::Term::new();
let mut runtime =
common::load_runtime::<completest_pty::FishRuntimeBuilder>("dynamic-command", "exhaustive");
let input = "exhaustive \t\t";
let expected = snapbox::str![[r#"
% exhaustive action
action pacman --generate (generate)
alias quote --global (everywhere)
help (Print this message or the help of the given subcommand(s)) value --help (Print help)
hint -h (Print help) --version (Print version)
last -V (Print version)
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#"
% exhaustive quote
cmd-backslash (Avoid '/n')
cmd-backticks (For more information see `echo test`)
cmd-brackets (List packages [filter])
cmd-double-quotes (Can be "always", "auto", or "never")
cmd-expansions (Execute the shell command with $SHELL)
cmd-single-quotes (Can be 'always', 'auto', or 'never')
escape-help (/tab "')
help (Print this message or the help of the given subcommand(s))
-h (Print help (see more with '--help'))
-V (Print version)
--backslash (Avoid '/n')
--backticks (For more information see `echo test`)
--brackets (List packages [filter])
--choice
--double-quotes (Can be "always", "auto", or "never")
--expansions (Execute the shell command with $SHELL)
--global (everywhere)
--help (Print help (see more with '--help'))
--single-quotes (Can be 'always', 'auto', or 'never')
--version (Print version)
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}

View file

@ -155,9 +155,8 @@ fn complete() {
let input = "exhaustive \t";
let expected = snapbox::str![[r#"
% exhaustive
complete -- Register shell completions for this program
help -- Print this message or the help of the given subcommand(s)
pacman action alias value quote hint last --
help -- Print this message or the help of the given subcommand(s)
pacman action alias value quote hint last --
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
@ -200,41 +199,3 @@ fn complete_dynamic_env() {
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn register_dynamic_command() {
common::register_example::<completest_pty::ZshRuntimeBuilder>("dynamic-command", "exhaustive");
}
#[test]
#[cfg(all(unix, feature = "unstable-command"))]
fn complete_dynamic_command() {
if !common::has_command("zsh") {
return;
}
let term = completest::Term::new();
let mut runtime =
common::load_runtime::<completest_pty::ZshRuntimeBuilder>("dynamic-command", "exhaustive");
let input = "exhaustive \t\t";
let expected = snapbox::str![[r#"
% exhaustive
--generate --help -V action help last quote
--global --version -h alias hint pacman value
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#"
% exhaustive quote
--backslash --double-quotes --single-quotes cmd-backslash cmd-expansions
--backticks --expansions --version cmd-backticks cmd-single-quotes
--brackets --global -V cmd-brackets escape-help
--choice --help -h cmd-double-quotes help
"#]];
let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected);
}