diff --git a/src/textarea.rs b/src/textarea.rs index eb6911c..e7a078c 100644 --- a/src/textarea.rs +++ b/src/textarea.rs @@ -13,7 +13,6 @@ use crate::widget::{Renderer, Viewport}; use crate::word::{find_word_end_forward, find_word_start_backward}; #[cfg(feature = "ratatui")] use ratatui::text::Line; -use std::mem; #[cfg(feature = "tuirs")] use tui::text::Spans as Line; use unicode_width::UnicodeWidthChar as _; @@ -625,11 +624,11 @@ impl<'a> TextArea<'a> { /// textarea.insert_str(", world\ngoodbye, world"); /// assert_eq!(textarea.lines(), ["hello, world", "goodbye, world"]); /// ``` - pub fn insert_str(&mut self, s: &str) -> bool { - let mut lines: Vec<_> = s.lines().map(ToString::to_string).collect(); + pub fn insert_str>(&mut self, s: S) -> bool { + let mut lines: Vec<_> = s.as_ref().lines().map(ToString::to_string).collect(); match lines.len() { 0 => false, - 1 => self.insert_piece(mem::take(&mut lines[0])), + 1 => self.insert_piece(lines.remove(0)), _ => self.insert_chunk(lines), } } @@ -647,7 +646,7 @@ impl<'a> TextArea<'a> { self.cursor = ( row + chunk.len() - 1, - col + chunk[chunk.len() - 1].chars().count(), + chunk[chunk.len() - 1].chars().count(), ); let edit = EditKind::InsertChunk(chunk, i); diff --git a/tests/textarea.rs b/tests/textarea.rs index e9adac6..99b3fe0 100644 --- a/tests/textarea.rs +++ b/tests/textarea.rs @@ -24,3 +24,364 @@ fn test_insert_soft_tab() { assert_eq!(line, expected, "{:?}", test); } } + +#[test] +fn test_insert_str_one_line() { + for i in 0..="ab".len() { + let mut t = TextArea::from(["ab"]); + t.move_cursor(CursorMove::Jump(0, i as u16)); + assert!(t.insert_str("x"), "{}", i); + let have = &t.lines()[0]; + + let mut want = "ab".to_string(); + want.insert(i, 'x'); + assert_eq!(&want, have, "{}", i); + } +} + +#[test] +fn test_insert_str_empty_line() { + let mut t = TextArea::from(["ab"]); + assert!(!t.insert_str("")); + assert_eq!(t.lines(), ["ab"]); +} + +#[test] +fn test_insert_str_multiple_lines() { + #[rustfmt::skip] + let tests = [ + // Positions + ( + // Text before edit + &[ + "ab", + "cd", + "ef", + ][..], + // (row, offset) position before edit + (0, 0), + // String to be inserted + "x\ny", + // (row, offset) position after edit + (1, 1), + // Text after edit + &[ + "x", + "yab", + "cd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (0, 1), + "x\ny", + (1, 1), + &[ + "ax", + "yb", + "cd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (0, 2), + "x\ny", + (1, 1), + &[ + "abx", + "y", + "cd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 0), + "x\ny", + (2, 1), + &[ + "ab", + "x", + "ycd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 1), + "x\ny", + (2, 1), + &[ + "ab", + "cx", + "yd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 2), + "x\ny", + (2, 1), + &[ + "ab", + "cdx", + "y", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (2, 0), + "x\ny", + (3, 1), + &[ + "ab", + "cd", + "x", + "yef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (2, 1), + "x\ny", + (3, 1), + &[ + "ab", + "cd", + "ex", + "yf", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (2, 2), + "x\ny", + (3, 1), + &[ + "ab", + "cd", + "efx", + "y", + ][..], + ), + // More than 2 lines + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 1), + "x\ny\nz\nw", + (4, 1), + &[ + "ab", + "cx", + "y", + "z", + "wd", + "ef", + ][..], + ), + // Empty lines + ( + &[ + "", + "", + "", + ][..], + (0, 0), + "x\ny\nz", + (2, 1), + &[ + "x", + "y", + "z", + "", + "", + ][..], + ), + ( + &[ + "", + "", + "", + ][..], + (1, 0), + "x\ny\nz", + (3, 1), + &[ + "", + "x", + "y", + "z", + "", + ][..], + ), + ( + &[ + "", + "", + "", + ][..], + (2, 0), + "x\ny\nz", + (4, 1), + &[ + "", + "", + "x", + "y", + "z", + ][..], + ), + // Empty buffer + ( + &[ + "", + ][..], + (0, 0), + "x\ny\nz", + (2, 1), + &[ + "x", + "y", + "z", + ][..], + ), + // Insert empty lines + ( + &[ + "ab", + "cd", + "ef", + ][..], + (0, 0), + "\n\n\n", + (2, 0), + &[ + "", + "", + "ab", + "cd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 0), + "\n\n\n", + (3, 0), + &[ + "ab", + "", + "", + "cd", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 1), + "\n\n\n", + (3, 0), + &[ + "ab", + "c", + "", + "d", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (1, 2), + "\n\n\n", + (3, 0), + &[ + "ab", + "cd", + "", + "", + "ef", + ][..], + ), + ( + &[ + "ab", + "cd", + "ef", + ][..], + (2, 2), + "\n\n\n", + (4, 0), + &[ + "ab", + "cd", + "ef", + "", + "", + ][..], + ), + ]; + + for test in tests { + let (before, before_pos, input, after_pos, expected) = test; + + let mut t = TextArea::from(before.iter().map(|s| s.to_string())); + let (row, col) = before_pos; + t.move_cursor(CursorMove::Jump(row, col)); + + assert!(t.insert_str(input), "{:?}", test); + assert_eq!(t.cursor(), after_pos, "{:?}", test); + assert_eq!(t.lines(), expected, "{:?}", test); + } +}