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}