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