From 78c3ca5486c848a7fbdb2b94465a9b771434e038 Mon Sep 17 00:00:00 2001 From: Ken Chou Date: Sat, 15 Jun 2024 12:48:09 +0800 Subject: [PATCH 1/3] Fix crash when jumping to the end of the line. (cherry picked from commit f5ca810515989640022b8eec807f959cc84ea926) --- termwiz/src/lineedit/buffer.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/termwiz/src/lineedit/buffer.rs b/termwiz/src/lineedit/buffer.rs index 1d922d1a227..97dbdca5f1c 100644 --- a/termwiz/src/lineedit/buffer.rs +++ b/termwiz/src/lineedit/buffer.rs @@ -174,15 +174,7 @@ impl LineEditBuffer { position } Movement::StartOfLine => 0, - Movement::EndOfLine => { - let mut cursor = - GraphemeCursor::new(self.line.len().saturating_sub(1), self.line.len(), false); - if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) { - pos - } else { - self.cursor - } - } + Movement::EndOfLine => self.line.len(), Movement::None => self.cursor, } } From 9abc8c23ac6d3360ef43b7d8b2132d8169074183 Mon Sep 17 00:00:00 2001 From: Ken Chou Date: Sat, 15 Jun 2024 22:24:10 +0800 Subject: [PATCH 2/3] test LineEditBuffer --- Cargo.lock | 1 + termwiz/Cargo.toml | 1 + termwiz/src/lineedit/buffer.rs | 62 ++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 73090883be9..ff0603501f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5212,6 +5212,7 @@ dependencies = [ "pest", "pest_derive", "phf", + "rstest", "serde", "sha2", "signal-hook", diff --git a/termwiz/Cargo.toml b/termwiz/Cargo.toml index f11de617629..349b6d88a0c 100644 --- a/termwiz/Cargo.toml +++ b/termwiz/Cargo.toml @@ -59,6 +59,7 @@ criterion = "0.5" varbincode = "0.1" k9 = "0.12" env_logger = "0.11" +rstest = "0.21.0" [target."cfg(unix)".dependencies] diff --git a/termwiz/src/lineedit/buffer.rs b/termwiz/src/lineedit/buffer.rs index 97dbdca5f1c..81624f85501 100644 --- a/termwiz/src/lineedit/buffer.rs +++ b/termwiz/src/lineedit/buffer.rs @@ -179,3 +179,65 @@ impl LineEditBuffer { } } } + +#[cfg(test)] +mod tests { + use rstest::rstest; + use super::*; + #[rstest] + #[case("Hello", 5, '!', "Hello!")] + #[case("你好", 6, '!', "你好!")] + #[case("你好世界", 6, ',', "你好,世界")] + fn test_insert_char(#[case] text: &str, #[case] cursor: usize, #[case] new_char: char, #[case] expect: &str) { + // test insert a char + let mut buffer = LineEditBuffer::new(text, cursor); + buffer.insert_char(new_char); + assert_eq!(buffer.get_line(), expect); + assert_eq!(buffer.get_cursor(), cursor + new_char.len_utf8()); + } + + #[rstest] + #[case("Hello", 5, ", world!", "Hello, world!")] + #[case("你好", 6, ",世界!", "你好,世界!")] // insert at end of line + #[case("和平", 0, "世界", "世界和平")] // insert at start of line + #[case("你好世界", 6, ",", "你好,世界")] // insert at middle of line + fn test_insert_unicode_text(#[case] text: &str, #[case] cursor: usize, #[case] new_text: &str, #[case] expect: &str) { + let mut buffer = LineEditBuffer::new(text, cursor); + buffer.insert_text(new_text); + assert_eq!(buffer.get_line(), expect); + assert_eq!(buffer.get_cursor(), cursor + new_text.len()); + } + + #[rstest] + #[case("Hello!", 6, Movement::BackwardChar(1), Movement::BackwardChar(1), "Hello", 5)] + #[case("你好!", 9, Movement::BackwardChar(1), Movement::BackwardChar(1), "你好", 6)] + fn test_kill_text_backward_char(#[case] text: &str, #[case] cursor:usize, #[case] op: Movement, #[case] cursor_action: Movement, #[case] expect_line: &str, #[case] expect_cursor: usize) { + let mut buffer = LineEditBuffer::new(text, cursor); + buffer.kill_text(op, cursor_action); + assert_eq!(buffer.get_line(), expect_line); + assert_eq!(buffer.get_cursor(), expect_cursor); + } + + #[test] + fn test_clear() { + let mut buffer = LineEditBuffer::new("Hello, world", 5); + buffer.clear(); + assert_eq!(buffer.get_line(), ""); + assert_eq!(buffer.get_cursor(), 0); + } + + #[test] + fn test_exec_movement() { + let text = "😀Hello, world! 你好,世界!こんにちは、世界!"; + let mut buffer = LineEditBuffer::new(text, text.len()); + buffer.exec_movement(Movement::StartOfLine); + assert_eq!(buffer.get_cursor(), 0); + buffer.exec_movement(Movement::ForwardChar(1)); + assert_eq!(buffer.get_cursor(), buffer.get_line().chars().next().unwrap().len_utf8()); + + buffer.exec_movement(Movement::EndOfLine); + assert_eq!(buffer.get_cursor(), buffer.get_line().len()); + buffer.exec_movement(Movement::BackwardChar(1)); + assert_eq!(buffer.get_cursor(), buffer.get_line().len() - (buffer.get_line().chars().last().unwrap().len_utf8())); + } +} From 8d55fd58b910e5807dd77ff9b61fc7baa1303284 Mon Sep 17 00:00:00 2001 From: Ken Chou Date: Sat, 15 Jun 2024 22:50:24 +0800 Subject: [PATCH 3/3] code format --- termwiz/src/lineedit/buffer.rs | 57 ++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/termwiz/src/lineedit/buffer.rs b/termwiz/src/lineedit/buffer.rs index 81624f85501..f1f120f57a3 100644 --- a/termwiz/src/lineedit/buffer.rs +++ b/termwiz/src/lineedit/buffer.rs @@ -182,13 +182,18 @@ impl LineEditBuffer { #[cfg(test)] mod tests { - use rstest::rstest; use super::*; + use rstest::rstest; #[rstest] #[case("Hello", 5, '!', "Hello!")] #[case("你好", 6, '!', "你好!")] #[case("你好世界", 6, ',', "你好,世界")] - fn test_insert_char(#[case] text: &str, #[case] cursor: usize, #[case] new_char: char, #[case] expect: &str) { + fn test_insert_char( + #[case] text: &str, + #[case] cursor: usize, + #[case] new_char: char, + #[case] expect: &str, + ) { // test insert a char let mut buffer = LineEditBuffer::new(text, cursor); buffer.insert_char(new_char); @@ -199,9 +204,14 @@ mod tests { #[rstest] #[case("Hello", 5, ", world!", "Hello, world!")] #[case("你好", 6, ",世界!", "你好,世界!")] // insert at end of line - #[case("和平", 0, "世界", "世界和平")] // insert at start of line - #[case("你好世界", 6, ",", "你好,世界")] // insert at middle of line - fn test_insert_unicode_text(#[case] text: &str, #[case] cursor: usize, #[case] new_text: &str, #[case] expect: &str) { + #[case("和平", 0, "世界", "世界和平")] // insert at start of line + #[case("你好世界", 6, ",", "你好,世界")] // insert at middle of line + fn test_insert_unicode_text( + #[case] text: &str, + #[case] cursor: usize, + #[case] new_text: &str, + #[case] expect: &str, + ) { let mut buffer = LineEditBuffer::new(text, cursor); buffer.insert_text(new_text); assert_eq!(buffer.get_line(), expect); @@ -209,9 +219,30 @@ mod tests { } #[rstest] - #[case("Hello!", 6, Movement::BackwardChar(1), Movement::BackwardChar(1), "Hello", 5)] - #[case("你好!", 9, Movement::BackwardChar(1), Movement::BackwardChar(1), "你好", 6)] - fn test_kill_text_backward_char(#[case] text: &str, #[case] cursor:usize, #[case] op: Movement, #[case] cursor_action: Movement, #[case] expect_line: &str, #[case] expect_cursor: usize) { + #[case( + "Hello!", + 6, + Movement::BackwardChar(1), + Movement::BackwardChar(1), + "Hello", + 5 + )] + #[case( + "你好!", + 9, + Movement::BackwardChar(1), + Movement::BackwardChar(1), + "你好", + 6 + )] + fn test_kill_text_backward_char( + #[case] text: &str, + #[case] cursor: usize, + #[case] op: Movement, + #[case] cursor_action: Movement, + #[case] expect_line: &str, + #[case] expect_cursor: usize, + ) { let mut buffer = LineEditBuffer::new(text, cursor); buffer.kill_text(op, cursor_action); assert_eq!(buffer.get_line(), expect_line); @@ -233,11 +264,17 @@ mod tests { buffer.exec_movement(Movement::StartOfLine); assert_eq!(buffer.get_cursor(), 0); buffer.exec_movement(Movement::ForwardChar(1)); - assert_eq!(buffer.get_cursor(), buffer.get_line().chars().next().unwrap().len_utf8()); + assert_eq!( + buffer.get_cursor(), + buffer.get_line().chars().next().unwrap().len_utf8() + ); buffer.exec_movement(Movement::EndOfLine); assert_eq!(buffer.get_cursor(), buffer.get_line().len()); buffer.exec_movement(Movement::BackwardChar(1)); - assert_eq!(buffer.get_cursor(), buffer.get_line().len() - (buffer.get_line().chars().last().unwrap().len_utf8())); + assert_eq!( + buffer.get_cursor(), + buffer.get_line().len() - (buffer.get_line().chars().last().unwrap().len_utf8()) + ); } }