Skip to main content

brush_core/interfaces/
keybindings.rs

1use std::{
2    collections::HashMap,
3    fmt::{self, Display, Formatter},
4};
5
6/// Represents an action that can be taken in response to a key sequence.
7#[derive(Clone, Debug, Eq, Hash, PartialEq)]
8pub enum KeyAction {
9    /// Execute a shell command.
10    ShellCommand(String),
11    /// Execute an input "function".
12    DoInputFunction(InputFunction),
13    /// Execute a sequence of actions (in order).
14    Sequence(Vec<Self>),
15}
16
17impl Display for KeyAction {
18    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
19        match self {
20            Self::ShellCommand(command) => write!(f, "shell command: {command}"),
21            Self::DoInputFunction(function) => function.fmt(f),
22            Self::Sequence(actions) => {
23                write!(f, "sequence[")?;
24                for (i, action) in actions.iter().enumerate() {
25                    if i > 0 {
26                        write!(f, ", ")?;
27                    }
28                    action.fmt(f)?;
29                }
30                write!(f, "]")
31            }
32        }
33    }
34}
35
36/// Defines all input functions. Based on standard `readline` functions,
37/// augmented with some `brush`-specific extensions.
38#[derive(
39    Clone,
40    Debug,
41    Eq,
42    Hash,
43    PartialEq,
44    strum_macros::EnumString,
45    strum_macros::Display,
46    strum_macros::EnumIter,
47    strum_macros::IntoStaticStr,
48)]
49#[strum(serialize_all = "kebab-case")]
50#[expect(missing_docs)]
51pub enum InputFunction {
52    Abort,
53    AcceptLine,
54    AliasExpandLine,
55    ArrowKeyPrefix,
56    BackwardByte,
57    BackwardChar,
58    BackwardDeleteChar,
59    BackwardKillLine,
60    BackwardKillWord,
61    BackwardWord,
62    BashViComplete,
63    BeginningOfHistory,
64    BeginningOfLine,
65    BracketedPasteBegin,
66    BrushAcceptHint,
67    BrushAcceptHintWord,
68    CallLastKbdMacro,
69    CapitalizeWord,
70    CharacterSearch,
71    CharacterSearchBackward,
72    ClearDisplay,
73    ClearScreen,
74    Complete,
75    CompleteCommand,
76    CompleteFilename,
77    CompleteHostname,
78    CompleteIntoBraces,
79    CompleteUsername,
80    CompleteVariable,
81    CopyBackwardWord,
82    CopyForwardWord,
83    CopyRegionAsKill,
84    DabbrevExpand,
85    DeleteChar,
86    DeleteCharOrList,
87    DeleteHorizontalSpace,
88    DigitArgument,
89    DisplayShellVersion,
90    DoLowercaseVersion,
91    DowncaseWord,
92    DumpFunctions,
93    DumpMacros,
94    DumpVariables,
95    DynamicCompleteHistory,
96    EditAndExecuteCommand,
97    EmacsEditingMode,
98    EndKbdMacro,
99    EndOfHistory,
100    EndOfLine,
101    ExchangePointAndMark,
102    ExecuteNamedCommand,
103    ExportCompletions,
104    FetchHistory,
105    ForwardBackwardDeleteChar,
106    ForwardByte,
107    ForwardChar,
108    ForwardSearchHistory,
109    ForwardWord,
110    GlobCompleteWord,
111    GlobExpandWord,
112    GlobListExpansions,
113    HistoryAndAliasExpandLine,
114    HistoryExpandLine,
115    HistorySearchBackward,
116    HistorySearchForward,
117    HistorySubstringSearchBackward,
118    HistorySubstringSearchForward,
119    InsertComment,
120    InsertCompletions,
121    InsertLastArgument,
122    KillLine,
123    KillRegion,
124    KillWholeLine,
125    KillWord,
126    MagicSpace,
127    MenuComplete,
128    MenuCompleteBackward,
129    NextHistory,
130    NextScreenLine,
131    NonIncrementalForwardSearchHistory,
132    NonIncrementalForwardSearchHistoryAgain,
133    NonIncrementalReverseSearchHistory,
134    NonIncrementalReverseSearchHistoryAgain,
135    OldMenuComplete,
136    OperateAndGetNext,
137    OverwriteMode,
138    PossibleCommandCompletions,
139    PossibleCompletions,
140    PossibleFilenameCompletions,
141    PossibleHostnameCompletions,
142    PossibleUsernameCompletions,
143    PossibleVariableCompletions,
144    PreviousHistory,
145    PreviousScreenLine,
146    PrintLastKbdMacro,
147    QuotedInsert,
148    ReReadInitFile,
149    RedrawCurrentLine,
150    ReverseSearchHistory,
151    RevertLine,
152    SelfInsert,
153    SetMark,
154    ShellBackwardKillWord,
155    ShellBackwardWord,
156    ShellExpandLine,
157    ShellForwardWord,
158    ShellKillWord,
159    ShellTransposeWords,
160    SkipCsiSequence,
161    SpellCorrectWord,
162    StartKbdMacro,
163    TabInsert,
164    TildeExpand,
165    TransposeChars,
166    TransposeWords,
167    TtyStatus,
168    Undo,
169    UniversalArgument,
170    UnixFilenameRubout,
171    UnixLineDiscard,
172    UnixWordRubout,
173    UpcaseWord,
174    ViAppendEol,
175    ViAppendMode,
176    ViArgDigit,
177    #[strum(serialize = "vi-bWord")]
178    ViBWord,
179    ViBackToIndent,
180    ViBackwardBigword,
181    ViBackwardWord,
182    ViBword,
183    ViChangeCase,
184    ViChangeChar,
185    ViChangeTo,
186    ViCharSearch,
187    ViColumn,
188    ViComplete,
189    ViDelete,
190    ViDeleteTo,
191    #[strum(serialize = "vi-eWord")]
192    ViEWord,
193    ViEditAndExecuteCommand,
194    ViEditingMode,
195    ViEndBigword,
196    ViEndWord,
197    ViEofMaybe,
198    ViEword,
199    #[strum(serialize = "vi-fWord")]
200    ViFWord,
201    ViFetchHistory,
202    ViFirstPrint,
203    ViForwardBigword,
204    ViForwardWord,
205    ViFword,
206    ViGotoMark,
207    ViInsertBeg,
208    ViInsertionMode,
209    ViMatch,
210    ViMovementMode,
211    ViNextWord,
212    ViOverstrike,
213    ViOverstrikeDelete,
214    ViPrevWord,
215    ViPut,
216    ViRedo,
217    ViReplace,
218    ViRubout,
219    ViSearch,
220    ViSearchAgain,
221    ViSetMark,
222    ViSubst,
223    ViTildeExpand,
224    ViUndo,
225    ViUnixWordRubout,
226    ViYankArg,
227    ViYankPop,
228    ViYankTo,
229    Yank,
230    YankLastArg,
231    YankNthArg,
232    YankPop,
233}
234
235/// Represents a sequence of keys.
236#[derive(Clone, Debug, Eq, Hash, PartialEq)]
237pub enum KeySequence {
238    /// Strokes that make up the sequence.
239    Strokes(Vec<KeyStroke>),
240    /// Raw bytes that were used to generate this sequence.
241    Bytes(Vec<Vec<u8>>),
242}
243
244impl Display for KeySequence {
245    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
246        match self {
247            Self::Strokes(strokes) => {
248                for stroke in strokes {
249                    stroke.fmt(f)?;
250                }
251            }
252            Self::Bytes(bytes) => {
253                for byte in bytes.iter().flatten() {
254                    if !byte.is_ascii_control() {
255                        write!(f, "{}", *byte as char)?;
256                    } else if *byte == b'\x1b' {
257                        write!(f, r"\e")?;
258                    } else if *byte >= 0x01 && *byte <= 0x1A {
259                        // Control characters: display as \C-<letter>
260                        let letter = (b'a' + (*byte - 1)) as char;
261                        write!(f, r"\C-{letter}")?;
262                    } else {
263                        write!(f, r"\x{byte:02x}")?;
264                    }
265                }
266            }
267        }
268
269        Ok(())
270    }
271}
272
273impl From<KeyStroke> for KeySequence {
274    /// Creates a new key sequence with a single stroke.
275    fn from(value: KeyStroke) -> Self {
276        Self::Strokes(vec![value])
277    }
278}
279
280#[derive(Clone, Debug, Eq, Hash, PartialEq)]
281/// Represents a single key press.
282pub struct KeyStroke {
283    /// Alt key was pressed.
284    pub alt: bool,
285    /// Control key was pressed.
286    pub control: bool,
287    /// Shift key was pressed.
288    pub shift: bool,
289    /// Primary key pressed.
290    pub key: Key,
291}
292
293impl Display for KeyStroke {
294    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
295        if self.alt {
296            write!(f, "\\e")?;
297        }
298        if self.control {
299            write!(f, "\\C-")?;
300        }
301        if self.shift {
302            // TODO(input): Figure out what to do here or if the key encodes the shift in it.
303        }
304        self.key.fmt(f)
305    }
306}
307
308impl From<Key> for KeyStroke {
309    /// Creates a new key stroke with a single key.
310    fn from(value: Key) -> Self {
311        Self {
312            alt: false,
313            control: false,
314            shift: false,
315            key: value,
316        }
317    }
318}
319
320#[derive(Clone, Debug, Eq, Hash, PartialEq)]
321/// Represents a single key.
322pub enum Key {
323    /// A simple character key.
324    Character(char),
325    /// Backspace key.
326    Backspace,
327    /// Enter key.
328    Enter,
329    /// Left arrow key.
330    Left,
331    /// Right arrow key.
332    Right,
333    /// Up arrow key.
334    Up,
335    /// Down arrow key.
336    Down,
337    /// Home key.
338    Home,
339    /// End key.
340    End,
341    /// Page up key.
342    PageUp,
343    /// Page down key.
344    PageDown,
345    /// Tab key.
346    Tab,
347    /// Shift + Tab key.
348    BackTab,
349    /// Delete key.
350    Delete,
351    /// Insert key.
352    Insert,
353    /// F key.
354    F(u8),
355    /// Escape key.
356    Escape,
357}
358
359impl Display for Key {
360    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
361        match self {
362            Self::Character(c @ ('\\' | '\"' | '\'')) => write!(f, "\\{c}")?,
363            Self::Character(c) => write!(f, "{c}")?,
364            Self::Backspace => write!(f, "Backspace")?,
365            Self::Enter => write!(f, "Enter")?,
366            Self::Left => write!(f, "Left")?,
367            Self::Right => write!(f, "Right")?,
368            Self::Up => write!(f, "Up")?,
369            Self::Down => write!(f, "Down")?,
370            Self::Home => write!(f, "Home")?,
371            Self::End => write!(f, "End")?,
372            Self::PageUp => write!(f, "PageUp")?,
373            Self::PageDown => write!(f, "PageDown")?,
374            Self::Tab => write!(f, "Tab")?,
375            Self::BackTab => write!(f, "BackTab")?,
376            Self::Delete => write!(f, "Delete")?,
377            Self::Insert => write!(f, "Insert")?,
378            Self::F(n) => write!(f, "F{n}")?,
379            Self::Escape => write!(f, "Esc")?,
380        }
381
382        Ok(())
383    }
384}
385
386/// Encapsulates the shell's interaction with key bindings for input.
387pub trait KeyBindings: Send {
388    /// Retrieves current bindings.
389    fn get_current(&self) -> HashMap<KeySequence, KeyAction>;
390
391    /// Tries to find a binding for an untranslated byte sequence.
392    fn get_untranslated(&self, bytes: &[u8]) -> Option<&KeyAction>;
393
394    /// Sets or updates a binding.
395    ///
396    /// # Arguments
397    ///
398    /// * `seq` - The key sequence to bind.
399    /// * `action` - The action to bind to the sequence.
400    fn bind(&mut self, seq: KeySequence, action: KeyAction) -> Result<(), std::io::Error>;
401
402    /// Unbinds a key sequence. Returns true if a binding was removed.
403    ///
404    /// # Arguments
405    ///
406    /// * `seq` - The key sequence to unbind.
407    fn try_unbind(&mut self, seq: KeySequence) -> bool;
408
409    /// Defines a macro that remaps a key sequence to another key sequence.
410    ///
411    /// # Arguments
412    ///
413    /// * `seq` - The key sequence to bind the macro to.
414    /// * `target` - The sequence that makes up the macro.
415    fn define_macro(&mut self, seq: KeySequence, target: KeySequence)
416    -> Result<(), std::io::Error>;
417
418    /// Retrieves all defined macros.
419    fn get_macros(&self) -> HashMap<KeySequence, KeySequence>;
420}