1use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer};
2use crate::enums::{EditType, UndoBehavior};
3use crate::{core_editor::get_default_clipboard, EditCommand};
4
5pub struct Editor {
10 line_buffer: LineBuffer,
11 cut_buffer: Box<dyn Clipboard>,
12
13 edit_stack: EditStack<LineBuffer>,
14 last_undo_behavior: UndoBehavior,
15}
16
17impl Default for Editor {
18 fn default() -> Self {
19 Editor {
20 line_buffer: LineBuffer::new(),
21 cut_buffer: Box::new(get_default_clipboard()),
22 edit_stack: EditStack::new(),
23 last_undo_behavior: UndoBehavior::CreateUndoPoint,
24 }
25 }
26}
27
28impl Editor {
29 pub const fn line_buffer(&self) -> &LineBuffer {
31 &self.line_buffer
32 }
33
34 pub(crate) fn set_line_buffer(&mut self, line_buffer: LineBuffer, undo_behavior: UndoBehavior) {
37 self.line_buffer = line_buffer;
38 self.update_undo_state(undo_behavior);
39 }
40
41 pub(crate) fn run_edit_command(&mut self, command: &EditCommand) {
42 match command {
43 EditCommand::MoveToStart => self.line_buffer.move_to_start(),
44 EditCommand::MoveToLineStart => self.line_buffer.move_to_line_start(),
45 EditCommand::MoveToEnd => self.line_buffer.move_to_end(),
46 EditCommand::MoveToLineEnd => self.line_buffer.move_to_line_end(),
47 EditCommand::MoveToPosition(pos) => self.line_buffer.set_insertion_point(*pos),
48 EditCommand::MoveLeft => self.line_buffer.move_left(),
49 EditCommand::MoveRight => self.line_buffer.move_right(),
50 EditCommand::MoveWordLeft => self.line_buffer.move_word_left(),
51 EditCommand::MoveBigWordLeft => self.line_buffer.move_big_word_left(),
52 EditCommand::MoveWordRight => self.line_buffer.move_word_right(),
53 EditCommand::MoveWordRightStart => self.line_buffer.move_word_right_start(),
54 EditCommand::MoveBigWordRightStart => self.line_buffer.move_big_word_right_start(),
55 EditCommand::MoveWordRightEnd => self.line_buffer.move_word_right_end(),
56 EditCommand::MoveBigWordRightEnd => self.line_buffer.move_big_word_right_end(),
57 EditCommand::InsertChar(c) => self.line_buffer.insert_char(*c),
58 EditCommand::Complete => {}
59 EditCommand::InsertString(str) => self.line_buffer.insert_str(str),
60 EditCommand::InsertNewline => self.line_buffer.insert_newline(),
61 EditCommand::ReplaceChar(chr) => self.replace_char(*chr),
62 EditCommand::ReplaceChars(n_chars, str) => self.replace_chars(*n_chars, str),
63 EditCommand::Backspace => self.line_buffer.delete_left_grapheme(),
64 EditCommand::Delete => self.line_buffer.delete_right_grapheme(),
65 EditCommand::CutChar => self.cut_char(),
66 EditCommand::BackspaceWord => self.line_buffer.delete_word_left(),
67 EditCommand::DeleteWord => self.line_buffer.delete_word_right(),
68 EditCommand::Clear => self.line_buffer.clear(),
69 EditCommand::ClearToLineEnd => self.line_buffer.clear_to_line_end(),
70 EditCommand::CutCurrentLine => self.cut_current_line(),
71 EditCommand::CutFromStart => self.cut_from_start(),
72 EditCommand::CutFromLineStart => self.cut_from_line_start(),
73 EditCommand::CutToEnd => self.cut_from_end(),
74 EditCommand::CutToLineEnd => self.cut_to_line_end(),
75 EditCommand::CutWordLeft => self.cut_word_left(),
76 EditCommand::CutBigWordLeft => self.cut_big_word_left(),
77 EditCommand::CutWordRight => self.cut_word_right(),
78 EditCommand::CutBigWordRight => self.cut_big_word_right(),
79 EditCommand::CutWordRightToNext => self.cut_word_right_to_next(),
80 EditCommand::CutBigWordRightToNext => self.cut_big_word_right_to_next(),
81 EditCommand::PasteCutBufferBefore => self.insert_cut_buffer_before(),
82 EditCommand::PasteCutBufferAfter => self.insert_cut_buffer_after(),
83 EditCommand::UppercaseWord => self.line_buffer.uppercase_word(),
84 EditCommand::LowercaseWord => self.line_buffer.lowercase_word(),
85 EditCommand::SwitchcaseChar => self.line_buffer.switchcase_char(),
86 EditCommand::CapitalizeChar => self.line_buffer.capitalize_char(),
87 EditCommand::SwapWords => self.line_buffer.swap_words(),
88 EditCommand::SwapGraphemes => self.line_buffer.swap_graphemes(),
89 EditCommand::Undo => self.undo(),
90 EditCommand::Redo => self.redo(),
91 EditCommand::CutRightUntil(c) => self.cut_right_until_char(*c, false, true),
92 EditCommand::CutRightBefore(c) => self.cut_right_until_char(*c, true, true),
93 EditCommand::MoveRightUntil(c) => self.move_right_until_char(*c, false, true),
94 EditCommand::MoveRightBefore(c) => self.move_right_until_char(*c, true, true),
95 EditCommand::CutLeftUntil(c) => self.cut_left_until_char(*c, false, true),
96 EditCommand::CutLeftBefore(c) => self.cut_left_until_char(*c, true, true),
97 EditCommand::MoveLeftUntil(c) => self.move_left_until_char(*c, false, true),
98 EditCommand::MoveLeftBefore(c) => self.move_left_until_char(*c, true, true),
99 }
100
101 let new_undo_behavior = match (command, command.edit_type()) {
102 (_, EditType::MoveCursor) => UndoBehavior::MoveCursor,
103 (EditCommand::InsertChar(c), EditType::EditText) => UndoBehavior::InsertCharacter(*c),
104 (EditCommand::Delete, EditType::EditText) => {
105 let deleted_char = self.edit_stack.current().grapheme_right().chars().next();
106 UndoBehavior::Delete(deleted_char)
107 }
108 (EditCommand::Backspace, EditType::EditText) => {
109 let deleted_char = self.edit_stack.current().grapheme_left().chars().next();
110 UndoBehavior::Backspace(deleted_char)
111 }
112 (_, EditType::UndoRedo) => UndoBehavior::UndoRedo,
113 (_, _) => UndoBehavior::CreateUndoPoint,
114 };
115 self.update_undo_state(new_undo_behavior);
116 }
117
118 pub(crate) fn move_line_up(&mut self) {
119 self.line_buffer.move_line_up();
120 self.update_undo_state(UndoBehavior::MoveCursor);
121 }
122
123 pub(crate) fn move_line_down(&mut self) {
124 self.line_buffer.move_line_down();
125 self.update_undo_state(UndoBehavior::MoveCursor);
126 }
127
128 pub fn get_buffer(&self) -> &str {
130 self.line_buffer.get_buffer()
131 }
132
133 pub fn edit_buffer<F>(&mut self, func: F, undo_behavior: UndoBehavior)
135 where
136 F: FnOnce(&mut LineBuffer),
137 {
138 self.update_undo_state(undo_behavior);
139 func(&mut self.line_buffer);
140 }
141
142 pub(crate) fn set_buffer(&mut self, buffer: String, undo_behavior: UndoBehavior) {
145 self.line_buffer.set_buffer(buffer);
146 self.update_undo_state(undo_behavior);
147 }
148
149 pub(crate) fn insertion_point(&self) -> usize {
150 self.line_buffer.insertion_point()
151 }
152
153 pub(crate) fn is_empty(&self) -> bool {
154 self.line_buffer.is_empty()
155 }
156
157 pub(crate) fn is_cursor_at_first_line(&self) -> bool {
158 self.line_buffer.is_cursor_at_first_line()
159 }
160
161 pub(crate) fn is_cursor_at_last_line(&self) -> bool {
162 self.line_buffer.is_cursor_at_last_line()
163 }
164
165 pub(crate) fn is_cursor_at_buffer_end(&self) -> bool {
166 self.line_buffer.insertion_point() == self.get_buffer().len()
167 }
168
169 pub(crate) fn reset_undo_stack(&mut self) {
170 self.edit_stack.reset();
171 }
172
173 pub(crate) fn move_to_start(&mut self, undo_behavior: UndoBehavior) {
174 self.line_buffer.move_to_start();
175 self.update_undo_state(undo_behavior);
176 }
177
178 pub(crate) fn move_to_end(&mut self, undo_behavior: UndoBehavior) {
179 self.line_buffer.move_to_end();
180 self.update_undo_state(undo_behavior);
181 }
182
183 #[allow(dead_code)]
184 pub(crate) fn move_to_line_start(&mut self, undo_behavior: UndoBehavior) {
185 self.line_buffer.move_to_line_start();
186 self.update_undo_state(undo_behavior);
187 }
188
189 pub(crate) fn move_to_line_end(&mut self, undo_behavior: UndoBehavior) {
190 self.line_buffer.move_to_line_end();
191 self.update_undo_state(undo_behavior);
192 }
193
194 fn undo(&mut self) {
195 let val = self.edit_stack.undo();
196 self.line_buffer = val.clone();
197 }
198
199 fn redo(&mut self) {
200 let val = self.edit_stack.redo();
201 self.line_buffer = val.clone();
202 }
203
204 fn update_undo_state(&mut self, undo_behavior: UndoBehavior) {
205 if matches!(undo_behavior, UndoBehavior::UndoRedo) {
206 self.last_undo_behavior = UndoBehavior::UndoRedo;
207 return;
208 }
209 if !undo_behavior.create_undo_point_after(&self.last_undo_behavior) {
210 self.edit_stack.undo();
211 }
212 self.edit_stack.insert(self.line_buffer.clone());
213 self.last_undo_behavior = undo_behavior;
214 }
215
216 fn cut_current_line(&mut self) {
217 let deletion_range = self.line_buffer.current_line_range();
218
219 let cut_slice = &self.line_buffer.get_buffer()[deletion_range.clone()];
220 if !cut_slice.is_empty() {
221 self.cut_buffer.set(cut_slice, ClipboardMode::Lines);
222 self.line_buffer.set_insertion_point(deletion_range.start);
223 self.line_buffer.clear_range(deletion_range);
224 }
225 }
226
227 fn cut_from_start(&mut self) {
228 let insertion_offset = self.line_buffer.insertion_point();
229 if insertion_offset > 0 {
230 self.cut_buffer.set(
231 &self.line_buffer.get_buffer()[..insertion_offset],
232 ClipboardMode::Normal,
233 );
234 self.line_buffer.clear_to_insertion_point();
235 }
236 }
237
238 fn cut_from_line_start(&mut self) {
239 let previous_offset = self.line_buffer.insertion_point();
240 self.line_buffer.move_to_line_start();
241 let deletion_range = self.line_buffer.insertion_point()..previous_offset;
242 let cut_slice = &self.line_buffer.get_buffer()[deletion_range.clone()];
243 if !cut_slice.is_empty() {
244 self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
245 self.line_buffer.clear_range(deletion_range);
246 }
247 }
248
249 fn cut_from_end(&mut self) {
250 let cut_slice = &self.line_buffer.get_buffer()[self.line_buffer.insertion_point()..];
251 if !cut_slice.is_empty() {
252 self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
253 self.line_buffer.clear_to_end();
254 }
255 }
256
257 fn cut_to_line_end(&mut self) {
258 let cut_slice = &self.line_buffer.get_buffer()
259 [self.line_buffer.insertion_point()..self.line_buffer.find_current_line_end()];
260 if !cut_slice.is_empty() {
261 self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
262 self.line_buffer.clear_to_line_end();
263 }
264 }
265
266 fn cut_word_left(&mut self) {
267 let insertion_offset = self.line_buffer.insertion_point();
268 let left_index = self.line_buffer.word_left_index();
269 if left_index < insertion_offset {
270 let cut_range = left_index..insertion_offset;
271 self.cut_buffer.set(
272 &self.line_buffer.get_buffer()[cut_range.clone()],
273 ClipboardMode::Normal,
274 );
275 self.line_buffer.clear_range(cut_range);
276 self.line_buffer.set_insertion_point(left_index);
277 }
278 }
279
280 fn cut_big_word_left(&mut self) {
281 let insertion_offset = self.line_buffer.insertion_point();
282 let left_index = self.line_buffer.big_word_left_index();
283 if left_index < insertion_offset {
284 let cut_range = left_index..insertion_offset;
285 self.cut_buffer.set(
286 &self.line_buffer.get_buffer()[cut_range.clone()],
287 ClipboardMode::Normal,
288 );
289 self.line_buffer.clear_range(cut_range);
290 self.line_buffer.set_insertion_point(left_index);
291 }
292 }
293
294 fn cut_word_right(&mut self) {
295 let insertion_offset = self.line_buffer.insertion_point();
296 let right_index = self.line_buffer.word_right_index();
297 if right_index > insertion_offset {
298 let cut_range = insertion_offset..right_index;
299 self.cut_buffer.set(
300 &self.line_buffer.get_buffer()[cut_range.clone()],
301 ClipboardMode::Normal,
302 );
303 self.line_buffer.clear_range(cut_range);
304 }
305 }
306
307 fn cut_big_word_right(&mut self) {
308 let insertion_offset = self.line_buffer.insertion_point();
309 let right_index = self.line_buffer.next_whitespace();
310 if right_index > insertion_offset {
311 let cut_range = insertion_offset..right_index;
312 self.cut_buffer.set(
313 &self.line_buffer.get_buffer()[cut_range.clone()],
314 ClipboardMode::Normal,
315 );
316 self.line_buffer.clear_range(cut_range);
317 }
318 }
319
320 fn cut_word_right_to_next(&mut self) {
321 let insertion_offset = self.line_buffer.insertion_point();
322 let right_index = self.line_buffer.word_right_start_index();
323 if right_index > insertion_offset {
324 let cut_range = insertion_offset..right_index;
325 self.cut_buffer.set(
326 &self.line_buffer.get_buffer()[cut_range.clone()],
327 ClipboardMode::Normal,
328 );
329 self.line_buffer.clear_range(cut_range);
330 }
331 }
332
333 fn cut_big_word_right_to_next(&mut self) {
334 let insertion_offset = self.line_buffer.insertion_point();
335 let right_index = self.line_buffer.big_word_right_start_index();
336 if right_index > insertion_offset {
337 let cut_range = insertion_offset..right_index;
338 self.cut_buffer.set(
339 &self.line_buffer.get_buffer()[cut_range.clone()],
340 ClipboardMode::Normal,
341 );
342 self.line_buffer.clear_range(cut_range);
343 }
344 }
345
346 fn cut_char(&mut self) {
347 let insertion_offset = self.line_buffer.insertion_point();
348 let right_index = self.line_buffer.grapheme_right_index();
349 if right_index > insertion_offset {
350 let cut_range = insertion_offset..right_index;
351 self.cut_buffer.set(
352 &self.line_buffer.get_buffer()[cut_range.clone()],
353 ClipboardMode::Normal,
354 );
355 self.line_buffer.clear_range(cut_range);
356 }
357 }
358
359 fn insert_cut_buffer_before(&mut self) {
360 match self.cut_buffer.get() {
361 (content, ClipboardMode::Normal) => {
362 self.line_buffer.insert_str(&content);
363 }
364 (mut content, ClipboardMode::Lines) => {
365 self.line_buffer.move_to_line_start();
367 self.line_buffer.move_line_up();
368 if !content.ends_with('\n') {
369 content.push('\n');
371 }
372 self.line_buffer.insert_str(&content);
373 }
374 }
375 }
376
377 fn insert_cut_buffer_after(&mut self) {
378 match self.cut_buffer.get() {
379 (content, ClipboardMode::Normal) => {
380 self.line_buffer.move_right();
381 self.line_buffer.insert_str(&content);
382 }
383 (mut content, ClipboardMode::Lines) => {
384 self.line_buffer.move_to_line_start();
386 self.line_buffer.move_line_down();
387 if !content.ends_with('\n') {
388 content.push('\n');
390 }
391 self.line_buffer.insert_str(&content);
392 }
393 }
394 }
395
396 fn move_right_until_char(&mut self, c: char, before_char: bool, current_line: bool) {
397 if before_char {
398 self.line_buffer.move_right_before(c, current_line);
399 } else {
400 self.line_buffer.move_right_until(c, current_line);
401 }
402 }
403
404 fn move_left_until_char(&mut self, c: char, before_char: bool, current_line: bool) {
405 if before_char {
406 self.line_buffer.move_left_before(c, current_line);
407 } else {
408 self.line_buffer.move_left_until(c, current_line);
409 }
410 }
411
412 fn cut_right_until_char(&mut self, c: char, before_char: bool, current_line: bool) {
413 if let Some(index) = self.line_buffer.find_char_right(c, current_line) {
414 let extra = if before_char { 0 } else { c.len_utf8() };
417 let cut_slice =
418 &self.line_buffer.get_buffer()[self.line_buffer.insertion_point()..index + extra];
419
420 if !cut_slice.is_empty() {
421 self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
422
423 if before_char {
424 self.line_buffer.delete_right_before_char(c, current_line);
425 } else {
426 self.line_buffer.delete_right_until_char(c, current_line);
427 }
428 }
429 }
430 }
431
432 fn cut_left_until_char(&mut self, c: char, before_char: bool, current_line: bool) {
433 if let Some(index) = self.line_buffer.find_char_left(c, current_line) {
434 let extra = if before_char { c.len_utf8() } else { 0 };
437 let cut_slice =
438 &self.line_buffer.get_buffer()[index + extra..self.line_buffer.insertion_point()];
439
440 if !cut_slice.is_empty() {
441 self.cut_buffer.set(cut_slice, ClipboardMode::Normal);
442
443 if before_char {
444 self.line_buffer.delete_left_before_char(c, current_line);
445 } else {
446 self.line_buffer.delete_left_until_char(c, current_line);
447 }
448 }
449 }
450 }
451
452 fn replace_char(&mut self, character: char) {
453 self.line_buffer.delete_right_grapheme();
454
455 self.line_buffer.insert_char(character);
456 }
457
458 fn replace_chars(&mut self, n_chars: usize, string: &str) {
459 for _ in 0..n_chars {
460 self.line_buffer.delete_right_grapheme();
461 }
462
463 self.line_buffer.insert_str(string);
464 }
465}
466
467#[cfg(test)]
468mod test {
469 use super::*;
470 use pretty_assertions::assert_eq;
471 use rstest::rstest;
472
473 fn editor_with(buffer: &str) -> Editor {
474 let mut editor = Editor::default();
475 editor.set_buffer(buffer.to_string(), UndoBehavior::CreateUndoPoint);
476 editor
477 }
478
479 #[rstest]
480 #[case("abc def ghi", 11, "abc def ")]
481 #[case("abc def-ghi", 11, "abc def-")]
482 #[case("abc def.ghi", 11, "abc ")]
483 fn test_cut_word_left(#[case] input: &str, #[case] position: usize, #[case] expected: &str) {
484 let mut editor = editor_with(input);
485 editor.line_buffer.set_insertion_point(position);
486
487 editor.cut_word_left();
488
489 assert_eq!(editor.get_buffer(), expected);
490 }
491
492 #[rstest]
493 #[case("abc def ghi", 11, "abc def ")]
494 #[case("abc def-ghi", 11, "abc ")]
495 #[case("abc def.ghi", 11, "abc ")]
496 #[case("abc def gh ", 11, "abc def ")]
497 fn test_cut_big_word_left(
498 #[case] input: &str,
499 #[case] position: usize,
500 #[case] expected: &str,
501 ) {
502 let mut editor = editor_with(input);
503 editor.line_buffer.set_insertion_point(position);
504
505 editor.cut_big_word_left();
506
507 assert_eq!(editor.get_buffer(), expected);
508 }
509
510 #[rstest]
511 #[case("hello world", 0, 'l', 1, false, "lo world")]
512 #[case("hello world", 0, 'l', 1, true, "llo world")]
513 #[ignore = "Deleting two consecutives is not implemented correctly and needs the multiplier explicitly."]
514 #[case("hello world", 0, 'l', 2, false, "o world")]
515 #[case("hello world", 0, 'h', 1, false, "hello world")]
516 #[case("hello world", 0, 'l', 3, true, "ld")]
517 #[case("hello world", 4, 'o', 1, true, "hellorld")]
518 #[case("hello world", 4, 'w', 1, false, "hellorld")]
519 #[case("hello world", 4, 'o', 1, false, "hellrld")]
520 fn test_cut_right_until_char(
521 #[case] input: &str,
522 #[case] position: usize,
523 #[case] search_char: char,
524 #[case] repeat: usize,
525 #[case] before_char: bool,
526 #[case] expected: &str,
527 ) {
528 let mut editor = editor_with(input);
529 editor.line_buffer.set_insertion_point(position);
530 for _ in 0..repeat {
531 editor.cut_right_until_char(search_char, before_char, true);
532 }
533 assert_eq!(editor.get_buffer(), expected);
534 }
535
536 #[rstest]
537 #[case("abc", 1, 'X', "aXc")]
538 #[case("abc", 1, '🔄', "a🔄c")]
539 #[case("a🔄c", 1, 'X', "aXc")]
540 #[case("a🔄c", 1, '🔀', "a🔀c")]
541 fn test_replace_char(
542 #[case] input: &str,
543 #[case] position: usize,
544 #[case] replacement: char,
545 #[case] expected: &str,
546 ) {
547 let mut editor = editor_with(input);
548 editor.line_buffer.set_insertion_point(position);
549
550 editor.replace_char(replacement);
551
552 assert_eq!(editor.get_buffer(), expected);
553 }
554
555 fn str_to_edit_commands(s: &str) -> Vec<EditCommand> {
556 s.chars().map(EditCommand::InsertChar).collect()
557 }
558
559 #[test]
560 fn test_undo_insert_works_on_work_boundaries() {
561 let mut editor = editor_with("This is a");
562 for cmd in str_to_edit_commands(" test") {
563 editor.run_edit_command(&cmd);
564 }
565 assert_eq!(editor.get_buffer(), "This is a test");
566 editor.run_edit_command(&EditCommand::Undo);
567 assert_eq!(editor.get_buffer(), "This is a");
568 editor.run_edit_command(&EditCommand::Redo);
569 assert_eq!(editor.get_buffer(), "This is a test");
570 }
571
572 #[test]
573 fn test_undo_backspace_works_on_word_boundaries() {
574 let mut editor = editor_with("This is a test");
575 for _ in 0..6 {
576 editor.run_edit_command(&EditCommand::Backspace);
577 }
578 assert_eq!(editor.get_buffer(), "This is ");
579 editor.run_edit_command(&EditCommand::Undo);
580 assert_eq!(editor.get_buffer(), "This is a");
581 editor.run_edit_command(&EditCommand::Undo);
582 assert_eq!(editor.get_buffer(), "This is a test");
583 }
584
585 #[test]
586 fn test_undo_delete_works_on_word_boundaries() {
587 let mut editor = editor_with("This is a test");
588 editor.line_buffer.set_insertion_point(0);
589 for _ in 0..7 {
590 editor.run_edit_command(&EditCommand::Delete);
591 }
592 assert_eq!(editor.get_buffer(), "s a test");
593 editor.run_edit_command(&EditCommand::Undo);
594 assert_eq!(editor.get_buffer(), "is a test");
595 editor.run_edit_command(&EditCommand::Undo);
596 assert_eq!(editor.get_buffer(), "This is a test");
597 }
598
599 #[test]
600 fn test_undo_insert_with_newline() {
601 let mut editor = editor_with("This is a");
602 for cmd in str_to_edit_commands(" \n test") {
603 editor.run_edit_command(&cmd);
604 }
605 assert_eq!(editor.get_buffer(), "This is a \n test");
606 editor.run_edit_command(&EditCommand::Undo);
607 assert_eq!(editor.get_buffer(), "This is a \n");
608 editor.run_edit_command(&EditCommand::Undo);
609 assert_eq!(editor.get_buffer(), "This is a");
610 }
611
612 #[test]
613 fn test_undo_backspace_with_newline() {
614 let mut editor = editor_with("This is a \n test");
615 for _ in 0..8 {
616 editor.run_edit_command(&EditCommand::Backspace);
617 }
618 assert_eq!(editor.get_buffer(), "This is ");
619 editor.run_edit_command(&EditCommand::Undo);
620 assert_eq!(editor.get_buffer(), "This is a");
621 editor.run_edit_command(&EditCommand::Undo);
622 assert_eq!(editor.get_buffer(), "This is a \n");
623 editor.run_edit_command(&EditCommand::Undo);
624 assert_eq!(editor.get_buffer(), "This is a \n test");
625 }
626
627 #[test]
628 fn test_undo_backspace_with_crlf() {
629 let mut editor = editor_with("This is a \r\n test");
630 for _ in 0..8 {
631 editor.run_edit_command(&EditCommand::Backspace);
632 }
633 assert_eq!(editor.get_buffer(), "This is ");
634 editor.run_edit_command(&EditCommand::Undo);
635 assert_eq!(editor.get_buffer(), "This is a");
636 editor.run_edit_command(&EditCommand::Undo);
637 assert_eq!(editor.get_buffer(), "This is a \r\n");
638 editor.run_edit_command(&EditCommand::Undo);
639 assert_eq!(editor.get_buffer(), "This is a \r\n test");
640 }
641
642 #[test]
643 fn test_undo_delete_with_newline() {
644 let mut editor = editor_with("This \n is a test");
645 editor.line_buffer.set_insertion_point(0);
646 for _ in 0..8 {
647 editor.run_edit_command(&EditCommand::Delete);
648 }
649 assert_eq!(editor.get_buffer(), "s a test");
650 editor.run_edit_command(&EditCommand::Undo);
651 assert_eq!(editor.get_buffer(), "is a test");
652 editor.run_edit_command(&EditCommand::Undo);
653 assert_eq!(editor.get_buffer(), "\n is a test");
654 editor.run_edit_command(&EditCommand::Undo);
655 assert_eq!(editor.get_buffer(), "This \n is a test");
656 }
657
658 #[test]
659 fn test_undo_delete_with_crlf() {
660 let mut editor = editor_with("This \r\n is a test");
663 editor.line_buffer.set_insertion_point(0);
664 for _ in 0..8 {
665 editor.run_edit_command(&EditCommand::Delete);
666 }
667 assert_eq!(editor.get_buffer(), "s a test");
668 editor.run_edit_command(&EditCommand::Undo);
669 assert_eq!(editor.get_buffer(), "is a test");
670 editor.run_edit_command(&EditCommand::Undo);
671 assert_eq!(editor.get_buffer(), "\r\n is a test");
672 editor.run_edit_command(&EditCommand::Undo);
673 assert_eq!(editor.get_buffer(), "This \r\n is a test");
674 }
675}