Skip to main content

KeyInput

Struct KeyInput 

Source
#[non_exhaustive]
pub struct KeyInput { /* private fields */ }
Expand description

A normalized key press: a Key plus the Modifiers held with it.

This is the unit a Keymap is keyed on, so its equality is the equality used for binding lookup.

§Normalization

For character-producing keys, the terminal has already resolved which glyph the keypress produces (A, !, , …). That resolution depends on the keyboard layout, which this crate cannot reconstruct from the glyph alone, so keymap-core does not re-case or re-map it.

Modifiers::SHIFT is cleared for a character key only on a bare press (when Shift is the sole modifier), because there it is redundant with the resolved glyph: Char('A') with Shift held normalizes to Char('A'), and "shift+a" parses to the same KeyInput as "a". When Ctrl, Alt, or Super is also held, terminals send the base glyph plus modifier bits (e.g. Ctrl+Shift+s arrives as Char('s') + CTRL + SHIFT), so Shift is the only signal distinguishing the chord from Ctrl+s and is kept. This is what makes cmd+shift+s usable as a binding distinct from cmd+s. Ctrl, Alt, and Super are always preserved; for non-character keys (e.g. Tab, arrows) Shift is kept as a modifier.

Two consequences left to a later capability-aware layer (not handled here): if a terminal reports the base-layout key (Char('a') + Shift) instead of the shifted glyph, a bare press normalizes to Char('a') and won’t match a Char('A') binding; and symbol chords (shift+1! vs 1+Shift) cannot be reconciled across terminals without layout knowledge.

Constructing a KeyInput directly via KeyInput::new does not apply this normalization — it is applied at backend boundaries (e.g. the crossterm conversion). Pass already-normalized values to new.

Implementations§

Source§

impl KeyInput

Source

pub const fn new(key: Key, mods: Modifiers) -> Self

Builds a KeyInput from an already-normalized key and modifier set.

Examples found in repository?
examples/modal_keymap.rs (line 34)
33fn ctrl(c: char) -> KeyInput {
34    KeyInput::new(Key::Char(c), Modifiers::CTRL)
35}
More examples
Hide additional examples
examples/ex_command.rs (line 45)
44fn plain(c: char) -> KeyInput {
45    KeyInput::new(Key::Char(c), Modifiers::NONE)
46}
47
48fn enter() -> KeyInput {
49    KeyInput::new(Key::Enter, Modifiers::NONE)
50}
examples/basic_lookup.rs (line 17)
15fn main() {
16    let mut keymap = Keymap::new();
17    keymap.bind(KeyInput::new(Key::Char('q'), Modifiers::CTRL), Action::Quit);
18    keymap.bind(KeyInput::new(Key::Char('s'), Modifiers::CTRL), Action::Save);
19    keymap.bind(
20        KeyInput::new(Key::Char('1'), Modifiers::SUPER),
21        Action::SplitPane,
22    );
23
24    // In a real app these come from the terminal backend (see the `crossterm`
25    // feature's `TryFrom<KeyEvent>`); here we construct them directly.
26    let inputs = [
27        KeyInput::new(Key::Char('q'), Modifiers::CTRL),
28        KeyInput::new(Key::Char('1'), Modifiers::SUPER),
29        KeyInput::new(Key::Char('x'), Modifiers::NONE),
30    ];
31
32    for input in inputs {
33        match keymap.get(&input) {
34            Some(action) => println!("{input:>8}  ->  consume {action:?}"),
35            None => println!("{input:>8}  ->  pass through"),
36        }
37    }
38}
examples/discovery.rs (line 18)
15fn main() {
16    let mut keymap = Keymap::new();
17    keymap.bind(
18        KeyInput::new(Key::Char('q'), Modifiers::CTRL),
19        Command {
20            id: "quit",
21            description: "Quit the application",
22        },
23    );
24    keymap.bind(
25        KeyInput::new(Key::Char('s'), Modifiers::CTRL),
26        Command {
27            id: "save",
28            description: "Save the current file",
29        },
30    );
31    keymap.bind(
32        KeyInput::new(Key::F(1), Modifiers::NONE),
33        Command {
34            id: "help",
35            description: "Show the help overlay",
36        },
37    );
38
39    // Listing: collect and sort by the canonical key string.
40    let mut listing: Vec<(String, &Command)> = keymap
41        .iter()
42        .map(|(input, cmd)| (input.to_string(), cmd))
43        .collect();
44    listing.sort_by(|a, b| a.0.cmp(&b.0));
45
46    println!("all bindings:");
47    for (key, cmd) in &listing {
48        println!("  {key:>8}  {:<6} — {}", cmd.id, cmd.description);
49    }
50
51    search(&listing, "sav");
52    search(&listing, "zzz");
53}
Source

