//! Example Completion //! //! > This example demonstrates how to use **Mingling** to create fully dynamic command-line completions //! //! ## About Completion Scripts //! //! To make your completions work, you need to generate a completion script using Mingling's tools //! //! 1. Enable features //! Enable the `comp` feature for `mingling` in `[dependencies]` //! //! 2. Generate completion scripts //! When the `comp` feature is enabled, `gen_program!()` automatically invokes //! `build_comp!()` at compile time, which generates the completion scripts //! (named after `CARGO_PKG_NAME`) into `target/mingling/`. //! //! 3. Verify //! Build your project with `cargo build`. The completion scripts will be generated in `target/mingling/` //! //! Execute the script or have it be automatically sourced by your Shell //! //! Run: //! ```bash //! cargo run --manifest-path examples/example-completion/Cargo.toml --quiet -- greet Alice --repeat 3 //! ``` //! //! Output: //! ```plaintext //! Hello, Alice, Alice, Alice! //! ``` use mingling::{ShellContext, Suggest, macros::suggest, prelude::*}; use std::io::Write; fn main() { let program = ThisProgram::new(); // TIP: Note that the completion script reads stdout, // so make sure no output is produced before the CMDCompletion is dispatched. program.exec_and_exit(); } // --------- IMPORTANT --------- // _________________________________________ Entry point bound to completion behavior // / _________________________ Shell context for obtaining user input state // | / ________ Suggest, used to return completion results // vvvvvvvvvv | / #[completion(EntryGreet)] // vvvvvvvvvvvv vvvvvvv fn complete_greet_entry(ctx: ShellContext) -> Suggest { // When the previous word is `greet` (the current command being typed) if ctx.previous_word == "greet" { // Return suggestions return suggest! { "Bob": "Likes to pass messages", "Alice": "Likes to receive messages", "Hacker": "YOU", "World" }; } // When the user is typing `--repeat` if ctx.previous_word == "-r" || ctx.previous_word == "--repeat" { return suggest! {}; // Don't suggest anything } // When the user is typing `-` if ctx.current_word.starts_with('-') { // Remove arguments that have already been typed by the user let typed: Vec<&str> = ctx.all_words.iter().map(String::as_str).collect(); let mut set = suggest! { "-r": "Number of repetitions", "--repeat": "Number of repetitions", }; if let Suggest::Suggest(items) = &mut set { items.retain(|item| !typed.contains(&item.suggest().as_str())); } return set; } // Otherwise, suggest nothing suggest!() // // You can also enable file completions using the following code, // // which will invoke the Shell's default behavior // Suggest::file_comp() } // --------- IMPORTANT --------- dispatcher!("greet", EntryGreet); #[derive(Grouped, Wrap)] pub struct ResultName((u8, String)); #[chain] fn handle_greet(args: EntryGreet) -> Next { let result: ResultName = args .pick_or(&arg![repeat: u8, 'r'], || 1) .pick_or(&arg![String], || "World".to_string()) .unwrap() .into(); result.into() } /// Renders the greeting with the result name and repeat count. #[renderer] fn render_name(result: ResultName) -> RenderResult { let (repeat, name) = result.0; let mut render_result = RenderResult::new(); let mut parts = Vec::with_capacity(repeat as usize); for _ in 0..repeat { parts.push(name.clone()); } writeln!(render_result, "Hello, {}!", parts.join(", ")).ok(); render_result } gen_program!();