reedline/
enums.rs

1use crossterm::event::{Event, KeyEvent, KeyEventKind};
2use serde::{Deserialize, Serialize};
3use std::fmt::{Display, Formatter};
4use strum_macros::EnumIter;
5
6/// Valid ways how `Reedline::read_line()` can return
7#[derive(Debug)]
8pub enum Signal {
9    /// Entry succeeded with the provided content
10    Success(String),
11    /// Entry was aborted with `Ctrl+C`
12    CtrlC, // Interrupt current editing
13    /// Abort with `Ctrl+D` signalling `EOF` or abort of a whole interactive session
14    CtrlD, // End terminal session
15}
16
17/// Editing actions which can be mapped to key bindings.
18///
19/// Executed by `Reedline::run_edit_commands()`
20#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, EnumIter)]
21pub enum EditCommand {
22    /// Move to the start of the buffer
23    MoveToStart,
24
25    /// Move to the start of the current line
26    MoveToLineStart,
27
28    /// Move to the end of the buffer
29    MoveToEnd,
30
31    /// Move to the end of the current line
32    MoveToLineEnd,
33
34    /// Move one character to the left
35    MoveLeft,
36
37    /// Move one character to the right
38    MoveRight,
39
40    /// Move one word to the left
41    MoveWordLeft,
42
43    /// Move one WORD to the left
44    MoveBigWordLeft,
45
46    /// Move one word to the right
47    MoveWordRight,
48
49    /// Move one word to the right, stop at start of word
50    MoveWordRightStart,
51
52    /// Move one WORD to the right, stop at start of WORD
53    MoveBigWordRightStart,
54
55    /// Move one word to the right, stop at end of word
56    MoveWordRightEnd,
57
58    /// Move one WORD to the right, stop at end of WORD
59    MoveBigWordRightEnd,
60
61    /// Move to position
62    MoveToPosition(usize),
63
64    /// Insert a character at the current insertion point
65    InsertChar(char),
66
67    /// Insert a string at the current insertion point
68    InsertString(String),
69
70    /// Inserts the system specific new line character
71    ///
72    /// - On Unix systems LF (`"\n"`)
73    /// - On Windows CRLF (`"\r\n"`)
74    InsertNewline,
75
76    /// Replace a character
77    ReplaceChar(char),
78
79    /// Replace characters with string
80    ReplaceChars(usize, String),
81
82    /// Backspace delete from the current insertion point
83    Backspace,
84
85    /// Delete in-place from the current insertion point
86    Delete,
87
88    /// Cut the grapheme right from the current insertion point
89    CutChar,
90
91    /// Backspace delete a word from the current insertion point
92    BackspaceWord,
93
94    /// Delete in-place a word from the current insertion point
95    DeleteWord,
96
97    /// Clear the current buffer
98    Clear,
99
100    /// Clear to the end of the current line
101    ClearToLineEnd,
102
103    /// Insert completion: entire completion if there is only one possibility, or else up to shared prefix.
104    Complete,
105
106    /// Cut the current line
107    CutCurrentLine,
108
109    /// Cut from the start of the buffer to the insertion point
110    CutFromStart,
111
112    /// Cut from the start of the current line to the insertion point
113    CutFromLineStart,
114
115    /// Cut from the insertion point to the end of the buffer
116    CutToEnd,
117
118    /// Cut from the insertion point to the end of the current line
119    CutToLineEnd,
120
121    /// Cut the word left of the insertion point
122    CutWordLeft,
123
124    /// Cut the WORD left of the insertion point
125    CutBigWordLeft,
126
127    /// Cut the word right of the insertion point
128    CutWordRight,
129
130    /// Cut the word right of the insertion point
131    CutBigWordRight,
132
133    /// Cut the word right of the insertion point and any following space
134    CutWordRightToNext,
135
136    /// Cut the WORD right of the insertion point and any following space
137    CutBigWordRightToNext,
138
139    /// Paste the cut buffer in front of the insertion point (Emacs, vi `P`)
140    PasteCutBufferBefore,
141
142    /// Paste the cut buffer in front of the insertion point (vi `p`)
143    PasteCutBufferAfter,
144
145    /// Upper case the current word
146    UppercaseWord,
147
148    /// Lower case the current word
149    LowercaseWord,
150
151    /// Capitalize the current character
152    CapitalizeChar,
153
154    /// Switch the case of the current character
155    SwitchcaseChar,
156
157    /// Swap the current word with the word to the right
158    SwapWords,
159
160    /// Swap the current grapheme/character with the one to the right
161    SwapGraphemes,
162
163    /// Undo the previous edit command
164    Undo,
165
166    /// Redo an edit command from the undo history
167    Redo,
168
169    /// CutUntil right until char
170    CutRightUntil(char),
171
172    /// CutUntil right before char
173    CutRightBefore(char),
174
175    /// CutUntil right until char
176    MoveRightUntil(char),
177
178    /// CutUntil right before char
179    MoveRightBefore(char),
180
181    /// CutUntil left until char
182    CutLeftUntil(char),
183
184    /// CutUntil left before char
185    CutLeftBefore(char),
186
187    /// CutUntil left until char
188    MoveLeftUntil(char),
189
190    /// CutUntil left before char
191    MoveLeftBefore(char),
192}
193
194impl Display for EditCommand {
195    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
196        match self {
197            EditCommand::MoveToStart => write!(f, "MoveToStart"),
198            EditCommand::MoveToLineStart => write!(f, "MoveToLineStart"),
199            EditCommand::MoveToEnd => write!(f, "MoveToEnd"),
200            EditCommand::MoveToLineEnd => write!(f, "MoveToLineEnd"),
201            EditCommand::MoveLeft => write!(f, "MoveLeft"),
202            EditCommand::MoveRight => write!(f, "MoveRight"),
203            EditCommand::MoveWordLeft => write!(f, "MoveWordLeft"),
204            EditCommand::MoveBigWordLeft => write!(f, "MoveBigWordLeft"),
205            EditCommand::MoveWordRight => write!(f, "MoveWordRight"),
206            EditCommand::MoveWordRightEnd => write!(f, "MoveWordRightEnd"),
207            EditCommand::MoveBigWordRightEnd => write!(f, "MoveBigWordRightEnd"),
208            EditCommand::MoveWordRightStart => write!(f, "MoveWordRightStart"),
209            EditCommand::MoveBigWordRightStart => write!(f, "MoveBigWordRightStart"),
210            EditCommand::MoveToPosition(_) => write!(f, "MoveToPosition  Value: <int>"),
211            EditCommand::InsertChar(_) => write!(f, "InsertChar  Value: <char>"),
212            EditCommand::InsertString(_) => write!(f, "InsertString Value: <string>"),
213            EditCommand::InsertNewline => write!(f, "InsertNewline"),
214            EditCommand::ReplaceChar(_) => write!(f, "ReplaceChar <char>"),
215            EditCommand::ReplaceChars(_, _) => write!(f, "ReplaceChars <int> <string>"),
216            EditCommand::Backspace => write!(f, "Backspace"),
217            EditCommand::Delete => write!(f, "Delete"),
218            EditCommand::CutChar => write!(f, "CutChar"),
219            EditCommand::BackspaceWord => write!(f, "BackspaceWord"),
220            EditCommand::DeleteWord => write!(f, "DeleteWord"),
221            EditCommand::Clear => write!(f, "Clear"),
222            EditCommand::ClearToLineEnd => write!(f, "ClearToLineEnd"),
223            EditCommand::Complete => write!(f, "Complete"),
224            EditCommand::CutCurrentLine => write!(f, "CutCurrentLine"),
225            EditCommand::CutFromStart => write!(f, "CutFromStart"),
226            EditCommand::CutFromLineStart => write!(f, "CutFromLineStart"),
227            EditCommand::CutToEnd => write!(f, "CutToEnd"),
228            EditCommand::CutToLineEnd => write!(f, "CutToLineEnd"),
229            EditCommand::CutWordLeft => write!(f, "CutWordLeft"),
230            EditCommand::CutBigWordLeft => write!(f, "CutBigWordLeft"),
231            EditCommand::CutWordRight => write!(f, "CutWordRight"),
232            EditCommand::CutBigWordRight => write!(f, "CutBigWordRight"),
233            EditCommand::CutWordRightToNext => write!(f, "CutWordRightToNext"),
234            EditCommand::CutBigWordRightToNext => write!(f, "CutBigWordRightToNext"),
235            EditCommand::PasteCutBufferBefore => write!(f, "PasteCutBufferBefore"),
236            EditCommand::PasteCutBufferAfter => write!(f, "PasteCutBufferAfter"),
237            EditCommand::UppercaseWord => write!(f, "UppercaseWord"),
238            EditCommand::LowercaseWord => write!(f, "LowercaseWord"),
239            EditCommand::SwitchcaseChar => write!(f, "SwitchcaseChar"),
240            EditCommand::CapitalizeChar => write!(f, "CapitalizeChar"),
241            EditCommand::SwapWords => write!(f, "SwapWords"),
242            EditCommand::SwapGraphemes => write!(f, "SwapGraphemes"),
243            EditCommand::Undo => write!(f, "Undo"),
244            EditCommand::Redo => write!(f, "Redo"),
245            EditCommand::CutRightUntil(_) => write!(f, "CutRightUntil Value: <char>"),
246            EditCommand::CutRightBefore(_) => write!(f, "CutRightBefore Value: <char>"),
247            EditCommand::MoveRightUntil(_) => write!(f, "MoveRightUntil Value: <char>"),
248            EditCommand::MoveRightBefore(_) => write!(f, "MoveRightBefore Value: <char>"),
249            EditCommand::CutLeftUntil(_) => write!(f, "CutLeftUntil Value: <char>"),
250            EditCommand::CutLeftBefore(_) => write!(f, "CutLeftBefore Value: <char>"),
251            EditCommand::MoveLeftUntil(_) => write!(f, "MoveLeftUntil Value: <char>"),
252            EditCommand::MoveLeftBefore(_) => write!(f, "MoveLeftBefore Value: <char>"),
253        }
254    }
255}
256
257impl EditCommand {
258    /// Determine if a certain operation should be undoable
259    /// or if the operations should be coalesced for undoing
260    pub fn edit_type(&self) -> EditType {
261        match self {
262            // Cursor moves
263            EditCommand::MoveToStart
264            | EditCommand::MoveToEnd
265            | EditCommand::MoveToLineStart
266            | EditCommand::MoveToLineEnd
267            | EditCommand::MoveToPosition(_)
268            | EditCommand::MoveLeft
269            | EditCommand::MoveRight
270            | EditCommand::MoveWordLeft
271            | EditCommand::MoveBigWordLeft
272            | EditCommand::MoveWordRight
273            | EditCommand::MoveWordRightStart
274            | EditCommand::MoveBigWordRightStart
275            | EditCommand::MoveWordRightEnd
276            | EditCommand::MoveBigWordRightEnd
277            | EditCommand::MoveRightUntil(_)
278            | EditCommand::MoveRightBefore(_)
279            | EditCommand::MoveLeftUntil(_)
280            | EditCommand::MoveLeftBefore(_) => EditType::MoveCursor,
281
282            // Text edits
283            EditCommand::InsertChar(_)
284            | EditCommand::Backspace
285            | EditCommand::Delete
286            | EditCommand::CutChar
287            | EditCommand::InsertString(_)
288            | EditCommand::InsertNewline
289            | EditCommand::ReplaceChar(_)
290            | EditCommand::ReplaceChars(_, _)
291            | EditCommand::BackspaceWord
292            | EditCommand::DeleteWord
293            | EditCommand::Clear
294            | EditCommand::ClearToLineEnd
295            | EditCommand::Complete
296            | EditCommand::CutCurrentLine
297            | EditCommand::CutFromStart
298            | EditCommand::CutFromLineStart
299            | EditCommand::CutToLineEnd
300            | EditCommand::CutToEnd
301            | EditCommand::CutWordLeft
302            | EditCommand::CutBigWordLeft
303            | EditCommand::CutWordRight
304            | EditCommand::CutBigWordRight
305            | EditCommand::CutWordRightToNext
306            | EditCommand::CutBigWordRightToNext
307            | EditCommand::PasteCutBufferBefore
308            | EditCommand::PasteCutBufferAfter
309            | EditCommand::UppercaseWord
310            | EditCommand::LowercaseWord
311            | EditCommand::SwitchcaseChar
312            | EditCommand::CapitalizeChar
313            | EditCommand::SwapWords
314            | EditCommand::SwapGraphemes
315            | EditCommand::CutRightUntil(_)
316            | EditCommand::CutRightBefore(_)
317            | EditCommand::CutLeftUntil(_)
318            | EditCommand::CutLeftBefore(_) => EditType::EditText,
319
320            EditCommand::Undo | EditCommand::Redo => EditType::UndoRedo,
321        }
322    }
323}
324
325/// Specifies the types of edit commands, used to simplify grouping edits
326/// to mark undo behavior
327#[derive(PartialEq, Eq)]
328pub enum EditType {
329    /// Cursor movement commands
330    MoveCursor,
331    /// Undo/Redo commands
332    UndoRedo,
333    /// Text editing commands
334    EditText,
335}
336
337/// Every line change should come with an `UndoBehavior` tag, which can be used to
338/// calculate how the change should be reflected on the undo stack
339#[derive(Debug)]
340pub enum UndoBehavior {
341    /// Character insertion, tracking the character inserted
342    InsertCharacter(char),
343    /// Backspace command, tracking the deleted character (left of cursor)
344    /// Warning: this does not track the whole grapheme, just the character
345    Backspace(Option<char>),
346    /// Delete command, tracking the deleted character (right of cursor)
347    /// Warning: this does not track the whole grapheme, just the character
348    Delete(Option<char>),
349    /// Move the cursor position
350    MoveCursor,
351    /// Navigated the history using up or down arrows
352    HistoryNavigation,
353    /// Catch-all for actions that should always form a unique undo point and never be
354    /// grouped with later edits
355    CreateUndoPoint,
356    /// Undo/Redo actions shouldn't be reflected on the edit stack
357    UndoRedo,
358}
359
360impl UndoBehavior {
361    /// Return if the current operation should start a new undo set, or be
362    /// combined with the previous operation
363    pub fn create_undo_point_after(&self, previous: &UndoBehavior) -> bool {
364        use UndoBehavior as UB;
365        match (previous, self) {
366            // Never start an undo set with cursor movement
367            (_, UB::MoveCursor) => false,
368            (UB::HistoryNavigation, UB::HistoryNavigation) => false,
369            // When inserting/deleting repeatedly, each undo set should encompass
370            // inserting/deleting a complete word and the associated whitespace
371            (UB::InsertCharacter(c_prev), UB::InsertCharacter(c_new)) => {
372                (*c_prev == '\n' || *c_prev == '\r')
373                    || (!c_prev.is_whitespace() && c_new.is_whitespace())
374            }
375            (UB::Backspace(Some(c_prev)), UB::Backspace(Some(c_new))) => {
376                (*c_new == '\n' || *c_new == '\r')
377                    || (c_prev.is_whitespace() && !c_new.is_whitespace())
378            }
379            (UB::Backspace(_), UB::Backspace(_)) => false,
380            (UB::Delete(Some(c_prev)), UB::Delete(Some(c_new))) => {
381                (*c_new == '\n' || *c_new == '\r')
382                    || (c_prev.is_whitespace() && !c_new.is_whitespace())
383            }
384            (UB::Delete(_), UB::Delete(_)) => false,
385            (_, _) => true,
386        }
387    }
388}
389
390/// Reedline supported actions.
391#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, EnumIter)]
392pub enum ReedlineEvent {
393    /// No op event
394    None,
395
396    /// Complete history hint (default in full)
397    HistoryHintComplete,
398
399    /// Complete a single token/word of the history hint
400    HistoryHintWordComplete,
401
402    /// Handle EndOfLine event
403    ///
404    /// Expected Behavior:
405    ///
406    /// - On empty line breaks execution to exit with [`Signal::CtrlD`]
407    /// - Secondary behavior [`EditCommand::Delete`]
408    CtrlD,
409
410    /// Handle SIGTERM key input
411    ///
412    /// Expected behavior:
413    ///
414    /// Abort entry
415    /// Run [`EditCommand::Clear`]
416    /// Clear the current undo
417    /// Bubble up [`Signal::CtrlC`]
418    CtrlC,
419
420    /// Clears the screen and sets prompt to first line
421    ClearScreen,
422
423    /// Clears the screen and the scrollback buffer
424    ///
425    /// Sets the prompt back to the first line
426    ClearScrollback,
427
428    /// Handle enter event
429    Enter,
430
431    /// Handle unconditional submit event
432    Submit,
433
434    /// Submit at the end of the *complete* text, otherwise newline
435    SubmitOrNewline,
436
437    /// Esc event
438    Esc,
439
440    /// Mouse
441    Mouse, // Fill in details later
442
443    /// trigger terminal resize
444    Resize(u16, u16),
445
446    /// Run these commands in the editor
447    Edit(Vec<EditCommand>),
448
449    /// Trigger full repaint
450    Repaint,
451
452    /// Navigate to the previous historic buffer
453    PreviousHistory,
454
455    /// Move up to the previous line, if multiline, or up into the historic buffers
456    Up,
457
458    /// Move down to the next line, if multiline, or down through the historic buffers
459    Down,
460
461    /// Move right to the next column, completion entry, or complete hint
462    Right,
463
464    /// Move left to the next column, or completion entry
465    Left,
466
467    /// Navigate to the next historic buffer
468    NextHistory,
469
470    /// Search the history for a string
471    SearchHistory,
472
473    /// In vi mode multiple reedline events can be chained while parsing the
474    /// command or movement characters
475    Multiple(Vec<ReedlineEvent>),
476
477    /// Test
478    UntilFound(Vec<ReedlineEvent>),
479
480    /// Trigger a menu event. It activates a menu with the event name
481    Menu(String),
482
483    /// Next element in the menu
484    MenuNext,
485
486    /// Previous element in the menu
487    MenuPrevious,
488
489    /// Moves up in the menu
490    MenuUp,
491
492    /// Moves down in the menu
493    MenuDown,
494
495    /// Moves left in the menu
496    MenuLeft,
497
498    /// Moves right in the menu
499    MenuRight,
500
501    /// Move to the next history page
502    MenuPageNext,
503
504    /// Move to the previous history page
505    MenuPagePrevious,
506
507    /// Way to bind the execution of a whole command (directly returning from [`crate::Reedline::read_line()`]) to a keybinding
508    ExecuteHostCommand(String),
509
510    /// Open text editor
511    OpenEditor,
512}
513
514impl Display for ReedlineEvent {
515    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
516        match self {
517            ReedlineEvent::None => write!(f, "None"),
518            ReedlineEvent::HistoryHintComplete => write!(f, "HistoryHintComplete"),
519            ReedlineEvent::HistoryHintWordComplete => write!(f, "HistoryHintWordComplete"),
520            ReedlineEvent::CtrlD => write!(f, "CtrlD"),
521            ReedlineEvent::CtrlC => write!(f, "CtrlC"),
522            ReedlineEvent::ClearScreen => write!(f, "ClearScreen"),
523            ReedlineEvent::ClearScrollback => write!(f, "ClearScrollback"),
524            ReedlineEvent::Enter => write!(f, "Enter"),
525            ReedlineEvent::Submit => write!(f, "Submit"),
526            ReedlineEvent::SubmitOrNewline => write!(f, "SubmitOrNewline"),
527            ReedlineEvent::Esc => write!(f, "Esc"),
528            ReedlineEvent::Mouse => write!(f, "Mouse"),
529            ReedlineEvent::Resize(_, _) => write!(f, "Resize <int> <int>"),
530            ReedlineEvent::Edit(_) => write!(
531                f,
532                "Edit: <EditCommand> or Edit: <EditCommand> value: <string>"
533            ),
534            ReedlineEvent::Repaint => write!(f, "Repaint"),
535            ReedlineEvent::PreviousHistory => write!(f, "PreviousHistory"),
536            ReedlineEvent::Up => write!(f, "Up"),
537            ReedlineEvent::Down => write!(f, "Down"),
538            ReedlineEvent::Right => write!(f, "Right"),
539            ReedlineEvent::Left => write!(f, "Left"),
540            ReedlineEvent::NextHistory => write!(f, "NextHistory"),
541            ReedlineEvent::SearchHistory => write!(f, "SearchHistory"),
542            ReedlineEvent::Multiple(_) => write!(f, "Multiple[ {{ ReedLineEvents, }} ]"),
543            ReedlineEvent::UntilFound(_) => write!(f, "UntilFound [ {{ ReedLineEvents, }} ]"),
544            ReedlineEvent::Menu(_) => write!(f, "Menu Name: <string>"),
545            ReedlineEvent::MenuNext => write!(f, "MenuNext"),
546            ReedlineEvent::MenuPrevious => write!(f, "MenuPrevious"),
547            ReedlineEvent::MenuUp => write!(f, "MenuUp"),
548            ReedlineEvent::MenuDown => write!(f, "MenuDown"),
549            ReedlineEvent::MenuLeft => write!(f, "MenuLeft"),
550            ReedlineEvent::MenuRight => write!(f, "MenuRight"),
551            ReedlineEvent::MenuPageNext => write!(f, "MenuPageNext"),
552            ReedlineEvent::MenuPagePrevious => write!(f, "MenuPagePrevious"),
553            ReedlineEvent::ExecuteHostCommand(_) => write!(f, "ExecuteHostCommand"),
554            ReedlineEvent::OpenEditor => write!(f, "OpenEditor"),
555        }
556    }
557}
558
559pub(crate) enum EventStatus {
560    Handled,
561    Inapplicable,
562    Exits(Signal),
563}
564
565/// A simple wrapper for [crossterm::event::Event]
566///
567/// Which will make sure that the given event doesn't contain [KeyEventKind::Release]
568/// and convert from [KeyEventKind::Repeat] to [KeyEventKind::Press]
569pub struct ReedlineRawEvent {
570    inner: Event,
571}
572
573impl ReedlineRawEvent {
574    /// It will return None if `evt` is released Key.
575    pub fn convert_from(evt: Event) -> Option<Self> {
576        match evt {
577            Event::Key(KeyEvent {
578                kind: KeyEventKind::Release,
579                ..
580            }) => None,
581            Event::Key(KeyEvent {
582                code,
583                modifiers,
584                kind: KeyEventKind::Repeat,
585                state,
586            }) => Some(Self {
587                inner: Event::Key(KeyEvent {
588                    code,
589                    modifiers,
590                    kind: KeyEventKind::Press,
591                    state,
592                }),
593            }),
594            other => Some(Self { inner: other }),
595        }
596    }
597
598    /// Consume and get crossterm event object.
599    pub fn into(self) -> Event {
600        self.inner
601    }
602}