diff options
Diffstat (limited to 'mingling_core/src/asset/comp.rs')
| -rw-r--r-- | mingling_core/src/asset/comp.rs | 338 |
1 files changed, 0 insertions, 338 deletions
diff --git a/mingling_core/src/asset/comp.rs b/mingling_core/src/asset/comp.rs deleted file mode 100644 index 4fb17c7..0000000 --- a/mingling_core/src/asset/comp.rs +++ /dev/null @@ -1,338 +0,0 @@ -mod flags; -mod shell_ctx; -mod suggest; - -use std::collections::BTreeSet; -use std::fmt::Display; - -#[doc(hidden)] -pub use flags::*; -#[doc(hidden)] -pub use shell_ctx::*; -#[doc(hidden)] -pub use suggest::*; - -use crate::{ProgramCollect, debug, only_debug, this, trace}; - -#[cfg(not(feature = "dispatch_tree"))] -use crate::exec::match_user_input; - -/// Trait for implementing completion logic. -/// -/// This trait defines the interface for generating command-line completions. -/// Types implementing this trait can provide custom completion suggestions -/// based on the current shell context. -pub trait Completion { - type Previous; - fn comp(ctx: &ShellContext) -> Suggest; -} - -/// Trait for extracting user input arguments for completion. -/// -/// When the `feat comp` feature is enabled, the `dispatcher!` macro will -/// automatically implement this trait for `Entry` types to extract the -/// arguments from user input for completion suggestions. -pub trait CompletionEntry { - fn get_input(self) -> Vec<String>; -} - -/// A helper struct for handling command-line completion logic. -/// -/// This struct provides static methods for executing completions based on -/// the current shell context and rendering the resulting suggestions in a -/// format appropriate for the target shell. -pub struct CompletionHelper; -impl CompletionHelper { - pub fn exec_completion<P>(ctx: &ShellContext) -> Suggest - where - P: ProgramCollect<Enum = P> + Display + PartialEq + 'static, - { - only_debug! { - crate::debug::init_env_logger(); - trace_ctx(ctx); - }; - - let args = ctx.all_words.iter().skip(1).cloned().collect::<Vec<_>>(); - trace!("arguments=\"{}\"", args.join(", ")); - - #[cfg(not(feature = "dispatch_tree"))] - let program = this::<P>(); - - #[cfg(not(feature = "dispatch_tree"))] - let suggest = if let Ok((dispatcher, args)) = match_user_input(program, &args) { - trace!( - "dispatcher matched, dispatcher=\"{}\"", - dispatcher.node().to_string(), - ); - let begin = dispatcher.begin(args); - if let crate::ChainProcess::Ok((any, _)) = begin { - trace!("entry type: {}", any.member_id); - let result = P::do_comp(&any, ctx); - trace!("do_comp result: {:?}", result); - Some(result) - } else { - trace!("begin not Ok"); - None - } - } else { - trace!("no dispatcher matched"); - None - }; - #[cfg(feature = "dispatch_tree")] - let suggest = if let Ok(any) = P::dispatch_args_trie(&args) { - trace!("entry type: {}", any.member_id); - - let dispatcher_not_found = <P::DispatcherNotFound as crate::Groupped<P>>::member_id(); - - if dispatcher_not_found == any.member_id { - trace!("begin not Ok"); - None - } else { - let result = P::do_comp(&any, ctx); - trace!("do_comp result: {:?}", result); - Some(result) - } - } else { - trace!("no dispatcher matched"); - None - }; - - match suggest { - Some(suggest) => { - trace!("using custom completion: {:?}", suggest); - suggest - } - None => { - trace!("using default completion"); - default_completion::<P>(ctx) - } - } - } - - pub fn render_suggest<P>(ctx: ShellContext, suggest: Suggest) - where - P: ProgramCollect<Enum = P> + Display + 'static, - { - trace!("render_suggest called with: {:?}", suggest); - match suggest { - Suggest::FileCompletion => { - trace!("rendering file completion"); - println!("_file_"); - std::process::exit(0); - } - Suggest::Suggest(suggestions) => { - trace!("rendering {} suggestions", suggestions.len()); - match ctx.shell_flag { - ShellFlag::Zsh | ShellFlag::Powershell => { - trace!("using zsh/pwsh format"); - print_suggest_with_description(suggestions) - } - ShellFlag::Fish => { - trace!("using fish format"); - print_suggest_with_description_fish(suggestions) - } - _ => { - trace!("using default format"); - print_suggest(suggestions) - } - } - } - } - } -} - -fn default_completion<P>(ctx: &ShellContext) -> Suggest -where - P: ProgramCollect<Enum = P> + Display + 'static, -{ - let cmd_nodes: Vec<String> = this::<P>() - .get_nodes() - .into_iter() - .filter(|(s, _)| s != "__comp") - .map(|(s, _)| s) - .collect(); - debug!("cmd_nodes: {:?}", cmd_nodes); - - // If the current position is less than 1, do not perform completion - if ctx.word_index < 1 { - debug!("word_index < 1, returning file suggestions"); - return file_suggest(); - }; - - // Get the current input path - debug!( - "input_path before filter: {:?}", - &ctx.all_words.get(1..ctx.word_index).unwrap_or(&[]) - ); - - let input_path: Vec<&str> = ctx - .all_words - .get(1..ctx.word_index) - .unwrap_or(&[]) - .iter() - .filter(|s| !s.is_empty()) - .map(|s| s.as_str()) - .collect(); - debug!("input_path after filter: {:?}", input_path); - - debug!( - "default_completion: input_path = {:?}, word_index = {}, all_words = {:?}", - input_path, ctx.word_index, ctx.all_words - ); - - // Filter command nodes that match the input path - let mut suggestions = Vec::new(); - - // Special case: if input_path is empty, return all first-level commands - if input_path.is_empty() { - for node in cmd_nodes { - let node_parts: Vec<&str> = node.split(' ').collect(); - if !node_parts.is_empty() && !suggestions.contains(&node_parts[0].to_string()) { - suggestions.push(node_parts[0].to_string()); - } - } - } else { - // Get the current word - let current_word = input_path.last().unwrap(); - - // First, handle partial match completion for the current word - // Only perform current word completion when current_word is not empty - if input_path.len() == 1 && !ctx.current_word.is_empty() { - for node in &cmd_nodes { - let node_parts: Vec<&str> = node.split(' ').collect(); - if !node_parts.is_empty() - && node_parts[0].starts_with(current_word) - && !suggestions.contains(&node_parts[0].to_string()) - { - suggestions.push(node_parts[0].to_string()); - } - } - - // If suggestions for the current word are found, return directly - if !suggestions.is_empty() { - suggestions.sort(); - suggestions.dedup(); - debug!( - "default_completion: current word suggestions = {:?}", - suggestions - ); - return suggestions.into(); - } - } - - // Handle next-level command suggestions - for node in cmd_nodes { - let node_parts: Vec<&str> = node.split(' ').collect(); - - debug!("Checking node: '{}', parts: {:?}", node, node_parts); - - // If input path is longer than node parts, skip - if input_path.len() > node_parts.len() { - continue; - } - - // Check if input path matches the beginning of node parts - let mut matches = true; - for i in 0..input_path.len() { - if i >= node_parts.len() { - matches = false; - break; - } - - if i == input_path.len() - 1 { - if !node_parts[i].starts_with(input_path[i]) { - matches = false; - break; - } - } else if input_path[i] != node_parts[i] { - matches = false; - break; - } - } - - if matches && input_path.len() <= node_parts.len() { - if input_path.len() == node_parts.len() && !ctx.current_word.is_empty() { - suggestions.push(node_parts[input_path.len() - 1].to_string()); - } else if input_path.len() < node_parts.len() { - suggestions.push(node_parts[input_path.len()].to_string()); - } - } - } - } - - // Remove duplicates and sort - suggestions.sort(); - suggestions.dedup(); - - debug!("default_completion: suggestions = {:?}", suggestions); - - if suggestions.is_empty() { - file_suggest() - } else { - suggestions.into() - } -} - -fn file_suggest() -> Suggest { - trace!("file_suggest called"); - Suggest::FileCompletion -} - -fn print_suggest(suggestions: BTreeSet<SuggestItem>) { - trace!("print_suggest called with {} items", suggestions.len()); - let mut sorted_suggestions: Vec<SuggestItem> = suggestions.into_iter().collect(); - sorted_suggestions.sort(); - - for suggest in sorted_suggestions { - println!("{}", suggest.suggest()); - } - std::process::exit(0); -} - -fn print_suggest_with_description(suggestions: BTreeSet<SuggestItem>) { - trace!( - "print_suggest_with_description called with {} items", - suggestions.len() - ); - let mut sorted_suggestions: Vec<SuggestItem> = suggestions.into_iter().collect(); - sorted_suggestions.sort(); - - for suggest in sorted_suggestions { - match suggest.description() { - Some(desc) => println!("{}$({})", suggest.suggest(), desc), - None => println!("{}", suggest.suggest()), - } - } - std::process::exit(0); -} - -fn print_suggest_with_description_fish(suggestions: BTreeSet<SuggestItem>) { - trace!( - "print_suggest_with_description_fish called with {} items", - suggestions.len() - ); - let mut sorted_suggestions: Vec<SuggestItem> = suggestions.into_iter().collect(); - sorted_suggestions.sort(); - - for suggest in sorted_suggestions { - match suggest.description() { - Some(desc) => println!("{}\t{}", suggest.suggest(), desc), - None => println!("{}", suggest.suggest()), - } - } - std::process::exit(0); -} - -#[cfg(feature = "debug")] -fn trace_ctx(ctx: &ShellContext) { - trace!("=== SHELL CTX BEGIN ==="); - trace!("command_line={}", ctx.command_line); - trace!("cursor_position={}", ctx.cursor_position); - trace!("current_word={}", ctx.current_word); - trace!("previous_word={}", ctx.previous_word); - trace!("command_name={}", ctx.command_name); - trace!("word_index={}", ctx.word_index); - trace!("all_words={:?}", ctx.all_words); - trace!("shell_flag={:?}", ctx.shell_flag); - trace!("=== SHELL CTX END ==="); -} |
