Crate keybindings
source ·Expand description
§keybindings
§Overview
This crate provides environment-agnostic interfaces for tracking and processing modal keybindings.
The ModalMachine component allows consumers to build input processors that support multiple modes of input, similar to applications descended from vi.
ModalMachine maintains a graph of Step-containing nodes, and follows edges with each InputKey it receives. Consumers populate the graph by mapping an EdgePath that describes a series of acceptible inputs to reach a Step.
When a new node is reached, Step::step is used to determine whether we can produce any actions, and whether we need to transition to another Mode. If any actions are produced, the keybinding is considered fully entered and the current InputState is taken. If the Step transitions to another Mode, then future input keys will be processed there. Otherwise, future keys will continue to be processed from the top of the currently entered Mode.
§Customization
For straightforward keybindings, consumers only need an action type and a Mode. More complex setups might require doing one or more of the following:
- Implementing InputKey for the environment’s key input (see TerminalKey for example)
- Defining a custom Step type
- Switching from EmptyKeyClass to a custom InputKeyClass
- Switching from EmptyKeyState to a custom InputKeyState
- Updating the context during Mode::enter
§Example
Here is a program that builds keybindings with a Normal mode for sending commands to the program, and an Insert mode for typing text. The Normal mode supports a “qq” sequence to quit the program, and the Insert mode supports Escape to return to Normal mode.
use keybindings::{
BindingMachine,
EmptyKeyClass,
EmptyKeyState,
InputBindings,
InputKey,
InputState,
ModalMachine,
Mode,
ModeKeys,
Step,
};
const ESC: char = '\u{1B}';
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
enum ProgMode {
Normal,
Insert
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum ProgAction {
Type(char),
NoOp,
Quit
}
impl Default for ProgAction {
fn default() -> Self {
ProgAction::NoOp
}
}
#[derive(Default)]
struct ProgBindings { }
impl Default for ProgMode {
fn default() -> ProgMode {
ProgMode::Normal
}
}
impl Mode<ProgAction, EmptyKeyState> for ProgMode { }
impl<K: InputKey> ModeKeys<K, ProgAction, EmptyKeyState> for ProgMode {
fn unmapped(&self, key: &K, _: &mut EmptyKeyState) -> (Vec<ProgAction>, Option<ProgMode>) {
match self {
ProgMode::Normal => {
return (vec![], None);
},
ProgMode::Insert => {
if let Some(c) = key.get_char() {
return (vec![ProgAction::Type(c)], None);
}
return (vec![], None);
},
}
}
}
impl InputBindings<char, ProgStep> for ProgBindings {
fn setup(&self, machine: &mut ProgMachine) {
use keybindings::EdgeRepeat::Once;
use keybindings::EdgeEvent::Key;
// Insert mode mappings
machine.add_mapping(ProgMode::Insert, &vec![
(Once, Key(ESC))
], &(None, Some(ProgMode::Normal)));
// Normal mode mappings
machine.add_mapping(ProgMode::Normal, &vec![
(Once, Key('i'))
], &(None, Some(ProgMode::Insert)));
machine.add_mapping(ProgMode::Normal, &vec![
(Once, Key('q')),
(Once, Key('q')),
], &(Some(ProgAction::Quit), None));
}
}
type ProgStep = (Option<ProgAction>, Option<ProgMode>);
type ProgMachine = ModalMachine<char, ProgStep>;
fn main() {
let mut pm = ProgMachine::from_bindings::<ProgBindings>();
let ctx = EmptyKeyState::default();
// We begin in the Default mode, Normal.
assert_eq!(pm.mode(), ProgMode::Normal);
// Pressing "i" takes us to Insert mode.
pm.input_key('i');
assert_eq!(pm.pop(), Some((ProgAction::NoOp, ctx.clone())));
assert_eq!(pm.mode(), ProgMode::Insert);
// "q" is unmapped in Insert mode, and types a key.
pm.input_key('q');
assert_eq!(pm.pop(), Some((ProgAction::Type('q'), ctx.clone())));
assert_eq!(pm.mode(), ProgMode::Insert);
// Escape takes us back to Normal mode.
pm.input_key(ESC);
assert_eq!(pm.pop(), Some((ProgAction::NoOp, ctx.clone())));
assert_eq!(pm.mode(), ProgMode::Normal);
// A single "q" does nothing.
pm.input_key('q');
assert_eq!(pm.pop(), None);
assert_eq!(pm.mode(), ProgMode::Normal);
// A second "q" produces the Quit action.
pm.input_key('q');
assert_eq!(pm.pop(), Some((ProgAction::Quit, ctx.clone())));
assert_eq!(pm.mode(), ProgMode::Normal);
}
Modules§
- Interactive dialog prompts
Structs§
- An implementation of InputKeyState that stores nothing.
- Iterate over the actions produced by ModalMachine, feeding keys into it as needed.
- Manage and process modal keybindings.
Enums§
- What kind of input is acceptible for continuing towards a Step.
- Specifies how many times an EdgeEvent is allowed to be repeated.
- A default InputKeyClass with no members.
- A default type for Step::Sequence with no members.
- Different ways to include an action in the current action sequence.
Traits§
- Trait for objects that can process input keys using previously mapped bindings.
- A collection of bindings that can be added to a ModalMachine.
- Trait for keys that can be used with ModalMachine.
- Trait for the classes of input keys specific to a consumer.
- Trait for context objects used within ModalMachine.
- Represents contextual information that is updated upon user input.
- Trait for the input modes specific to a consumer.
- Key-specific behaviour associated with a Mode.
- Sequence-specific behaviour associated with a Mode.
- Trait for controlling the behaviour of ModalMachine during a sequence of input keys.