1use crossterm::event::{
2 KeyCode,
3 KeyEvent,
4};
5use unicode_width::UnicodeWidthChar;
6
7#[derive(Default, Debug, PartialEq, Clone)]
9pub(crate) struct AreaBuffer {
10 pub(crate) content: Vec<Vec<char>>,
11 content_width: usize,
12 cursor_loc_x: usize,
13 cursor_loc_y: usize,
14}
15
16impl AreaBuffer {
17 fn add_char(&mut self, c: char) {
18 let line = self
19 .content
20 .get_mut(self.cursor_loc_y)
21 .expect("must have a line");
22 line.insert(self.cursor_loc_x, c);
23 self.cursor_loc_x += 1;
24 self.calc_content_width();
25 }
26
27 fn calc_content_width(&mut self) {
28 self.content_width = self
29 .content
30 .iter()
31 .map(|line| line.len())
32 .max()
33 .unwrap_or(0);
34 }
35
36 pub(crate) fn add_line<S: ToString>(&mut self, s: S) {
37 let line = s.to_string().chars().collect();
38 self.content.push(line);
39 self.cursor_loc_y += 1;
40 self.calc_content_width();
41 }
42
43 pub fn process_key_event(
44 &mut self,
45 KeyEvent { code, modifiers: _ }: KeyEvent,
46 ) {
47 match code {
48 KeyCode::Char(c) => {
49 self.add_char(c);
50 }
51 KeyCode::Enter => {
52 if let Some(line) = self.content.get_mut(self.cursor_loc_y) {
53 let new_line = line.split_off(self.cursor_loc_x);
54 self.cursor_loc_y += 1;
55 self.cursor_loc_x = 0;
56 self.content.insert(self.cursor_loc_y, new_line);
57 self.calc_content_width();
58 }
59 }
60 KeyCode::Left => {
61 if self.cursor_loc_x > 0 {
62 self.cursor_loc_x -= 1;
63 }
64 }
65 KeyCode::Right => {
66 if let Some(line) = self.content.get(self.cursor_loc_y) {
67 if self.cursor_loc_x < line.len() {
68 self.cursor_loc_x += 1;
69 }
70 }
71 }
72 KeyCode::Up => {
73 if self.cursor_loc_y > 0 {
74 self.cursor_loc_y -= 1;
75 if let Some(line) = self.content.get(self.cursor_loc_y) {
76 if self.cursor_loc_x > line.len() {
77 self.cursor_loc_x = line.len();
78 }
79 }
80 }
81 }
82 KeyCode::Down => {
83 if self.cursor_loc_y < self.content.len() - 1 {
84 self.cursor_loc_y += 1;
85 if let Some(line) = self.content.get(self.cursor_loc_y) {
86 if self.cursor_loc_x > line.len() {
87 self.cursor_loc_x = line.len();
88 }
89 }
90 }
91 }
92 KeyCode::Backspace => {
93 if let Some(line) = self.content.get_mut(self.cursor_loc_y) {
94 if self.cursor_loc_x > 0 && line.len() > 0 {
95 self.cursor_loc_x -= 1;
96 line.remove(self.cursor_loc_x);
97 }
98 self.calc_content_width();
99 }
100 }
101 KeyCode::Delete => {
102 if let Some(line) = self.content.get_mut(self.cursor_loc_y) {
103 if self.cursor_loc_x < line.len() {
104 line.remove(self.cursor_loc_x);
105 }
106 self.calc_content_width();
107 }
108 }
109 _ => (),
110 }
111 }
112
113 pub fn set_cursor_loc(&mut self, cursor_x: usize, cursor_y: usize) {
114 self.cursor_loc_x = cursor_x;
115 self.cursor_loc_y = cursor_y;
116 }
117
118 pub fn get_cursor_location(&self) -> (usize, usize) {
119 (self.cursor_loc_x, self.cursor_loc_y)
120 }
121
122 pub fn height(&self) -> usize {
123 self.content.len()
124 }
125
126 pub fn width(&self) -> usize {
127 self.content_width
128 }
129}
130
131impl From<String> for AreaBuffer {
132 fn from(s: String) -> Self {
133 let mut content = vec![];
134 let mut cursor_loc_x = 0;
135 let mut cursor_loc_y = 0;
136 let mut content_width = 0;
137 for line in s.lines() {
138 cursor_loc_x = 0;
139 let mut row = vec![];
140 for ch in line.chars() {
141 row.push(ch);
142 cursor_loc_x += 1;
143 if let Some(width) = ch.width() {
144 for _i in 1..width {
145 row.push('\0');
146 cursor_loc_x += 1;
147 }
148 }
149 }
150 if content_width < row.len() {
151 content_width = row.len();
152 }
153 content.push(row);
154 cursor_loc_y += 1;
155 }
156 if cursor_loc_y > 0 {
157 cursor_loc_y = cursor_loc_y - 1;
158 }
159
160 AreaBuffer {
161 content,
162 content_width,
163 cursor_loc_x,
164 cursor_loc_y,
165 }
166 }
167}
168
169impl ToString for AreaBuffer {
170 fn to_string(&self) -> String {
171 let mut lines = vec![];
172 for row in self.content.iter() {
173 let row_contents: Vec<String> = row
174 .iter()
175 .filter(|ch| **ch != '\0')
176 .map(ToString::to_string)
177 .collect();
178 let line = row_contents.join("").trim_end().to_string();
179 lines.push(line);
180 }
181 lines.join("\n")
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use crossterm::event::KeyCode;
189
190 #[test]
191 fn add_char1() {
192 let s = "The quick brown fox ".to_string();
193 let mut area_buffer = AreaBuffer::from(s);
194 assert_eq!(0, area_buffer.cursor_loc_y);
195 assert_eq!(20, area_buffer.cursor_loc_x);
196 area_buffer.add_char('j');
197 assert_eq!("The quick brown fox j", area_buffer.to_string());
198 }
199
200 #[test]
201 fn add_char2() {
202 let s = "The quick brown fox ".to_string();
203 let mut area_buffer = AreaBuffer::from(s);
204 assert_eq!(0, area_buffer.cursor_loc_y);
205 assert_eq!(20, area_buffer.cursor_loc_x);
206 area_buffer.add_char('j');
207 assert_eq!(21, area_buffer.cursor_loc_x);
208 area_buffer.add_char('u');
209 assert_eq!(22, area_buffer.cursor_loc_x);
210 assert_eq!("The quick brown fox ju", area_buffer.to_string());
211 }
212
213 #[test]
214 fn add_enter() {
215 let s = "The quick brown fox ".to_string();
216 let mut area_buffer = AreaBuffer::from(s);
217 assert_eq!(0, area_buffer.cursor_loc_y);
218 assert_eq!(20, area_buffer.cursor_loc_x);
219 area_buffer.process_key_event(KeyCode::Enter.into());
220 assert_eq!(1, area_buffer.cursor_loc_y);
221 assert_eq!(0, area_buffer.cursor_loc_x);
222 area_buffer.process_key_event(KeyCode::Char('j').into());
223 assert_eq!(1, area_buffer.cursor_loc_y);
224 assert_eq!(1, area_buffer.cursor_loc_x);
225 }
226}