diff --git a/CHANGELOG.md b/CHANGELOG.md index 7604ef8a..90cc86db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - switch deprecated transitive dependency `net2`->`socket2` [in `crossterm`->`mio`] ([#66](https://github.com/extrawurst/gitui/issues/66)) -- crash diffing stash created on command line ([#178](https://github.com/extrawurst/gitui/issues/178)) -- delta file size diff on untracked binary files ([#171](https://github.com/extrawurst/gitui/issues/171)) +- crash diffing a stash that was created via cli ([#178](https://github.com/extrawurst/gitui/issues/178)) +- zero delta file size in diff of untracked binary file ([#171](https://github.com/extrawurst/gitui/issues/171)) +- newlines not visualized correctly in commit editor ([#169](https://github.com/extrawurst/gitui/issues/169)) + +![](assets/newlines.gif) ## [0.8.0] - 2020-07-06 diff --git a/assets/newlines.gif b/assets/newlines.gif new file mode 100644 index 00000000..3b73955c Binary files /dev/null and b/assets/newlines.gif differ diff --git a/src/components/textinput.rs b/src/components/textinput.rs index 4f2bf0f5..d5de6b2c 100644 --- a/src/components/textinput.rs +++ b/src/components/textinput.rs @@ -86,6 +86,13 @@ impl TextInputComponent { Some(index) } + fn backspace(&mut self) { + if self.cursor_position > 0 { + self.decr_cursor(); + self.msg.remove(self.cursor_position); + } + } + /// Set the `msg`. pub fn set_text(&mut self, msg: String) { self.msg = msg; @@ -96,6 +103,54 @@ impl TextInputComponent { pub fn set_title(&mut self, t: String) { self.title = t; } + + fn get_draw_text(&self) -> Vec { + let style = self.theme.text(true, false); + + let mut txt = Vec::new(); + + // the portion of the text before the cursor is added + // if the cursor is not at the first character + if self.cursor_position > 0 { + txt.push(Text::styled( + &self.msg[..self.cursor_position], + style, + )); + } + + let cursor_str = if let Some(pos) = self.next_char_position() + { + &self.msg[self.cursor_position..pos] + } else { + // if the cursor is at the end of the msg + // a whitespace is used to underline + " " + }; + + if cursor_str == "\n" { + txt.push(Text::styled( + "\u{21b5}", + self.theme + .text(false, false) + .modifier(Modifier::UNDERLINED), + )); + } + + txt.push(Text::styled( + cursor_str, + style.modifier(Modifier::UNDERLINED), + )); + + // the final portion of the text is added if there is + // still remaining characters + if let Some(pos) = self.next_char_position() { + if pos < self.msg.len() { + txt.push(Text::styled(&self.msg[pos..], style)); + } + } + + txt + } } impl DrawableComponent for TextInputComponent { @@ -105,46 +160,13 @@ impl DrawableComponent for TextInputComponent { _rect: Rect, ) -> Result<()> { if self.visible { - let mut txt: Vec = Vec::new(); - - if self.msg.is_empty() { - txt.push(Text::styled( + let txt = if self.msg.is_empty() { + vec![Text::styled( self.default_msg.as_str(), self.theme.text(false, false), - )); + )] } else { - let style = self.theme.text(true, false); - - // the portion of the text before the cursor is added - // if the cursor is not at the first character - if self.cursor_position > 0 { - txt.push(Text::styled( - &self.msg[..self.cursor_position], - style, - )); - } - - txt.push(Text::styled( - if let Some(pos) = self.next_char_position() { - &self.msg[self.cursor_position..pos] - } else { - // if the cursor is at the end of the msg - // a whitespace is used to underline - " " - }, - style.modifier(Modifier::UNDERLINED), - )); - - // the final portion of the text is added if there is - // still remaining characters - if let Some(pos) = self.next_char_position() { - if pos < self.msg.len() { - txt.push(Text::styled( - &self.msg[pos..], - style, - )); - } - } + self.get_draw_text() }; let area = ui::centered_rect(60, 20, f.size()); @@ -203,12 +225,7 @@ impl Component for TextInputComponent { return Ok(true); } KeyCode::Backspace => { - if self.cursor_position > 0 { - self.decr_cursor(); - if self.cursor_position < self.msg.len() { - } - self.msg.remove(self.cursor_position); - } + self.backspace(); return Ok(true); } KeyCode::Left => { @@ -248,3 +265,50 @@ impl Component for TextInputComponent { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_smoke() { + let mut comp = + TextInputComponent::new(SharedTheme::default(), "", ""); + + comp.set_text(String::from("a\nb")); + + assert_eq!(comp.cursor_position, 0); + + comp.incr_cursor(); + assert_eq!(comp.cursor_position, 1); + + comp.decr_cursor(); + assert_eq!(comp.cursor_position, 0); + } + + fn get_text<'a>(t: &'a Text) -> Option<&'a str> { + if let Text::Styled(c, _) = t { + Some(c.as_ref()) + } else { + None + } + } + + #[test] + fn test_visualize_newline() { + let mut comp = + TextInputComponent::new(SharedTheme::default(), "", ""); + + comp.set_text(String::from("a\nb")); + + comp.incr_cursor(); + + let txt = comp.get_draw_text(); + + assert_eq!(txt.len(), 4); + assert_eq!(get_text(&txt[0]), Some("a")); + assert_eq!(get_text(&txt[1]), Some("\u{21b5}")); + assert_eq!(get_text(&txt[2]), Some("\n")); + assert_eq!(get_text(&txt[3]), Some("b")); + } +}