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    DocStart,
27    DocEnd,
28    /// Vertical moves keep a remembered visual column.
29    Up,
30    Down,
31    /// Page moves by the given number of lines (the view's page height).
32    PageUp(usize),
33    PageDown(usize),
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
37pub enum Direction {
38    Up,
39    Down,
40    Left,
41    Right,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
45pub enum Action {
46    InsertText(String),
47    /// Like `InsertText`, but when there are several selections and the text
48    /// has exactly that many lines, each selection gets one line.
49    Paste(String),
50    /// Tab key: a tab character, or spaces to the next tab stop when the
51    /// editor is set to indent with spaces.
52    InsertTab,
53    /// Newline plus the current line's leading whitespace (auto-indent).
54    InsertNewline,
55    DeleteBackward,
56    DeleteForward,
57    DeleteWordBackward,
58    DeleteWordForward,
59    MoveCursor {
60        motion: Motion,
61        extend: bool,
62    },
63    SelectAll,
64    SelectWord,
65    SelectLine,
66    AddCursorAbove,
67    AddCursorBelow,
68    /// Collapse to a caret at the primary selection's head.
69    CollapseSelections,
70    /// Replace the selections (mouse clicks and drags). Positions are clamped
71    /// into the document and onto char boundaries.
72    SetSelections(SelectionSet),
73    /// Rectangular selection in visual columns: one selection per line from
74    /// the anchor corner to the head corner. Lines shorter than the rectangle
75    /// get an empty selection at their end (no virtual space).
76    ColumnSelect {
77        anchor_line: usize,
78        anchor_col: VisualCol,
79        head_line: usize,
80        head_col: VisualCol,
81    },
82    /// Grow or shrink the rectangle by moving its head corner (Alt+Shift+
83    /// arrows); starts a rectangle at the primary caret if there is none.
84    ColumnExtend(Direction),
85    /// Paste a rectangular clip: line `i` goes to the caret's visual column
86    /// on the `i`-th line below it, padding short lines with spaces and
87    /// adding lines at the end of the document as needed. With several
88    /// selections or a non-empty one it behaves like `Paste`.
89    PasteBlock(String),
90    /// Select the next (or previous) match after the primary selection.
91    /// Fails with `NotFound` when there is none (macros stop there).
92    Find {
93        query: SearchQuery,
94        backward: bool,
95    },
96    /// Replace the primary selection if it is exactly a match, then find
97    /// the next match.
98    Replace {
99        query: SearchQuery,
100        replacement: String,
101    },
102    /// Replace every match (inside the selections with "in selection") as
103    /// one undo step.
104    ReplaceAll {
105        query: SearchQuery,
106        replacement: String,
107    },
108    /// One selection per match.
109    SelectAllMatches(SearchQuery),
110    /// Convert each selection (or the caret's line) to a case.
111    ConvertCase(CaseMode),
112    /// Sort the selected lines, the rectangle's lines keyed by the text in
113    /// the rectangle, or the whole document when nothing is selected.
114    SortLines(SortOptions),
115    /// Remove trailing spaces and tabs from the selected lines (all lines
116    /// when nothing is selected).
117    TrimTrailingWhitespace,
118    /// Duplicate the caret's line, or each selection.
119    DuplicateLine,
120    MoveLinesUp,
121    MoveLinesDown,
122    /// Join the selected lines (or the caret's line and the next).
123    JoinLines,
124    Undo,
125    Redo,
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
129pub enum ActionError {
130    UnknownView(ViewId),
131    UnknownBuffer(BufferId),
132    InvalidPattern(String),
133    NotFound,
134}
135
136impl fmt::Display for ActionError {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            ActionError::UnknownView(v) => write!(f, "no view with id {}", v.0),
140            ActionError::UnknownBuffer(b) => write!(f, "no buffer with id {}", b.0),
141            ActionError::InvalidPattern(e) => write!(f, "invalid search pattern: {e}"),
142            ActionError::NotFound => f.write_str("no match found"),
143        }
144    }
145}
146
147impl std::error::Error for ActionError {}