aboutsummaryrefslogtreecommitdiff
path: root/examples/example-completion/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/example-completion/src/main.rs')
-rw-r--r--examples/example-completion/src/main.rs205
1 files changed, 99 insertions, 106 deletions
diff --git a/examples/example-completion/src/main.rs b/examples/example-completion/src/main.rs
index 31528d1..7ca23b9 100644
--- a/examples/example-completion/src/main.rs
+++ b/examples/example-completion/src/main.rs
@@ -1,136 +1,129 @@
-//! `Mingling` Example - Completion
+//! Example Completion
//!
-//! # How to Deploy
-//! 1. Enable the `comp` feature
-//! ```toml
-//! [dependencies]
-//! mingling = { version = "...", features = [
-//! "comp", // Enable this feature
-//! "parser"
-//! ] }
-//! ```
+//! > This example demonstrates how to use **Mingling** to create fully dynamic command-line completions
//!
-//! 2. Add `mingling` as a build dependency, enabling the `builds` and `comp` features
-//! ```toml
-//! [build-dependencies]
-//! mingling = { version = "...", features = [
-//! "builds", // Enable this feature for build scripts
-//! "comp"
-//! ] }
-//! ```
+//! ## About Completion Scripts
+//!
+//! To make your completions work, you need to generate a completion script using Mingling's tools
+//!
+//! 1. Enable features
+//! You need to enable the `builds` and `comp` features for `mingling` in `[build-dependencies]`
+//!
+//! 2. Write `build.rs`
+//! Write the following in `build.rs`
//!
-//! 3. Write `build.rs` to generate completion scripts at compile time
-//! ```ignore
-//! use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name};
+//! ```rust,ignore
//! fn main() {
-//! // Generate completion scripts for the current program, using the Cargo package name as the binary filename
-//! build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap();
+//! build_scripts();
+//! }
//!
-//! // Or, explicitly specify the binary filename
-//! // build_comp_scripts("your_bin").unwrap();
+//! /// Generate completion scripts
+//! fn build_scripts() {
+//! // `env!("CARGO_PKG_NAME")` equals the crate name, which matches the binary name.
+//! // If your binary name differs from the crate name, specify it explicitly.
+//! mingling::build::build_comp_scripts(
+//! // Your binary name:
+//! env!("CARGO_PKG_NAME"),
+//! )
+//! .unwrap();
//! }
//! ```
//!
-//! 4. Write `main.rs`, adding completion logic for your command entry point
-//! 5. Execute `cargo install --path ./`, then run the corresponding completion script in your shell
-
-use mingling::prelude::*;
-use mingling::{
- macros::{suggest, suggest_enum},
- parser::{PickableEnum, Picker},
- EnumTag, Groupped, ShellContext, Suggest,
-};
-
-// Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry`
-dispatcher!("fruit", FruitCommand => FruitEntry);
+//! 3. Verify
+//! Build your project with `cargo build --release`. The completion scripts will be generated in `target/release/`
+//!
+//! 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!
+//! ```
-#[completion(FruitEntry)]
-fn comp_fruit_command(ctx: &ShellContext) -> Suggest {
- if ctx.filling_argument_first("--name") {
- return suggest!();
- }
- if ctx.filling_argument_first("--type") {
- return suggest_enum!(FruitType);
- }
- if ctx.typing_argument() {
- return suggest! {
- "--name": "Fruit name",
- "--type": "Fruit type"
- }
- .strip_typed_argument(ctx);
- }
- return suggest!();
-}
+use mingling::{macros::suggest, prelude::*, ShellContext, Suggest};
fn main() {
let mut program = ThisProgram::new();
- program.with_dispatcher(CompletionDispatcher);
- program.with_dispatcher(FruitCommand);
- program.exec();
-}
-#[derive(Groupped)]
-struct FruitInfo {
- name: String,
- fruit_type: FruitType,
-}
+ program.with_dispatcher(CMDGreet);
-#[derive(Default, Debug, EnumTag)]
-enum FruitType {
- #[enum_desc("It's Apple")]
- #[enum_rename("apple")]
- FruitApple,
+ // --------- IMPORTANT ---------
+ // The `comp` feature makes `gen_program!()` generate a CompletionDispatcher automatically
+ // It adds a hidden `__comp` subcommand for communication with the completion script
+ program.with_dispatcher(crate::CompletionDispatcher);
+ // --------- IMPORTANT ---------
- #[enum_desc("It's Banana")]
- #[enum_rename("banana")]
- FruitBanana,
+ // TIP: Note that the completion script reads stdout,
+ // so make sure no output is produced before the CompletionDispatcher is dispatched.
+ program.exec_and_exit();
+}
- #[enum_desc("It's Cherry")]
- #[enum_rename("cherry")]
- FruitCherry,
+// --------- 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"
+ };
+ }
- #[enum_desc("It's Date")]
- #[enum_rename("date")]
- FruitDate,
+ // When the user is typing `--repeat`
+ if ctx.filling_argument(["-r", "--repeat"]) {
+ return suggest! {}; // Don't suggest anything
+ }
- #[enum_desc("It's Elderberry")]
- #[enum_rename("elderberry")]
- FruitElderberry,
+ // When the user is typing `-`
+ if ctx.typing_argument() {
+ return suggest! {
+ "-r": "Number of repetitions",
+ "--repeat": "Number of repetitions",
+ }
+ // Remove arguments that have already been typed by the user
+ .strip_typed_argument(ctx);
+ }
- #[default]
- #[enum_rename("unknown")]
- Unknown,
+ // 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 ---------
-impl PickableEnum for FruitType {}
+dispatcher!("greet", CMDGreet => EntryGreet);
+pack!(ResultName = (u8, String));
#[chain]
-fn parse_fruit_info(prev: FruitEntry) -> Next {
- let picker = Picker::from(prev.inner);
- let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack();
- let info = FruitInfo {
- name: fruit_name,
- fruit_type,
- };
- info.to_render()
+fn handle_greet(args: EntryGreet) -> Next {
+ let result: ResultName = args
+ .pick(["-r", "--repeat"])
+ .pick_or((), "World")
+ .unpack()
+ .into();
+ result
}
#[renderer]
-fn render_fruit(prev: FruitInfo) {
- match (prev.name.is_empty(), prev.fruit_type) {
- (true, FruitType::Unknown) => {
- r_println!("Fruit name is empty and type is unknown");
- }
- (true, fruit_type) => {
- r_println!("Fruit name is empty, Type: {:?}", fruit_type);
- }
- (false, FruitType::Unknown) => {
- r_println!("Fruit name: {}, Type is unknown", prev.name);
- }
- (false, fruit_type) => {
- r_println!("Fruit name: {}, Type: {:?}", prev.name, fruit_type);
- }
+fn render_name(result: ResultName) {
+ let (repeat, name) = result.inner;
+ let mut parts = Vec::with_capacity(repeat as usize);
+ for _ in 0..repeat {
+ parts.push(name.clone());
}
+ r_println!("Hello, {}!", parts.join(", "));
}
gen_program!();