diff options
| -rw-r--r-- | CHANGELOG.md | 7 | ||||
| -rw-r--r-- | mingling_core/src/comp/suggest.rs | 36 | ||||
| -rw-r--r-- | mingling_macros/src/func/suggest.rs | 43 |
3 files changed, 74 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d46f4d..4780d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,13 @@ None _No behavioral change for downstream code — all public items are re-exported with the same names. The `__this_program_impl` module is `#[doc(hidden)]` and not part of the public API._ +3. **[`core:comp`]** Added `add_suggest()` and `add_suggest_with_description()` methods to `Suggest` for batch-adding suggestion items: + + - **`add_suggest(&mut self, items: impl Into<Vec<String>>)`** — Wraps each item in `SuggestItem::Simple` and inserts it into the underlying `BTreeSet`. + - **`add_suggest_with_description(&mut self, items: impl Into<Vec<String>>, desc: impl Into<String>)`** — Wraps each item in `SuggestItem::WithDescription` using the provided description and inserts it into the set. + + These methods enable ergonomic batch population of suggestion sets from collections of strings, complementing the existing `insert()` method. + #### Features: 1. **[`picker:value:paths`]** Added new path wrapper types to `arg_picker::value` for filesystem-aware argument parsing: diff --git a/mingling_core/src/comp/suggest.rs b/mingling_core/src/comp/suggest.rs index 88fa5fc..dda5026 100644 --- a/mingling_core/src/comp/suggest.rs +++ b/mingling_core/src/comp/suggest.rs @@ -56,6 +56,42 @@ impl Suggest { (suggest, _) => suggest, } } + + /// Adds multiple simple suggestions (without descriptions) to the `Suggest` set. + /// + /// Each item produced by the iterator is wrapped in a [`SuggestItem::Simple`] + /// variant and inserted into the underlying `BTreeSet`. + /// + /// # Arguments + /// + /// * `items` — A collection of suggestion strings to add. + pub fn add_suggest(&mut self, items: impl Into<Vec<String>>) { + for item in items.into() { + self.insert(SuggestItem::Simple(item)); + } + } + + /// Adds multiple suggestions with a shared description to the `Suggest` set. + /// + /// Each item produced by the iterator is wrapped in a + /// [`SuggestItem::WithDescription`] variant using the provided description, + /// and inserted into the underlying `BTreeSet`. + /// + /// # Arguments + /// + /// * `items` — A collection of suggestion strings to add. + /// * `desc` — The description to attach to each suggestion. Must implement + /// `Into<String>`. + pub fn add_suggest_with_description( + &mut self, + items: impl Into<Vec<String>>, + desc: impl Into<String>, + ) { + let desc_str = desc.into(); + for item in items.into() { + self.insert(SuggestItem::WithDescription(item, desc_str.clone())); + } + } } impl<T> From<T> for Suggest diff --git a/mingling_macros/src/func/suggest.rs b/mingling_macros/src/func/suggest.rs index 85f1afe..6613a98 100644 --- a/mingling_macros/src/func/suggest.rs +++ b/mingling_macros/src/func/suggest.rs @@ -2,15 +2,15 @@ use proc_macro::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{Expr, LitStr, Token, parse_macro_input}; +use syn::{Expr, Token, parse_macro_input}; struct SuggestInput { items: Punctuated<SuggestItem, Token![,]>, } enum SuggestItem { - WithDesc(Box<(LitStr, Expr)>), // "-i" = "Insert something" - Simple(LitStr), // "-I" + WithDesc(Box<(Expr, Expr)>), // "-i" = "Insert something" + Simple(Expr), // "-I" } impl Parse for SuggestInput { @@ -22,7 +22,7 @@ impl Parse for SuggestInput { impl Parse for SuggestItem { fn parse(input: ParseStream) -> syn::Result<Self> { - let key: LitStr = input.parse()?; + let key: Expr = input.parse()?; if input.peek(Token![:]) { let _colon: Token![:] = input.parse()?; @@ -34,36 +34,55 @@ impl Parse for SuggestItem { } } +/// 判断表达式是否是一个纯字符串字面量(仅由一对引号包裹) +fn is_pure_lit_str(expr: &Expr) -> bool { + matches!(expr, Expr::Lit(lit) if matches!(lit.lit, syn::Lit::Str(_))) +} + #[cfg(feature = "comp")] pub(crate) fn suggest(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as SuggestInput); let mut items = Vec::new(); + let mut simple_items = Vec::new(); for item in input.items { match item { SuggestItem::WithDesc(boxed) => { let (key, value) = *boxed; - items.push(quote! { - ::mingling::SuggestItem::new_with_desc(#key.to_string(), #value.to_string()) - }); + if is_pure_lit_str(&key) { + items.push(quote! { + vec![#key .to_string()], #value + }); + } else { + items.push(quote! { + #key, #value + }); + } } SuggestItem::Simple(key) => { - items.push(quote! { - ::mingling::SuggestItem::new(#key.to_string()) - }); + if is_pure_lit_str(&key) { + simple_items.push(quote! { + vec![#key .to_string()] + }); + } else { + simple_items.push(quote! { + #key + }); + } } } } - let expanded = if items.is_empty() { + let expanded = if items.is_empty() && simple_items.is_empty() { quote! { ::mingling::Suggest::new() } } else { quote! {{ let mut suggest = ::mingling::Suggest::new(); - #(suggest.insert(#items);)* + #(suggest.add_suggest_with_description(#items);)* + #(suggest.add_suggest(#simple_items);)* suggest }} }; |
