From b9ec27d89c26bcf4776d93061267df4e34db5cd5 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Tue, 7 Jul 2020 12:33:32 +0200 Subject: [PATCH] fix newline visualization in commit msg editor (closes #169) --- CHANGELOG.md | 7 +- assets/newlines.gif | Bin 0 -> 102753 bytes src/components/textinput.rs | 150 +++++++++++++++++++++++++----------- 3 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 assets/newlines.gif 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 0000000000000000000000000000000000000000..3b73955c54e1a250754e436945bb82a25526430a GIT binary patch literal 102753 zcmeI52|U#M{{P3WFvc2^#u5@8O{$^I7Gqx$qSFx?aylKPLY=Z3``8UCTMQ}t+H%BT zEMuz>DxpZ5HEZVo8Pz%W-rxP*dw;+G@7#N;&pD5d4)guY=kuNMcs%F*dcR(8T|FJF z>am}g1exX#2pWxsM0HJKEDmaEm>$4vRc2vgd~1P~4F%q8ij21u7%#q2hYQ zSxvF>2lI|J6<=#fRCZ`i)ho%SrC?8w4>XnJv|m;CU%NrSl-!w#i&gl(xit5Fj%lX; zW&N_ehu5u3Tpl-S_buf&`YT+An=czw+5wr7!Q34_-e? zt1f%hQ|Qog#pqV~oBqnvub$9uRlIvzagg(VU0xABWp z3R(B&b=)^*B9;0RV%1exYGO@yVp%s?tTgH+=cZbTZSv?oF?!kaMegX83-4c!UiD!g zA62J}9V1In$jo*M?Da8gmsV`_1DFe@yRV0wB!#W zSRP;|n5CW-nW9pj#w zcXBm-^DBZwaiWHG-N}i2oO1aSwcOvOHq@bX6(=czRwpOxMV#^{8^j$_(J9|ie6C{G zAg~e|G#{(>;OP#l&4{^D2hCRYtd!7zOk6J#%Mzhkpx*4vlRLGVJH);Y;V4XH026oC*x9617fB_#=nA8%=e>A|XYI^ID=+er7+j>82`v)2+9HTEQJCUzC>!rI}e>csE*~G6= zEB=Cp(?Ci6UUlS%$un;mY~$WRXZCHT+TrJ0(@mWwf+d!pMV(K>U7G8t3#JN42@ke= zPM&UF##AQpxVx&653;fEuPj5wm~NBUbL^Nox%OURVW6*XGMB(fp^;9I7>wl(+fO;%n?Q1l!evEeIKx zSkl|}Xg`ZGyRa?XfIEHCUJ{AUV&?pAtPj-@?Zb>_<{Lroypj`Z6Aq*wl(w6R_laz*4$r zjMrX8S}zyHaiM#R*F#?G=t!EthNDEC<5^6>S_oZkb2s+m1cby9!%orLqG2M=KXOiY z;7dh3=@NSd)dgwl9N5S;4J`pvX`Du6&2`NkL25JUs0gyQ+}chhNBJ;TGaZ(Ld@R#9 zazzJw{S?+%w433Sqw~g`gRXcs@<)AqzS zv2Tt>D6taxDFmi%L4&+VW&RCuNgPsx!AQOV=@f!MV4P1wm*hYuBG1`rJMMiEzSTE( zXz(y|GpmYj#*Q1xkL<+Ost0fC)VR4#NcUWFdf=THzKb73O%%<~UBTA~6g;B56t|zC zo}%M%yxfK4Eu;EYZWeqc_7w7A)fzvgBgF`0-*fr$!9#9n<_)PM>KiQL5N1f<(rsuC zE|eK!Yw;l4G$By_uFZii7Y(5~LZC23((e_sZ^;?!cgDWE1+F5sS-p+a72DJ!-z!K9 zt4cSD+c{prEV>Kch`BGStx+!=;-@}l8aS^l$$UsXihm_3bAEA)93N3DheBYR8MBQQ zOc-?t(T+(C*Oe3cnM?0S`=YQr3F!h+6O?VTZXw(99BwI3ZgRbRXvJGRcH=G9Iw@Rn z%m+!owE;SbKxgpg!gMH8*}9mD2w#_)25nt?jAbeI4pk%NtBK^yRBlnn9pVxn#7{+ zV@w%d$lDE4)e0LC6wk#9JT9=3q2EiX8?&hSs{C= z?M#`}fFrZwo7aaO1UJ|ar|l2i>y~7GlfQy$+$()q=bq-Pj^{hijdOo+QoYfC`uq*W z!cjCbtnX2;*+Zw&nbe=J_U^I?E8ku=xjC{pL5*h!-KhR{PkSFRe73lPrNf=+EB0~xCkERP#~q#7LeYBIJ6o|cME2vQ+qIlJSd3DlU*tJNmz~;Z zkT|?GzDifu)U*B8lexCJ{kikT>mHxuvQnR~UB5CxFu6Unu6@2?_sS$OYR|A%x=+n@ z<3JVa?H9Y+7uwFROsDbQ8Phdc>`K`;gCj2|XDr{le`{s7FzU|hV^_Uf>RjJfw%>Vs zF3S61{|2?&ymu#pFD>>J9Gys#yE~Qm>xE(ZifU`r-I+7*E_9!EUF>bYIQh%ZAGlrU zOM`P4=(Ew6Y9~_mFAx8EVWGYK;_Dpx`!`Yhr=LF6T-r3#1Ogi|Alf96hzigoTZcNfh zH}#YC^pj8WQ>gS)?Dg9^@24!{j~VmZnCa_LA?O){8#(5`%TuVm#~(}eSH}tM#|Gfc z0(6p`>4^dRChEEHa`S~1lToGCVVrJZmCmT@%COy)!FN3!Jw?JLJHsf>;r9sPcgf+msNt1k;boi= z#qtpaxQKk`h+INMHaQ}b8j&^@ahWqRSw1om7a8Xq8BK_cBu9o(BMD=Xft*o(@==#? zQ5T$}ya-We$x)}NQLbZAKXDRWa3W!7+z&Vs%JUh;$Ax`^6++KFfv`)Bb`5_jMcxzI;O;!An zNAX7&;_XBe>{SyS4=3Qg5}Z#Y1i7g_uxCECkl-qs=&qXRaX9g;SE45=(Yq?~!lT5C z3yD6D5^V(x1P>(zc_k4@NugCq;g6ESyhH~0NHMCUxWl9bFH#bTlw3theMGvlKuQx$ z&QMLZIGmjAl}sij=T#-=KT5v2kX%@Gnu%%k!|X};VHQ?=V8!QC#RqTGm2al@6iusr5?klVNt;nIet222Ao~xU*rVSP!*fXZ2)+Yj~#kD6Ub} z*evX5tNn&KYU94e$tWN0EHsB6RV7l2=^#Z)ROm2OM{O#a7@j2~C#tJ`mM^S-0kvsY zQp!_g5n&$*gpsF(_d4|OM2zk!3y35jjCQCVD zv-}-T)P;-D89ed1#^)585}(Xm^P1Ug60C2r4@t>h_kGrZu;vHZYO}ef5qTCl`VaGL zX7j9Rd6pH;4{v(UUbo}9Zr!NgU7S3d?`(A4rl+~Pyn6PAYsB>@w_n%N+o1Noh!~OYC};4f+2(z55-lIUtL0If_xqAG zo*T}(27O)0?@P%>H(ab*`ns#%mla0bIO$~2-#66vzPyrl^3z z{vIl_FhI`m*;?EAT9LNGpk1xckmu*?(8wZ!uHg`W$~;BhxG2=Bbx8Que7$mHQMi-g zuvq_m1GcRw@?7h%#L|4DITbn@e<cUCmfPBmkgST#AgN)Z7TSyJqME;?Zy8gj=u@25%V*Z@TM2QKGn^KFv8to{{Mg2 zNB_#P&$_KRp`!Kq*7FN3cF2;XM#C4Fl!aDjRRAG}kY75R6MR*3rgvb}x1kBTKX7j9%*3ZAY)dBqxMP2$K*d zAxuJ;gfRJa!(@2#e)g#-;p&M%(4dBk!PaX>LDthYKV!r zHD)!#TBlnxCFZEQm}2GdI5Y>xM5X4YwOK^_*O^l5ny5N9Zh>35Wl$5IxOKvm=p+D> zvz6JZM1S!?l#CjhL@`R9iX&@FZz@NzAT4m>{)Qv_%l#vFW=|zlwr +7Y^4zHTFd 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 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")); + } +}