Skip to main content

ochrepad_core/
action.rs

1//! `Action`: what the user intends, independent of where the carets are
2//! (PLAN 3.5). Key bindings map to actions and macros record them.
3
4use std::fmt;
5
6use serde::{Deserialize, Serialize};
7
8use crate::buffer::BufferId;
9use crate::position::VisualCol;
10use crate::search::SearchQuery;
11use crate::selection::SelectionSet;
12use crate::transform::{CaseMode, SortOptions};
13use crate::view::ViewId;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub enum Motion {
17    /// One grapheme cluster.
18    Left,
19    Right,
20    WordLeft,
21    WordRight,
22    /// Smart home: first non-blank of the line, or column 0 if already there.
23    Home,
24    LineStart,
25    LineEnd,
26    /// Home with word wrap: to the start of the caret's display row, or
27    /// `Home` when already there or on a line's first row. Without wrap it
28    /// is `Home`.
29    RowHome,
30    /// End with word wrap: to the end of the caret's display row, or the
31    /// line end when already there. Without wrap it is `LineEnd`.
32    RowEnd,
33    DocStart,
34    DocEnd,
35    /// Vertical moves keep a remembered visual column.
36    Up,
37    Down,
38    /// Page moves by the given number of lines (the view's page height).
39    PageUp(usize),
40    PageDown(usize),
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
44pub enum Direction {
45    Up,
46    Down,
47    Left,
48    Right,
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub enum Action {
53    InsertText(String),
54    /// Like `InsertText`, but when there are several selections and the text
55    /// has exactly that many lines, each selection gets one line.
56    Paste(String),
57    /// Tab key: a tab character, or spaces to the next tab stop when the
58    /// editor is set to indent with spaces.
59    InsertTab,
60    /// Newline plus the current line's leading whitespace (auto-indent).
61    InsertNewline,
62    DeleteBackward,
63    DeleteForward,
64    DeleteWordBackward,
65    DeleteWordForward,
66    MoveCursor {
67        motion: Motion,
68        extend: bool,
69    },
70    SelectAll,
71    SelectWord,
72    SelectLine,
73    AddCursorAbove,
74    AddCursorBelow,
75    /// Collapse to a caret at the primary selection's head.
76    CollapseSelections,
77    /// Replace the selections (mouse clicks and drags). Positions are clamped
78    /// into the document and onto char boundaries.
79    SetSelections(SelectionSet),
80    /// Rectangular selection in visual columns: one selection per line from
81    /// the anchor corner to the head corner. Lines shorter than the rectangle
82    /// get an empty selection at their end (no virtual space).
83    ColumnSelect {
84        anchor_line: usize,
85        anchor_col: VisualCol,
86        head_line: usize,
87        head_col: VisualCol,
88    },
89    /// Grow or shrink the rectangle by moving its head corner (Alt+Shift+
90    /// arrows); starts a rectangle at the primary caret if there is none.
91    ColumnExtend(Direction),
92    /// Paste a rectangular clip: line `i` goes to the caret's visual column
93    /// on the `i`-th line below it, padding short lines with spaces and
94    /// adding lines at the end of the document as needed. With several
95    /// selections or a non-empty one it behaves like `Paste`.
96    PasteBlock(String),
97    /// Select the next (or previous) match after the primary selection.
98    /// Fails with `NotFound` when there is none (macros stop there).
99    Find {
100        query: SearchQuery,
101        backward: bool,
102    },
103    /// Replace the primary selection if it is exactly a match, then find
104    /// the next match.
105    Replace {
106        query: SearchQuery,
107        replacement: String,
108    },
109    /// Replace every match (inside the selections with "in selection") as
110    /// one undo step.
111    ReplaceAll {
112        query: SearchQuery,
113        replacement: String,
114    },
115    /// One selection per match.
116    SelectAllMatches(SearchQuery),
117    /// Convert each selection (or the caret's line) to a case.
118    ConvertCase(CaseMode),
119    /// Sort the selected lines, the rectangle's lines keyed by the text in
120    /// the rectangle, or the whole document when nothing is selected.
121    SortLines(SortOptions),
122    /// Remove trailing spaces and tabs from the selected lines (all lines
123    /// when nothing is selected).
124    TrimTrailingWhitespace,
125    /// Duplicate the caret's line, or each selection.
126    DuplicateLine,
127    MoveLinesUp,
128    MoveLinesDown,
129    /// Join the selected lines (or the caret's line and the next).
130    JoinLines,
131    /// Remove empty lines from the selected lines (all lines when nothing
132    /// is selected); with `include_whitespace`, also lines of only spaces
133    /// and tabs.
134    RemoveEmptyLines {
135        include_whitespace: bool,
136    },
137    /// Remove lines equal to an earlier selected line (all lines when
138    /// nothing is selected), keeping the order; with `consecutive_only`,
139    /// only lines equal to the line right before them.
140    RemoveDuplicateLines {
141        consecutive_only: bool,
142    },
143    /// Fold the region holding the caret: the caret's line if it starts a
144    /// region, else the innermost region around it. Fold actions change the
145    /// view only (no undo step) and are not recorded in macros.
146    Fold,
147    /// Unfold the caret's line, or every fold inside the region around it.
148    Unfold,
149    /// Unfold the caret's line if it is folded, else `Fold`.
150    ToggleFold,
151    FoldAll,
152    UnfoldAll,
153    /// Fold every region of this level: the nesting depth for indentation
154    /// (1 is outermost), the heading level in Markdown.
155    FoldLevel(usize),
156    UnfoldLevel(usize),
157    Undo,
158    Redo,
159}
160
161#[derive(Clone, Debug, PartialEq, Eq)]
162pub enum ActionError {
163    UnknownView(ViewId),
164    UnknownBuffer(BufferId),
165    InvalidPattern(String),
166    NotFound,
167    /// The buffer is read-only.
168    ReadOnly,
169}
170
171impl fmt::Display for ActionError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            ActionError::UnknownView(v) => write!(f, "no view with id {}", v.0),
175            ActionError::UnknownBuffer(b) => write!(f, "no buffer with id {}", b.0),
176            ActionError::InvalidPattern(e) => write!(f, "invalid search pattern: {e}"),
177            ActionError::NotFound => f.write_str("no match found"),
178            ActionError::ReadOnly => f.write_str("the document is read-only"),
179        }
180    }
181}
182
183impl std::error::Error for ActionError {}