Skip to main content

repl_lang/
edit.rs

1//! The editing commands an [`Editor`](crate::Editor) understands.
2
3/// One editing command for an [`Editor`](crate::Editor).
4///
5/// `Edit` is the boundary between the terminal and the editor. The host reads
6/// key events however its platform delivers them — a raw-mode terminal library,
7/// a GUI text field, a test script — and maps each one to an `Edit`; the editor
8/// applies it to the line and reports whether anything changed. Keeping the
9/// mapping on the host's side means the editor never touches a terminal and
10/// any keymap (Emacs, Vi-style, custom) is a lookup table away.
11///
12/// Motions and deletions work in *display units*: a visible character together
13/// with any zero-width characters attached to it, such as a combining accent.
14/// The cursor never stops inside a unit. A *word* is a run of identifier
15/// characters (Unicode `XID_Continue`: letters, digits, `_`, combining marks);
16/// everything else separates words.
17///
18/// The usual Emacs/readline bindings are noted on each variant as a guide; they
19/// are not built in.
20///
21/// The enum is `#[non_exhaustive]`, so new commands can arrive in a minor
22/// release. Hosts build `Edit` values rather than matching on them, so this
23/// costs nothing in practice.
24///
25/// # Examples
26///
27/// ```
28/// use repl_lang::{Edit, Editor};
29///
30/// let mut editor = Editor::new();
31/// for c in "print x".chars() {
32///     editor.apply(Edit::Insert(c));
33/// }
34/// assert!(editor.apply(Edit::WordLeft));    // cursor before `x`
35/// assert!(editor.apply(Edit::KillToStart)); // removes "print "
36/// assert_eq!(editor.line(), "x");
37/// assert!(editor.apply(Edit::Yank));        // and puts it back
38/// assert_eq!(editor.line(), "print x");
39/// ```
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub enum Edit {
43    /// Inserts a character at the cursor and moves the cursor past it.
44    ///
45    /// Control characters (`'\n'`, `'\t'`, escape, and the rest of the C0/C1
46    /// ranges) are refused: they have no printable form on a single line, and
47    /// a terminal key handler should turn them into commands instead. So are
48    /// the bidirectional formatting characters (U+061C, U+200E, U+200F,
49    /// U+202A to U+202E, U+2066 to U+2069), which are invisible and change how
50    /// the text around them is displayed.
51    Insert(char),
52    /// Deletes the display unit before the cursor. `Backspace`, `Ctrl-H`.
53    Backspace,
54    /// Deletes the display unit under the cursor. `Delete`, `Ctrl-D` on a
55    /// non-empty line.
56    Delete,
57    /// Moves one display unit left. `←`, `Ctrl-B`.
58    Left,
59    /// Moves one display unit right. `→`, `Ctrl-F`.
60    Right,
61    /// Moves to the start of the current or previous word. `Ctrl-←`, `Alt-B`.
62    WordLeft,
63    /// Moves to the end of the current or next word. `Ctrl-→`, `Alt-F`.
64    WordRight,
65    /// Moves to the start of the line. `Home`, `Ctrl-A`.
66    Home,
67    /// Moves to the end of the line. `End`, `Ctrl-E`.
68    End,
69    /// Cuts everything before the cursor into the kill buffer. `Ctrl-U`.
70    KillToStart,
71    /// Cuts everything from the cursor to the end into the kill buffer.
72    /// `Ctrl-K`.
73    KillToEnd,
74    /// Cuts the word before the cursor into the kill buffer. `Ctrl-W`,
75    /// `Alt-Backspace`.
76    KillWordLeft,
77    /// Cuts the word after the cursor into the kill buffer. `Alt-D`.
78    KillWordRight,
79    /// Inserts the kill buffer at the cursor. `Ctrl-Y`.
80    Yank,
81    /// Replaces the line with the previous (older) history entry. The first
82    /// step saves the line being typed so [`HistoryNext`](Edit::HistoryNext)
83    /// can return to it. `↑`, `Ctrl-P`.
84    HistoryPrev,
85    /// Replaces the line with the next (newer) history entry, or restores the
86    /// line that was being typed when browsing started. `↓`, `Ctrl-N`.
87    HistoryNext,
88}