pub fn normalized(key: Key, mods: Modifiers) -> Self

Builds a KeyInput, applying the shared [normalize] rule first.

This is the constructor every boundary that turns raw key parts into a KeyInput should use — the string grammar, the crossterm adapter, and the keymap-term byte decoder all funnel through it. Routing them through one normalizing constructor is what guarantees a binding parsed from text and an event produced at runtime land on the same KeyInput, so lookup can’t silently miss. Prefer this over new unless the parts are already known to be normalized.

Source

pub const fn key(&self) -> Key

The key that was pressed.

Examples found in repository?
examples/ex_command.rs (line 95)
52fn main() {
53    let mut map = Keymap::new();
54    map.bind(plain(':'), Action::EnterCommandMode);
55
56    // Stage 3's resolver: command name -> action. Same shape as
57    // `keymap_config`'s `FnMut(&str) -> Option<A>`, intentionally a plain
58    // closure rather than that crate's type.
59    let resolve = |name: &str| -> Option<Action> {
60        match name {
61            "w" => Some(Action::Write),
62            "q" => Some(Action::Quit),
63            "wq" => Some(Action::WriteQuit),
64            _ => None,
65        }
66    };
67
68    // A simulated keystroke stream: `:wq⏎`, then `:q⏎`, then an unknown `:zz⏎`.
69    let stream = [
70        plain(':'),
71        plain('w'),
72        plain('q'),
73        enter(),
74        plain(':'),
75        plain('q'),
76        enter(),
77        plain(':'),
78        plain('z'),
79        plain('z'),
80        enter(),
81    ];
82
83    let mut mode = Mode::Normal;
84    for key in stream {
85        match &mut mode {
86            Mode::Normal => {
87                if map.get(&key) == Some(&Action::EnterCommandMode) {
88                    println!(": -> enter command mode");
89                    mode = Mode::Command(String::new());
90                } else {
91                    // Any other normal-mode key would run its own binding; this
92                    // demo only cares about the `:` entry point.
93                }
94            }
95            Mode::Command(buf) => match key.key() {
96                Key::Char(c) => {
97                    buf.push(c);
98                    println!("  :{buf}");
99                }
100                Key::Enter => {
101                    // Take the buffer out and drop back to Normal in one move.
102                    let name = std::mem::take(buf);
103                    match resolve(&name) {
104                        Some(action) => println!("  :{name} -> fire {action:?}"),
105                        None => println!("  :{name} -> unknown command, no-op"),
106                    }
107                    mode = Mode::Normal;
108                }
109                Key::Esc => {
110                    println!("  esc -> abandon command line");
111                    mode = Mode::Normal;
112                }
113                // `Key` is #[non_exhaustive]; other editing keys are ignored here.
114                _ => {}
115            },
116        }
117    }
118}
Source

pub const fn modifiers(&self) -> Modifiers

The modifiers held during the press.

Source§

impl KeyInput

Source

pub fn legacy_form(&self) -> LegacyForm

Reports how this chord fares under the legacy 7-bit C0 terminal encoding (see LegacyForm and the module docs).

This is a pure, terminal-independent structural fact, not a reachability verdict. Only the cases that are certain regardless of terminal are reported as CollapsesTo / Unrepresentable; everything else is Representable, which means “has a legacy C0 encoding”, not “guaranteed to arrive on every terminal”.

Trait Implementations§

Source§

impl Clone for KeyInput

Source§

fn clone(&self) -> KeyInput

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for KeyInput

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for KeyInput

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for KeyInput

Source§

type Err = ParseKeyInputError

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl Hash for KeyInput

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for KeyInput

Source§

fn eq(&self, other: &KeyInput) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Copy for KeyInput

Source§

impl Eq for KeyInput

Source§

impl StructuralPartialEq for KeyInput

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.