command_palette/command_palette.rs
1//! Command palette with front-prefix completion.
2//!
3//! This demo shows [`CommandIndex`] in the role it is designed for: a caller
4//! types a partial command name, the index enumerates matching candidates in
5//! lexicographic order, and the caller dispatches the chosen (or the exact)
6//! command.
7//!
8//! **What [`CommandIndex`] owns:**
9//! - Binding name → action (`bind`).
10//! - Exact lookup (`get`).
11//! - Prefix enumeration in lexicographic order (`complete`).
12//! - Full listing for a palette menu (`iter`).
13//!
14//! **What stays with the caller:**
15//! - Case normalization and trimming — the index stores names byte-for-byte.
16//! - The line-editing buffer that accumulates what the user types.
17//! - Selecting among multiple completions and actual dispatch.
18//!
19//! For the *execution* shape of an ex-command (`:` key → line buffer → Enter →
20//! `FnMut(&str) -> Option<A>`) see `examples/ex_command.rs`.
21//!
22//! Run with: `cargo run -p keymap-core --example command_palette`
23
24use keymap_core::cmd::CommandIndex;
25
26#[derive(Debug, PartialEq)]
27enum Action {
28 Write,
29 WriteQuit,
30 WriteQuitAll,
31 Quit,
32 QuitAll,
33}
34
35fn main() {
36 let mut idx = CommandIndex::new();
37 idx.bind("write", Action::Write);
38 idx.bind("write-quit", Action::WriteQuit);
39 idx.bind("write-quit-all", Action::WriteQuitAll);
40 idx.bind("quit", Action::Quit);
41 idx.bind("quit-all", Action::QuitAll);
42
43 // --- Full palette listing (e.g. user opens the palette with no text yet) ---
44 println!("All commands:");
45 for (name, action) in idx.iter() {
46 println!(" {name:20} -> {action:?}");
47 }
48
49 // --- Prefix completion: user has typed "w" ---
50 println!("\nCompletions for \"w\":");
51 for (name, action) in idx.complete("w") {
52 println!(" {name:20} -> {action:?}");
53 }
54
55 // --- Prefix completion: user has typed "write-q" ---
56 println!("\nCompletions for \"write-q\":");
57 for (name, action) in idx.complete("write-q") {
58 println!(" {name:20} -> {action:?}");
59 }
60
61 // --- Exact lookup after the user presses Enter on "write" ---
62 println!("\nExact lookup:");
63 match idx.get("write") {
64 Some(action) => println!(" \"write\" -> {action:?}"),
65 None => println!(" \"write\" -> unknown"),
66 }
67
68 // --- Case normalization is the caller's responsibility ---
69 //
70 // The index stores names byte-for-byte. If the user typed "Write" (capital
71 // W), the caller normalizes to lowercase before calling get/complete:
72 let user_input = "Write";
73 let normalized = user_input.to_lowercase();
74 println!("\nUser typed {user_input:?}, normalized to {normalized:?}:");
75 match idx.get(&normalized) {
76 Some(action) => println!(" -> {action:?}"),
77 None => println!(" -> unknown"),
78 }
79
80 // --- Non-exclusive: ":w" can be both exact and a prefix ---
81 //
82 // In a vim-style command line `:w` executes "write" and `:wq` executes
83 // "write-quit". Both can coexist because get and complete are orthogonal:
84 // get(":w") is exact, complete(":w") enumerates ":w" and ":wq" and ":wqa".
85 let mut vim = CommandIndex::new();
86 vim.bind(":w", "write");
87 vim.bind(":wq", "write-quit");
88 vim.bind(":wqa", "write-quit-all");
89
90 println!("\nVim-style coexistence:");
91 println!(" get(\":w\") = {:?}", vim.get(":w"));
92 let completions: Vec<&str> = vim.complete(":w").map(|(n, _)| n).collect();
93 println!(" complete(\":w\") = {completions:?}");
94}