1use serde::{Deserialize, Serialize};
2
3use super::Execute;
4use crate::{
5 debug::log_to_file,
6 helper::{max_col, max_row, set_selection, skip_whitespace, skip_whitespace_rev},
7 EditorMode, EditorState,
8};
9
10#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
11pub struct MoveForward(pub usize);
12
13impl Execute for MoveForward {
14 fn execute(&mut self, state: &mut EditorState) {
15 for _ in 0..self.0 {
16 if state.cursor.col >= max_col(&state.lines, &state.cursor, state.mode) {
17 break;
18 }
19 state.cursor.col += 1;
20 }
21 if state.mode == EditorMode::Visual {
22 set_selection(&mut state.selection, state.cursor);
23 }
24 }
25}
26
27#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
28pub struct MoveBackward(pub usize);
29
30impl Execute for MoveBackward {
31 fn execute(&mut self, state: &mut EditorState) {
32 for _ in 0..self.0 {
33 if state.cursor.col == 0 {
34 break;
35 }
36 let max_col = max_col(&state.lines, &state.cursor, state.mode);
37 if state.cursor.col > max_col {
38 state.cursor.col = max_col;
39 }
40 state.cursor.col = state.cursor.col.saturating_sub(1);
41 }
42 if state.mode == EditorMode::Visual {
43 set_selection(&mut state.selection, state.cursor);
44 }
45 }
46}
47
48#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
49pub struct MoveUp(pub usize);
50
51impl Execute for MoveUp {
52 fn execute(&mut self, state: &mut EditorState) {
53 for _ in 0..self.0 {
54 if state.cursor.row == 0 {
55 break;
56 }
57 state.cursor.row = state.cursor.row.saturating_sub(1);
58 }
59 if state.mode == EditorMode::Visual {
60 set_selection(&mut state.selection, state.cursor);
61 }
62 }
63}
64
65#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
66pub struct MoveDown(pub usize);
67
68impl Execute for MoveDown {
69 fn execute(&mut self, state: &mut EditorState) {
70 for _ in 0..self.0 {
71 if state.cursor.row >= max_row(state) {
72 break;
73 }
74 state.cursor.row += 1;
75 }
76 if state.mode == EditorMode::Visual {
77 set_selection(&mut state.selection, state.cursor);
78 }
79 }
80}
81
82#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
86pub struct MoveWordForwardStart(pub usize);
87
88impl Execute for MoveWordForwardStart {
89 fn execute(&mut self, state: &mut EditorState) {
90 fn move_word_right(state: &mut EditorState) {
91 let lines = &state.lines;
92 let mut index = state.cursor;
93 let first_char = state.lines.get(index);
94 let mut iter = state.lines.iter().from(index);
95 iter.next();
96 for (val, i) in iter {
97 index = i;
98 if state.cursor.col >= max_col(&state.lines, &state.cursor, state.mode) {
100 break;
101 }
102 if !is_same_word_class(val, first_char) {
104 break;
105 }
106 }
107 skip_whitespace(lines, &mut index);
109
110 state.cursor = index;
111 }
112
113 if state.lines.is_empty() {
114 return;
115 }
116
117 for _ in 0..self.0 {
118 move_word_right(state);
119 }
120
121 if state.mode == EditorMode::Visual {
122 set_selection(&mut state.selection, state.cursor);
123 }
124 }
125}
126
127#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
131pub struct MoveWordForwardEnd(pub usize);
132
133impl Execute for MoveWordForwardEnd {
134 fn execute(&mut self, state: &mut EditorState) {
135 fn move_word_right(state: &mut EditorState) {
136 let lines = &state.lines;
137 let mut index = state.cursor;
138 let first_char = state.lines.get(index);
139 let mut iter = state.lines.iter().from(index);
140 iter.next();
141 log_to_file(format!("index: {:?}", index));
142 skip_whitespace(lines, &mut index);
143 log_to_file(format!("index_after: {:?}", index));
144 for (val, i) in iter {
145 if state.cursor.col >= max_col(&state.lines, &state.cursor, state.mode) {
147 break;
148 }
149 if !is_same_word_class(val, first_char) {
151 break;
152 }
153 index = i;
154 }
155
156 state.cursor = index;
157 }
158
159 if state.lines.is_empty() {
160 return;
161 }
162
163 for _ in 0..self.0 {
164 move_word_right(state);
165 }
166
167 if state.mode == EditorMode::Visual {
168 set_selection(&mut state.selection, state.cursor);
169 }
170 }
171}
172
173#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
177pub struct MoveWordBackward(pub usize);
178
179impl Execute for MoveWordBackward {
180 fn execute(&mut self, state: &mut EditorState) {
181 fn move_word_left(state: &mut EditorState) {
182 let lines = &state.lines;
183 let mut index = state.cursor;
184 if index.row == 0 && index.col == 0 {
185 return;
186 }
187
188 if index.col == 0 {
189 index.row = index.row.saturating_sub(1);
190 if let Some(len) = lines.len_col(index.row) {
191 state.cursor.col = len.saturating_sub(1);
192 }
193 state.cursor.row = index.row;
194 return;
195 }
196
197 index.col = index.col.saturating_sub(1);
198 skip_whitespace_rev(lines, &mut index);
199 let first_char = lines.get(index);
200 for (val, i) in lines.iter().from(index).rev() {
201 if i.col == 0 {
203 index = i;
204 break;
205 }
206 if !is_same_word_class(val, first_char) {
208 break;
209 }
210 index = i;
211 }
212 state.cursor = index;
213 }
214
215 if state.lines.is_empty() {
216 return;
217 }
218
219 let max_col = max_col(&state.lines, &state.cursor, state.mode);
220 if state.cursor.col > max_col {
221 state.cursor.col = max_col;
222 }
223
224 for _ in 0..self.0 {
225 move_word_left(state);
226 }
227
228 if state.mode == EditorMode::Visual {
229 set_selection(&mut state.selection, state.cursor);
230 }
231 }
232}
233
234fn is_same_word_class(a: Option<&char>, b: Option<&char>) -> bool {
236 match (a, b) {
237 (Some(a), Some(b)) => {
238 a.is_ascii_alphanumeric() && b.is_ascii_alphanumeric()
239 || (a.is_ascii_punctuation() && b.is_ascii_punctuation())
240 || (a.is_ascii_whitespace() && b.is_ascii_whitespace())
241 },
242 _ => false,
243 }
244}
245
246#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
248pub struct MoveToStart();
249
250impl Execute for MoveToStart {
251 fn execute(&mut self, state: &mut EditorState) {
252 state.cursor.col = 0;
253
254 if state.mode == EditorMode::Visual {
255 set_selection(&mut state.selection, state.cursor);
256 }
257 }
258}
259
260#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
262pub struct MoveToFirst();
263
264impl Execute for MoveToFirst {
265 fn execute(&mut self, state: &mut EditorState) {
266 state.cursor.col = 0;
267
268 if state.mode == EditorMode::Visual {
269 set_selection(&mut state.selection, state.cursor);
270 }
271 }
272}
273
274#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
276pub struct MoveToEnd();
277
278impl Execute for MoveToEnd {
279 fn execute(&mut self, state: &mut EditorState) {
280 state.cursor.col = max_col(&state.lines, &state.cursor, state.mode);
281
282 if state.mode == EditorMode::Visual {
283 set_selection(&mut state.selection, state.cursor);
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use crate::{Index2, Lines};
292 fn test_state() -> EditorState {
293 EditorState::new(Lines::from("Hello World!\n\n123."), "txt")
294 }
295
296 #[test]
297 fn test_move_forward() {
298 let mut state = test_state();
299
300 MoveForward(1).execute(&mut state);
301 assert_eq!(state.cursor, Index2::new(0, 1));
302
303 MoveForward(10).execute(&mut state);
304 assert_eq!(state.cursor, Index2::new(0, 11));
305
306 MoveForward(1).execute(&mut state);
307 assert_eq!(state.cursor, Index2::new(0, 11));
308 }
309
310 #[test]
311 fn test_move_backward() {
312 let mut state = test_state();
313 state.cursor = Index2::new(0, 11);
314
315 MoveBackward(1).execute(&mut state);
316 assert_eq!(state.cursor, Index2::new(0, 10));
317
318 MoveBackward(10).execute(&mut state);
319 assert_eq!(state.cursor, Index2::new(0, 0));
320
321 MoveBackward(1).execute(&mut state);
322 assert_eq!(state.cursor, Index2::new(0, 0));
323 }
324
325 #[test]
326 fn test_move_down() {
327 let mut state = test_state();
328 state.cursor = Index2::new(0, 6);
329
330 MoveDown(1).execute(&mut state);
331 assert_eq!(state.cursor, Index2::new(1, 6));
332
333 MoveDown(1).execute(&mut state);
334 assert_eq!(state.cursor, Index2::new(2, 6));
335
336 MoveDown(1).execute(&mut state);
337 assert_eq!(state.cursor, Index2::new(2, 6));
338 }
339
340 #[test]
341 fn test_move_up() {
342 let mut state = test_state();
343 state.cursor = Index2::new(2, 2);
344
345 MoveUp(1).execute(&mut state);
346 assert_eq!(state.cursor, Index2::new(1, 2));
347
348 MoveUp(1).execute(&mut state);
349 assert_eq!(state.cursor, Index2::new(0, 2));
350
351 MoveUp(1).execute(&mut state);
352 assert_eq!(state.cursor, Index2::new(0, 2));
353 }
354
355 #[test]
356 fn test_move_word_forward() {
357 let mut state = test_state();
358
359 MoveWordForwardStart(1).execute(&mut state);
360 assert_eq!(state.cursor, Index2::new(0, 6));
361
362 MoveWordForwardStart(1).execute(&mut state);
363 assert_eq!(state.cursor, Index2::new(0, 11));
364
365 MoveWordForwardStart(1).execute(&mut state);
366 assert_eq!(state.cursor, Index2::new(1, 0));
367
368 MoveWordForwardStart(1).execute(&mut state);
369 assert_eq!(state.cursor, Index2::new(2, 0));
370
371 MoveWordForwardStart(1).execute(&mut state);
372 assert_eq!(state.cursor, Index2::new(2, 3));
373 }
374
375 #[test]
376 fn test_move_word_backward() {
377 let mut state = test_state();
378 state.cursor = Index2::new(2, 3);
379
380 MoveWordBackward(1).execute(&mut state);
381 assert_eq!(state.cursor, Index2::new(2, 0));
382
383 MoveWordBackward(1).execute(&mut state);
384 assert_eq!(state.cursor, Index2::new(1, 0));
385
386 MoveWordBackward(1).execute(&mut state);
387 assert_eq!(state.cursor, Index2::new(0, 11));
388
389 MoveWordBackward(1).execute(&mut state);
390 assert_eq!(state.cursor, Index2::new(0, 6));
391
392 MoveWordBackward(1).execute(&mut state);
393 assert_eq!(state.cursor, Index2::new(0, 0));
394
395 MoveWordBackward(1).execute(&mut state);
396 assert_eq!(state.cursor, Index2::new(0, 0));
397 }
398
399 #[test]
400 fn test_move_to_start() {
401 let mut state = test_state();
402 state.cursor = Index2::new(0, 2);
403
404 MoveToStart().execute(&mut state);
405 assert_eq!(state.cursor, Index2::new(0, 0));
406 }
407
408 #[test]
409 fn test_move_to_end() {
410 let mut state = test_state();
411 state.cursor = Index2::new(0, 2);
412
413 MoveToEnd().execute(&mut state);
414 assert_eq!(state.cursor, Index2::new(0, 11));
415 }
416}