Skip to main content

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}