1use std::{
2 collections::HashMap,
3 fmt::{self, Display, Formatter},
4};
5
6#[derive(Clone, Debug, Eq, Hash, PartialEq)]
8pub enum KeyAction {
9 ShellCommand(String),
11 DoInputFunction(InputFunction),
13 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#[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#[derive(Clone, Debug, Eq, Hash, PartialEq)]
237pub enum KeySequence {
238 Strokes(Vec<KeyStroke>),
240 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 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 fn from(value: KeyStroke) -> Self {
276 Self::Strokes(vec![value])
277 }
278}
279
280#[derive(Clone, Debug, Eq, Hash, PartialEq)]
281pub struct KeyStroke {
283 pub alt: bool,
285 pub control: bool,
287 pub shift: bool,
289 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 }
304 self.key.fmt(f)
305 }
306}
307
308impl From<Key> for KeyStroke {
309 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)]
321pub enum Key {
323 Character(char),
325 Backspace,
327 Enter,
329 Left,
331 Right,
333 Up,
335 Down,
337 Home,
339 End,
341 PageUp,
343 PageDown,
345 Tab,
347 BackTab,
349 Delete,
351 Insert,
353 F(u8),
355 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
386pub trait KeyBindings: Send {
388 fn get_current(&self) -> HashMap<KeySequence, KeyAction>;
390
391 fn get_untranslated(&self, bytes: &[u8]) -> Option<&KeyAction>;
393
394 fn bind(&mut self, seq: KeySequence, action: KeyAction) -> Result<(), std::io::Error>;
401
402 fn try_unbind(&mut self, seq: KeySequence) -> bool;
408
409 fn define_macro(&mut self, seq: KeySequence, target: KeySequence)
416 -> Result<(), std::io::Error>;
417
418 fn get_macros(&self) -> HashMap<KeySequence, KeySequence>;
420}