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.
48    Insert(char),
49    /// Deletes the display unit before the cursor. `Backspace`, `Ctrl-H`.
50    Backspace,
51    /// Deletes the display unit under the cursor. `Delete`, `Ctrl-D` on a
52    /// non-empty line.
53    Delete,
54    /// Moves one display unit left. `←`, `Ctrl-B`.
55    Left,
56    /// Moves one display unit right. `→`, `Ctrl-F`.
57    Right,
58    /// Moves to the start of the current or previous word. `Ctrl-←`, `Alt-B`.
59    WordLeft,
60    /// Moves to the end of the current or next word. `Ctrl-→`, `Alt-F`.
61    WordRight,
62    /// Moves to the start of the line. `Home`, `Ctrl-A`.
63    Home,
64    /// Moves to the end of the line. `End`, `Ctrl-E`.
65    End,
66    /// Cuts everything before the cursor into the kill buffer. `Ctrl-U`.
67    KillToStart,
68    /// Cuts everything from the cursor to the end into the kill buffer.
69    /// `Ctrl-K`.
70    KillToEnd,
71    /// Cuts the word before the cursor into the kill buffer. `Ctrl-W`,
72    /// `Alt-Backspace`.
73    KillWordLeft,
74    /// Cuts the word after the cursor into the kill buffer. `Alt-D`.
75    KillWordRight,
76    /// Inserts the kill buffer at the cursor. `Ctrl-Y`.
77    Yank,
78    /// Replaces the line with the previous (older) history entry. The first
79    /// step saves the line being typed so [`HistoryNext`](Edit::HistoryNext)
80    /// can return to it. `↑`, `Ctrl-P`.
81    HistoryPrev,
82    /// Replaces the line with the next (newer) history entry, or restores the
83    /// line that was being typed when browsing started. `↓`, `Ctrl-N`.
84    HistoryNext,
85}