1pub enum Key {
30 Char(char),
31 Backspace,
32 Enter,
33 Up,
34 Down,
35 Left,
36 Right,
37}
38
39pub struct Text {
76 lines: Vec<String>,
78
79 cursor: (usize, usize),
81
82 multi_line: bool,
84
85 preferred_column: usize,
87}
88
89impl Text {
90 pub fn new(multi_line: bool) -> Self {
108 Self {
109 lines: vec![String::new()],
110 cursor: (0, 0),
111 multi_line,
112 preferred_column: 0,
113 }
114 }
115
116 pub fn from(value: &str, cursor: (usize, usize), multi_line: bool) -> Self {
145 let mut lines = if multi_line {
146 value.lines().map(|line| line.to_string()).collect()
147 } else {
148 vec![value.replace("\n", "").replace("\r", "")]
149 };
150
151 if lines.is_empty() || value.ends_with("\n") || value.ends_with("\r\n") {
152 lines.push(String::new());
153 }
154
155 let mut text = Self {
156 lines,
157 cursor: (0, 0),
158 multi_line,
159 preferred_column: 0,
160 };
161
162 text.set_cursor(cursor);
163
164 text
165 }
166
167 pub fn cursor(&self) -> (usize, usize) {
169 self.cursor
170 }
171
172 pub fn value(&self) -> String {
174 self.lines.join("\n")
175 }
176
177 pub fn lines(&self) -> &Vec<String> {
179 &self.lines
180 }
181
182 pub fn set_cursor(&mut self, position: (usize, usize)) {
185 self.cursor = position;
186
187 if self.cursor.1 >= self.lines.len() {
189 self.cursor.1 = self.lines.len() - 1;
190 }
191
192 let line_length = self.get_line_length(self.cursor.1);
194 if self.cursor.0 > line_length {
195 self.cursor.0 = line_length;
196 }
197
198 self.preferred_column = self.cursor.0;
199 }
200
201 pub fn handle_input(&mut self, input: Key) {
203 match input {
204 Key::Char(ch) => self.insert_character(ch),
205 Key::Backspace => self.backspace_character(),
206 Key::Enter => self.insert_newline(),
207 Key::Up => self.move_up(),
208 Key::Down => self.move_down(),
209 Key::Left => self.move_left(),
210 Key::Right => self.move_right(),
211 }
212 }
213
214 fn insert_character(&mut self, ch: char) {
216 self.lines[self.cursor.1].insert(self.cursor.0, ch);
217 self.cursor.0 += 1;
218
219 self.preferred_column = self.cursor.0;
220 }
221
222 fn backspace_character(&mut self) {
224 let at_start_of_line = self.cursor.0 == 0;
225 if at_start_of_line {
226 let on_first_line = self.cursor.1 == 0;
227 if !on_first_line {
228 let line = self.lines.remove(self.cursor.1);
230
231 let prior_line_index = self.cursor.1 - 1;
233 self.cursor = (self.get_line_length(prior_line_index), prior_line_index);
234
235 self.lines[self.cursor.1].push_str(&line);
237 }
238 } else {
239 self.cursor.0 -= 1;
240 self.lines[self.cursor.1].remove(self.cursor.0);
241 }
242
243 self.preferred_column = self.cursor.0;
244 }
245
246 fn insert_newline(&mut self) {
248 if !self.multi_line {
249 return;
250 }
251
252 let (prefix, suffix) = self.lines[self.cursor.1].split_at(self.cursor.0).to_owned();
254 let (prefix, suffix) = (prefix.to_string(), suffix.to_string());
255
256 self.lines[self.cursor.1] = prefix;
258
259 let new_line_index = self.cursor.1 + 1;
261 self.lines.insert(new_line_index, suffix);
262
263 self.cursor = (0, new_line_index);
265
266 if self.lines[self.cursor.1 - 1].starts_with(" - ") {
268 self.lines[new_line_index].insert_str(0, " - ");
269 self.cursor.0 += 3;
270 }
271
272 self.preferred_column = self.cursor.0;
273 }
274
275 fn move_up(&mut self) {
277 if !self.multi_line {
278 return;
279 }
280
281 let on_first_line = self.cursor.1 == 0;
282 if !on_first_line {
283 let previous_line = self.cursor.1 - 1;
284 let desired_column = std::cmp::max(self.cursor.0, self.preferred_column);
285 let new_column = std::cmp::min(desired_column, self.get_line_length(previous_line));
286
287 self.cursor = (new_column, previous_line);
288 }
289 }
290
291 fn move_down(&mut self) {
293 if !self.multi_line {
294 return;
295 }
296
297 let next_line = self.cursor.1 + 1;
298
299 let is_last_line = next_line == self.lines.len();
300 if !is_last_line {
301 let desired_column = std::cmp::max(self.cursor.0, self.preferred_column);
302 let new_column = std::cmp::min(desired_column, self.get_line_length(next_line));
303 self.cursor = (new_column, self.cursor.1 + 1);
304 }
305 }
306
307 fn move_left(&mut self) {
309 let at_start_of_line = self.cursor.0 == 0;
310 let on_first_line = self.cursor.1 == 0;
311
312 if !at_start_of_line {
313 self.cursor.0 -= 1;
314 } else if !on_first_line {
315 let previous_line = self.cursor.1 - 1;
316 self.cursor = (self.get_line_length(previous_line), previous_line);
317 }
318
319 self.preferred_column = self.cursor.0;
320 }
321
322 fn move_right(&mut self) {
324 let at_end_of_line = self.cursor.0 == self.get_line_length(self.cursor.1);
325 let on_last_line = self.cursor.1 + 1 == self.lines.len();
326
327 if !at_end_of_line {
328 self.cursor.0 += 1;
329 } else if !on_last_line {
330 self.cursor = (0, self.cursor.1 + 1);
331 }
332
333 self.preferred_column = self.cursor.0;
334 }
335
336 fn get_line_length(&self, line_index: usize) -> usize {
338 self.lines[line_index].len()
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 macro_rules! svec {
347 ($($x:expr),*) => (vec![$($x.to_string()),*]);
348 }
349
350 macro_rules! assert_text {
351 ($text: ident, $cursor: expr, $value: expr, $lines: expr) => {
352 assert_eq!($cursor, $text.cursor());
353 assert_eq!($value, $text.value());
354 assert_eq!(&$lines, $text.lines());
355 };
356 }
357
358 #[test]
359 fn new() {
360 let text = Text::new(false);
361 assert_text!(text, (0, 0), "", svec![""]);
362 }
363
364 #[test]
365 fn from() {
366 let text = Text::from("a\nbc", (1, 1), true);
367 assert_text!(text, (1, 1), "a\nbc", svec!["a", "bc"]);
368 }
369
370 #[test]
371 fn from_clamp_cursor() {
372 let text = Text::from("a\nbc", (5, 5), true);
373 assert_text!(text, (2, 1), "a\nbc", svec!["a", "bc"]);
374 }
375
376 #[test]
377 fn from_collapse_single_line() {
378 let text = Text::from("a\n\nbc", (1, 0), false);
379 assert_text!(text, (1, 0), "abc", svec!["abc"]);
380 }
381
382 #[test]
383 fn from_clamp_cursor_single_line() {
384 let text = Text::from("a\n\nbc", (3, 0), false);
385 assert_text!(text, (3, 0), "abc", svec!["abc"]);
386 }
387
388 #[test]
389 fn blank_lines() {
390 let text = Text::from("abc\n\r\n", (0, 1), true);
391 assert_text!(text, (0, 1), "abc\n\n", svec!["abc", "", ""]);
392 }
393
394 #[test]
395 fn set_cursor() {
396 let mut text = Text::from("a\nbc", (0, 0), true);
397 assert_eq!((0, 0), text.cursor());
398
399 text.set_cursor((1, 1));
400 assert_eq!((1, 1), text.cursor());
401 }
402
403 #[test]
404 fn set_cursor_clamping() {
405 let mut text = Text::from("a\nbc", (0, 0), true);
406 assert_eq!((0, 0), text.cursor());
407
408 text.set_cursor((5, 5));
409 assert_eq!((2, 1), text.cursor());
410 }
411
412 #[test]
413 fn handle_input() {
414 let mut text = Text::from("abc\ndef", (2, 1), true);
415 assert_text!(text, (2, 1), "abc\ndef", svec!["abc", "def"]);
416
417 text.handle_input(Key::Char('X'));
418 assert_text!(text, (3, 1), "abc\ndeXf", svec!["abc", "deXf"]);
419
420 text.handle_input(Key::Left);
421 assert_text!(text, (2, 1), "abc\ndeXf", svec!["abc", "deXf"]);
422
423 text.handle_input(Key::Backspace);
424 assert_text!(text, (1, 1), "abc\ndXf", svec!["abc", "dXf"]);
425
426 text.handle_input(Key::Right);
427 text.handle_input(Key::Right);
428 assert_text!(text, (3, 1), "abc\ndXf", svec!["abc", "dXf"]);
429
430 text.handle_input(Key::Char('g'));
431 text.handle_input(Key::Char('h'));
432 text.handle_input(Key::Char('i'));
433 assert_text!(text, (6, 1), "abc\ndXfghi", svec!["abc", "dXfghi"]);
434
435 text.handle_input(Key::Up);
436 assert_text!(text, (3, 0), "abc\ndXfghi", svec!["abc", "dXfghi"]);
437
438 text.handle_input(Key::Left);
439 text.handle_input(Key::Left);
440 assert_text!(text, (1, 0), "abc\ndXfghi", svec!["abc", "dXfghi"]);
441
442 text.handle_input(Key::Down);
443 assert_text!(text, (1, 1), "abc\ndXfghi", svec!["abc", "dXfghi"]);
444
445 text.handle_input(Key::Backspace);
446 text.handle_input(Key::Backspace);
447 assert_text!(text, (3, 0), "abcXfghi", svec!["abcXfghi"]);
448
449 text.handle_input(Key::Right);
450 text.handle_input(Key::Right);
451 assert_text!(text, (5, 0), "abcXfghi", svec!["abcXfghi"]);
452
453 text.handle_input(Key::Enter);
454 assert_text!(text, (0, 1), "abcXf\nghi", svec!["abcXf", "ghi"]);
455
456 text.handle_input(Key::Left);
457 assert_text!(text, (5, 0), "abcXf\nghi", svec!["abcXf", "ghi"]);
458
459 text.handle_input(Key::Down);
460 assert_text!(text, (3, 1), "abcXf\nghi", svec!["abcXf", "ghi"]);
461 }
462
463 #[test]
464 fn handle_input_single_line() {
465 let mut text = Text::from("abcdef", (3, 0), false);
466 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
467
468 text.handle_input(Key::Char('X'));
469 assert_text!(text, (4, 0), "abcXdef", svec!["abcXdef"]);
470
471 text.handle_input(Key::Enter);
472 assert_text!(text, (4, 0), "abcXdef", svec!["abcXdef"]);
473
474 text.handle_input(Key::Up);
475 assert_text!(text, (4, 0), "abcXdef", svec!["abcXdef"]);
476
477 text.handle_input(Key::Down);
478 assert_text!(text, (4, 0), "abcXdef", svec!["abcXdef"]);
479
480 text.handle_input(Key::Backspace);
481 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
482
483 text.set_cursor((0, 0));
484 text.move_left();
485 assert_text!(text, (0, 0), "abcdef", svec!["abcdef"]);
486
487 text.set_cursor((6, 0));
488 text.move_right();
489 assert_text!(text, (6, 0), "abcdef", svec!["abcdef"]);
490 }
491
492 #[test]
493 fn insert_character_end_line() {
494 let mut text = Text::new(true);
495
496 text.insert_character('a');
497 text.insert_character('b');
498 text.insert_character('c');
499
500 assert_text!(text, (3, 0), "abc", svec!["abc"]);
501 }
502
503 #[test]
504 fn insert_character_mid_line() {
505 let mut text = Text::from("abc", (1, 0), true);
506
507 text.insert_character('X');
508
509 assert_text!(text, (2, 0), "aXbc", svec!["aXbc"]);
510 }
511
512 #[test]
513 fn insert_character_start_line() {
514 let mut text = Text::from("abc", (0, 0), true);
515
516 text.insert_character('X');
517
518 assert_text!(text, (1, 0), "Xabc", svec!["Xabc"]);
519 }
520
521 #[test]
522 fn backspace_character_all() {
523 let mut text = Text::from("abc", (3, 0), true);
524
525 text.backspace_character();
526 text.backspace_character();
527 text.backspace_character();
528
529 assert_text!(text, (0, 0), "", svec![""]);
530 }
531
532 #[test]
533 fn backspace_character_mid_line() {
534 let mut text = Text::from("abc", (2, 0), true);
535
536 text.backspace_character();
537 text.backspace_character();
538 text.backspace_character();
539
540 assert_text!(text, (0, 0), "c", svec!["c"]);
541 }
542
543 #[test]
544 fn backspace_character_start_line() {
545 let mut text = Text::from("abc", (0, 0), true);
546
547 text.backspace_character();
548 text.backspace_character();
549 text.backspace_character();
550
551 assert_text!(text, (0, 0), "abc", svec!["abc"]);
552 }
553
554 #[test]
555 fn backspace_character_multi_line() {
556 let mut text = Text::from("abc\ndef", (0, 1), true);
557
558 text.backspace_character();
559
560 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
561 }
562
563 #[test]
564 fn insert_newline_end_line() {
565 let mut text = Text::from("abc", (3, 0), true);
566
567 text.insert_newline();
568
569 assert_text!(text, (0, 1), "abc\n", svec!["abc", ""]);
570 }
571
572 #[test]
573 fn insert_newline_start_line() {
574 let mut text = Text::from("abc", (0, 0), true);
575
576 text.insert_newline();
577
578 assert_text!(text, (0, 1), "\nabc", svec!["", "abc"]);
579 }
580
581 #[test]
582 fn insert_newline_mid_line() {
583 let mut text = Text::from("abc", (1, 0), true);
584
585 text.insert_newline();
586
587 assert_text!(text, (0, 1), "a\nbc", svec!["a", "bc"]);
588 }
589
590 #[test]
591 fn insert_newline_empty() {
592 let mut text = Text::from("", (0, 0), true);
593
594 text.insert_newline();
595
596 assert_text!(text, (0, 1), "\n", svec!["", ""]);
597 }
598
599 #[test]
600 fn insert_newline_single_line() {
601 let mut text = Text::from("abcdef", (3, 0), false);
602
603 text.insert_newline();
604
605 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
606 }
607
608 #[test]
609 fn move_up_start_line() {
610 let mut text = Text::from("abc\ndef", (0, 1), true);
611
612 text.move_up();
613
614 assert_text!(text, (0, 0), "abc\ndef", svec!["abc", "def"]);
615 }
616
617 #[test]
618 fn move_up_mid_line() {
619 let mut text = Text::from("abc\ndef", (2, 1), true);
620
621 text.move_up();
622
623 assert_text!(text, (2, 0), "abc\ndef", svec!["abc", "def"]);
624 }
625
626 #[test]
627 fn move_up_end_line() {
628 let mut text = Text::from("abc\ndef", (3, 1), true);
629
630 text.move_up();
631
632 assert_text!(text, (3, 0), "abc\ndef", svec!["abc", "def"]);
633 }
634
635 #[test]
636 fn move_up_shorter_line() {
637 let mut text = Text::from("a\ndef", (2, 1), true);
638
639 text.move_up();
640
641 assert_text!(text, (1, 0), "a\ndef", svec!["a", "def"]);
642 }
643
644 #[test]
645 fn move_up_single_line() {
646 let mut text = Text::from("abcdef", (3, 0), true);
647
648 text.move_up();
649
650 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
651 }
652
653 #[test]
654 fn move_down_start_line() {
655 let mut text = Text::from("abc\ndef", (0, 0), true);
656
657 text.move_down();
658
659 assert_text!(text, (0, 1), "abc\ndef", svec!["abc", "def"]);
660 }
661
662 #[test]
663 fn move_down_mid_line() {
664 let mut text = Text::from("abc\ndef", (2, 0), true);
665
666 text.move_down();
667
668 assert_text!(text, (2, 1), "abc\ndef", svec!["abc", "def"]);
669 }
670
671 #[test]
672 fn move_down_end_line() {
673 let mut text = Text::from("abc\ndef", (3, 0), true);
674
675 text.move_down();
676
677 assert_text!(text, (3, 1), "abc\ndef", svec!["abc", "def"]);
678 }
679
680 #[test]
681 fn move_down_shorter_line() {
682 let mut text = Text::from("a\ndef", (2, 0), true);
683
684 text.move_down();
685
686 assert_text!(text, (1, 1), "a\ndef", svec!["a", "def"]);
687 }
688
689 #[test]
690 fn move_down_single_line() {
691 let mut text = Text::from("abcdef", (3, 0), false);
692
693 text.move_down();
694
695 assert_text!(text, (3, 0), "abcdef", svec!["abcdef"]);
696 }
697
698 #[test]
699 fn move_left_mid_line() {
700 let mut text = Text::from("abc", (2, 0), true);
701
702 text.move_left();
703
704 assert_text!(text, (1, 0), "abc", svec!["abc"]);
705 }
706
707 #[test]
708 fn move_left_end_line() {
709 let mut text = Text::from("abc", (3, 0), true);
710
711 text.move_left();
712
713 assert_text!(text, (2, 0), "abc", svec!["abc"]);
714 }
715
716 #[test]
717 fn move_left_start_value() {
718 let mut text = Text::from("abc", (0, 0), true);
719
720 text.move_left();
721
722 assert_text!(text, (0, 0), "abc", svec!["abc"]);
723 }
724
725 #[test]
726 fn move_left_wrap_up() {
727 let mut text = Text::from("abc\ndef", (0, 1), true);
728
729 text.move_left();
730
731 assert_text!(text, (3, 0), "abc\ndef", svec!["abc", "def"]);
732 }
733
734 #[test]
735 fn move_left_wrap_up_empty_line() {
736 let mut text = Text::from("abc\n\ndef", (0, 2), true);
737
738 text.move_left();
739
740 assert_text!(text, (0, 1), "abc\n\ndef", svec!["abc", "", "def"]);
741 }
742
743 #[test]
744 fn move_left_single_line() {
745 let mut text = Text::from("abcdef", (0, 0), false);
746
747 text.move_left();
748
749 assert_text!(text, (0, 0), "abcdef", svec!["abcdef"]);
750 }
751
752 #[test]
753 fn move_right_mid_line() {
754 let mut text = Text::from("abc", (1, 0), true);
755
756 text.move_right();
757
758 assert_text!(text, (2, 0), "abc", svec!["abc"]);
759 }
760
761 #[test]
762 fn move_right_start_line() {
763 let mut text = Text::from("abc", (0, 0), true);
764
765 text.move_right();
766
767 assert_text!(text, (1, 0), "abc", svec!["abc"]);
768 }
769
770 #[test]
771 fn move_right_end_value() {
772 let mut text = Text::from("abc", (3, 0), true);
773
774 text.move_right();
775
776 assert_text!(text, (3, 0), "abc", svec!["abc"]);
777 }
778
779 #[test]
780 fn move_right_wrap_down() {
781 let mut text = Text::from("abc\ndef", (3, 0), true);
782
783 text.move_right();
784
785 assert_text!(text, (0, 1), "abc\ndef", svec!["abc", "def"]);
786 }
787
788 #[test]
789 fn move_right_wrap_down_empty_line() {
790 let mut text = Text::from("abc\n\ndef", (3, 0), true);
791
792 text.move_right();
793
794 assert_text!(text, (0, 1), "abc\n\ndef", svec!["abc", "", "def"]);
795 }
796
797 #[test]
798 fn move_right_single_line() {
799 let mut text = Text::from("abcdef", (6, 0), false);
800
801 text.move_right();
802
803 assert_text!(text, (6, 0), "abcdef", svec!["abcdef"]);
804 }
805}