1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
//! 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!();
|