From bc6b5ac2e3f587bbfdc907888447d477cf10b9b5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 14:02:23 -0500 Subject: [PATCH 01/30] test(derive): Require opt-in These are now running in other situations in CI and feature combinations have odd side effects on the output. --- Cargo.toml | 1 + Makefile | 2 +- tests/derive_ui.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ab65eca293d..dc72812dce6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,6 +170,7 @@ string = ["clap_builder/string"] # Allow runtime generated strings unstable-v5 = ["clap_builder/unstable-v5", "clap_derive?/unstable-v5", "deprecated"] unstable-ext = ["clap_builder/unstable-ext"] unstable-styles = ["clap_builder/unstable-styles"] # deprecated +unstable-derive-ui-tests = [] [lib] bench = false diff --git a/Makefile b/Makefile index 0fac242ea85..47596be8ccc 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ clippy-%: cargo clippy ${_FEATURES_${@:clippy-%=%}} ${ARGS} --all-targets -- -D warnings -A deprecated test-ui-%: - cargo +${STABLE} test --test derive_ui --features derive ${_FEATURES_${@:test-ui-%=%}} + cargo +${STABLE} test --test derive_ui --features derive,unstable-derive-ui-tests ${_FEATURES_${@:test-ui-%=%}} doc: cargo doc --workspace --all-features --no-deps --document-private-items diff --git a/tests/derive_ui.rs b/tests/derive_ui.rs index 6f3e1c14a9b..7f679c2e274 100644 --- a/tests/derive_ui.rs +++ b/tests/derive_ui.rs @@ -5,6 +5,8 @@ // , at your // option. This file may not be copied, modified, or distributed +#![cfg(feature = "unstable-derive-ui-tests")] + #[cfg(feature = "derive")] #[rustversion::attr(not(stable(1.80)), ignore)] // STABLE #[test] From 4c386138d20f763e47d14bdbf9c53990278bf3e5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 12:18:58 -0500 Subject: [PATCH 02/30] test(derive): Show ValueParser error --- tests/derive_ui/value_parser_unsupported.rs | 13 ++++++ .../derive_ui/value_parser_unsupported.stderr | 42 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/derive_ui/value_parser_unsupported.rs create mode 100644 tests/derive_ui/value_parser_unsupported.stderr diff --git a/tests/derive_ui/value_parser_unsupported.rs b/tests/derive_ui/value_parser_unsupported.rs new file mode 100644 index 00000000000..405f7793baf --- /dev/null +++ b/tests/derive_ui/value_parser_unsupported.rs @@ -0,0 +1,13 @@ +use clap::Parser; + +#[derive(Parser, Debug)] +struct Cli { + foo: Custom, +} + +#[derive(Clone, Debug)] +struct Custom; + +fn main() { + Cli::parse(); +} diff --git a/tests/derive_ui/value_parser_unsupported.stderr b/tests/derive_ui/value_parser_unsupported.stderr new file mode 100644 index 00000000000..8b4940bd7c4 --- /dev/null +++ b/tests/derive_ui/value_parser_unsupported.stderr @@ -0,0 +1,42 @@ +error[E0599]: the method `value_parser` exists for reference `&&&&&&_AutoValueParser`, but its trait bounds were not satisfied + --> tests/derive_ui/value_parser_unsupported.rs:5:5 + | +5 | foo: Custom, + | ^^^ method cannot be called on `&&&&&&_AutoValueParser` due to unsatisfied trait bounds +... +9 | struct Custom; + | ------------- doesn't satisfy 7 bounds + | + ::: clap_builder/src/builder/value_parser.rs + | + | pub struct _AutoValueParser(std::marker::PhantomData); + | ------------------------------ doesn't satisfy `_: _ValueParserViaParse` + | + = note: the following trait bounds were not satisfied: + `Custom: ValueEnum` + which is required by `&&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaValueEnum` + `Custom: ValueParserFactory` + which is required by `&&&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFactory` + `Custom: From` + which is required by `&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromOsString` + `Custom: From<&'s std::ffi::OsStr>` + which is required by `&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromOsStr` + `Custom: From` + which is required by `&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromString` + `Custom: From<&'s str>` + which is required by `&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromStr` + `Custom: FromStr` + which is required by `_AutoValueParser: clap::builder::via_prelude::_ValueParserViaParse` +note: the traits `From`, `FromStr`, `ValueEnum`, and `ValueParserFactory` must be implemented + --> clap_builder/src/builder/value_parser.rs + | + | pub trait ValueParserFactory { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: clap_builder/src/derive.rs + | + | pub trait ValueEnum: Sized + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $RUST/core/src/convert/mod.rs + --> $RUST/core/src/str/traits.rs + = note: this error originates in the macro `clap::value_parser` (in Nightly builds, run with -Z macro-backtrace for more info) From a3524273b1b86c4a5a3f667778e11ddebeec62ef Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 12:19:38 -0500 Subject: [PATCH 03/30] fix(derive): Improve unsupported field type error message --- clap_builder/src/builder/mod.rs | 4 +- clap_builder/src/builder/value_parser.rs | 68 +++++++++---------- .../derive_ui/value_parser_unsupported.stderr | 22 +++--- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/clap_builder/src/builder/mod.rs b/clap_builder/src/builder/mod.rs index b428fb03914..171e730af8b 100644 --- a/clap_builder/src/builder/mod.rs +++ b/clap_builder/src/builder/mod.rs @@ -41,8 +41,8 @@ pub use resettable::Resettable; pub use styled_str::StyledStr; pub use styling::Styles; pub use value_hint::ValueHint; -pub use value_parser::_AutoValueParser; -pub use value_parser::via_prelude; +pub use value_parser::_InferValueParserFor; +pub use value_parser::impl_prelude; pub use value_parser::BoolValueParser; pub use value_parser::BoolishValueParser; pub use value_parser::EnumValueParser; diff --git a/clap_builder/src/builder/value_parser.rs b/clap_builder/src/builder/value_parser.rs index 3b62eb761e0..269c5e62982 100644 --- a/clap_builder/src/builder/value_parser.rs +++ b/clap_builder/src/builder/value_parser.rs @@ -2419,9 +2419,9 @@ where #[doc(hidden)] #[derive(Debug)] -pub struct _AutoValueParser(std::marker::PhantomData); +pub struct _InferValueParserFor(std::marker::PhantomData); -impl _AutoValueParser { +impl _InferValueParserFor { #[doc(hidden)] #[allow(clippy::new_without_default)] pub fn new() -> Self { @@ -2437,15 +2437,15 @@ impl _AutoValueParser { pub struct _AnonymousValueParser(ValueParser); #[doc(hidden)] -pub mod via_prelude { +pub mod impl_prelude { use super::*; #[doc(hidden)] - pub trait _ValueParserViaFactory: private::_ValueParserViaFactorySealed { + pub trait _ImplsValueParserFactory: private::_ImplsValueParserFactorySealed { type Parser; fn value_parser(&self) -> Self::Parser; } - impl _ValueParserViaFactory for &&&&&&_AutoValueParser

