feat(ui): implement which-key popup for command completions
Implement `WhichKey` popup to show available command completions. - Added `format_key_label` to convert `KeyCode` to human-readable strings. - Added `binding_hints` to `Keymap` to extract available commands for a given prefix. - Added `src/ui/which_key.rs` for the widget implementation. - Updated `src/ui/mod.rs` to export the new module. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
This commit is contained in:
@ -8,6 +8,29 @@ use crate::ui::app::AppMode;
|
||||
use crate::ui::effect::Effect;
|
||||
|
||||
use super::cmd::{self, CmdContext, CmdRegistry};
|
||||
|
||||
/// Format a KeyCode as a short human-readable label for which-key display.
|
||||
fn format_key_label(code: &KeyCode) -> String {
|
||||
match code {
|
||||
KeyCode::Char(c) => c.to_string(),
|
||||
KeyCode::Enter => "Enter".to_string(),
|
||||
KeyCode::Esc => "Esc".to_string(),
|
||||
KeyCode::Tab => "Tab".to_string(),
|
||||
KeyCode::BackTab => "S-Tab".to_string(),
|
||||
KeyCode::Backspace => "BS".to_string(),
|
||||
KeyCode::Delete => "Del".to_string(),
|
||||
KeyCode::Left => "←".to_string(),
|
||||
KeyCode::Right => "→".to_string(),
|
||||
KeyCode::Up => "↑".to_string(),
|
||||
KeyCode::Down => "↓".to_string(),
|
||||
KeyCode::Home => "Home".to_string(),
|
||||
KeyCode::End => "End".to_string(),
|
||||
KeyCode::PageUp => "PgUp".to_string(),
|
||||
KeyCode::PageDown => "PgDn".to_string(),
|
||||
KeyCode::F(n) => format!("F{n}"),
|
||||
_ => format!("{code:?}"),
|
||||
}
|
||||
}
|
||||
// `cmd` module imported for `default_registry()` in default_keymaps()
|
||||
|
||||
/// A key pattern that can be matched against a KeyEvent.
|
||||
@ -146,6 +169,35 @@ impl Keymap {
|
||||
.insert(KeyPattern::Any, Binding::Cmd { name, args: vec![] });
|
||||
}
|
||||
|
||||
/// Return human-readable hints for all concrete bindings in this keymap.
|
||||
/// Used by the which-key popup to show available completions after a prefix key.
|
||||
pub fn binding_hints(&self) -> Vec<(String, &'static str)> {
|
||||
let mut hints: Vec<(String, &'static str)> = self
|
||||
.bindings
|
||||
.iter()
|
||||
.filter_map(|(pattern, binding)| {
|
||||
let label = match pattern {
|
||||
KeyPattern::Key(code, _) => format_key_label(code),
|
||||
KeyPattern::AnyChar | KeyPattern::Any => return None,
|
||||
};
|
||||
let name = match binding {
|
||||
Binding::Cmd { name, .. } => *name,
|
||||
Binding::Prefix(_) => return None,
|
||||
Binding::Sequence(steps) => {
|
||||
if let Some((name, _)) = steps.first() {
|
||||
*name
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
Some((label, name))
|
||||
})
|
||||
.collect();
|
||||
hints.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
hints
|
||||
}
|
||||
|
||||
/// Look up the binding for a key.
|
||||
/// For Char keys, if exact (key, mods) match fails, retries with NONE
|
||||
/// modifiers since terminals vary in whether they send SHIFT for
|
||||
|
||||
Reference in New Issue
Block a user