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§
- dialog
- Interactive dialog prompts
Structs§
- Empty
KeyState - An implementation of InputKeyState that stores nothing.
- Input
Iterator - Iterate over the actions produced by ModalMachine, feeding keys into it as needed.
- Modal
Machine - Manage and process modal keybindings.
Enums§
- Edge
Event - What kind of input is acceptible for continuing towards a Step.
- Edge
Repeat - Specifies how many times an EdgeEvent is allowed to be repeated.
- Empty
KeyClass - A default InputKeyClass with no members.
- Empty
Sequence - A default type for Step::Sequence with no members.
- Sequence
Status - Different ways to include an action in the current action sequence.
Traits§
- Binding
Machine - Trait for objects that can process input keys using previously mapped bindings.
- Input
Bindings - A collection of bindings that can be added to a ModalMachine.
- Input
Key - Trait for keys that can be used with ModalMachine.
- Input
KeyClass - Trait for the classes of input keys specific to a consumer.
- Input
KeyState - Trait for context objects used within ModalMachine.
- Input
State - Represents contextual information that is updated upon user input.
- Mode
- Trait for the input modes specific to a consumer.
- Mode
Keys - Key-specific behaviour associated with a Mode.
- Mode
Sequence - Sequence-specific behaviour associated with a Mode.
- Step
- Trait for controlling the behaviour of ModalMachine during a sequence of input keys.
Type Aliases§
- Edge
Path - A description of a sequence of input keys that leads to a Step.
- Edge
Path Part - Part of a sequence of input keys that leads to a Step.