tty_text/
lib.rs

1//! # tty-text
2//!
3//! Provides agnostic text editing state management.
4//!
5//! # Examples
6//! For more examples, see [Text].
7//! ```
8//! use tty_text::{Text, Key};
9//!
10//! let mut text = Text::from("Hello,\nworld!", (1, 0), true);
11//!
12//! // Move cursor from "e" to "w"
13//! text.handle_input(Key::Down);
14//! text.handle_input(Key::Down);
15//! text.handle_input(Key::Left);
16//!
17//! // Combine into single line, add " "
18//! text.handle_input(Key::Backspace);
19//! text.handle_input(Key::Char(' '));
20//!
21//! // Add another "!"
22//! text.set_cursor((13, 0));
23//! text.handle_input(Key::Char('!'));
24//!
25//! assert_eq!("Hello, world!!", text.value());
26//! assert_eq!((14, 0), text.cursor());
27//! ```
28
29pub enum Key {
30    Char(char),
31    Backspace,
32    Enter,
33    Up,
34    Down,
35    Left,
36    Right,
37}
38
39/// A multi-line text editor with cursor management capabilities.
40///
41/// # Examples
42/// ## Single-line mode
43/// ```
44/// use tty_text::{Text, Key};
45///
46/// let mut text = Text::new(false);
47///
48/// text.handle_input(Key::Char('a'));
49/// text.handle_input(Key::Enter);
50/// text.handle_input(Key::Char('b'));
51///
52/// assert_eq!((2, 0), text.cursor());
53/// assert_eq!("ab", text.value());
54/// assert_eq!(&vec![
55///     "ab".to_string(),
56/// ], text.lines());
57/// ```
58/// ## Multi-line mode
59/// ```
60/// use tty_text::{Text, Key};
61///
62/// let mut text = Text::new(true);
63///
64/// text.handle_input(Key::Char('a'));
65/// text.handle_input(Key::Enter);
66/// text.handle_input(Key::Char('b'));
67
68/// assert_eq!((1, 1), text.cursor());
69/// assert_eq!("a\nb", text.value());
70/// assert_eq!(&vec![
71///     "a".to_string(),
72///     "b".to_string(),
73/// ], text.lines());
74/// ```
75pub struct Text {
76    /// The lines that comprise this editor's value.
77    lines: Vec<String>,
78
79    /// The cursor's position in the editor in (columns, lines).
80    cursor: (usize, usize),
81
82    /// Whether this editor is configured for multi-line value editing.
83    multi_line: bool,
84
85    /// The preferred position to use when restoring across vertical movements.
86    preferred_column: usize,
87}
88
89impl Text {
90    /// Create a new, empty editor in the specified mode.
91    ///
92    /// # Examples
93    /// ```
94    /// use tty_text::{Text, Key};
95    ///
96    /// let mut text = Text::new(false);
97    ///
98    /// text.handle_input(Key::Char('a'));
99    /// text.handle_input(Key::Char('b'));
100    /// text.handle_input(Key::Char('d'));
101    /// text.set_cursor((2, 0));
102    /// text.handle_input(Key::Char('c'));
103    ///
104    /// assert_eq!((3, 0), text.cursor());
105    /// assert_eq!("abcd", text.value());
106    /// ```
107    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    /// Create a new editor from the specified value and cursor state and in the specified mode.
117    ///
118    /// # Examples
119    /// ## Multi-line value and mode
120    /// ```
121    /// use tty_text::{Text, Key};
122    ///
123    /// let mut text = Text::from("Hello,\nworld!", (2, 1), true);
124    ///
125    /// assert_eq!("Hello,\nworld!", text.value());
126    /// assert_eq!((2, 1), text.cursor());
127    /// assert_eq!(&vec![
128    ///     "Hello,".to_string(),
129    ///     "world!".to_string(),
130    /// ], text.lines());
131    /// ```
132    /// ## Multi-line value collapsed by single-line mode
133    /// ```
134    /// use tty_text::{Text, Key};
135    ///
136    /// let mut text = Text::from("Hello,\n world!", (7, 1), false);
137    ///
138    /// assert_eq!("Hello, world!", text.value());
139    /// assert_eq!((7, 0), text.cursor());
140    /// assert_eq!(&vec![
141    ///     "Hello, world!".to_string(),
142    /// ], text.lines());
143    /// ```
144    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    /// This editor's current cursor position as (columns, lines).
168    pub fn cursor(&self) -> (usize, usize) {
169        self.cursor
170    }
171
172    /// This editor's current value.
173    pub fn value(&self) -> String {
174        self.lines.join("\n")
175    }
176
177    /// This editor's value's lines.
178    pub fn lines(&self) -> &Vec<String> {
179        &self.lines
180    }
181
182    /// Update this editor's cursor position. The position will be clamped to the editor's current
183    /// value.
184    pub fn set_cursor(&mut self, position: (usize, usize)) {
185        self.cursor = position;
186
187        // Clamp the line
188        if self.cursor.1 >= self.lines.len() {
189            self.cursor.1 = self.lines.len() - 1;
190        }
191
192        // Clamp the column
193        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    /// Update this editor's state from the specified input.
202    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    /// Insert the specified character at the editor's current cursor position.
215    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    /// Backspace the character preceding the editor's current cursor position.
223    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                // Remove the current line
229                let line = self.lines.remove(self.cursor.1);
230
231                // Move the cursor to the end of the previous line
232                let prior_line_index = self.cursor.1 - 1;
233                self.cursor = (self.get_line_length(prior_line_index), prior_line_index);
234
235                // Append the just-deleted line after the cursor in the previous line
236                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    /// Insert a newline at the editor's current cursor position.
247    fn insert_newline(&mut self) {
248        if !self.multi_line {
249            return;
250        }
251
252        // Split the current line at the cursor
253        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        // Shorten the current line to the content preceding the cursor
257        self.lines[self.cursor.1] = prefix;
258
259        // Insert a new line after the current one with the content after the cursor
260        let new_line_index = self.cursor.1 + 1;
261        self.lines.insert(new_line_index, suffix);
262
263        // Move the cursor to the start of the next line
264        self.cursor = (0, new_line_index);
265
266        // If the previous line started with a bullet, continue it
267        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    /// Attempt to move the editor's cursor up one line.
276    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    /// Attempt to move the editor's cursor down one line.
292    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    /// Attempt to move the editor's cursor left one character.
308    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    /// Attempt to move the editor's cursor right one character.
323    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    /// Get the specified line's length.
337    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}