1pub mod cpaste;
3pub mod delete;
4pub mod insert;
5pub mod motion;
6pub mod search;
7pub mod select;
8
9use enum_dispatch::enum_dispatch;
10use serde::{Deserialize, Serialize};
11
12pub use self::{
13 cpaste::{CopySelection, Paste},
14 delete::{DeleteChar, DeleteLine, DeleteSelection, RemoveChar},
15 insert::{AppendNewline, InsertChar, InsertNewline, LineBreak},
16 motion::MoveWordForwardEnd,
17 motion::{
18 MoveBackward, MoveDown, MoveForward, MoveToEnd, MoveToFirst, MoveToStart, MoveUp, MoveWordBackward,
19 MoveWordForwardStart,
20 },
21 search::{
22 AppendCharToSearch, FindNext, FindPrevious, RemoveCharFromSearch, StartSearch, StopSearch, TriggerSearch,
23 },
24 select::SelectBetween,
25};
26
27use crate::{helper::clamp_column, state::selection::Selection, EditorMode, EditorState};
28
29#[enum_dispatch(Execute, Clone, Serialize, Deserialize)]
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(tag = "action", content = "payload")]
32pub enum Action<I: Clone + Execute> {
33 SwitchMode(SwitchMode),
34 Append(Append),
35 MoveForward(MoveForward),
36 MoveBackward(MoveBackward),
37 MoveUp(MoveUp),
38 MoveDown(MoveDown),
39 MoveWordForwardStart(MoveWordForwardStart),
40 MoveWordFowardEnd(MoveWordForwardEnd),
41 MoveWordBackward(MoveWordBackward),
42 MoveToStart(MoveToStart),
43 MoveToFirst(MoveToFirst),
44 MoveToEnd(MoveToEnd),
45 InsertChar(InsertChar),
46 LineBreak(LineBreak),
47 AppendNewline(AppendNewline),
48 InsertNewline(InsertNewline),
49 RemoveChar(RemoveChar),
50 DeleteChar(DeleteChar),
51 DeleteLine(DeleteLine),
52 DeleteSelection(DeleteSelection),
53 SelectBetween(SelectBetween),
54 Undo(Undo),
55 Redo(Redo),
56 Paste(Paste),
57 CopySelection(CopySelection),
58 Composed(Composed<I>),
59 StartSearch(StartSearch),
60 StopSearch(StopSearch),
61 TriggerSearch(TriggerSearch),
62 FindNext(FindNext),
63 FindPrevious(FindPrevious),
64 AppendCharToSearch(AppendCharToSearch),
65 RemoveCharFromSearch(RemoveCharFromSearch),
66 Custom(Custom<I>),
67}
68
69#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
70pub struct Custom<I: Clone + Execute>(pub I);
71
72impl<I> Execute for Custom<I>
73where
74 I: Clone + Execute,
75{
76 fn execute(&mut self, state: &mut EditorState) {
77 self.0.execute(state);
78 }
79}
80
81#[enum_dispatch]
82pub trait Execute {
83 fn execute(&mut self, state: &mut EditorState);
84}
85
86#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
87pub struct SwitchMode(pub EditorMode);
88
89impl Execute for SwitchMode {
90 fn execute(&mut self, state: &mut EditorState) {
91 clamp_column(state);
92 match self.0 {
93 EditorMode::Normal => {
94 state.selection = None;
95 },
96 EditorMode::Visual => {
97 state.selection = Some(Selection::new(state.cursor, state.cursor));
98 },
99 EditorMode::Insert | EditorMode::Search | EditorMode::Command => {},
100 }
101 state.mode = self.0;
102 }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
107pub struct Append;
108
109impl Execute for Append {
110 fn execute(&mut self, state: &mut EditorState) {
111 SwitchMode(EditorMode::Insert).execute(state);
112 MoveForward(1).execute(state);
113 }
114}
115
116#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
117pub struct Undo;
118
119impl Execute for Undo {
120 fn execute(&mut self, state: &mut EditorState) {
121 state.undo();
122 }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
126pub struct Redo;
127
128impl Execute for Redo {
129 fn execute(&mut self, state: &mut EditorState) {
130 state.redo();
131 }
132}
133
134#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
136pub struct Composed<I: Clone + Execute>(Vec<Action<I>>);
137
138impl<I> Composed<I>
139where
140 I: Clone + Execute + Serialize,
141{
142 #[must_use]
143 pub fn new<A: Into<Action<I>>>(action: A) -> Self {
144 Self(vec![action.into()])
145 }
146
147 #[must_use]
148 pub fn chain<A: Into<Action<I>>>(mut self, action: A) -> Self {
149 self.0.push(action.into());
150 self
151 }
152}
153
154impl<I> Execute for Composed<I>
155where
156 I: Clone + Execute,
157{
158 fn execute(&mut self, state: &mut EditorState) {
159 for action in &mut self.0 {
160 action.execute(state);
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::{clipboard::InternalClipboard, Index2, Lines};
169 enum TestAction {
170 Test,
171 }
172 fn test_state() -> EditorState {
173 let mut state = EditorState::new(Lines::from("Hello World!\n\n123."), "txt");
174 state.set_clipboard(InternalClipboard::default());
175 state
176 }
177
178 #[test]
179 fn test_switch_mode() {
180 let mut state = test_state();
181 assert_eq!(state.mode, EditorMode::Normal);
182
183 SwitchMode(EditorMode::Insert).execute(&mut state);
184 assert_eq!(state.mode, EditorMode::Insert);
185
186 SwitchMode(EditorMode::Visual).execute(&mut state);
187 assert_eq!(state.mode, EditorMode::Visual);
188 }
189
190 #[test]
191 fn test_append() {
192 let mut state = test_state();
193
194 Append.execute(&mut state);
195 assert_eq!(state.mode, EditorMode::Insert);
196 assert_eq!(state.cursor, Index2::new(0, 1));
197
198 state.mode = EditorMode::Normal;
199 state.cursor = Index2::new(0, 11);
200 Append.execute(&mut state);
201 assert_eq!(state.mode, EditorMode::Insert);
202 assert_eq!(state.cursor, Index2::new(0, 12));
203 }
204}