From 3b649d719fce16df9bd3f7d7571f5bcbdde9177a Mon Sep 17 00:00:00 2001 From: TheBlackSheep3 <40034835+TheBlackSheep3@users.noreply.github.com> Date: Thu, 20 Jul 2023 20:59:40 +0200 Subject: [PATCH 1/5] swapped textwrap for bwrap --- Cargo.lock | 54 ++----- Cargo.toml | 2 +- src/components/commit_details/details.rs | 174 ++++++++++++++++++++--- 3 files changed, 163 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c4c0ed65e..1d2cbc4402 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.3" @@ -173,6 +162,15 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bwrap" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a552ac54fcbecc5eedf95d6008637809dc01f895c090cd36f461dcd673ce44f" +dependencies = [ + "unicode-width", +] + [[package]] name = "bytemuck" version = "1.13.0" @@ -753,6 +751,7 @@ dependencies = [ "backtrace", "bitflags", "bugreport", + "bwrap", "bytesize", "chrono", "clap", @@ -780,7 +779,6 @@ dependencies = [ "struct-patch", "syntect", "tempfile", - "textwrap", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -792,9 +790,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hermit-abi" @@ -858,7 +853,7 @@ version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" dependencies = [ - "ahash 0.8.3", + "ahash", "indexmap", "is-terminal", "itoa", @@ -1662,12 +1657,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" -[[package]] -name = "smawk" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1801,17 +1790,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.39" @@ -1896,16 +1874,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" -[[package]] -name = "unicode-linebreak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" -dependencies = [ - "hashbrown", - "regex", -] - [[package]] name = "unicode-normalization" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index 79468a6675..11f6c5ab98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ asyncgit = { path = "./asyncgit", version = "0.23", default-features = false } backtrace = "0.3" bitflags = "1.3" bugreport = "0.5" +bwrap = { version = "1.2.1", features = [ "use_std" ] } bytesize = { version = "1.2", default-features = false } chrono = { version = "0.4", default-features = false, features = [ "clock" ] } clap = { version = "4.1", features = [ "env", "cargo" ] } @@ -49,7 +50,6 @@ serde = "1.0" simplelog = { version = "0.12", default-features = false } struct-patch = "0.2" syntect = { version = "5.0", default-features = false, features = ["parsing", "default-syntaxes", "default-themes", "html"] } -textwrap = "0.16" unicode-segmentation = "1.10" unicode-truncate = "0.2" unicode-width = "0.1" diff --git a/src/components/commit_details/details.rs b/src/components/commit_details/details.rs index 8ff2775e81..4a89c2c4be 100644 --- a/src/components/commit_details/details.rs +++ b/src/components/commit_details/details.rs @@ -82,15 +82,35 @@ impl DetailsComponent { } } - fn wrap_commit_details( + fn wrap_commit_details<'a>( message: &CommitMessage, width: usize, - ) -> WrappedCommitMessage<'_> { - let wrapped_title = textwrap::wrap(&message.subject, width); + title_buffer: &'a mut [u8], + message_buffer: &'a mut [u8], + ) -> WrappedCommitMessage<'a> { + let _ = bwrap::Wrapper::new( + &message.subject, + width, + title_buffer, + ) + .unwrap() + .wrap(); + let wrapped_title = std::str::from_utf8(title_buffer) + .unwrap() + .split('\n') + .map(|s| Cow::Borrowed(s.trim_null())) + .collect(); if let Some(ref body) = message.body { + let _ = bwrap::Wrapper::new(body, width, message_buffer) + .unwrap() + .wrap(); let wrapped_message: Vec> = - textwrap::wrap(body, width).into_iter().collect(); + std::str::from_utf8(message_buffer) + .unwrap() + .split('\n') + .map(|s| Cow::Borrowed(s.trim_null())) + .collect(); (wrapped_title, wrapped_message) } else { @@ -98,13 +118,20 @@ impl DetailsComponent { } } - fn get_wrapped_lines( + fn get_wrapped_lines<'a>( data: &Option, width: usize, - ) -> WrappedCommitMessage<'_> { + title_buffer: &'a mut [u8], + message_buffer: &'a mut [u8], + ) -> WrappedCommitMessage<'a> { if let Some(ref data) = data { if let Some(ref message) = data.message { - return Self::wrap_commit_details(message, width); + return Self::wrap_commit_details( + message, + width, + title_buffer, + message_buffer, + ); } } @@ -115,10 +142,28 @@ impl DetailsComponent { details: &Option, width: usize, ) -> usize { - let (wrapped_title, wrapped_message) = - Self::get_wrapped_lines(details, width); - - wrapped_title.len() + wrapped_message.len() + if let Some(ref data) = details { + if let Some(ref message) = data.message { + let title_len = message.subject.len(); + let mut title_buffer = + vec![0; title_len + title_len / width + 1]; + let message_len = match message.body { + Some(ref body) => body.len(), + None => 0usize, + }; + let mut message_buffer = + vec![0; message_len + message_len / width + 1]; + let (wrapped_title, wrapped_message) = + Self::wrap_commit_details( + message, + width, + &mut title_buffer, + &mut message_buffer, + ); + return wrapped_title.len() + wrapped_message.len(); + } + } + return 0usize; } fn get_theme_for_line(&self, bold: bool) -> Style { @@ -129,13 +174,20 @@ impl DetailsComponent { } } - fn get_wrapped_text_message( - &self, + fn get_wrapped_text_message<'a>( + &'a self, width: usize, height: usize, + title_buffer: &'a mut [u8], + message_buffer: &'a mut [u8], ) -> Vec { let (wrapped_title, wrapped_message) = - Self::get_wrapped_lines(&self.data, width); + Self::get_wrapped_lines( + &self.data, + width, + title_buffer, + message_buffer, + ); [&wrapped_title[..], &wrapped_message[..]] .concat() @@ -302,6 +354,27 @@ impl DrawableComponent for DetailsComponent { let can_scroll = usize::from(height) < number_of_lines; + let (title_len, message_len) = match self.data { + None => (0usize, 0usize), + Some(ref data) => match data.message { + None => (0usize, 0usize), + Some(ref message) => { + let title_len = message.subject.len(); + let message_len = match message.body { + None => 0usize, + Some(ref body) => body.len(), + }; + (title_len, message_len) + } + }, + }; + let mut title_buffer = + vec![0u8; title_len + title_len / usize::from(width) + 1]; + let mut message_buffer = + vec![ + 0u8; + message_len + message_len / usize::from(width) + 1 + ]; f.render_widget( dialog_paragraph( &format!( @@ -318,6 +391,8 @@ impl DrawableComponent for DetailsComponent { Text::from(self.get_wrapped_text_message( width as usize, height as usize, + &mut title_buffer, + &mut message_buffer, )), &self.theme, self.focused, @@ -403,16 +478,37 @@ impl Component for DetailsComponent { } } +trait NullTrim { + fn trim_null(&self) -> &Self; +} + +impl NullTrim for str { + fn trim_null(&self) -> &Self { + let end = match self.find('\0') { + None => self.len(), + Some(pos) => pos, + }; + &self[..end] + } +} + #[cfg(test)] mod tests { use super::*; - fn get_wrapped_lines( + fn get_wrapped_lines<'a>( message: &CommitMessage, width: usize, - ) -> Vec> { + title_buffer: &'a mut [u8], + message_buffer: &'a mut [u8], + ) -> Vec> { let (wrapped_title, wrapped_message) = - DetailsComponent::wrap_commit_details(message, width); + DetailsComponent::wrap_commit_details( + message, + width, + title_buffer, + message_buffer, + ); [&wrapped_title[..], &wrapped_message[..]].concat() } @@ -420,13 +516,25 @@ mod tests { #[test] fn test_textwrap() { let message = CommitMessage::from("Commit message"); + let mut title_buffer = vec![0; 32]; + let mut message_buffer = vec![0; 32]; assert_eq!( - get_wrapped_lines(&message, 7), + get_wrapped_lines( + &message, + 7, + &mut title_buffer, + &mut message_buffer + ), vec!["Commit", "message"] ); assert_eq!( - get_wrapped_lines(&message, 14), + get_wrapped_lines( + &message, + 14, + &mut title_buffer, + &mut message_buffer + ), vec!["Commit message"] ); @@ -434,11 +542,21 @@ mod tests { CommitMessage::from("Commit message\n"); assert_eq!( - get_wrapped_lines(&message_with_newline, 7), + get_wrapped_lines( + &message_with_newline, + 7, + &mut title_buffer, + &mut message_buffer + ), vec!["Commit", "message"] ); assert_eq!( - get_wrapped_lines(&message_with_newline, 14), + get_wrapped_lines( + &message_with_newline, + 14, + &mut title_buffer, + &mut message_buffer + ), vec!["Commit message"] ); @@ -447,14 +565,24 @@ mod tests { ); assert_eq!( - get_wrapped_lines(&message_with_body, 7), + get_wrapped_lines( + &message_with_body, + 7, + &mut title_buffer, + &mut message_buffer + ), vec![ "Commit", "message", "First", "line", "Second", "line" ] ); assert_eq!( - get_wrapped_lines(&message_with_body, 14), + get_wrapped_lines( + &message_with_body, + 14, + &mut title_buffer, + &mut message_buffer + ), vec!["Commit message", "First line", "Second line"] ); } From 25ab0fea6e56d6ab6ecf0d16d07cc9be6b3660e2 Mon Sep 17 00:00:00 2001 From: TheBlackSheep3 <40034835+TheBlackSheep3@users.noreply.github.com> Date: Thu, 20 Jul 2023 21:42:10 +0200 Subject: [PATCH 2/5] clippy fixes --- src/components/commit_details/details.rs | 49 +++++++++++------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/components/commit_details/details.rs b/src/components/commit_details/details.rs index 4a89c2c4be..5d20ef230f 100644 --- a/src/components/commit_details/details.rs +++ b/src/components/commit_details/details.rs @@ -93,21 +93,21 @@ impl DetailsComponent { width, title_buffer, ) - .unwrap() + .expect("insufficiant buffer size") .wrap(); let wrapped_title = std::str::from_utf8(title_buffer) - .unwrap() + .expect("failed to create str from utf8") .split('\n') .map(|s| Cow::Borrowed(s.trim_null())) .collect(); if let Some(ref body) = message.body { let _ = bwrap::Wrapper::new(body, width, message_buffer) - .unwrap() + .expect("insufficiant buffer size") .wrap(); let wrapped_message: Vec> = std::str::from_utf8(message_buffer) - .unwrap() + .expect("failed to create str from utf8") .split('\n') .map(|s| Cow::Borrowed(s.trim_null())) .collect(); @@ -147,10 +147,8 @@ impl DetailsComponent { let title_len = message.subject.len(); let mut title_buffer = vec![0; title_len + title_len / width + 1]; - let message_len = match message.body { - Some(ref body) => body.len(), - None => 0usize, - }; + let message_len = + message.body.as_ref().map_or(0usize, String::len); let mut message_buffer = vec![0; message_len + message_len / width + 1]; let (wrapped_title, wrapped_message) = @@ -163,7 +161,7 @@ impl DetailsComponent { return wrapped_title.len() + wrapped_message.len(); } } - return 0usize; + 0usize } fn get_theme_for_line(&self, bold: bool) -> Style { @@ -354,20 +352,20 @@ impl DrawableComponent for DetailsComponent { let can_scroll = usize::from(height) < number_of_lines; - let (title_len, message_len) = match self.data { - None => (0usize, 0usize), - Some(ref data) => match data.message { - None => (0usize, 0usize), - Some(ref message) => { - let title_len = message.subject.len(); - let message_len = match message.body { - None => 0usize, - Some(ref body) => body.len(), - }; - (title_len, message_len) - } - }, - }; + let (title_len, message_len) = + self.data.as_ref().map_or((0usize, 0usize), |data| { + data.message.as_ref().map_or( + (0usize, 0usize), + |message| { + let title_len = message.subject.len(); + let message_len = message + .body + .as_ref() + .map_or(0usize, String::len); + (title_len, message_len) + }, + ) + }); let mut title_buffer = vec![0u8; title_len + title_len / usize::from(width) + 1]; let mut message_buffer = @@ -484,10 +482,7 @@ trait NullTrim { impl NullTrim for str { fn trim_null(&self) -> &Self { - let end = match self.find('\0') { - None => self.len(), - Some(pos) => pos, - }; + let end = self.find('\0').map_or(self.len(), |pos| pos); &self[..end] } } From ffd004b559b95f406f4b2799b9dbdf94d1a03d9a Mon Sep 17 00:00:00 2001 From: TheBlackSheep3 <40034835+TheBlackSheep3@users.noreply.github.com> Date: Thu, 20 Jul 2023 22:11:33 +0200 Subject: [PATCH 3/5] removed unused bwrap feature from dependencies --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 11f6c5ab98..b2b00bdd76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ asyncgit = { path = "./asyncgit", version = "0.23", default-features = false } backtrace = "0.3" bitflags = "1.3" bugreport = "0.5" -bwrap = { version = "1.2.1", features = [ "use_std" ] } +bwrap = "1.2.1" bytesize = { version = "1.2", default-features = false } chrono = { version = "0.4", default-features = false, features = [ "clock" ] } clap = { version = "4.1", features = [ "env", "cargo" ] } From 4f1d4237405abee590cf55e1dc74e289afedf226 Mon Sep 17 00:00:00 2001 From: TheBlackSheep3 <40034835+TheBlackSheep3@users.noreply.github.com> Date: Thu, 20 Jul 2023 22:17:10 +0200 Subject: [PATCH 4/5] modified changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bceb31185c..7350c1faff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * support 'n'/'p' key to move to the next/prev hunk in diff component [[@hamflx](https://github.com/hamflx)] ([#1523](https://github.com/extrawurst/gitui/issues/1523)) * simplify theme overrides [[@cruessler](https://github.com/cruessler)] ([#1367](https://github.com/extrawurst/gitui/issues/1367)) +* switched from textwrap to bwrap for text wrapping [[@TheBlackSheep3](https://github.com/TheBlackSheep3/)] ([#1762](https://github.com/extrawurst/gitui/issues/1762)) ### Fixes * fix commit dialog char count for multibyte characters ([#1726](https://github.com/extrawurst/gitui/issues/1726)) From a73fa5163b646c8e2f8f2d74f850adc7cd380776 Mon Sep 17 00:00:00 2001 From: TheBlackSheep3 <40034835+TheBlackSheep3@users.noreply.github.com> Date: Mon, 24 Jul 2023 20:23:47 +0200 Subject: [PATCH 5/5] added feature use_std --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b2b00bdd76..11f6c5ab98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ asyncgit = { path = "./asyncgit", version = "0.23", default-features = false } backtrace = "0.3" bitflags = "1.3" bugreport = "0.5" -bwrap = "1.2.1" +bwrap = { version = "1.2.1", features = [ "use_std" ] } bytesize = { version = "1.2", default-features = false } chrono = { version = "0.4", default-features = false, features = [ "clock" ] } clap = { version = "4.1", features = [ "env", "cargo" ] }