{ + impl _ImplsValueParserFactory for &&&&&&_InferValueParserFor

{ type Parser = P::Parser; fn value_parser(&self) -> Self::Parser { P::value_parser() @@ -2453,13 +2453,13 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaValueEnum: private::_ValueParserViaValueEnumSealed { + pub trait _ImplsValueEnum: private::_ImplsValueEnumSealed { type Output; fn value_parser(&self) -> Self::Output; } - impl _ValueParserViaValueEnum - for &&&&&_AutoValueParser + impl _ImplsValueEnum + for &&&&&_InferValueParserFor { type Output = EnumValueParser; @@ -2469,10 +2469,10 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaFromOsString: private::_ValueParserViaFromOsStringSealed { + pub trait _ImplsFromOsString: private::_ImplsFromOsStringSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ValueParserViaFromOsString for &&&&_AutoValueParser + impl _ImplsFromOsString for &&&&_InferValueParserFor where FromOsString: From + std::any::Any + Clone + Send + Sync + 'static, { @@ -2486,10 +2486,10 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaFromOsStr: private::_ValueParserViaFromOsStrSealed { + pub trait _ImplsFromOsStr: private::_ImplsFromOsStrSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ValueParserViaFromOsStr for &&&_AutoValueParser + impl _ImplsFromOsStr for &&&_InferValueParserFor where FromOsStr: for<'s> From<&'s std::ffi::OsStr> + std::any::Any + Clone + Send + Sync + 'static, @@ -2504,10 +2504,10 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaFromString: private::_ValueParserViaFromStringSealed { + pub trait _ImplsFromString: private::_ImplsFromStringSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ValueParserViaFromString for &&_AutoValueParser + impl _ImplsFromString for &&_InferValueParserFor where FromString: From + std::any::Any + Clone + Send + Sync + 'static, { @@ -2517,10 +2517,10 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaFromStr: private::_ValueParserViaFromStrSealed { + pub trait _ImplsFromStr: private::_ImplsFromStrSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ValueParserViaFromStr for &_AutoValueParser + impl _ImplsFromStr for &_InferValueParserFor where FromStr: for<'s> From<&'s str> + std::any::Any + Clone + Send + Sync + 'static, { @@ -2530,10 +2530,10 @@ pub mod via_prelude { } #[doc(hidden)] - pub trait _ValueParserViaParse: private::_ValueParserViaParseSealed { + pub trait _ImplsStrParse: private::_ImplsStrParseSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ValueParserViaParse for _AutoValueParser + impl _ImplsStrParse for _InferValueParserFor where Parse: std::str::FromStr + std::any::Any + Clone + Send + Sync + 'static, ::Err: Into>, @@ -2601,8 +2601,8 @@ pub mod via_prelude { #[macro_export] macro_rules! value_parser { ($name:ty) => {{ - use $crate::builder::via_prelude::*; - let auto = $crate::builder::_AutoValueParser::<$name>::new(); + use $crate::builder::impl_prelude::*; + let auto = $crate::builder::_InferValueParserFor::<$name>::new(); (&&&&&&auto).value_parser() }}; } @@ -2610,38 +2610,38 @@ macro_rules! value_parser { mod private { use super::*; - pub trait _ValueParserViaFactorySealed {} - impl _ValueParserViaFactorySealed for &&&&&&_AutoValueParser

{} + pub trait _ImplsValueParserFactorySealed {} + impl _ImplsValueParserFactorySealed for &&&&&&_InferValueParserFor

{} - pub trait _ValueParserViaValueEnumSealed {} - impl _ValueParserViaValueEnumSealed for &&&&&_AutoValueParser {} + pub trait _ImplsValueEnumSealed {} + impl _ImplsValueEnumSealed for &&&&&_InferValueParserFor {} - pub trait _ValueParserViaFromOsStringSealed {} - impl _ValueParserViaFromOsStringSealed for &&&&_AutoValueParser where + pub trait _ImplsFromOsStringSealed {} + impl _ImplsFromOsStringSealed for &&&&_InferValueParserFor where FromOsString: From + std::any::Any + Send + Sync + 'static { } - pub trait _ValueParserViaFromOsStrSealed {} - impl _ValueParserViaFromOsStrSealed for &&&_AutoValueParser where + pub trait _ImplsFromOsStrSealed {} + impl _ImplsFromOsStrSealed for &&&_InferValueParserFor where FromOsStr: for<'s> From<&'s std::ffi::OsStr> + std::any::Any + Send + Sync + 'static { } - pub trait _ValueParserViaFromStringSealed {} - impl _ValueParserViaFromStringSealed for &&_AutoValueParser where + pub trait _ImplsFromStringSealed {} + impl _ImplsFromStringSealed for &&_InferValueParserFor where FromString: From + std::any::Any + Send + Sync + 'static { } - pub trait _ValueParserViaFromStrSealed {} - impl _ValueParserViaFromStrSealed for &_AutoValueParser where + pub trait _ImplsFromStrSealed {} + impl _ImplsFromStrSealed for &_InferValueParserFor where FromStr: for<'s> From<&'s str> + std::any::Any + Send + Sync + 'static { } - pub trait _ValueParserViaParseSealed {} - impl _ValueParserViaParseSealed for _AutoValueParser + pub trait _ImplsStrParseSealed {} + impl _ImplsStrParseSealed for _InferValueParserFor where Parse: std::str::FromStr + std::any::Any + Send + Sync + 'static, ::Err: Into>, diff --git a/tests/derive_ui/value_parser_unsupported.stderr b/tests/derive_ui/value_parser_unsupported.stderr index 8b4940bd7c4..aa57104cba1 100644 --- a/tests/derive_ui/value_parser_unsupported.stderr +++ b/tests/derive_ui/value_parser_unsupported.stderr @@ -1,32 +1,32 @@ -error[E0599]: the method `value_parser` exists for reference `&&&&&&_AutoValueParser`, but its trait bounds were not satisfied +error[E0599]: the method `value_parser` exists for reference `&&&&&&_InferValueParserFor`, but its trait bounds were not satisfied --> tests/derive_ui/value_parser_unsupported.rs:5:5 | 5 | foo: Custom, - | ^^^ method cannot be called on `&&&&&&_AutoValueParser` due to unsatisfied trait bounds + | ^^^ method cannot be called on `&&&&&&_InferValueParserFor` due to unsatisfied trait bounds ... 9 | struct Custom; | ------------- doesn't satisfy 7 bounds | ::: clap_builder/src/builder/value_parser.rs | - | pub struct _AutoValueParser(std::marker::PhantomData); - | ------------------------------ doesn't satisfy `_: _ValueParserViaParse` + | pub struct _InferValueParserFor(std::marker::PhantomData); + | ---------------------------------- doesn't satisfy `_: _ImplsStrParse` | = note: the following trait bounds were not satisfied: `Custom: ValueEnum` - which is required by `&&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaValueEnum` + which is required by `&&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsValueEnum` `Custom: ValueParserFactory` - which is required by `&&&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFactory` + which is required by `&&&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsValueParserFactory` `Custom: From` - which is required by `&&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromOsString` + which is required by `&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromOsString` `Custom: From<&'s std::ffi::OsStr>` - which is required by `&&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromOsStr` + which is required by `&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromOsStr` `Custom: From` - which is required by `&&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromString` + which is required by `&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromString` `Custom: From<&'s str>` - which is required by `&_AutoValueParser: clap::builder::via_prelude::_ValueParserViaFromStr` + which is required by `&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromStr` `Custom: FromStr` - which is required by `_AutoValueParser: clap::builder::via_prelude::_ValueParserViaParse` + which is required by `_InferValueParserFor: clap::builder::impl_prelude::_ImplsStrParse` note: the traits `From`, `FromStr`, `ValueEnum`, and `ValueParserFactory` must be implemented --> clap_builder/src/builder/value_parser.rs | From dc133659386d8f0585343839b67745b0ebf759d2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 12:27:06 -0500 Subject: [PATCH 04/30] fix(derive): Improve unsupported field type error message This is a bit more unconventional but I think its sufficient improvement to readability to justify it. --- clap_builder/src/builder/mod.rs | 2 +- clap_builder/src/builder/value_parser.rs | 79 +++++++++++-------- .../derive_ui/value_parser_unsupported.stderr | 22 +++--- 3 files changed, 59 insertions(+), 44 deletions(-) diff --git a/clap_builder/src/builder/mod.rs b/clap_builder/src/builder/mod.rs index 171e730af8b..abc0b753689 100644 --- a/clap_builder/src/builder/mod.rs +++ b/clap_builder/src/builder/mod.rs @@ -41,7 +41,7 @@ pub use resettable::Resettable; pub use styled_str::StyledStr; pub use styling::Styles; pub use value_hint::ValueHint; -pub use value_parser::_InferValueParserFor; +pub use value_parser::_infer_ValueParser_for; pub use value_parser::impl_prelude; pub use value_parser::BoolValueParser; pub use value_parser::BoolishValueParser; diff --git a/clap_builder/src/builder/value_parser.rs b/clap_builder/src/builder/value_parser.rs index 269c5e62982..ffa174f358e 100644 --- a/clap_builder/src/builder/value_parser.rs +++ b/clap_builder/src/builder/value_parser.rs @@ -2419,9 +2419,10 @@ where #[doc(hidden)] #[derive(Debug)] -pub struct _InferValueParserFor(std::marker::PhantomData); +#[allow(non_camel_case_types)] +pub struct _infer_ValueParser_for(std::marker::PhantomData); -impl _InferValueParserFor { +impl _infer_ValueParser_for { #[doc(hidden)] #[allow(clippy::new_without_default)] pub fn new() -> Self { @@ -2441,11 +2442,12 @@ pub mod impl_prelude { use super::*; #[doc(hidden)] - pub trait _ImplsValueParserFactory: private::_ImplsValueParserFactorySealed { + #[allow(non_camel_case_types)] + pub trait _impls_ValueParserFactory: private::_impls_ValueParserFactorySealed { type Parser; fn value_parser(&self) -> Self::Parser; } - impl _ImplsValueParserFactory for &&&&&&_InferValueParserFor

{ + impl _impls_ValueParserFactory for &&&&&&_infer_ValueParser_for

{ type Parser = P::Parser; fn value_parser(&self) -> Self::Parser { P::value_parser() @@ -2453,13 +2455,14 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsValueEnum: private::_ImplsValueEnumSealed { + #[allow(non_camel_case_types)] + pub trait _impls_ValueEnum: private::_impls_ValueEnumSealed { type Output; fn value_parser(&self) -> Self::Output; } - impl _ImplsValueEnum - for &&&&&_InferValueParserFor + impl _impls_ValueEnum + for &&&&&_infer_ValueParser_for { type Output = EnumValueParser; @@ -2469,10 +2472,11 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsFromOsString: private::_ImplsFromOsStringSealed { + #[allow(non_camel_case_types)] + pub trait _impls_From_OsString: private::_impls_From_OsStringSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ImplsFromOsString for &&&&_InferValueParserFor + impl _impls_From_OsString for &&&&_infer_ValueParser_for where FromOsString: From + std::any::Any + Clone + Send + Sync + 'static, { @@ -2486,10 +2490,11 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsFromOsStr: private::_ImplsFromOsStrSealed { + #[allow(non_camel_case_types)] + pub trait _impls_From_OsStr: private::_impls_From_OsStrSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ImplsFromOsStr for &&&_InferValueParserFor + impl _impls_From_OsStr for &&&_infer_ValueParser_for where FromOsStr: for<'s> From<&'s std::ffi::OsStr> + std::any::Any + Clone + Send + Sync + 'static, @@ -2504,10 +2509,11 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsFromString: private::_ImplsFromStringSealed { + #[allow(non_camel_case_types)] + pub trait _impls_From_String: private::_impls_From_StringSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ImplsFromString for &&_InferValueParserFor + impl _impls_From_String for &&_infer_ValueParser_for where FromString: From + std::any::Any + Clone + Send + Sync + 'static, { @@ -2517,10 +2523,11 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsFromStr: private::_ImplsFromStrSealed { + #[allow(non_camel_case_types)] + pub trait _impls_From_str: private::_impls_From_strSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ImplsFromStr for &_InferValueParserFor + impl _impls_From_str for &_infer_ValueParser_for where FromStr: for<'s> From<&'s str> + std::any::Any + Clone + Send + Sync + 'static, { @@ -2530,10 +2537,11 @@ pub mod impl_prelude { } #[doc(hidden)] - pub trait _ImplsStrParse: private::_ImplsStrParseSealed { + #[allow(non_camel_case_types)] + pub trait _impls_FromStr: private::_impls_FromStrSealed { fn value_parser(&self) -> _AnonymousValueParser; } - impl _ImplsStrParse for _InferValueParserFor + impl _impls_FromStr for _infer_ValueParser_for where Parse: std::str::FromStr + std::any::Any + Clone + Send + Sync + 'static, ::Err: Into>, @@ -2602,7 +2610,7 @@ pub mod impl_prelude { macro_rules! value_parser { ($name:ty) => {{ use $crate::builder::impl_prelude::*; - let auto = $crate::builder::_InferValueParserFor::<$name>::new(); + let auto = $crate::builder::_infer_ValueParser_for::<$name>::new(); (&&&&&&auto).value_parser() }}; } @@ -2610,38 +2618,45 @@ macro_rules! value_parser { mod private { use super::*; - pub trait _ImplsValueParserFactorySealed {} - impl _ImplsValueParserFactorySealed for &&&&&&_InferValueParserFor

{} + #[allow(non_camel_case_types)] + pub trait _impls_ValueParserFactorySealed {} + impl _impls_ValueParserFactorySealed for &&&&&&_infer_ValueParser_for

{} - pub trait _ImplsValueEnumSealed {} - impl _ImplsValueEnumSealed for &&&&&_InferValueParserFor {} + #[allow(non_camel_case_types)] + pub trait _impls_ValueEnumSealed {} + impl _impls_ValueEnumSealed for &&&&&_infer_ValueParser_for {} - pub trait _ImplsFromOsStringSealed {} - impl _ImplsFromOsStringSealed for &&&&_InferValueParserFor where + #[allow(non_camel_case_types)] + pub trait _impls_From_OsStringSealed {} + impl _impls_From_OsStringSealed for &&&&_infer_ValueParser_for where FromOsString: From + std::any::Any + Send + Sync + 'static { } - pub trait _ImplsFromOsStrSealed {} - impl _ImplsFromOsStrSealed for &&&_InferValueParserFor where + #[allow(non_camel_case_types)] + pub trait _impls_From_OsStrSealed {} + impl _impls_From_OsStrSealed for &&&_infer_ValueParser_for where FromOsStr: for<'s> From<&'s std::ffi::OsStr> + std::any::Any + Send + Sync + 'static { } - pub trait _ImplsFromStringSealed {} - impl _ImplsFromStringSealed for &&_InferValueParserFor where + #[allow(non_camel_case_types)] + pub trait _impls_From_StringSealed {} + impl _impls_From_StringSealed for &&_infer_ValueParser_for where FromString: From + std::any::Any + Send + Sync + 'static { } - pub trait _ImplsFromStrSealed {} - impl _ImplsFromStrSealed for &_InferValueParserFor where + #[allow(non_camel_case_types)] + pub trait _impls_From_strSealed {} + impl _impls_From_strSealed for &_infer_ValueParser_for where FromStr: for<'s> From<&'s str> + std::any::Any + Send + Sync + 'static { } - pub trait _ImplsStrParseSealed {} - impl _ImplsStrParseSealed for _InferValueParserFor + #[allow(non_camel_case_types)] + pub trait _impls_FromStrSealed {} + impl _impls_FromStrSealed for _infer_ValueParser_for where Parse: std::str::FromStr + std::any::Any + Send + Sync + 'static, ::Err: Into>, diff --git a/tests/derive_ui/value_parser_unsupported.stderr b/tests/derive_ui/value_parser_unsupported.stderr index aa57104cba1..b414dec6aef 100644 --- a/tests/derive_ui/value_parser_unsupported.stderr +++ b/tests/derive_ui/value_parser_unsupported.stderr @@ -1,32 +1,32 @@ -error[E0599]: the method `value_parser` exists for reference `&&&&&&_InferValueParserFor`, but its trait bounds were not satisfied +error[E0599]: the method `value_parser` exists for reference `&&&&&&_infer_ValueParser_for`, but its trait bounds were not satisfied --> tests/derive_ui/value_parser_unsupported.rs:5:5 | 5 | foo: Custom, - | ^^^ method cannot be called on `&&&&&&_InferValueParserFor` due to unsatisfied trait bounds + | ^^^ method cannot be called on `&&&&&&_infer_ValueParser_for` due to unsatisfied trait bounds ... 9 | struct Custom; | ------------- doesn't satisfy 7 bounds | ::: clap_builder/src/builder/value_parser.rs | - | pub struct _InferValueParserFor(std::marker::PhantomData); - | ---------------------------------- doesn't satisfy `_: _ImplsStrParse` + | pub struct _infer_ValueParser_for(std::marker::PhantomData); + | ------------------------------------ doesn't satisfy `_: _impls_FromStr` | = note: the following trait bounds were not satisfied: `Custom: ValueEnum` - which is required by `&&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsValueEnum` + which is required by `&&&&&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_ValueEnum` `Custom: ValueParserFactory` - which is required by `&&&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsValueParserFactory` + which is required by `&&&&&&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_ValueParserFactory` `Custom: From` - which is required by `&&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromOsString` + which is required by `&&&&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_From_OsString` `Custom: From<&'s std::ffi::OsStr>` - which is required by `&&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromOsStr` + which is required by `&&&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_From_OsStr` `Custom: From` - which is required by `&&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromString` + which is required by `&&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_From_String` `Custom: From<&'s str>` - which is required by `&_InferValueParserFor: clap::builder::impl_prelude::_ImplsFromStr` + which is required by `&_infer_ValueParser_for: clap::builder::impl_prelude::_impls_From_str` `Custom: FromStr` - which is required by `_InferValueParserFor: clap::builder::impl_prelude::_ImplsStrParse` + which is required by `_infer_ValueParser_for: clap::builder::impl_prelude::_impls_FromStr` note: the traits `From`, `FromStr`, `ValueEnum`, and `ValueParserFactory` must be implemented --> clap_builder/src/builder/value_parser.rs | From d51922c79e17fafa9374a2e2e8935611d637e0ce Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 21:29:05 -0500 Subject: [PATCH 05/30] test(complete): Use more snapshotting --- clap_complete/tests/testsuite/bash.rs | 8 +++++--- clap_complete/tests/testsuite/fish.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 00c57c8fd25..258a13cf2d8 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -171,15 +171,17 @@ fn complete() { common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t\t"; - let expected = r#"% + let expected = snapbox::str![[r#" +% -h --global --help action value last hint help --V --generate --version quote pacman alias complete "#; +-V --generate --version quote pacman alias complete +"#]]; 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); diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index d50d587895c..98aac6934f6 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -152,16 +152,20 @@ fn complete() { common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t"; - let expected = r#"% exhaustive + let expected = snapbox::str![[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 "#; +alias help (Print this message or the help of the given subcommand(s)) last quote +"#]]; 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); } From 72dda07a423d62bcc09aed8bf4743050a2e70dd9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 28 Aug 2024 21:02:32 -0500 Subject: [PATCH 06/30] fix(complete)!: Remove CompleteCommand This just has too many caveats to be worth maintaining --- Cargo.lock | 1 - clap_complete/Cargo.toml | 6 +- clap_complete/examples/dynamic.rs | 14 +- clap_complete/examples/exhaustive.rs | 15 +- clap_complete/src/command/mod.rs | 253 --------- clap_complete/src/command/shells.rs | 529 ------------------ clap_complete/src/lib.rs | 6 - .../dynamic-command/exhaustive/bash/.bashrc | 32 -- .../exhaustive/elvish/elvish/rc.elv | 14 - .../fish/fish/completions/exhaustive.fish | 1 - .../exhaustive/fish/fish/config.fish | 7 - .../dynamic-command/exhaustive/zsh/.zshenv | 5 - .../exhaustive/zsh/zsh/_exhaustive | 17 - .../home/static/exhaustive/bash/.bashrc | 38 +- .../static/exhaustive/elvish/elvish/rc.elv | 11 - .../fish/fish/completions/exhaustive.fish | 21 +- .../static/exhaustive/zsh/zsh/_exhaustive | 27 - clap_complete/tests/testsuite/bash.rs | 60 +- clap_complete/tests/testsuite/common.rs | 2 - clap_complete/tests/testsuite/elvish.rs | 46 -- clap_complete/tests/testsuite/fish.rs | 61 +- clap_complete/tests/testsuite/zsh.rs | 43 +- 22 files changed, 23 insertions(+), 1186 deletions(-) delete mode 100644 clap_complete/src/command/mod.rs delete mode 100644 clap_complete/src/command/shells.rs delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv delete mode 100644 clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive diff --git a/Cargo.lock b/Cargo.lock index 278ec60036e..79915ec0975 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,7 +485,6 @@ dependencies = [ "shlex", "snapbox", "trycmd", - "unicode-xid", ] [[package]] diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index f359b82ce00..1397450ceb9 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -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] diff --git a/clap_complete/examples/dynamic.rs b/clap_complete/examples/dynamic.rs index ba61f5fdc35..2c393a3e749 100644 --- a/clap_complete/examples/dynamic.rs +++ b/clap_complete/examples/dynamic.rs @@ -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] diff --git a/clap_complete/examples/exhaustive.rs b/clap_complete/examples/exhaustive.rs index 83050bdd56f..cbe604ac054 100644 --- a/clap_complete/examples/exhaustive.rs +++ b/clap_complete/examples/exhaustive.rs @@ -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(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 + ]) } diff --git a/clap_complete/src/command/mod.rs b/clap_complete/src/command/mod.rs deleted file mode 100644 index ed3ebeabd58..00000000000 --- a/clap_complete/src/command/mod.rs +++ /dev/null @@ -1,253 +0,0 @@ -//! [` 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, -/// -/// /// Input file path -/// #[clap(short, long, value_hint = clap::ValueHint::FilePath)] -/// input: Option, -/// /// Output format -/// #[clap(short = 'F', long, value_parser = ["json", "yaml", "toml"])] -/// format: Option, -/// } -/// -/// 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, - - #[arg(raw = true, value_name = "ARG", hide = true)] - comp_words: Option>, -} - -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 ` 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, - current_dir: Option<&std::path::Path>, - buf: &mut dyn std::io::Write, - ) -> Result<(), std::io::Error>; -} diff --git a/clap_complete/src/command/shells.rs b/clap_complete/src/command/shells.rs deleted file mode 100644 index cf1cbdd8099..00000000000 --- a/clap_complete/src/command/shells.rs +++ /dev/null @@ -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) -> Option { - 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 { - 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, - 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 { - 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 { - 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 { - 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, - 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 = std::env::var("_CLAP_COMPLETE_SPACE") - .ok() - .and_then(|i| i.parse().ok()); - let ifs: Option = 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 { - 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, - 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 = 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, - 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, - 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, - 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 = 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(()) - } -} diff --git a/clap_complete/src/lib.rs b/clap_complete/src/lib.rs index 099ed6929f2..43feab1efb7 100644 --- a/clap_complete/src/lib.rs +++ b/clap_complete/src/lib.rs @@ -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; diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc deleted file mode 100644 index 71a223df5be..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/bash/.bashrc +++ /dev/null @@ -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 - - diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv deleted file mode 100644 index 614e4a29534..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/elvish/elvish/rc.elv +++ /dev/null @@ -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 -} - - diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish deleted file mode 100644 index 34301ce985c..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/completions/exhaustive.fish +++ /dev/null @@ -1 +0,0 @@ -complete -x -c exhaustive -a "("'exhaustive'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))" diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish deleted file mode 100644 index 74bd2f00021..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/fish/fish/config.fish +++ /dev/null @@ -1,7 +0,0 @@ -set -U fish_greeting "" -set -U fish_autosuggestion_enabled 0 -function fish_title -end -function fish_prompt - printf '%% ' -end; diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv deleted file mode 100644 index 6d309f24b41..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/.zshenv +++ /dev/null @@ -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='%% ' diff --git a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive deleted file mode 100644 index fbc8bc36c4d..00000000000 --- a/clap_complete/tests/snapshots/home/dynamic-command/exhaustive/zsh/zsh/_exhaustive +++ /dev/null @@ -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 diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index 68a5a1a16cb..b8ce1da590d 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -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 diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv index 2fc8920678b..afde72f4678 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv +++ b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv @@ -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'= { } ] diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish index 92d6d42e9c3..dd17413c1e7 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish +++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish @@ -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`' diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive index 73f57fdf860..4ceaca2d9ca 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive +++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive @@ -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=() diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 258a13cf2d8..a90b180b3da 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -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"; @@ -173,8 +155,8 @@ fn complete() { let input = "exhaustive \t\t"; let expected = snapbox::str![[r#" % --h --global --help action value last hint help --V --generate --version quote pacman alias complete +-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); @@ -287,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::("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::("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); -} diff --git a/clap_complete/tests/testsuite/common.rs b/clap_complete/tests/testsuite/common.rs index 1c6290f8378..d388718dec4 100644 --- a/clap_complete/tests/testsuite/common.rs +++ b/clap_complete/tests/testsuite/common.rs @@ -321,7 +321,6 @@ pub(crate) fn register_example(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(); diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index 137c2662bf3..8cc49700286 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -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::( - "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::( - "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); -} diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 98aac6934f6..04dc559d8b7 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -154,8 +154,8 @@ fn complete() { let input = "exhaustive \t"; let expected = snapbox::str![[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 +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); @@ -225,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::("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::("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); -} diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index 02e662b2bd8..17a5e347985 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -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::("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::("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); -} From 3a53bfba4f34df8460dc8efd8f66537679f3aca9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 30 Aug 2024 13:10:34 -0500 Subject: [PATCH 07/30] docs: Ensure examples are scraped Fixes #5709 --- Cargo.toml | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index dc72812dce6..11b5af630e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,263 +193,317 @@ clap-cargo = { version = "0.14.1", default-features = false } [[example]] name = "demo" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "cargo-example" required-features = ["cargo", "color"] +doc-scrape-examples = true [[example]] name = "cargo-example-derive" required-features = ["derive", "color"] +doc-scrape-examples = true [[example]] name = "escaped-positional" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "escaped-positional-derive" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "find" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "git-derive" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "typed-derive" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "busybox" path = "examples/multicall-busybox.rs" +doc-scrape-examples = true [[example]] name = "hostname" path = "examples/multicall-hostname.rs" +doc-scrape-examples = true [[example]] name = "repl" path = "examples/repl.rs" required-features = ["help"] +doc-scrape-examples = true [[example]] name = "repl-derive" path = "examples/repl-derive.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "01_quick" path = "examples/tutorial_builder/01_quick.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "02_apps" path = "examples/tutorial_builder/02_apps.rs" +doc-scrape-examples = true [[example]] name = "02_crate" path = "examples/tutorial_builder/02_crate.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "02_app_settings" path = "examples/tutorial_builder/02_app_settings.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_01_flag_bool" path = "examples/tutorial_builder/03_01_flag_bool.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_01_flag_count" path = "examples/tutorial_builder/03_01_flag_count.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_02_option" path = "examples/tutorial_builder/03_02_option.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_02_option_mult" path = "examples/tutorial_builder/03_02_option_mult.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_03_positional" path = "examples/tutorial_builder/03_03_positional.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_03_positional_mult" path = "examples/tutorial_builder/03_03_positional_mult.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_04_subcommands" path = "examples/tutorial_builder/03_04_subcommands.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "03_05_default_values" path = "examples/tutorial_builder/03_05_default_values.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_01_possible" path = "examples/tutorial_builder/04_01_possible.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_01_enum" path = "examples/tutorial_builder/04_01_enum.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_02_parse" path = "examples/tutorial_builder/04_02_parse.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_02_validate" path = "examples/tutorial_builder/04_02_validate.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_03_relations" path = "examples/tutorial_builder/04_03_relations.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "04_04_custom" path = "examples/tutorial_builder/04_04_custom.rs" required-features = ["cargo"] +doc-scrape-examples = true [[example]] name = "05_01_assert" path = "examples/tutorial_builder/05_01_assert.rs" required-features = ["cargo"] test = true +doc-scrape-examples = true [[example]] name = "01_quick_derive" path = "examples/tutorial_derive/01_quick.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "02_apps_derive" path = "examples/tutorial_derive/02_apps.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "02_crate_derive" path = "examples/tutorial_derive/02_crate.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "02_app_settings_derive" path = "examples/tutorial_derive/02_app_settings.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_01_flag_bool_derive" path = "examples/tutorial_derive/03_01_flag_bool.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_01_flag_count_derive" path = "examples/tutorial_derive/03_01_flag_count.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_02_option_derive" path = "examples/tutorial_derive/03_02_option.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_02_option_mult_derive" path = "examples/tutorial_derive/03_02_option_mult.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_03_positional_derive" path = "examples/tutorial_derive/03_03_positional.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_03_positional_mult_derive" path = "examples/tutorial_derive/03_03_positional_mult.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_04_subcommands_derive" path = "examples/tutorial_derive/03_04_subcommands.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_04_subcommands_alt_derive" path = "examples/tutorial_derive/03_04_subcommands_alt.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "03_05_default_values_derive" path = "examples/tutorial_derive/03_05_default_values.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "04_01_enum_derive" path = "examples/tutorial_derive/04_01_enum.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "04_02_parse_derive" path = "examples/tutorial_derive/04_02_parse.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "04_02_validate_derive" path = "examples/tutorial_derive/04_02_validate.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "04_03_relations_derive" path = "examples/tutorial_derive/04_03_relations.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "04_04_custom_derive" path = "examples/tutorial_derive/04_04_custom.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "05_01_assert_derive" path = "examples/tutorial_derive/05_01_assert.rs" required-features = ["derive"] test = true +doc-scrape-examples = true [[example]] name = "interop_augment_args" path = "examples/derive_ref/augment_args.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "interop_augment_subcommands" path = "examples/derive_ref/augment_subcommands.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "interop_hand_subcommand" path = "examples/derive_ref/hand_subcommand.rs" required-features = ["derive"] +doc-scrape-examples = true [[example]] name = "interop_flatten_hand_args" path = "examples/derive_ref/flatten_hand_args.rs" required-features = ["derive"] +doc-scrape-examples = true [profile.test] opt-level = 1 From d7daf45adafe74cabfcfdddebe8e21eb3cee7108 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 01:18:01 +0000 Subject: [PATCH 08/30] chore(deps): Update EmbarkStudios/cargo-deny-action action to v2 --- .github/workflows/audit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 35b3da846f1..2da233df687 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -47,7 +47,7 @@ jobs: - bans licenses sources steps: - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 + - uses: EmbarkStudios/cargo-deny-action@v2 with: command: check ${{ matrix.checks }} rust-version: stable From 68f924818d23bf958b1373eed563ddb8e23db10c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 10:42:22 -0500 Subject: [PATCH 09/30] chore: Update completest --- Cargo.lock | 4 ++-- clap_complete/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79915ec0975..ba2b04a5864 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "completest-pty" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3635aa91b48c47ea30fe12fe7c04efbbf17dade642d4dbaa1e9cfbf3593eed4a" +checksum = "641f8ea301379c034f4d0b82f243b03dd4f6946c882aa914d47c45060482fb25" dependencies = [ "completest", "ptyprocess", diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 1397450ceb9..cf8b4d23d60 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -43,8 +43,8 @@ shlex = { version = "1.3.0", optional = true } snapbox = { version = "0.6.0", features = ["diff", "dir", "examples"] } # Cutting out `filesystem` feature trycmd = { version = "0.15.1", default-features = false, features = ["color-auto", "diff", "examples"] } -completest = "0.4.1" -completest-pty = "0.5.2" +completest = "0.4.2" +completest-pty = "0.5.4" clap = { path = "../", version = "4.5.15", default-features = false, features = ["std", "derive", "help"] } automod = "1.0.14" From 093308029433c119bcb2da4de6e8405bdc837e33 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 11:21:43 -0500 Subject: [PATCH 10/30] chore(complete): Update completest --- Cargo.lock | 4 ++-- clap_complete/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba2b04a5864..f468a951708 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "completest-pty" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641f8ea301379c034f4d0b82f243b03dd4f6946c882aa914d47c45060482fb25" +checksum = "fbd2f22a999db122bd2861c504aa363bbacaa32ebea29edf6924ee6cfe044313" dependencies = [ "completest", "ptyprocess", diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index cf8b4d23d60..32fab99a560 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -44,7 +44,7 @@ snapbox = { version = "0.6.0", features = ["diff", "dir", "examples"] } # Cutting out `filesystem` feature trycmd = { version = "0.15.1", default-features = false, features = ["color-auto", "diff", "examples"] } completest = "0.4.2" -completest-pty = "0.5.4" +completest-pty = "0.5.5" clap = { path = "../", version = "4.5.15", default-features = false, features = ["std", "derive", "help"] } automod = "1.0.14" From a8c8268946f0d3aa28f29cfe3302d09fea6e0f5e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 12:28:35 -0500 Subject: [PATCH 11/30] test(complete): Run completion tests in parallel This leaves out bash's tests because we seem to be hittin race conditions with them in CI when split. --- clap_complete/tests/testsuite/bash.rs | 14 +++++++++++++- clap_complete/tests/testsuite/elvish.rs | 16 ++++++++++++++-- clap_complete/tests/testsuite/fish.rs | 14 +++++++++++++- clap_complete/tests/testsuite/zsh.rs | 14 +++++++++++++- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index a90b180b3da..2baec1a08b5 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -240,7 +240,7 @@ fn register_dynamic_env() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] -fn complete_dynamic_env() { +fn complete_dynamic_env_toplevel() { if !common::has_command("bash") { return; } @@ -257,6 +257,18 @@ fn complete_dynamic_env() { "#]]; let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); +} + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_help() { + if !common::has_command("bash") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index 8cc49700286..564a39634df 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -142,7 +142,7 @@ fn register_completion() { #[test] #[cfg(unix)] -fn complete() { +fn complete_static_toplevel() { if !common::has_command("elvish") { return; } @@ -182,7 +182,7 @@ fn register_dynamic_env() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] -fn complete_dynamic_env() { +fn complete_dynamic_env_toplevel() { if !common::has_command("elvish") { return; } @@ -200,6 +200,18 @@ fn complete_dynamic_env() { "#]]; let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); +} + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_help() { + if !common::has_command("elvish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 04dc559d8b7..4ea55e10e85 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -177,7 +177,7 @@ fn register_dynamic_env() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] -fn complete_dynamic_env() { +fn complete_dynamic_env_toplevel() { if !common::has_command("fish") { return; } @@ -197,6 +197,18 @@ last -V (Print ve "#]]; let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); +} + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_help() { + if !common::has_command("fish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index 17a5e347985..43273ffe96d 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -170,7 +170,7 @@ fn register_dynamic_env() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] -fn complete_dynamic_env() { +fn complete_dynamic_env_toplevel() { if !common::has_command("zsh") { return; } @@ -187,6 +187,18 @@ fn complete_dynamic_env() { "#]]; let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); +} + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_help() { + if !common::has_command("zsh") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" From c3e5d16183f01540739e813de3ac73a4393c93a7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 09:32:21 -0500 Subject: [PATCH 12/30] test(complete): Verify prefix completions --- clap_complete/tests/testsuite/bash.rs | 22 ++++++++++++++++++ clap_complete/tests/testsuite/elvish.rs | 30 +++++++++++++++++++++++++ clap_complete/tests/testsuite/fish.rs | 25 +++++++++++++++++++++ clap_complete/tests/testsuite/zsh.rs | 25 +++++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 2baec1a08b5..e0019d86be4 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -281,3 +281,25 @@ fn complete_dynamic_env_quoted_help() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_option_value() { + if !common::has_command("bash") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive action --choice=\t\t"; + let expected = snapbox::str!["% "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive action --choice=f\t"; + let expected = snapbox::str!["exhaustive action --choice=f % exhaustive action --choice=f"]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index 564a39634df..9821f264e80 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -225,3 +225,33 @@ fn complete_dynamic_env_quoted_help() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_option_value() { + if !common::has_command("elvish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive action --choice=\t"; + let expected = snapbox::str![[r#" +% exhaustive action '--choice=first' + COMPLETING argument +--choice=first --choice=second +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive action --choice=f\t"; + let expected = snapbox::str![[r#" +% exhaustive action '--choice=first' + COMPLETING argument +--choice=first +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 4ea55e10e85..451b57faa3a 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -237,3 +237,28 @@ 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-dynamic"))] +fn complete_dynamic_env_option_value() { + if !common::has_command("fish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive action --choice=\t\t"; + let expected = snapbox::str![[r#" +% exhaustive action --choice=first +--choice=first --choice=second +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive action --choice=f\t"; + let expected = snapbox::str!["% exhaustive action --choice=first "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index 43273ffe96d..1d0e507c63a 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -211,3 +211,28 @@ fn complete_dynamic_env_quoted_help() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_option_value() { + if !common::has_command("zsh") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive action --choice=\t\t"; + let expected = snapbox::str![[r#" +% exhaustive action --choice= +--choice=first --choice=second +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive action --choice=f\t\t"; + let expected = snapbox::str!["% exhaustive action --choice=first "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} From 5286385dacdcbe2693821671b1fbc567a082400d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 09:42:52 -0500 Subject: [PATCH 13/30] test(complete): Verify space in completed value --- clap_complete/examples/exhaustive.rs | 1 + .../home/static/exhaustive/bash/.bashrc | 2 +- .../fish/fish/completions/exhaustive.fish | 2 +- .../static/exhaustive/zsh/zsh/_exhaustive | 3 +- clap_complete/tests/testsuite/bash.rs | 26 ++++++++++++++++ clap_complete/tests/testsuite/elvish.rs | 30 +++++++++++++++++++ clap_complete/tests/testsuite/fish.rs | 27 ++++++++++++++++- clap_complete/tests/testsuite/zsh.rs | 25 ++++++++++++++++ 8 files changed, 112 insertions(+), 4 deletions(-) diff --git a/clap_complete/examples/exhaustive.rs b/clap_complete/examples/exhaustive.rs index cbe604ac054..7079ed14233 100644 --- a/clap_complete/examples/exhaustive.rs +++ b/clap_complete/examples/exhaustive.rs @@ -86,6 +86,7 @@ fn cli() -> clap::Command { .long("choice") .action(clap::ArgAction::Set) .value_parser(clap::builder::PossibleValuesParser::new([ + PossibleValue::new("another shell").help("something with a space"), PossibleValue::new("bash").help("bash (shell)"), PossibleValue::new("fish").help("fish shell"), PossibleValue::new("zsh").help("zsh shell"), diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index b8ce1da590d..be558dec0c1 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -728,7 +728,7 @@ _exhaustive() { fi case "${prev}" in --choice) - COMPREPLY=($(compgen -W "bash fish zsh" -- "${cur}")) + COMPREPLY=($(compgen -W "another shell bash fish zsh" -- "${cur}")) return 0 ;; *) diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish index dd17413c1e7..82c56229a11 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish +++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish @@ -43,7 +43,7 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l count - complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -l global -d 'everywhere' complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -s h -l help -d 'Print help' complete -c exhaustive -n "__fish_exhaustive_using_subcommand action" -s V -l version -d 'Print version' -complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l choice -r -f -a "{bash\t'bash (shell)',fish\t'fish shell',zsh\t'zsh shell'}" +complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l choice -r -f -a "{another shell\t'something with a space',bash\t'bash (shell)',fish\t'fish shell',zsh\t'zsh shell'}" complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l single-quotes -d 'Can be \'always\', \'auto\', or \'never\'' complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l double-quotes -d 'Can be "always", "auto", or "never"' complete -c exhaustive -n "__fish_exhaustive_using_subcommand quote; and not __fish_seen_subcommand_from cmd-single-quotes cmd-double-quotes cmd-backticks cmd-backslash cmd-brackets cmd-expansions escape-help help" -l backticks -d 'For more information see `echo test`' diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive index 4ceaca2d9ca..528191442d0 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive +++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive @@ -45,7 +45,8 @@ _arguments "${_arguments_options[@]}" : \ ;; (quote) _arguments "${_arguments_options[@]}" : \ -'--choice=[]: :((bash\:"bash (shell)" +'--choice=[]: :((another\ shell\:"something with a space" +bash\:"bash (shell)" fish\:"fish shell" zsh\:"zsh shell"))' \ '--single-quotes[Can be '\''always'\'', '\''auto'\'', or '\''never'\'']' \ diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index e0019d86be4..88dffe835ba 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -303,3 +303,29 @@ fn complete_dynamic_env_option_value() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_value() { + if !common::has_command("bash") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive quote --choice \t\t"; + let expected = snapbox::str![[r#" +% +another shell bash fish zsh +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive quote --choice an\t"; + let expected = + snapbox::str!["exhaustive quote --choice an % exhaustive quote --choice another shell "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index 9821f264e80..b0018a1b668 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -255,3 +255,33 @@ fn complete_dynamic_env_option_value() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_value() { + if !common::has_command("elvish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive quote --choice \t"; + let expected = snapbox::str![[r#" +% exhaustive quote --choice 'another shell' + COMPLETING argument +another shell bash fish zsh +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive quote --choice an\t"; + let expected = snapbox::str![[r#" +% exhaustive quote --choice 'another shell' + COMPLETING argument +another shell +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 451b57faa3a..9dccb53c129 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -164,7 +164,7 @@ alias hint pacma let actual = runtime.complete(input, &term).unwrap(); let expected = snapbox::str![[r#" % exhaustive quote --choice -bash (bash (shell)) fish (fish shell) zsh (zsh shell) +another shell (something with a space) bash (bash (shell)) fish (fish shell) zsh (zsh shell) "#]]; assert_data_eq!(actual, expected); } @@ -262,3 +262,28 @@ fn complete_dynamic_env_option_value() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_value() { + if !common::has_command("fish") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive quote --choice \t\t"; + let expected = snapbox::str![[r#" +% exhaustive quote --choice another/ shell +another shell (something with a space) bash (bash (shell)) fish (fish shell) zsh (zsh shell) +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive quote --choice an\t"; + let expected = snapbox::str!["% exhaustive quote --choice another/ shell "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index 1d0e507c63a..4bc30a4d16d 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -236,3 +236,28 @@ fn complete_dynamic_env_option_value() { let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); } + +#[test] +#[cfg(all(unix, feature = "unstable-dynamic"))] +fn complete_dynamic_env_quoted_value() { + if !common::has_command("zsh") { + return; + } + + let term = completest::Term::new(); + let mut runtime = + common::load_runtime::("dynamic-env", "exhaustive"); + + let input = "exhaustive quote --choice \t\t"; + let expected = snapbox::str![[r#" +% exhaustive quote --choice +another/ shell bash fish zsh +"#]]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); + + let input = "exhaustive quote --choice an\t\t"; + let expected = snapbox::str!["% exhaustive quote --choice another/ shell "]; + let actual = runtime.complete(input, &term).unwrap(); + assert_data_eq!(actual, expected); +} From 931d9acb71a33a87401150a9e1471ab980e02051 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 09:47:21 -0500 Subject: [PATCH 14/30] refactor(complete): Make it easier to compare tests --- clap_complete/tests/testsuite/bash.rs | 34 ++++++++++++------------- clap_complete/tests/testsuite/elvish.rs | 34 ++++++++++++------------- clap_complete/tests/testsuite/fish.rs | 34 ++++++++++++------------- clap_complete/tests/testsuite/zsh.rs | 34 ++++++++++++------------- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 88dffe835ba..25461d6f93a 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -2,6 +2,11 @@ use snapbox::assert_data_eq; use crate::common; +#[cfg(unix)] +const CMD: &str = "bash"; +#[cfg(unix)] +type RuntimeBuilder = completest_pty::BashRuntimeBuilder; + #[test] fn basic() { let name = "my-app"; @@ -138,19 +143,18 @@ fn subcommand_last() { #[test] #[cfg(unix)] fn register_completion() { - common::register_example::("static", "exhaustive"); + common::register_example::("static", "exhaustive"); } #[test] #[cfg(unix)] fn complete() { - if !common::has_command("bash") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("static", "exhaustive"); + let mut runtime = common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t\t"; let expected = snapbox::str![[r#" @@ -235,19 +239,18 @@ fn complete() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn register_dynamic_env() { - common::register_example::("dynamic-env", "exhaustive"); + common::register_example::("dynamic-env", "exhaustive"); } #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_toplevel() { - if !common::has_command("bash") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive \t\t"; let expected = snapbox::str![[r#" @@ -262,13 +265,12 @@ fn complete_dynamic_env_toplevel() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_help() { - if !common::has_command("bash") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" @@ -285,13 +287,12 @@ fn complete_dynamic_env_quoted_help() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_option_value() { - if !common::has_command("bash") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive action --choice=\t\t"; let expected = snapbox::str!["% "]; @@ -307,13 +308,12 @@ fn complete_dynamic_env_option_value() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_value() { - if !common::has_command("bash") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote --choice \t\t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index b0018a1b668..2dbf9437eb5 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -1,6 +1,11 @@ use crate::common; use snapbox::assert_data_eq; +#[cfg(unix)] +const CMD: &str = "elvish"; +#[cfg(unix)] +type RuntimeBuilder = completest_pty::ElvishRuntimeBuilder; + #[test] fn basic() { let name = "my-app"; @@ -137,19 +142,18 @@ fn subcommand_last() { #[test] #[cfg(unix)] fn register_completion() { - common::register_example::("static", "exhaustive"); + common::register_example::("static", "exhaustive"); } #[test] #[cfg(unix)] fn complete_static_toplevel() { - if !common::has_command("elvish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("static", "exhaustive"); + let mut runtime = common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t"; let expected = snapbox::str![[r#" @@ -177,19 +181,18 @@ value value #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn register_dynamic_env() { - common::register_example::("dynamic-env", "exhaustive"); + common::register_example::("dynamic-env", "exhaustive"); } #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_toplevel() { - if !common::has_command("elvish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive \t"; let expected = snapbox::str![[r#" @@ -205,13 +208,12 @@ fn complete_dynamic_env_toplevel() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_help() { - if !common::has_command("elvish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t"; let expected = snapbox::str![[r#" @@ -229,13 +231,12 @@ fn complete_dynamic_env_quoted_help() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_option_value() { - if !common::has_command("elvish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive action --choice=\t"; let expected = snapbox::str![[r#" @@ -259,13 +260,12 @@ fn complete_dynamic_env_option_value() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_value() { - if !common::has_command("elvish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote --choice \t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 9dccb53c129..f2ddf4e55b4 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -1,6 +1,11 @@ use crate::common; use snapbox::assert_data_eq; +#[cfg(unix)] +const CMD: &str = "fish"; +#[cfg(unix)] +type RuntimeBuilder = completest_pty::FishRuntimeBuilder; + #[test] fn basic() { let name = "my-app"; @@ -137,19 +142,18 @@ fn subcommand_last() { #[test] #[cfg(unix)] fn register_completion() { - common::register_example::("static", "exhaustive"); + common::register_example::("static", "exhaustive"); } #[test] #[cfg(unix)] fn complete() { - if !common::has_command("fish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("static", "exhaustive"); + let mut runtime = common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t"; let expected = snapbox::str![[r#" @@ -172,19 +176,18 @@ another shell (something with a space) bash (bash (shell)) fish (fish shell #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn register_dynamic_env() { - common::register_example::("dynamic-env", "exhaustive"); + common::register_example::("dynamic-env", "exhaustive"); } #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_toplevel() { - if !common::has_command("fish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive \t\t"; let expected = snapbox::str![[r#" @@ -202,13 +205,12 @@ last -V (Print ve #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_help() { - if !common::has_command("fish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" @@ -241,13 +243,12 @@ help (Print this message or the help of the given subcommand(s)) #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_option_value() { - if !common::has_command("fish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive action --choice=\t\t"; let expected = snapbox::str![[r#" @@ -266,13 +267,12 @@ fn complete_dynamic_env_option_value() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_value() { - if !common::has_command("fish") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote --choice \t\t"; let expected = snapbox::str![[r#" diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index 4bc30a4d16d..2331ff4da08 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -2,6 +2,11 @@ use snapbox::assert_data_eq; use crate::common; +#[cfg(unix)] +const CMD: &str = "zsh"; +#[cfg(unix)] +type RuntimeBuilder = completest_pty::ZshRuntimeBuilder; + #[test] fn basic() { let name = "my-app"; @@ -138,19 +143,18 @@ fn subcommand_last() { #[test] #[cfg(unix)] fn register_completion() { - common::register_example::("static", "exhaustive"); + common::register_example::("static", "exhaustive"); } #[test] #[cfg(unix)] fn complete() { - if !common::has_command("zsh") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("static", "exhaustive"); + let mut runtime = common::load_runtime::("static", "exhaustive"); let input = "exhaustive \t"; let expected = snapbox::str![[r#" @@ -165,19 +169,18 @@ pacman action alias value quote hint last -- #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn register_dynamic_env() { - common::register_example::("dynamic-env", "exhaustive"); + common::register_example::("dynamic-env", "exhaustive"); } #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_toplevel() { - if !common::has_command("zsh") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive \t\t"; let expected = snapbox::str![[r#" @@ -192,13 +195,12 @@ fn complete_dynamic_env_toplevel() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_help() { - if !common::has_command("zsh") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" @@ -215,13 +217,12 @@ fn complete_dynamic_env_quoted_help() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_option_value() { - if !common::has_command("zsh") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive action --choice=\t\t"; let expected = snapbox::str![[r#" @@ -240,13 +241,12 @@ fn complete_dynamic_env_option_value() { #[test] #[cfg(all(unix, feature = "unstable-dynamic"))] fn complete_dynamic_env_quoted_value() { - if !common::has_command("zsh") { + if !common::has_command(CMD) { return; } let term = completest::Term::new(); - let mut runtime = - common::load_runtime::("dynamic-env", "exhaustive"); + let mut runtime = common::load_runtime::("dynamic-env", "exhaustive"); let input = "exhaustive quote --choice \t\t"; let expected = snapbox::str![[r#" From df19cbb5ebdeabe01e1692d14a73951f65349a52 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 13:51:58 -0500 Subject: [PATCH 15/30] refactor(error): Avoid variable name ambiguity --- clap_builder/src/error/format.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clap_builder/src/error/format.rs b/clap_builder/src/error/format.rs index 8696c79be6d..6345df3c3a3 100644 --- a/clap_builder/src/error/format.rs +++ b/clap_builder/src/error/format.rs @@ -527,7 +527,7 @@ fn try_help(styled: &mut StyledStr, styles: &Styles, help: Option<&str>) { } #[cfg(feature = "error-context")] -fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, valid: &ContextValue) { +fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, possibles: &ContextValue) { use std::fmt::Write as _; let _ = write!( @@ -536,26 +536,26 @@ fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, valid: & styles.get_valid().render(), styles.get_valid().render_reset() ); - if let ContextValue::String(valid) = valid { + if let ContextValue::String(possible) = possibles { let _ = write!( styled, - " a similar {context} exists: '{}{valid}{}'", + " a similar {context} exists: '{}{possible}{}'", styles.get_valid().render(), styles.get_valid().render_reset() ); - } else if let ContextValue::Strings(valid) = valid { - if valid.len() == 1 { + } else if let ContextValue::Strings(possibles) = possibles { + if possibles.len() == 1 { let _ = write!(styled, " a similar {context} exists: ",); } else { let _ = write!(styled, " some similar {context}s exist: ",); } - for (i, valid) in valid.iter().enumerate() { + for (i, possible) in possibles.iter().enumerate() { if i != 0 { styled.push_str(", "); } let _ = write!( styled, - "'{}{valid}{}'", + "'{}{possible}{}'", styles.get_valid().render(), styles.get_valid().render_reset() ); From 8bd63d272182bba26eeba58e4c1de917bc6db001 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 13:59:06 -0500 Subject: [PATCH 16/30] refactor: Use newer anstyle API --- clap_builder/src/builder/arg.rs | 32 ++---- clap_builder/src/error/format.rs | 123 +++++------------------ clap_builder/src/error/mod.rs | 25 +---- clap_builder/src/macros.rs | 2 +- clap_builder/src/output/help_template.rs | 77 ++------------ clap_builder/src/output/usage.rs | 55 ++-------- 6 files changed, 56 insertions(+), 258 deletions(-) diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs index 549cdc797ad..b5450d16e35 100644 --- a/clap_builder/src/builder/arg.rs +++ b/clap_builder/src/builder/arg.rs @@ -4329,14 +4329,9 @@ impl Arg { let mut styled = StyledStr::new(); // Write the name such --long or -l if let Some(l) = self.get_long() { - let _ = write!( - styled, - "{}--{l}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, "{literal}--{l}{literal:#}",); } else if let Some(s) = self.get_short() { - let _ = write!(styled, "{}-{s}{}", literal.render(), literal.render_reset()); + let _ = write!(styled, "{literal}-{s}{literal:#}"); } styled.push_styled(&self.stylize_arg_suffix(styles, required)); styled @@ -4364,32 +4359,17 @@ impl Arg { } else { (placeholder, " ") }; - let _ = write!(styled, "{}{start}{}", style.render(), style.render_reset()); + let _ = write!(styled, "{style}{start}{style:#}"); } if self.is_takes_value_set() || self.is_positional() { let required = required.unwrap_or_else(|| self.is_required_set()); let arg_val = self.render_arg_val(required); - let _ = write!( - styled, - "{}{arg_val}{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}{arg_val}{placeholder:#}",); } else if matches!(*self.get_action(), ArgAction::Count) { - let _ = write!( - styled, - "{}...{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}...{placeholder:#}",); } if need_closing_bracket { - let _ = write!( - styled, - "{}]{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}]{placeholder:#}",); } styled diff --git a/clap_builder/src/error/format.rs b/clap_builder/src/error/format.rs index 6345df3c3a3..7114d5fedcf 100644 --- a/clap_builder/src/error/format.rs +++ b/clap_builder/src/error/format.rs @@ -108,12 +108,7 @@ impl ErrorFormatter for RichFormatter { styled.push_str("\n"); } for suggestion in suggestions { - let _ = write!( - styled, - "\n{TAB}{}tip:{} ", - valid.render(), - valid.render_reset() - ); + let _ = write!(styled, "\n{TAB}{valid}tip:{valid:#} ",); styled.push_styled(suggestion); } } @@ -132,7 +127,7 @@ impl ErrorFormatter for RichFormatter { fn start_error(styled: &mut StyledStr, styles: &Styles) { use std::fmt::Write as _; let error = &styles.get_error(); - let _ = write!(styled, "{}error:{} ", error.render(), error.render_reset()); + let _ = write!(styled, "{error}error:{error:#} "); } #[must_use] @@ -155,16 +150,12 @@ fn write_dynamic_context( prior_arg = None; let _ = write!( styled, - "the argument '{}{invalid_arg}{}' cannot be used multiple times", - invalid.render(), - invalid.render_reset() + "the argument '{invalid}{invalid_arg}{invalid:#}' cannot be used multiple times", ); } else { let _ = write!( styled, - "the argument '{}{invalid_arg}{}' cannot be used with", - invalid.render(), - invalid.render_reset() + "the argument '{invalid}{invalid_arg}{invalid:#}' cannot be used with", ); } } else if let Some(ContextValue::String(invalid_arg)) = @@ -172,9 +163,7 @@ fn write_dynamic_context( { let _ = write!( styled, - "the subcommand '{}{invalid_arg}{}' cannot be used with", - invalid.render(), - invalid.render_reset() + "the subcommand '{invalid}{invalid_arg}{invalid:#}' cannot be used with", ); } else { styled.push_str(error.kind().as_str().unwrap()); @@ -185,21 +174,11 @@ fn write_dynamic_context( ContextValue::Strings(values) => { styled.push_str(":"); for v in values { - let _ = write!( - styled, - "\n{TAB}{}{v}{}", - invalid.render(), - invalid.render_reset() - ); + let _ = write!(styled, "\n{TAB}{invalid}{v}{invalid:#}",); } } ContextValue::String(value) => { - let _ = write!( - styled, - " '{}{value}{}'", - invalid.render(), - invalid.render_reset() - ); + let _ = write!(styled, " '{invalid}{value}{invalid:#}'",); } _ => { styled.push_str(" one or more of the other specified arguments"); @@ -214,9 +193,7 @@ fn write_dynamic_context( if let Some(ContextValue::String(invalid_arg)) = invalid_arg { let _ = write!( styled, - "equal sign is needed when assigning values to '{}{invalid_arg}{}'", - invalid.render(), - invalid.render_reset() + "equal sign is needed when assigning values to '{invalid}{invalid_arg}{invalid:#}'", ); true } else { @@ -234,18 +211,12 @@ fn write_dynamic_context( if invalid_value.is_empty() { let _ = write!( styled, - "a value is required for '{}{invalid_arg}{}' but none was supplied", - invalid.render(), - invalid.render_reset() + "a value is required for '{invalid}{invalid_arg}{invalid:#}' but none was supplied", ); } else { let _ = write!( styled, - "invalid value '{}{invalid_value}{}' for '{}{invalid_arg}{}'", - invalid.render(), - invalid.render_reset(), - literal.render(), - literal.render_reset() + "invalid value '{invalid}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}'", ); } @@ -262,9 +233,7 @@ fn write_dynamic_context( if let Some(ContextValue::String(invalid_sub)) = invalid_sub { let _ = write!( styled, - "unrecognized subcommand '{}{invalid_sub}{}'", - invalid.render(), - invalid.render_reset() + "unrecognized subcommand '{invalid}{invalid_sub}{invalid:#}'", ); true } else { @@ -276,12 +245,7 @@ fn write_dynamic_context( if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg { styled.push_str("the following required arguments were not provided:"); for v in invalid_arg { - let _ = write!( - styled, - "\n{TAB}{}{v}{}", - valid.render(), - valid.render_reset() - ); + let _ = write!(styled, "\n{TAB}{valid}{v}{valid:#}",); } true } else { @@ -293,9 +257,7 @@ fn write_dynamic_context( if let Some(ContextValue::String(invalid_sub)) = invalid_sub { let _ = write!( styled, - "'{}{invalid_sub}{}' requires a subcommand but one was not provided", - invalid.render(), - invalid.render_reset() + "'{invalid}{invalid_sub}{invalid:#}' requires a subcommand but one was not provided", ); let values = error.get(ContextKind::ValidSubcommand); write_values_list("subcommands", styled, valid, values); @@ -316,11 +278,7 @@ fn write_dynamic_context( { let _ = write!( styled, - "unexpected value '{}{invalid_value}{}' for '{}{invalid_arg}{}' found; no more were expected", - invalid.render(), - invalid.render_reset(), - literal.render(), - literal.render_reset(), + "unexpected value '{invalid}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}' found; no more were expected", ); true } else { @@ -340,13 +298,7 @@ fn write_dynamic_context( let were_provided = singular_or_plural(*actual_num_values as usize); let _ = write!( styled, - "{}{min_values}{} values required by '{}{invalid_arg}{}'; only {}{actual_num_values}{}{were_provided}", - valid.render(), - valid.render_reset(), - literal.render(), - literal.render_reset(), - invalid.render(), - invalid.render_reset(), + "{valid}{min_values}{valid:#} values required by '{literal}{invalid_arg}{literal:#}'; only {invalid}{actual_num_values}{invalid:#}{were_provided}", ); true } else { @@ -363,11 +315,7 @@ fn write_dynamic_context( { let _ = write!( styled, - "invalid value '{}{invalid_value}{}' for '{}{invalid_arg}{}'", - invalid.render(), - invalid.render_reset(), - literal.render(), - literal.render_reset(), + "invalid value '{invalid}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}'", ); if let Some(source) = error.inner.source.as_deref() { let _ = write!(styled, ": {source}"); @@ -390,13 +338,7 @@ fn write_dynamic_context( let were_provided = singular_or_plural(*actual_num_values as usize); let _ = write!( styled, - "{}{num_values}{} values required for '{}{invalid_arg}{}' but {}{actual_num_values}{}{were_provided}", - valid.render(), - valid.render_reset(), - literal.render(), - literal.render_reset(), - invalid.render(), - invalid.render_reset(), + "{valid}{num_values}{valid:#} values required for '{literal}{invalid_arg}{literal:#}' but {invalid}{actual_num_values}{invalid:#}{were_provided}", ); true } else { @@ -408,9 +350,7 @@ fn write_dynamic_context( if let Some(ContextValue::String(invalid_arg)) = invalid_arg { let _ = write!( styled, - "unexpected argument '{}{invalid_arg}{}' found", - invalid.render(), - invalid.render_reset(), + "unexpected argument '{invalid}{invalid_arg}{invalid:#}' found", ); true } else { @@ -437,13 +377,11 @@ fn write_values_list( if !possible_values.is_empty() { let _ = write!(styled, "\n{TAB}[{list_name}: "); - let style = valid.render(); - let reset = valid.render_reset(); for (idx, val) in possible_values.iter().enumerate() { if idx > 0 { styled.push_str(", "); } - let _ = write!(styled, "{style}{}{reset}", Escape(val)); + let _ = write!(styled, "{valid}{}{valid:#}", Escape(val)); } styled.push_str("]"); @@ -517,9 +455,7 @@ fn try_help(styled: &mut StyledStr, styles: &Styles, help: Option<&str>) { let literal = &styles.get_literal(); let _ = write!( styled, - "\n\nFor more information, try '{}{help}{}'.\n", - literal.render(), - literal.render_reset() + "\n\nFor more information, try '{literal}{help}{literal:#}'.\n", ); } else { styled.push_str("\n"); @@ -530,18 +466,12 @@ fn try_help(styled: &mut StyledStr, styles: &Styles, help: Option<&str>) { fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, possibles: &ContextValue) { use std::fmt::Write as _; - let _ = write!( - styled, - "{TAB}{}tip:{}", - styles.get_valid().render(), - styles.get_valid().render_reset() - ); + let valid = &styles.get_valid(); + let _ = write!(styled, "{TAB}{valid}tip:{valid:#}",); if let ContextValue::String(possible) = possibles { let _ = write!( styled, - " a similar {context} exists: '{}{possible}{}'", - styles.get_valid().render(), - styles.get_valid().render_reset() + " a similar {context} exists: '{valid}{possible}{valid:#}'", ); } else if let ContextValue::Strings(possibles) = possibles { if possibles.len() == 1 { @@ -553,12 +483,7 @@ fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, possible if i != 0 { styled.push_str(", "); } - let _ = write!( - styled, - "'{}{possible}{}'", - styles.get_valid().render(), - styles.get_valid().render_reset() - ); + let _ = write!(styled, "'{valid}{possible}{valid:#}'",); } } } diff --git a/clap_builder/src/error/mod.rs b/clap_builder/src/error/mod.rs index cfba5a818b4..7bd627375c1 100644 --- a/clap_builder/src/error/mod.rs +++ b/clap_builder/src/error/mod.rs @@ -490,11 +490,7 @@ impl Error { let mut styled_suggestion = StyledStr::new(); let _ = write!( styled_suggestion, - "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'", - invalid.render(), - invalid.render_reset(), - valid.render(), - valid.render_reset() + "to pass '{invalid}{subcmd}{invalid:#}' as a value, use '{valid}{name} -- {subcmd}{valid:#}'", ); suggestions.push(styled_suggestion); } @@ -726,11 +722,7 @@ impl Error { let mut styled_suggestion = StyledStr::new(); let _ = write!( styled_suggestion, - "to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'", - invalid.render(), - invalid.render_reset(), - valid.render(), - valid.render_reset() + "to pass '{invalid}{arg}{invalid:#}' as a value, use '{valid}-- {arg}{valid:#}'", ); suggestions.push(styled_suggestion); } @@ -744,12 +736,7 @@ impl Error { match did_you_mean { Some((flag, Some(sub))) => { let mut styled_suggestion = StyledStr::new(); - let _ = write!( - styled_suggestion, - "'{}{sub} {flag}{}' exists", - valid.render(), - valid.render_reset() - ); + let _ = write!(styled_suggestion, "'{valid}{sub} {flag}{valid:#}' exists",); suggestions.push(styled_suggestion); } Some((flag, None)) => { @@ -787,11 +774,7 @@ impl Error { let mut styled_suggestion = StyledStr::new(); let _ = write!( styled_suggestion, - "subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it", - valid.render(), - valid.render_reset(), - invalid.render(), - invalid.render_reset() + "subcommand '{valid}{arg}{valid:#}' exists; to use it, remove the '{invalid}--{invalid:#}' before it", ); err = err.extend_context_unchecked([ diff --git a/clap_builder/src/macros.rs b/clap_builder/src/macros.rs index 26d14742972..83db012e08a 100644 --- a/clap_builder/src/macros.rs +++ b/clap_builder/src/macros.rs @@ -540,7 +540,7 @@ macro_rules! debug { let module_path = module_path!(); let body = format!($($arg)*); let mut styled = $crate::builder::StyledStr::new(); - let _ = write!(styled, "{}[{module_path:>28}]{body}{}\n", hint.render(), hint.render_reset()); + let _ = write!(styled, "{hint}[{module_path:>28}]{body}{hint:#}\n"); let color = $crate::output::fmt::Colorizer::new($crate::output::fmt::Stream::Stderr, $crate::ColorChoice::Auto).with_content(styled); let _ = color.print(); }) diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs index 3abeab1487a..da08ccd3895 100644 --- a/clap_builder/src/output/help_template.rs +++ b/clap_builder/src/output/help_template.rs @@ -406,12 +406,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .cmd .get_subcommand_help_heading() .unwrap_or(&default_help_heading); - let _ = write!( - self.writer, - "{}{help_heading}:{}\n", - header.render(), - header.render_reset() - ); + let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",); self.write_subcommands(self.cmd); } @@ -423,12 +418,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { first = false; // Write positional args if any let help_heading = "Arguments"; - let _ = write!( - self.writer, - "{}{help_heading}:{}\n", - header.render(), - header.render_reset() - ); + let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",); self.write_args(&pos, "Arguments", positional_sort_key); } @@ -438,12 +428,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { } first = false; let help_heading = "Options"; - let _ = write!( - self.writer, - "{}{help_heading}:{}\n", - header.render(), - header.render_reset() - ); + let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",); self.write_args(&non_pos, "Options", option_sort_key); } if !custom_headings.is_empty() { @@ -465,12 +450,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { self.writer.push_str("\n\n"); } first = false; - let _ = write!( - self.writer, - "{}{heading}:{}\n", - header.render(), - header.render_reset() - ); + let _ = write!(self.writer, "{header}{heading}:{header:#}\n",); self.write_args(&args, heading, option_sort_key); } } @@ -554,12 +534,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { let literal = &self.styles.get_literal(); if let Some(s) = arg.get_short() { - let _ = write!( - self.writer, - "{}-{s}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(self.writer, "{literal}-{s}{literal:#}",); } else if arg.get_long().is_some() { self.writer.push_str(" "); } @@ -575,12 +550,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { if arg.get_short().is_some() { self.writer.push_str(", "); } - let _ = write!( - self.writer, - "{}--{long}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(self.writer, "{literal}--{long}{literal:#}",); } } @@ -707,12 +677,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { let name = pv.get_name(); let mut descr = StyledStr::new(); - let _ = write!( - &mut descr, - "{}{name}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(&mut descr, "{literal}{name}{literal:#}",); if let Some(help) = pv.get_help() { debug!("HelpTemplate::help: Possible Value help"); // To align help messages @@ -917,12 +882,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .or_else(|| subcommand.get_long_about()) .unwrap_or_default(); - let _ = write!( - self.writer, - "{}{heading}:{}\n", - header.render(), - header.render_reset() - ); + let _ = write!(self.writer, "{header}{heading}:{header:#}\n",); if !about.is_empty() { let _ = write!(self.writer, "{about}\n",); } @@ -962,27 +922,12 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { { let mut styled = StyledStr::new(); let name = subcommand.get_name(); - let _ = write!( - styled, - "{}{name}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, "{literal}{name}{literal:#}",); if let Some(short) = subcommand.get_short_flag() { - let _ = write!( - styled, - ", {}-{short}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, ", {literal}-{short}{literal:#}",); } if let Some(long) = subcommand.get_long_flag() { - let _ = write!( - styled, - ", {}--{long}{}", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, ", {literal}--{long}{literal:#}",); } longest = longest.max(styled.display_width()); ord_v.push((subcommand.get_display_order(), styled, subcommand)); diff --git a/clap_builder/src/output/usage.rs b/clap_builder/src/output/usage.rs index d75b704ba74..bbaa73136fd 100644 --- a/clap_builder/src/output/usage.rs +++ b/clap_builder/src/output/usage.rs @@ -144,12 +144,7 @@ impl<'cmd> Usage<'cmd> { .cmd .get_subcommand_value_name() .unwrap_or(DEFAULT_SUB_VALUE_NAME); - let _ = write!( - styled, - "{}<{value_name}>{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}<{value_name}>{placeholder:#}",); } } @@ -162,21 +157,11 @@ impl<'cmd> Usage<'cmd> { let bin_name = self.cmd.get_usage_name_fallback(); if !bin_name.is_empty() { // the trim won't properly remove a leading space due to the formatting - let _ = write!( - styled, - "{}{bin_name}{} ", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, "{literal}{bin_name}{literal:#} ",); } if used.is_empty() && self.needs_options_tag() { - let _ = write!( - styled, - "{}[OPTIONS]{} ", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}[OPTIONS]{placeholder:#} ",); } self.write_args(styled, used, !incl_reqs); @@ -202,35 +187,15 @@ impl<'cmd> Usage<'cmd> { if self.cmd.is_args_conflicts_with_subcommands_set() { let bin_name = self.cmd.get_usage_name_fallback(); // Short-circuit full usage creation since no args will be relevant - let _ = write!( - styled, - "{}{bin_name}{} ", - literal.render(), - literal.render_reset() - ); + let _ = write!(styled, "{literal}{bin_name}{literal:#} ",); } else { self.write_arg_usage(styled, &[], false); } - let _ = write!( - styled, - "{}<{value_name}>{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}<{value_name}>{placeholder:#}",); } else if self.cmd.is_subcommand_required_set() { - let _ = write!( - styled, - "{}<{value_name}>{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}<{value_name}>{placeholder:#}",); } else { - let _ = write!( - styled, - "{}[{value_name}]{}", - placeholder.render(), - placeholder.render_reset() - ); + let _ = write!(styled, "{placeholder}[{value_name}]{placeholder:#}",); } } } @@ -373,7 +338,7 @@ impl<'cmd> Usage<'cmd> { if pos.is_last_set() { let styled = required_positionals[index].take().unwrap(); let mut new = StyledStr::new(); - let _ = write!(new, "{}--{} ", literal.render(), literal.render_reset()); + let _ = write!(new, "{literal}--{literal:#} "); new.push_styled(&styled); required_positionals[index] = Some(new); } @@ -381,9 +346,9 @@ impl<'cmd> Usage<'cmd> { let mut styled; if pos.is_last_set() { styled = StyledStr::new(); - let _ = write!(styled, "{}[--{} ", literal.render(), literal.render_reset()); + let _ = write!(styled, "{literal}[--{literal:#} "); styled.push_styled(&pos.stylized(self.styles, Some(true))); - let _ = write!(styled, "{}]{}", literal.render(), literal.render_reset()); + let _ = write!(styled, "{literal}]{literal:#}"); } else { styled = pos.stylized(self.styles, Some(false)); } From 220597eb971fe6c30d3cf398085a70cb40739dbb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:03:33 -0500 Subject: [PATCH 17/30] refactor: Clean up group formatting --- clap_builder/src/builder/command.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs index 6480e28b807..8961827f25f 100644 --- a/clap_builder/src/builder/command.rs +++ b/clap_builder/src/builder/command.rs @@ -4561,6 +4561,8 @@ impl Command { } pub(crate) fn format_group(&self, g: &Id) -> StyledStr { + use std::fmt::Write as _; + let g_string = self .unroll_args_in_group(g) .iter() @@ -4577,9 +4579,7 @@ impl Command { .collect::>() .join("|"); let mut styled = StyledStr::new(); - styled.push_str("<"); - styled.push_string(g_string); - styled.push_str(">"); + write!(&mut styled, "<{g_string}>").unwrap(); styled } } From 75365ad7d52550592f02bb81e78aba1c313b95eb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:07:59 -0500 Subject: [PATCH 18/30] fix(help): Style arg groups --- clap_builder/src/builder/command.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs index 8961827f25f..32e6ea4fb0f 100644 --- a/clap_builder/src/builder/command.rs +++ b/clap_builder/src/builder/command.rs @@ -4578,8 +4578,9 @@ impl Command { }) .collect::>() .join("|"); + let placeholder = self.get_styles().get_placeholder(); let mut styled = StyledStr::new(); - write!(&mut styled, "<{g_string}>").unwrap(); + write!(&mut styled, "{placeholder}<{g_string}>{placeholder:#}").unwrap(); styled } } From 4b42ce402316a8a1d0d8efc6cede3b88def7cb7b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:15:57 -0500 Subject: [PATCH 19/30] docs: Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aac9832a6b..8ee44966fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +### Fixes + +- *(help)* Style required argument groups +- *(derive)* Improve error messages when unsupported fields are used + ## [4.5.16] - 2024-08-15 ### Fixes From 19460ee00d3a8f09154503defd2a3b15e9041245 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:16:31 -0500 Subject: [PATCH 20/30] chore: Release --- CHANGELOG.md | 5 ++++- CITATION.cff | 4 ++-- Cargo.lock | 16 ++++++++-------- Cargo.toml | 4 ++-- clap_builder/Cargo.toml | 2 +- src/lib.rs | 2 +- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ee44966fc3..425d8af0bcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.5.17] - 2024-09-04 + ### Fixes - *(help)* Style required argument groups @@ -4633,7 +4635,8 @@ Minimum version of Rust is now v1.13.0 (Stable) * **arg** allow lifetimes other than 'static in arguments ([9e8c1fb9](https://github.com/clap-rs/clap/commit/9e8c1fb9406f8448873ca58bab07fe905f1551e5)) -[Unreleased]: https://github.com/clap-rs/clap/compare/v4.5.16...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/v4.5.17...HEAD +[4.5.17]: https://github.com/clap-rs/clap/compare/v4.5.16...v4.5.17 [4.5.16]: https://github.com/clap-rs/clap/compare/v4.5.15...v4.5.16 [4.5.15]: https://github.com/clap-rs/clap/compare/v4.5.14...v4.5.15 [4.5.14]: https://github.com/clap-rs/clap/compare/v4.5.13...v4.5.14 diff --git a/CITATION.cff b/CITATION.cff index 719168529ed..8085d03f850 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,8 +3,8 @@ cff-version: 1.2.0 message: Please cite this crate using these information. # Version information. -date-released: 2024-08-15 -version: 4.5.16 +date-released: 2024-09-04 +version: 4.5.17 # Project information. abstract: A full featured, fast Command Line Argument Parser for Rust diff --git a/Cargo.lock b/Cargo.lock index f468a951708..4ec17f3a151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,11 +410,11 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" dependencies = [ "automod", "clap-cargo", - "clap_builder 4.5.15", + "clap_builder 4.5.17", "clap_derive", "humantime", "rustversion", @@ -437,7 +437,7 @@ dependencies = [ name = "clap_bench" version = "0.0.0" dependencies = [ - "clap 4.5.16", + "clap 4.5.17", "divan", "lazy_static", ] @@ -457,7 +457,7 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" dependencies = [ "anstream", "anstyle", @@ -477,7 +477,7 @@ name = "clap_complete" version = "4.5.24" dependencies = [ "automod", - "clap 4.5.16", + "clap 4.5.17", "clap_lex 0.7.2", "completest", "completest-pty", @@ -491,7 +491,7 @@ dependencies = [ name = "clap_complete_fig" version = "4.5.2" dependencies = [ - "clap 4.5.16", + "clap 4.5.17", "clap_complete", "snapbox", ] @@ -500,7 +500,7 @@ dependencies = [ name = "clap_complete_nushell" version = "4.5.3" dependencies = [ - "clap 4.5.16", + "clap 4.5.17", "clap_complete", "completest", "completest-nu", @@ -535,7 +535,7 @@ name = "clap_mangen" version = "0.2.23" dependencies = [ "automod", - "clap 4.5.16", + "clap 4.5.17", "roff", "snapbox", ] diff --git a/Cargo.toml b/Cargo.toml index 11b5af630e9..01cfc3df6fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,7 +99,7 @@ blocks_in_conditions = "allow" [package] name = "clap" -version = "4.5.16" +version = "4.5.17" description = "A simple to use, efficient, and full-featured Command Line Argument Parser" categories = ["command-line-interface"] keywords = [ @@ -176,7 +176,7 @@ unstable-derive-ui-tests = [] bench = false [dependencies] -clap_builder = { path = "./clap_builder", version = "=4.5.15", default-features = false } +clap_builder = { path = "./clap_builder", version = "=4.5.17", default-features = false } clap_derive = { path = "./clap_derive", version = "=4.5.13", optional = true } [dev-dependencies] diff --git a/clap_builder/Cargo.toml b/clap_builder/Cargo.toml index 553bb746914..f6005848875 100644 --- a/clap_builder/Cargo.toml +++ b/clap_builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" description = "A simple to use, efficient, and full-featured Command Line Argument Parser" categories = ["command-line-interface"] keywords = [ diff --git a/src/lib.rs b/src/lib.rs index ca6177b6281..7a9ecee540d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! - [Cookbook][_cookbook] //! - [FAQ][_faq] //! - [Discussions](https://github.com/clap-rs/clap/discussions) -//! - [CHANGELOG](https://github.com/clap-rs/clap/blob/v4.5.16/CHANGELOG.md) (includes major version migration +//! - [CHANGELOG](https://github.com/clap-rs/clap/blob/v4.5.17/CHANGELOG.md) (includes major version migration //! guides) //! //! ## Aspirations From 059db0c1394a904cc83bed3cac2d9c5d6cef7bb0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:26:44 -0500 Subject: [PATCH 21/30] docs(complete): Clarify what flags we're using --- clap_complete/src/env/shells.rs | 2 +- .../exhaustive/fish/fish/completions/exhaustive.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clap_complete/src/env/shells.rs b/clap_complete/src/env/shells.rs index 2c527d94dff..2d177168214 100644 --- a/clap_complete/src/env/shells.rs +++ b/clap_complete/src/env/shells.rs @@ -226,7 +226,7 @@ impl EnvCompleter for Fish { writeln!( buf, - r#"complete -x -c {bin} -a "({var}=fish "'{completer}'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""# + r#"complete --exclusive --command {bin} --arguments "({var}=fish "'{completer}'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""# ) } fn write_complete( diff --git a/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish index 0aae8215946..b36cb34a99d 100644 --- a/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish +++ b/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish @@ -1 +1 @@ -complete -x -c exhaustive -a "(COMPLETE=fish "'exhaustive'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))" +complete --exclusive --command exhaustive --arguments "(COMPLETE=fish "'exhaustive'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))" From e2d336b95f9370132d9ae638112d31be68ad9904 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:28:03 -0500 Subject: [PATCH 22/30] fix(complete): Put clap in chage of Fish ordering --- clap_complete/src/env/shells.rs | 2 +- .../fish/fish/completions/exhaustive.fish | 2 +- clap_complete/tests/testsuite/fish.rs | 36 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/clap_complete/src/env/shells.rs b/clap_complete/src/env/shells.rs index 2d177168214..eb2cb10a080 100644 --- a/clap_complete/src/env/shells.rs +++ b/clap_complete/src/env/shells.rs @@ -226,7 +226,7 @@ impl EnvCompleter for Fish { writeln!( buf, - r#"complete --exclusive --command {bin} --arguments "({var}=fish "'{completer}'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""# + r#"complete --keep-order --exclusive --command {bin} --arguments "({var}=fish "'{completer}'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""# ) } fn write_complete( diff --git a/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish index b36cb34a99d..bd534ef312a 100644 --- a/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish +++ b/clap_complete/tests/snapshots/home/dynamic-env/exhaustive/fish/fish/completions/exhaustive.fish @@ -1 +1 @@ -complete --exclusive --command exhaustive --arguments "(COMPLETE=fish "'exhaustive'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))" +complete --keep-order --exclusive --command exhaustive --arguments "(COMPLETE=fish "'exhaustive'" -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))" diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index f2ddf4e55b4..82498ff8cd5 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -191,12 +191,12 @@ fn complete_dynamic_env_toplevel() { 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) +% exhaustive --global +--global (everywhere) -V (Print version) last +--generate (generate) action pacman +--help (Print help) alias quote +--version (Print version) help (Print this message or the help of the given subcommand(s)) value +-h (Print help) hint "#]]; let actual = runtime.complete(input, &term).unwrap(); assert_data_eq!(actual, expected); @@ -215,6 +215,18 @@ fn complete_dynamic_env_quoted_help() { let input = "exhaustive quote \t\t"; let expected = snapbox::str![[r#" % exhaustive quote +--single-quotes (Can be 'always', 'auto', or 'never') +--double-quotes (Can be "always", "auto", or "never") +--backticks (For more information see `echo test`) +--backslash (Avoid '/n') +--brackets (List packages [filter]) +--expansions (Execute the shell command with $SHELL) +--choice +--global (everywhere) +--help (Print help (see more with '--help')) +--version (Print version) +-h (Print help (see more with '--help')) +-V (Print version) cmd-backslash (Avoid '/n') cmd-backticks (For more information see `echo test`) cmd-brackets (List packages [filter]) @@ -223,18 +235,6 @@ 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); From 1cf50c77543cab6e648b967f8edc05115a24fe8b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:36:38 -0500 Subject: [PATCH 23/30] docs: Update changelog --- clap_complete/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index 2deb3eab301..67dcb8f28ad 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +### Compatibility + +- *(dynamic)* Removed `CompleteCommand` + +### Fixes + +- *(dynamic)* Take over ordering of Fish completions + ## [4.5.24] - 2024-08-27 ### Fixes From 64e37904615c3e2df85fd38370beb5961a31d557 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 4 Sep 2024 14:36:46 -0500 Subject: [PATCH 24/30] chore: Release --- Cargo.lock | 2 +- clap_complete/CHANGELOG.md | 5 ++++- clap_complete/Cargo.toml | 2 +- clap_complete/README.md | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ec17f3a151..bbc09e0b463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,7 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.24" +version = "4.5.25" dependencies = [ "automod", "clap 4.5.17", diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index 67dcb8f28ad..64d9a6fd37e 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.5.25] - 2024-09-04 + ### Compatibility - *(dynamic)* Removed `CompleteCommand` @@ -406,7 +408,8 @@ MSRV changed to 1.64.0 ## [3.0.1] - 2022-01-03 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.24...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.25...HEAD +[4.5.25]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.24...clap_complete-v4.5.25 [4.5.24]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.23...clap_complete-v4.5.24 [4.5.23]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.22...clap_complete-v4.5.23 [4.5.22]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.21...clap_complete-v4.5.22 diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 32fab99a560..6f3eecc1d8b 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete" -version = "4.5.24" +version = "4.5.25" description = "Generate shell completion scripts for your clap::Command" categories = ["command-line-interface"] keywords = [ diff --git a/clap_complete/README.md b/clap_complete/README.md index c73e1805047..fb256da12db 100644 --- a/clap_complete/README.md +++ b/clap_complete/README.md @@ -5,16 +5,16 @@ [![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) [![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.24/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.24/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_complete) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.24/clap_complete/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.24/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/clap_complete/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/README.md#sponsors) ## About From 1d97c293ba7968d7bcd06b624104218cab6fbe3f Mon Sep 17 00:00:00 2001 From: mart-mihkel Date: Tue, 6 Aug 2024 10:46:18 +0300 Subject: [PATCH 25/30] test(complete): Test cases for allow_hyphen_values --- clap_complete/tests/testsuite/engine.rs | 155 ++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/clap_complete/tests/testsuite/engine.rs b/clap_complete/tests/testsuite/engine.rs index 5acb77bc9e2..d4377ebb370 100644 --- a/clap_complete/tests/testsuite/engine.rs +++ b/clap_complete/tests/testsuite/engine.rs @@ -981,6 +981,161 @@ a_pos,c_pos" ); } +#[test] +fn suggest_allow_hyhpen() { + let mut cmd = Command::new("exhaustive") + .arg( + clap::Arg::new("format") + .long("format") + .short('F') + .allow_hyphen_values(true) + .value_parser(["--json", "--toml", "--yaml"]), + ) + .arg(clap::Arg::new("json").long("json")); + + assert_data_eq!(complete!(cmd, "--format --j[TAB]"), snapbox::str!["--json"]); + assert_data_eq!(complete!(cmd, "-F --j[TAB]"), snapbox::str!["--json"]); + assert_data_eq!(complete!(cmd, "--format --t[TAB]"), snapbox::str!["--toml"]); + assert_data_eq!(complete!(cmd, "-F --t[TAB]"), snapbox::str!["--toml"]); + + assert_data_eq!( + complete!(cmd, "--format --[TAB]"), + snapbox::str![ + "--json +--toml +--yaml" + ] + ); + + assert_data_eq!( + complete!(cmd, "-F --[TAB]"), + snapbox::str![ + "--json +--toml +--yaml" + ] + ); + + assert_data_eq!( + complete!(cmd, "--format --json --j[TAB]"), + snapbox::str![""] + ); + + assert_data_eq!(complete!(cmd, "-F --json --j[TAB]"), snapbox::str![""]); +} + +#[test] +fn suggest_positional_long_allow_hyhpen() { + let mut cmd = Command::new("exhaustive") + .arg( + clap::Arg::new("format") + .long("format") + .short('F') + .allow_hyphen_values(true) + .value_parser(["--json", "--toml", "--yaml"]), + ) + .arg( + clap::Arg::new("positional_a") + .value_parser(["--pos_a"]) + .index(1) + .allow_hyphen_values(true), + ) + .arg( + clap::Arg::new("positional_b") + .index(2) + .value_parser(["pos_b"]), + ); + + assert_data_eq!( + complete!(cmd, "--format --json --pos[TAB]"), + snapbox::str!["--pos_a"] + ); + assert_data_eq!( + complete!(cmd, "-F --json --pos[TAB]"), + snapbox::str!["--pos_a"] + ); + + assert_data_eq!( + complete!(cmd, "--format --json --pos_a [TAB]"), + snapbox::str![ + "--format +--help Print help +-F +-h Print help +--pos_a" + ] + ); + assert_data_eq!( + complete!(cmd, "-F --json --pos_a [TAB]"), + snapbox::str![ + "--format +--help Print help +-F +-h Print help +--pos_a" + ] + ); + + assert_data_eq!( + complete!(cmd, "--format --json --pos_a p[TAB]"), + snapbox::str![""] + ); + assert_data_eq!( + complete!(cmd, "-F --json --pos_a p[TAB]"), + snapbox::str![""] + ); +} + +#[test] +fn suggest_positional_short_allow_hyhpen() { + let mut cmd = Command::new("exhaustive") + .arg( + clap::Arg::new("format") + .long("format") + .short('F') + .allow_hyphen_values(true) + .value_parser(["--json", "--toml", "--yaml"]), + ) + .arg( + clap::Arg::new("positional_a") + .value_parser(["-a"]) + .index(1) + .allow_hyphen_values(true), + ) + .arg( + clap::Arg::new("positional_b") + .index(2) + .value_parser(["pos_b"]), + ); + + assert_data_eq!( + complete!(cmd, "--format --json -a [TAB]"), + snapbox::str![ + "--format +--help Print help +-F +-h Print help +-a" + ] + ); + assert_data_eq!( + complete!(cmd, "-F --json -a [TAB]"), + snapbox::str![ + "--format +--help Print help +-F +-h Print help +-a" + ] + ); + + assert_data_eq!( + complete!(cmd, "--format --json -a p[TAB]"), + snapbox::str![""] + ); + assert_data_eq!(complete!(cmd, "-F --json -a p[TAB]"), snapbox::str![""]); +} + fn complete(cmd: &mut Command, args: impl AsRef, current_dir: Option<&Path>) -> String { let input = args.as_ref(); let mut args = vec![std::ffi::OsString::from(cmd.get_name())]; From b7cfbdcf96c96da64f7d91c0bef915ef1a6dc37f Mon Sep 17 00:00:00 2001 From: mart-mihkel Date: Thu, 15 Aug 2024 13:46:05 +0300 Subject: [PATCH 26/30] feat(complete): Native support for hyphen values --- clap_complete/src/engine/complete.rs | 54 ++++++++++++++++++++----- clap_complete/tests/testsuite/engine.rs | 26 +++++++----- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/clap_complete/src/engine/complete.rs b/clap_complete/src/engine/complete.rs index b9bb252293e..c78b3d62e8e 100644 --- a/clap_complete/src/engine/complete.rs +++ b/clap_complete/src/engine/complete.rs @@ -59,6 +59,13 @@ pub fn complete( } else if arg.is_escape() { is_escaped = true; } else if let Some((flag, value)) = arg.to_long() { + if let ParseState::Opt((opt, count)) = current_state { + if opt.is_allow_hyphen_values_set() { + next_state = parse_opt(opt, count); + continue; + } + } + if let Ok(flag) = flag { let opt = current_cmd.get_arguments().find(|a| { let longs = a.get_long_and_visible_aliases(); @@ -69,18 +76,32 @@ pub fn complete( }); is_find.unwrap_or(false) }); - if opt.map(|o| o.get_action().takes_values()).unwrap_or(false) { - if value.is_none() { - next_state = ParseState::Opt((opt.unwrap(), 1)); + + if let Some(opt) = opt { + if opt.get_action().takes_values() && value.is_none() { + next_state = ParseState::Opt((opt, 1)); }; + } else if pos_allows_hyphen(current_cmd, pos_index) { + (next_state, pos_index) = + parse_positional(current_cmd, pos_index, is_escaped, current_state); } } } else if let Some(short) = arg.to_short() { + if let ParseState::Opt((opt, count)) = current_state { + if opt.is_allow_hyphen_values_set() { + next_state = parse_opt(opt, count); + continue; + } + } + let (_, takes_value_opt, mut short) = parse_shortflags(current_cmd, short); if let Some(opt) = takes_value_opt { if short.next_value_os().is_none() { next_state = ParseState::Opt((opt, 1)); } + } else if pos_allows_hyphen(current_cmd, pos_index) { + (next_state, pos_index) = + parse_positional(current_cmd, pos_index, is_escaped, current_state); } } else { match current_state { @@ -88,14 +109,7 @@ pub fn complete( (next_state, pos_index) = parse_positional(current_cmd, pos_index, is_escaped, current_state); } - - ParseState::Opt((opt, count)) => { - let range = opt.get_num_args().expect("built"); - let max = range.max_values(); - if count < max { - next_state = ParseState::Opt((opt, count + 1)); - } - } + ParseState::Opt((opt, count)) => next_state = parse_opt(opt, count), } } } @@ -546,3 +560,21 @@ fn parse_positional<'a>( ), } } + +/// Parse optional flag argument. Return new state +fn parse_opt(opt: &clap::Arg, count: usize) -> ParseState<'_> { + let range = opt.get_num_args().expect("built"); + let max = range.max_values(); + if count < max { + ParseState::Opt((opt, count + 1)) + } else { + ParseState::ValueDone + } +} + +fn pos_allows_hyphen(cmd: &clap::Command, pos_index: usize) -> bool { + cmd.get_positionals() + .find(|a| a.get_index() == Some(pos_index)) + .map(|p| p.is_allow_hyphen_values_set()) + .unwrap_or(false) +} diff --git a/clap_complete/tests/testsuite/engine.rs b/clap_complete/tests/testsuite/engine.rs index d4377ebb370..923335b10dd 100644 --- a/clap_complete/tests/testsuite/engine.rs +++ b/clap_complete/tests/testsuite/engine.rs @@ -1018,10 +1018,13 @@ fn suggest_allow_hyhpen() { assert_data_eq!( complete!(cmd, "--format --json --j[TAB]"), - snapbox::str![""] + snapbox::str!["--json"] ); - assert_data_eq!(complete!(cmd, "-F --json --j[TAB]"), snapbox::str![""]); + assert_data_eq!( + complete!(cmd, "-F --json --j[TAB]"), + snapbox::str!["--json"] + ); } #[test] @@ -1062,7 +1065,7 @@ fn suggest_positional_long_allow_hyhpen() { --help Print help -F -h Print help ---pos_a" +pos_b" ] ); assert_data_eq!( @@ -1072,17 +1075,17 @@ fn suggest_positional_long_allow_hyhpen() { --help Print help -F -h Print help ---pos_a" +pos_b" ] ); assert_data_eq!( complete!(cmd, "--format --json --pos_a p[TAB]"), - snapbox::str![""] + snapbox::str!["pos_b"] ); assert_data_eq!( complete!(cmd, "-F --json --pos_a p[TAB]"), - snapbox::str![""] + snapbox::str!["pos_b"] ); } @@ -1115,7 +1118,7 @@ fn suggest_positional_short_allow_hyhpen() { --help Print help -F -h Print help --a" +pos_b" ] ); assert_data_eq!( @@ -1125,15 +1128,18 @@ fn suggest_positional_short_allow_hyhpen() { --help Print help -F -h Print help --a" +pos_b" ] ); assert_data_eq!( complete!(cmd, "--format --json -a p[TAB]"), - snapbox::str![""] + snapbox::str!["pos_b"] + ); + assert_data_eq!( + complete!(cmd, "-F --json -a p[TAB]"), + snapbox::str!["pos_b"] ); - assert_data_eq!(complete!(cmd, "-F --json -a p[TAB]"), snapbox::str![""]); } fn complete(cmd: &mut Command, args: impl AsRef, current_dir: Option<&Path>) -> String { From fbec05e639f82635bd64fc7bd20d18796cd4b598 Mon Sep 17 00:00:00 2001 From: mart-mihkel Date: Tue, 3 Sep 2024 11:41:09 +0300 Subject: [PATCH 27/30] refactor(complete): Fix typo in hyhpen --- clap_complete/tests/testsuite/engine.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clap_complete/tests/testsuite/engine.rs b/clap_complete/tests/testsuite/engine.rs index 923335b10dd..d802bb148cf 100644 --- a/clap_complete/tests/testsuite/engine.rs +++ b/clap_complete/tests/testsuite/engine.rs @@ -982,7 +982,7 @@ a_pos,c_pos" } #[test] -fn suggest_allow_hyhpen() { +fn suggest_allow_hyphen() { let mut cmd = Command::new("exhaustive") .arg( clap::Arg::new("format") @@ -1028,7 +1028,7 @@ fn suggest_allow_hyhpen() { } #[test] -fn suggest_positional_long_allow_hyhpen() { +fn suggest_positional_long_allow_hyphen() { let mut cmd = Command::new("exhaustive") .arg( clap::Arg::new("format") @@ -1090,7 +1090,7 @@ pos_b" } #[test] -fn suggest_positional_short_allow_hyhpen() { +fn suggest_positional_short_allow_hyphen() { let mut cmd = Command::new("exhaustive") .arg( clap::Arg::new("format") From 57b6cb8e4781216e7c01397d2bd54b97028a5520 Mon Sep 17 00:00:00 2001 From: mart-mihkel Date: Tue, 3 Sep 2024 11:42:55 +0300 Subject: [PATCH 28/30] refactor(complete): Simplify engine::complete --- clap_complete/src/engine/complete.rs | 34 +++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/clap_complete/src/engine/complete.rs b/clap_complete/src/engine/complete.rs index c78b3d62e8e..75dad1b2b14 100644 --- a/clap_complete/src/engine/complete.rs +++ b/clap_complete/src/engine/complete.rs @@ -58,14 +58,12 @@ pub fn complete( parse_positional(current_cmd, pos_index, is_escaped, current_state); } else if arg.is_escape() { is_escaped = true; - } else if let Some((flag, value)) = arg.to_long() { - if let ParseState::Opt((opt, count)) = current_state { - if opt.is_allow_hyphen_values_set() { - next_state = parse_opt(opt, count); - continue; - } + } else if opt_allows_hyphen(¤t_state, &arg) { + match current_state { + ParseState::Opt((opt, count)) => next_state = parse_opt_value(opt, count), + _ => unreachable!("else branch is only reachable in Opt state"), } - + } else if let Some((flag, value)) = arg.to_long() { if let Ok(flag) = flag { let opt = current_cmd.get_arguments().find(|a| { let longs = a.get_long_and_visible_aliases(); @@ -87,13 +85,6 @@ pub fn complete( } } } else if let Some(short) = arg.to_short() { - if let ParseState::Opt((opt, count)) = current_state { - if opt.is_allow_hyphen_values_set() { - next_state = parse_opt(opt, count); - continue; - } - } - let (_, takes_value_opt, mut short) = parse_shortflags(current_cmd, short); if let Some(opt) = takes_value_opt { if short.next_value_os().is_none() { @@ -109,7 +100,7 @@ pub fn complete( (next_state, pos_index) = parse_positional(current_cmd, pos_index, is_escaped, current_state); } - ParseState::Opt((opt, count)) => next_state = parse_opt(opt, count), + ParseState::Opt((opt, count)) => next_state = parse_opt_value(opt, count), } } } @@ -562,7 +553,7 @@ fn parse_positional<'a>( } /// Parse optional flag argument. Return new state -fn parse_opt(opt: &clap::Arg, count: usize) -> ParseState<'_> { +fn parse_opt_value(opt: &clap::Arg, count: usize) -> ParseState<'_> { let range = opt.get_num_args().expect("built"); let max = range.max_values(); if count < max { @@ -578,3 +569,14 @@ fn pos_allows_hyphen(cmd: &clap::Command, pos_index: usize) -> bool { .map(|p| p.is_allow_hyphen_values_set()) .unwrap_or(false) } + +fn opt_allows_hyphen(state: &ParseState<'_>, arg: &clap_lex::ParsedArg<'_>) -> bool { + let val = arg.to_value_os(); + if val.starts_with("-") { + if let ParseState::Opt((opt, _)) = state { + return opt.is_allow_hyphen_values_set(); + } + } + + false +} From d48bef65af6930c26386e63d85cbdeb2d21bd359 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Sep 2024 09:17:58 -0500 Subject: [PATCH 29/30] docs: Update changelog --- clap_complete/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index 64d9a6fd37e..def3c55d893 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +### Features + +- *(dynamic)* `allow_hyphen_values` support + ## [4.5.25] - 2024-09-04 ### Compatibility From df1efca03509f736e26d2f16766b7ea06acc3ecf Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Sep 2024 09:18:05 -0500 Subject: [PATCH 30/30] chore: Release --- Cargo.lock | 2 +- clap_complete/CHANGELOG.md | 5 ++++- clap_complete/Cargo.toml | 2 +- clap_complete/README.md | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbc09e0b463..86b22c8bacd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,7 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.25" +version = "4.5.26" dependencies = [ "automod", "clap 4.5.17", diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index def3c55d893..3406453c74d 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.5.26] - 2024-09-05 + ### Features - *(dynamic)* `allow_hyphen_values` support @@ -412,7 +414,8 @@ MSRV changed to 1.64.0 ## [3.0.1] - 2022-01-03 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.25...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.26...HEAD +[4.5.26]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.25...clap_complete-v4.5.26 [4.5.25]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.24...clap_complete-v4.5.25 [4.5.24]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.23...clap_complete-v4.5.24 [4.5.23]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.22...clap_complete-v4.5.23 diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 6f3eecc1d8b..5f7d1b1d9b9 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete" -version = "4.5.25" +version = "4.5.26" description = "Generate shell completion scripts for your clap::Command" categories = ["command-line-interface"] keywords = [ diff --git a/clap_complete/README.md b/clap_complete/README.md index fb256da12db..58fa8969f07 100644 --- a/clap_complete/README.md +++ b/clap_complete/README.md @@ -5,16 +5,16 @@ [![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) [![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.26/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.26/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_complete) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/clap_complete/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.25/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.26/clap_complete/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.26/README.md#sponsors) ## About