7 releases (3 breaking)
| 1.0.0-rc.3 | Jul 11, 2025 |
|---|---|
| 1.0.0-rc.2 | Jun 10, 2025 |
| 0.4.1 | Nov 24, 2024 |
| 0.4.0 | Oct 11, 2023 |
| 0.1.0 | Jul 30, 2023 |
#136 in Command-line interface
432 downloads per month
91KB
1.5K
SLoC
keymap-rs
keymap-rs is a lightweight and extensible key mapping library for Rust that simplifies input processing for terminal user interfaces (TUIs), WebAssembly (WASM) applications, and more. It parses keymaps from derive macros or configuration files and maps them to actions from various input backends, including crossterm, termion, and wasm.
ð§ Features
- â Declarative Key Mappings: Define keymaps via simple configuration (e.g., TOML, YAML) or directly in your code using derive macros.
- âĻïļ Key Patterns: Supports single keys (
a), combinations (ctrl-b), and multi-key sequences (ctrl-b n). - ð§ Key Groups: Use built-in pattern matching for common key groups:
@upperâ Uppercase letters@lowerâ Lowercase letters@alphaâ All alphabetic characters@alnumâ Alphanumeric characters@anyâ Match any key
- ð§Ž Compile-Time Safety: The
keymap_derivemacro validates key syntax at compile time, preventing runtime errors. - ð Backend Agnostic: Works with multiple backends, including
crossterm,termion, andwasm. - ðŠķ Lightweight & Extensible: Designed to be minimal and easy to extend with new backends or features.
ðđïļ Demo
See keymap-rs in action with the WASM example:
ðĶ Installation
Add keymap to your Cargo.toml, enabling the feature for your chosen backend:
cargo add keymap --feature {crossterm | termion | wasm}
ð Usage
1. Deriving Keymaps
The easiest way to get started is with the keymap::KeyMap derive macro.
Define your actions:
use keymap::KeyMap;
/// Application actions.
#[derive(KeyMap, Debug, PartialEq, Eq)]
pub enum Action {
/// Quit the application.
#[key("q", "esc")]
Quit,
/// Move left.
#[key("left", "h")]
Left,
/// Move right.
#[key("right", "l")]
Right,
/// Jump.
#[key("space")]
Jump,
}
Use the generated keymap:
The KeyMap derive macro generates an associated keymap_config() method that returns a Config<Action>.
// Retrieve the config
let config = Action::keymap_config();
// `key` is a key code from the input backend, e.g., `crossterm::event::KeyCode`
match config.get(&key) {
Some(action) => match action {
Action::Quit => break,
Action::Jump => println!("Jump!"),
_ => println!("Action: {action:?} - {}", action.keymap_item().description),
}
_ => {}
}
2. Using External Configuration
You can also load keymaps from external files (e.g., config.toml). This is useful for user-configurable keybindings.
Example config.toml:
# Override or add new keybindings
Jump = { keys = ["j", "up"], description = "Jump with 'j' or up arrow!" }
Quit = { keys = ["@any"], description = "Quit on any key press." }
You have two ways to load this configuration:
Config<T>: Load from File Only
This deserializes only the keybindings from the configuration file, ignoring any #[key("...")] attributes on your enum.
// This config will only contain 'Jump' and 'Quit' from the TOML file.
let config: Config<Action> = toml::from_str(config_str).unwrap();
| Key | Action |
|---|---|
"j", "up" |
Jump |
@any |
Quit |
DerivedConfig<T>: Merge Derived and File Configs
This merges the keybindings from the #[key("...")] attributes with the ones from the configuration file. Keys from the external file will override any conflicting keys defined in the enum.
// This config contains keys from both the derive macro and the TOML file.
let config: DerivedConfig<Action> = toml::from_str(config_str).unwrap();
| Key | Action |
|---|---|
"j", "up" |
Jump |
"h", "left" |
Left |
"l", "right" |
Right |
@any |
Quit |
"q", "esc", "space" are ignored |
3. Compile-Time Validation
The keymap_derive macro validates all key strings at compile time, so you get immediate feedback on invalid syntax.
Invalid Key Example:
#[derive(keymap::KeyMap)]
enum Action {
// "enter2" is not a valid key.
#[key("enter2", "ctrl-b n")]
Invalid,
}
Compiler Error:
This code will fail to compile with a clear error message:
error: Invalid key "enter2": Parse error at position 5: expect end of input, found: 2
--> keymap_derive/tests/derive.rs:7:11
|
7 | #[key("enter2", "ctrl-b n")]
| ^^^^^^^^
4. Direct Key Parsing
You can also parse key strings directly into a KeyMap or a backend-specific key event.
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use keymap::{backend::crossterm::parse, Key, KeyMap, Modifier};
// Parse into a generic KeyMap
assert_eq!(
"ctrl-l".parse::<KeyMap>(),
Ok(KeyMap::new(Some(Modifier::Ctrl), Key::Char('l')))
);
// Or use the backend-specific parser
assert_eq!(
parse("ctrl-l").unwrap(),
KeyEvent::new(KeyCode::Char('l'), KeyModifiers::CONTROL)
);
ð Examples
For complete, runnable examples, check out the /examples directory, which includes demos for:
crosstermtermionwasm
ð License
This project is licensed under the MIT License.
ð Contributions
Contributions, issues, and feature requests are welcome! Feel free to open an issue or submit a pull request.
Dependencies
~0.4â7.5MB
~159K SLoC