aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/completion.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-04-10 16:47:40 +0800
committer魏曹先生 <1992414357@qq.com>2026-04-10 16:47:40 +0800
commitb18749170b6006e53976dbb6df9f59a3b9c34127 (patch)
treea0f9288fdc9082e26daab218167da1f54521d32b /mingling_macros/src/completion.rs
parent3bb5afcbe01ad16293a66084dc1ad35f3378a833 (diff)
Add completion macro infrastructure without logic
Diffstat (limited to 'mingling_macros/src/completion.rs')
-rw-r--r--mingling_macros/src/completion.rs132
1 files changed, 29 insertions, 103 deletions
diff --git a/mingling_macros/src/completion.rs b/mingling_macros/src/completion.rs
index cf66f13..23b509f 100644
--- a/mingling_macros/src/completion.rs
+++ b/mingling_macros/src/completion.rs
@@ -5,91 +5,7 @@
use proc_macro::TokenStream;
use quote::quote;
-use syn::spanned::Spanned;
-use syn::{
- FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input,
-};
-
-/// Extracts the previous type from function arguments
-fn extract_previous_type(sig: &Signature) -> syn::Result<TypePath> {
- // The function should have exactly one parameter: ShellContext
- if sig.inputs.len() != 1 {
- return Err(syn::Error::new(
- sig.inputs.span(),
- "Completion function must have exactly one parameter (ShellContext)",
- ));
- }
-
- let arg = &sig.inputs[0];
- match arg {
- FnArg::Typed(PatType { ty, .. }) => {
- match &**ty {
- Type::Path(type_path) => {
- // Check if it's ShellContext
- let last_segment = type_path.path.segments.last().unwrap();
- if last_segment.ident != "ShellContext" {
- return Err(syn::Error::new(
- ty.span(),
- "Parameter type must be ShellContext",
- ));
- }
- Ok(type_path.clone())
- }
- _ => Err(syn::Error::new(
- ty.span(),
- "Parameter type must be a type path",
- )),
- }
- }
- FnArg::Receiver(_) => Err(syn::Error::new(
- arg.span(),
- "Completion function cannot have self parameter",
- )),
- }
-}
-
-/// Extracts the return type from the function signature
-fn extract_return_type(sig: &Signature) -> syn::Result<TypePath> {
- match &sig.output {
- ReturnType::Type(_, ty) => match &**ty {
- Type::Path(type_path) => {
- // Check if it's Suggest
- let last_segment = type_path.path.segments.last().unwrap();
- if last_segment.ident != "Suggest" {
- return Err(syn::Error::new(ty.span(), "Return type must be Suggest"));
- }
- Ok(type_path.clone())
- }
- _ => Err(syn::Error::new(
- ty.span(),
- "Return type must be a type path",
- )),
- },
- ReturnType::Default => Err(syn::Error::new(
- sig.span(),
- "Completion function must have a return type",
- )),
- }
-}
-
-/// Extracts the parameter name from function arguments
-fn extract_param_name(sig: &Signature) -> syn::Result<Pat> {
- if sig.inputs.len() != 1 {
- return Err(syn::Error::new(
- sig.inputs.span(),
- "Completion function must have exactly one parameter",
- ));
- }
-
- let arg = &sig.inputs[0];
- match arg {
- FnArg::Typed(PatType { pat, .. }) => Ok((**pat).clone()),
- FnArg::Receiver(_) => Err(syn::Error::new(
- arg.span(),
- "Completion function cannot have self parameter",
- )),
- }
-}
+use syn::{Ident, ItemFn, parse_macro_input};
#[cfg(feature = "comp")]
pub fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
@@ -110,25 +26,28 @@ pub fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
// Validate the function is not async
if input_fn.sig.asyncness.is_some() {
+ use syn::spanned::Spanned;
+
return syn::Error::new(input_fn.sig.span(), "Completion function cannot be async")
.to_compile_error()
.into();
}
- // Extract the parameter name
- let param_name = match extract_param_name(&input_fn.sig) {
- Ok(name) => name,
- Err(e) => return e.to_compile_error().into(),
- };
+ // Get the function signature parts
+ let sig = &input_fn.sig;
+ let inputs = &sig.inputs;
+ let output = &sig.output;
- // Extract and validate the parameter type (must be ShellContext)
- if let Err(e) = extract_previous_type(&input_fn.sig) {
- return e.to_compile_error().into();
- }
+ // Check that the function has exactly one parameter
+ if inputs.len() != 1 {
+ use syn::spanned::Spanned;
- // Extract and validate the return type (must be Suggest)
- if let Err(e) = extract_return_type(&input_fn.sig) {
- return e.to_compile_error().into();
+ return syn::Error::new(
+ inputs.span(),
+ "Completion function must have exactly one parameter",
+ )
+ .to_compile_error()
+ .into();
}
// Get the function body
@@ -142,7 +61,7 @@ pub fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
let vis = &input_fn.vis;
// Get function name
- let fn_name = &input_fn.sig.ident;
+ let fn_name = &sig.ident;
// Generate struct name from function name using pascal_case
let pascal_case_name = just_fmt::pascal_case!(fn_name.to_string());
@@ -156,20 +75,27 @@ pub fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
impl ::mingling::Completion for #struct_name {
type Previous = #previous_type_ident;
- fn comp(#param_name: ::mingling::ShellContext) -> ::mingling::Suggest {
- // This is just to prevent warnings about imported ShellContext and Suggest
- let _ = ShellContext::default();
- let _ = Suggest::file_comp();
+ fn comp(#inputs) #output {
#fn_body
}
}
// Keep the original function for internal use
#(#fn_attrs)*
- #vis fn #fn_name(#param_name: ::mingling::ShellContext) -> ::mingling::Suggest {
+ #vis fn #fn_name(#inputs) #output {
#fn_body
}
};
+ let completion_entry = quote! {
+ Self::#previous_type_ident => <#struct_name as ::mingling::Completion>::comp(ctx),
+ };
+
+ let mut completions = crate::COMPLETIONS.lock().unwrap();
+ let completion_str = completion_entry.to_string();
+ if !completions.contains(&completion_str) {
+ completions.push(completion_str);
+ }
+
expanded.into()
}