1use crate::{util, TextBuffer};
2pub use action::Action;
3pub use history::Recorded;
4use nalgebra::Point2;
5use std::fmt;
6use unicode_width::UnicodeWidthChar;
7
8mod action;
9mod history;
10
11#[derive(Default, Clone)]
13pub struct TextEdit {
14 text_buffer: TextBuffer,
15 recorded: Recorded,
17 pub selection: Selection,
18}
19
20#[derive(Default, Clone)]
21pub struct Selection {
22 pub start: Option<Point2<i32>>,
23 pub end: Option<Point2<i32>>,
24}
25
26impl fmt::Debug for Selection {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 let start = if let Some(start) = self.start {
29 format!("({},{})", start.x, start.y)
30 } else {
31 "..".to_string()
32 };
33 let end = if let Some(end) = self.end {
34 format!("({},{})", end.x, end.y)
35 } else {
36 "..".to_string()
37 };
38
39 write!(f, "{start} -> {end}")
40 }
41}
42
43#[derive(Debug, Clone, Copy)]
44pub enum SelectionMode {
45 Linear,
46 Block,
47}
48
49impl Default for SelectionMode {
50 fn default() -> Self {
51 Self::Linear
52 }
53}
54
55impl TextEdit {
56 pub fn new_from_str(content: &str) -> Self {
57 let text_buffer = TextBuffer::new_from_str(content);
58 TextEdit {
59 text_buffer,
60 recorded: Recorded::new(),
61 selection: Selection::default(),
62 }
63 }
64
65 pub fn text_buffer(&self) -> &TextBuffer {
66 &self.text_buffer
67 }
68
69 pub fn set_selection(&mut self, start: Point2<i32>, end: Point2<i32>) {
70 self.selection.start = Some(start);
71 self.selection.end = Some(end);
72 }
73
74 pub fn set_selection_start(&mut self, start: Point2<i32>) {
75 self.selection.start = Some(start);
76 }
77
78 pub fn clear(&mut self) {
79 self.text_buffer.clear();
80 self.clear_selection();
81 self.recorded.clear();
82 }
83
84 pub fn set_selection_end(&mut self, end: Point2<i32>) {
85 self.selection.end = Some(end);
86 }
87
88 pub fn selection(&self) -> &Selection {
89 &self.selection
90 }
91
92 pub fn command_insert_char(&mut self, ch: char) {
93 let cursor = self.text_buffer.get_position();
94 self.text_buffer.command_insert_char(ch);
95 self.recorded.insert_char(cursor, ch);
96 }
97
98 pub fn get_char(&self, loc: Point2<usize>) -> Option<char> {
99 self.text_buffer.get_char(loc)
100 }
101
102 pub fn command_smart_replace_insert_char(&mut self, ch: char) {
103 let cursor = self.text_buffer.get_position();
104 let has_right_char = if let Some(ch) = self.get_char(Point2::new(cursor.x + 1, cursor.y)) {
105 !ch.is_whitespace()
106 } else {
107 false
108 };
109 if has_right_char {
110 self.command_insert_char(ch);
111 } else {
112 self.command_replace_char(ch);
113 self.command_move_right();
114 }
115 }
116
117 pub fn command_replace_char(&mut self, ch: char) {
118 let cursor = self.text_buffer.get_position();
119 if let Some(old_ch) = self.text_buffer.command_replace_char(ch) {
120 self.recorded.replace_char(cursor, old_ch, ch);
121 }
122 }
123
124 pub fn command_delete_back(&mut self) {
125 let ch = self.text_buffer.command_delete_back();
126 let cursor = self.text_buffer.get_position();
127 self.recorded.delete(cursor, ch);
128 }
129
130 pub fn command_delete_forward(&mut self) {
131 let ch = self.text_buffer.command_delete_forward();
132 let cursor = self.text_buffer.get_position();
133 self.recorded.delete(cursor, ch);
134 }
135
136 pub fn command_move_up(&mut self) {
137 self.text_buffer.move_up();
138 }
139
140 pub fn command_move_up_clamped(&mut self) {
141 self.text_buffer.move_up_clamped();
142 }
143
144 pub fn command_move_down(&mut self) {
145 self.text_buffer.move_down();
146 }
147
148 pub fn command_move_down_clamped(&mut self) {
149 self.text_buffer.move_down_clamped();
150 }
151
152 pub fn command_move_left(&mut self) {
153 self.text_buffer.move_left();
154 }
155
156 pub fn command_move_left_start(&mut self) {
157 self.text_buffer.move_left_start();
158 }
159
160 pub fn command_move_right(&mut self) {
161 self.text_buffer.move_right();
162 }
163
164 pub fn command_move_right_end(&mut self) {
165 self.text_buffer.move_right_end();
166 }
167
168 pub fn command_move_right_clamped(&mut self) {
169 self.text_buffer.move_right_clamped();
170 }
171
172 pub fn command_break_line(&mut self) {
173 let pos = self.text_buffer.get_position();
174 self.text_buffer.command_break_line(pos);
175 self.recorded.break_line(pos);
176 }
177
178 pub fn command_join_line(&mut self) {
179 let pos = self.text_buffer.get_position();
180 self.text_buffer.command_join_line(pos);
181 self.recorded.join_line(pos);
182 }
183
184 pub fn command_insert_text(&mut self, text: &str) {
185 self.text_buffer.command_insert_text(text);
186 }
187
188 pub fn command_set_position(&mut self, cursor: Point2<usize>) {
189 self.text_buffer.set_position(cursor);
190 }
191
192 pub fn command_set_position_clamped(&mut self, cursor: Point2<usize>) {
193 self.text_buffer.set_position_clamped(cursor);
194 }
195
196 pub fn command_set_selection(&mut self, start: Point2<i32>, end: Point2<i32>) {
197 self.set_selection(start, end)
198 }
199
200 pub fn command_select_all(&mut self) {
201 let start = Point2::new(0, 0);
202 let max = self.text_buffer.last_char_position();
203 let end = Point2::new(max.x as i32, max.y as i32);
204 self.set_selection(start, end);
205 log::info!(
206 "in text_edit: select_all selected text: {:?}",
207 self.selected_text_in_linear_mode()
208 );
209 }
210
211 pub fn command_select_all_block_mode(&mut self) {
212 let start = Point2::new(0, 0);
213 let max = self.text_buffer.max_position();
214 let end = Point2::new(max.x as i32, max.y as i32);
215 self.set_selection(start, end);
216 }
217
218 pub fn bump_history(&mut self) {
221 self.recorded.bump_history();
222 }
223
224 pub fn command_undo(&mut self) {
225 if let Some(location) = self.recorded.undo(&mut self.text_buffer) {
226 self.text_buffer.set_position(location);
227 }
228 }
229
230 pub fn command_redo(&mut self) {
231 if let Some(location) = self.recorded.redo(&mut self.text_buffer) {
232 self.text_buffer.set_position(location);
233 }
234 }
235
236 pub fn is_selected_in_linear_mode(&self, loc: Point2<i32>) -> bool {
237 match (self.selection.start, self.selection.end) {
238 (Some(start), Some(end)) => {
239 let (start, end) = util::reorder_top_down_left_right(start, end);
240 let only_one_line = start.y == end.y;
241 let in_first_line = loc.y == start.y;
242 let in_inner_line = loc.y > start.y && loc.y < end.y;
243 let in_last_line = loc.y == end.y;
244 if in_first_line {
245 if only_one_line {
246 loc.x >= start.x && loc.x <= end.x
247 } else {
248 loc.x >= start.x
249 }
250 } else if in_inner_line {
251 true
252 } else if in_last_line {
253 if only_one_line {
254 loc.x >= start.x && loc.x <= end.x
255 } else {
256 loc.x <= end.x
257 }
258 } else {
259 false
261 }
262 }
263 _ => false,
264 }
265 }
266
267 pub fn is_selected_in_block_mode(&self, loc: Point2<i32>) -> bool {
268 match (self.selection.start, self.selection.end) {
269 (Some(start), Some(end)) => {
270 let (start, end) = util::normalize_points(start, end);
271 loc.x >= start.x && loc.x <= end.x && loc.y >= start.y && loc.y <= end.y
272 }
273 _ => false,
274 }
275 }
276
277 pub fn clear_selection(&mut self) {
279 self.selection.start = None;
280 self.selection.end = None;
281 }
282
283 pub fn selected_text_in_linear_mode(&self) -> Option<String> {
284 match self.selection_reorder_casted() {
285 Some((start, end)) => Some(self.text_buffer.get_text_in_linear_mode(start, end)),
286 _ => None,
287 }
288 }
289
290 pub fn selected_text_in_block_mode(&self) -> Option<String> {
291 match self.selection_normalized_casted() {
292 Some((start, end)) => Some(self.text_buffer.get_text_in_block_mode(start, end)),
293 _ => None,
294 }
295 }
296
297 pub fn cut_selected_text_in_linear_mode(&mut self) -> Option<String> {
298 match self.selection_reorder_casted() {
299 Some((start, end)) => {
300 let cut_text = self.text_buffer.cut_text_in_linear_mode(start, end);
301 if !cut_text.is_empty() {
302 self.record_deleted_text(start, end, &cut_text);
303 }
304 Some(cut_text)
305 }
306 _ => None,
307 }
308 }
309
310 fn record_deleted_text(&mut self, start: Point2<usize>, _end: Point2<usize>, cut_text: &str) {
311 let lines = cut_text.lines();
312 for (y, line) in lines.enumerate() {
313 for (_x, ch) in line.chars().enumerate() {
314 let position = Point2::new(start.x, start.y + y);
315 self.recorded.delete(position, Some(ch));
316 }
317 }
318 }
319
320 pub fn selection_normalized_casted(&self) -> Option<(Point2<usize>, Point2<usize>)> {
322 match (self.selection.start, self.selection.end) {
323 (Some(start), Some(end)) => {
324 let (start, end) = util::normalize_points(start, end);
325 let start = util::cast_point(start);
326 let end = util::cast_point(end);
327 let start = self.text_buffer.clamp_position(start);
328 let end = self.text_buffer.clamp_position(end);
329 Some((start, end))
330 }
331 _ => None,
332 }
333 }
334
335 pub fn selection_reorder_casted(&self) -> Option<(Point2<usize>, Point2<usize>)> {
336 match (self.selection.start, self.selection.end) {
337 (Some(start), Some(end)) => {
338 let (start, end) = util::reorder_top_down_left_right(start, end);
339 let start = util::cast_point(start);
340 let end = util::cast_point(end);
341 let start = self.text_buffer.clamp_position(start);
342 let end = self.text_buffer.clamp_position(end);
343 Some((start, end))
344 }
345 _ => None,
346 }
347 }
348
349 pub fn cut_selected_text_in_block_mode(&mut self) -> Option<String> {
350 match self.selection_normalized_casted() {
351 Some((start, end)) => {
352 let cut_text = self.text_buffer.cut_text_in_block_mode(start, end);
353 if !cut_text.is_empty() {
354 self.record_deleted_text(start, end, &cut_text);
355 }
356 Some(cut_text)
357 }
358 _ => None,
359 }
360 }
361
362 pub fn paste_text_in_block_mode(&mut self, text_block: String) {
363 self.text_buffer.paste_text_in_block_mode(text_block);
364 }
365
366 pub fn command_merge_text(&mut self, text_block: String) {
369 for (line_index, line) in text_block.lines().enumerate() {
370 let mut width = 0;
371 let y = line_index;
372 for ch in line.chars() {
373 if ch != crate::BLANK_CH {
374 let x = width;
375 self.command_set_position(Point2::new(x, y));
376 self.command_replace_char(ch);
377 }
378 width += ch.width().unwrap_or(0);
379 }
380 }
381 }
382
383 pub fn get_position(&self) -> Point2<usize> {
384 self.text_buffer.get_position()
385 }
386
387 pub fn max_position(&self) -> Point2<usize> {
388 self.text_buffer.max_position()
389 }
390
391 pub fn get_content(&self) -> String {
392 self.text_buffer.to_string()
393 }
394
395 pub fn total_lines(&self) -> usize {
396 self.text_buffer.total_lines()
397 }
398
399 pub fn lines(&self) -> Vec<String> {
400 self.text_buffer.lines()
401 }
402
403 pub fn numberline_wide(&self) -> usize {
406 self.text_buffer.numberline_wide()
407 }
408}