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}