#key-bindings #configuration #shortcut #keyboard-shortcuts #bindings #keyboard

keybinds

Platform&Framework-agnostic key binding (keyboard shortcut) dispatcher, parser, and generator written in Safe Rust

12 releases

new 0.1.1 Mar 21, 2025
0.1.0 Mar 16, 2025
0.0.9 Mar 9, 2025
0.0.7 Feb 25, 2025

#120 in GUI

Download history 39/week @ 2025-02-05 234/week @ 2025-02-12 437/week @ 2025-02-19 109/week @ 2025-02-26 257/week @ 2025-03-05 145/week @ 2025-03-12

975 downloads per month
Used in minim-app

MIT license

150KB
2.5K SLoC

keybinds-rs

crate docs CI

keybinds-rs is a small platform&framework-agnostic crate to parse/generate/dispatch key bindings (keyboard shortcuts) written in Safe Rust. You can easily introduce customizable key bindings to your application using this library.

  • Provide the syntax to easily define key bindings in a configuration file like Ctrl+a.
  • Support key sequences like Ctrl+x Ctrl+s for complicated key bindings like Vim style. (example)
  • Provide the core API independent from any platforms and frameworks with minimal (only two crates) dependencies. (example)
  • Support several platforms and frameworks as optional features.
  • Support parsing/generating a key bindings configuration using serde optionally.
  • Support structure-aware fuzzing using arbitrary optionally. (example)

API Documentation

Installation

cargo add keybinds

Usage

The following code demonstrates the usage by parsing key bindings configuration from TOML input and dispatching actions to move the cursor inside terminal with the serde and crossterm optional features. This code can be run as the example. See the API documentation for more details.

use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crossterm::{cursor, event, execute};
use keybinds::Keybinds;
use serde::Deserialize;
use std::io;

// Actions dispatched by key bindings
#[derive(Deserialize)]
enum Action {
    Exit,
    Up,
    Down,
    Left,
    Right,
    Top,
    Bottom,
    Home,
    End,
}

// Configuration of your application
#[derive(Deserialize)]
struct Config {
    keyboard: Keybinds<Action>,
}

const CONFIG_FILE_CONTENT: &str = r#"
[keyboard]

# Standard bindings
"Up" = "Up"
"Down" = "Down"
"Left" = "Left"
"Right" = "Right"
"PageUp" = "Top"
"PageDown" = "Bottom"
"Home" = "Home"
"End" = "End"
"Mod+q" = "Exit"

# Emacs-like bindings
"Ctrl+p" = "Up"
"Ctrl+n" = "Down"
"Ctrl+b" = "Left"
"Ctrl+f" = "Right"
"Alt+<" = "Top"
"Alt+>" = "Bottom"
"Ctrl+a" = "Home"
"Ctrl+e" = "End"
"Ctrl+x Ctrl+c" = "Exit"

# Vim-like bindings
"k" = "Up"
"j" = "Down"
"h" = "Left"
"l" = "Right"
"g g" = "Top"
"G" = "Bottom"
"^" = "Home"
"$" = "End"
"Esc" = "Exit"
"#;

fn main() -> io::Result<()> {
    // Parse the configuration from the file content
    let config: Config = toml::from_str(CONFIG_FILE_CONTENT).unwrap();

    // `Keybinds` instance is a key bindings dispatcher that receives key inputs and
    // dispatches the corresponding actions.
    let mut keybinds = config.keyboard;

    enable_raw_mode()?;
    let mut stdout = io::stdout();

    // Read the crossterm's key events and pass it to `Keybinds::dispatch` directly.
    while let Ok(event) = event::read() {
        // If the event triggered some action, handle it using `match`
        if let Some(action) = keybinds.dispatch(&event) {
            match action {
                Action::Exit => break,
                Action::Up => execute!(stdout, cursor::MoveUp(1))?,
                Action::Down => execute!(stdout, cursor::MoveDown(1))?,
                Action::Left => execute!(stdout, cursor::MoveLeft(1))?,
                Action::Right => execute!(stdout, cursor::MoveRight(1))?,
                Action::Top => execute!(stdout, cursor::MoveUp(9999))?,
                Action::Bottom => execute!(stdout, cursor::MoveDown(9999))?,
                Action::Home => execute!(stdout, cursor::MoveLeft(9999))?,
                Action::End => execute!(stdout, cursor::MoveRight(9999))?,
            }
        }
    }

    disable_raw_mode()
}

Examples

For more usage, please see the working examples. They can be run locally by cargo run inside this repository. Some examples require some features enabled. For instance, to run the above crossterm example:

cargo run --example crossterm --features=crossterm,serde

Features

The list of crate features can be found in [features] section of Cargo.toml. Please read the comments on each features which explains about it.

Minimal supported Rust version (MSRV)

See rust-version field of Cargo.toml for the minimal supported Rust version. Note that enabling optional features may require some higher Rust versions due to the optional dependencies introduced by them.

Versioning

See the document.

License

This crate is licensed under the MIT license.

Dependencies

~0.1–23MB
~380K SLoC