edtui_papier/actions/
insert.rs

1use jagged::index::RowIndex;
2use serde::{Deserialize, Serialize};
3
4use super::{Execute, SwitchMode};
5use crate::{
6    helper::{insert_char, line_break},
7    EditorMode, EditorState,
8};
9
10/// Inserts a single character at the current cursor position
11#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
12pub struct InsertChar(pub char);
13
14impl Execute for InsertChar {
15    fn execute(&mut self, state: &mut EditorState) {
16        insert_char(&mut state.lines, &mut state.cursor, self.0, false, &mut state.highlighter);
17    }
18}
19
20/// Inserts a newline at the current cursor position
21#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
22pub struct LineBreak(pub usize);
23
24impl Execute for LineBreak {
25    fn execute(&mut self, state: &mut EditorState) {
26        for _ in 0..self.0 {
27            line_break(&mut state.lines, &mut state.cursor, &mut state.highlighter);
28        }
29    }
30}
31
32/// Appends a newline below the current cursor position
33/// and switches into insert mode.
34#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
35pub struct AppendNewline(pub usize);
36
37impl Execute for AppendNewline {
38    fn execute(&mut self, state: &mut EditorState) {
39        state.cursor.col = 0;
40        for _ in 0..self.0 {
41            state.cursor.row += 1;
42            state.lines.insert(RowIndex::new(state.cursor.row), vec![]);
43            state.highlighter.insert_line(state.cursor.row, &"".to_string());
44        }
45        SwitchMode(EditorMode::Insert).execute(state);
46    }
47}
48
49/// Appends a newline at the current cursor position
50/// and switches into insert mode.
51#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
52pub struct InsertNewline(pub usize);
53
54impl Execute for InsertNewline {
55    fn execute(&mut self, state: &mut EditorState) {
56        state.cursor.col = 0;
57        for _ in 0..self.0 {
58            state.lines.insert(RowIndex::new(state.cursor.row), vec![]);
59            state.highlighter.insert_line(state.cursor.row, &"".to_string());
60        }
61        SwitchMode(EditorMode::Insert).execute(state);
62    }
63}
64
65/// Pushes a line to the back of the buffer.
66/// Does not affect the cursor position.
67#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
68pub struct PushLine<'a>(pub &'a str);
69
70impl Execute for PushLine<'_> {
71    fn execute(&mut self, state: &mut EditorState) {
72        let chars: Vec<char> = self.0.chars().collect();
73        state.lines.push(chars.clone());
74        state.highlighter.append(&chars.iter().collect::<String>());
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use crate::{Index2, Lines};
82    fn test_state() -> EditorState {
83        EditorState::new(Lines::from("Hello World!\n\n123."), "txt")
84    }
85
86    #[test]
87    fn test_insert_char() {
88        let mut state = test_state();
89
90        InsertChar('!').execute(&mut state);
91        assert_eq!(state.cursor, Index2::new(0, 1));
92        assert_eq!(state.lines, Lines::from("!Hello World!\n\n123."));
93
94        state.cursor = Index2::new(0, 13);
95        InsertChar('!').execute(&mut state);
96        assert_eq!(state.cursor, Index2::new(0, 14));
97        assert_eq!(state.lines, Lines::from("!Hello World!!\n\n123."));
98    }
99
100    #[test]
101    fn test_linebreak() {
102        let mut state = test_state();
103
104        LineBreak(1).execute(&mut state);
105        assert_eq!(state.cursor, Index2::new(1, 0));
106        assert_eq!(state.lines, Lines::from("\nHello World!\n\n123."));
107
108        state.cursor = Index2::new(1, 5);
109        LineBreak(1).execute(&mut state);
110        assert_eq!(state.cursor, Index2::new(2, 0));
111        assert_eq!(state.lines, Lines::from("\nHello\n World!\n\n123."));
112    }
113
114    #[test]
115    fn test_append_newline() {
116        let mut state = test_state();
117
118        AppendNewline(1).execute(&mut state);
119        assert_eq!(state.cursor, Index2::new(1, 0));
120        assert_eq!(state.lines, Lines::from("Hello World!\n\n\n123."));
121    }
122
123    #[test]
124    fn test_insert_newline() {
125        let mut state = test_state();
126
127        InsertNewline(1).execute(&mut state);
128        assert_eq!(state.cursor, Index2::new(0, 0));
129        assert_eq!(state.lines, Lines::from("\nHello World!\n\n123."));
130
131        state.cursor = Index2::new(2, 1);
132        InsertNewline(1).execute(&mut state);
133        assert_eq!(state.cursor, Index2::new(2, 0));
134        assert_eq!(state.lines, Lines::from("\nHello World!\n\n\n123."));
135    }
136
137    #[test]
138    fn test_push_line() {
139        let mut state = test_state();
140
141        PushLine("456.").execute(&mut state);
142        assert_eq!(state.lines, Lines::from("Hello World!\n\n123.\n456."));
143    }
144}