From 240149caba6b08aacfd0e6ab8ffac1fe258f4aaa Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 18 Dec 2024 19:30:19 -0500 Subject: [PATCH] feat: `x` in range mode will extend the range --- docs/index.md | 3 ++- examples/test.icalc | Bin 3920 -> 0 bytes examples/test.xlsx | Bin 3898 -> 3872 bytes src/book/mod.rs | 18 +++++++++++++++++- src/ui/mod.rs | 25 ++++++++++++++++++++----- 5 files changed, 39 insertions(+), 7 deletions(-) delete mode 100644 examples/test.icalc diff --git a/docs/index.md b/docs/index.md index 186123e..36ebf95 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,7 +68,8 @@ will clear the numeric prefix if you want to cancel it. **Other Keybindings** -* `Ctrl-r` will enter range selection mode +* `Ctrl-r` will enter range selection mode. +* `v` will enter range selection mode with the start of the range already selected. * `Ctrl-s` will save the sheet. * `Ctrl-c`, `y` Copy the cell or range contents. * `Ctrl-v`, `p` Paste into the sheet. diff --git a/examples/test.icalc b/examples/test.icalc deleted file mode 100644 index c45659964fcf604ea57fc411bad6bab750a8fd20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3920 zcmWIWW@Zs#U|`^22ox@jWM`h1Va>?EAjiVMAk4tP5bd0wSCX1n5+71okXjt8SCN~u zHu$U`lcB)A-y**2Q=&g4^dvrfa6ZuU2>Xh+I=3(EFX1|L+j{o`LpDd{<;&*2Uq5-~ z$CAgg(avtt&S%||PDrxI_VRYm+J8oVRra|JD-x!3GiL2$eRBEa(TaV(4Lv6qv+qrt zl()p?Zd15MQj^0}wY2qIv7epPmQK%N{8VJIXOdML)54IYmmf_2C~K1Slxz3>3!n8o z*xKjZeb+EI{A{S#xvbC?de;xu6tDco*EGYHEqZo=1A`vFRms%(yx;byzw+6}cEjiC zrxi8}pBy{6ea7_lhI&T}Co*d$Uyk5ado@+P>%bI8$sES6@T7=dOIg-CXKH>4%b2x$r!+~ zRqErHFZ4Wj(RbFvg_7It&P=}L{M6^h>(unEOg&SK-c|kGTN!VYeN@PxwNs#~mgQRZ z{|9#3qAMmfhAJz3uM*MJPPXVXV1GBO$5(R2ImcU3?iGuF7&CWOZuxfdfFpzO z64#v!UuW*Pb$agwmyciM^UaT6s<^jziqTVvC9PlNr+zT?39Rj9ICSn#m-_Q>ciQ4_ zo!j=)_|N@0JK4VPNA<6cFIPo9BLjm0GXsMlvVW8Fi&8-z?wxcn?~nmc+xzM+t?!ez z8JL7#@Jx1^WwOMx!R+lcRW_qrDqcKG^Y?FQWs+{coAmp;`15}E{&pq)ZwYg+WcfUb zb72aI;a+(yc~gwfVRj*l6t4|2+7gqwWGc_RbGx%7Cc0wttp-<4y$=dcgYu50G-m{P z$=-K37;|2GivGGSx)Q%GtXr8_bNZUYWDTiHOcOZUdafO~P~uYL!gWva?73&W4h3A6 zA`32D+#!B>v)DngcWKVSI|Iaow3*G9E>Bu+XK|DB^rkmc|GK?;-v9FWo6>tWrc+jJ z>ta6HH1UC^*BM3Wi=Qvfzw_?3w8gfUdLPpB8W(Q6*?l$ZP?4OD>$k4G_3?b*=6zg$|DBom@Lk69vsa!@O1Fo_7G@%5V_;y2FG|fpPr>5b%q?CrFfe#C zF)(n$)#!m#uAOw&@2~++!+RUoC%<3id2lRTx?5q%T>&lr1flQlxsNqCFYL8_*buQa z_g3QZBj28%pSFE>rPNjN_8Z-oH|Ti}dZ>!Y3*VC^mub6QnQtIf&HfHx5q2;&D zX?M`a;@}tUU2cy z+p3%LjvFha{>CFgL!s?3J{Tb!$+?nu>oLjb-Ktrhi#>wtnZ$|H83$JNRMg15$cT*tV{< zkdc9bpP7My7vj_6lFFP^un(qAI+)iC$;leuO>_A#n=oEA;mmwqc4?g^i`2=&fJtE+ zmd|%yAoA8kZ{vT9`So*B+V3&!)_lJ8=$!Xv(dR_Mwq3jN?9b~EyZuGanvX;^xlaA; zSXrkr&q$sv;COdXqab7LIeo>Fe5W6;7WI6QzOA`r?lCK6=B4XI_Z53xvy(GY_;&kk zw?UAy{-$F|AJ%LN=SfKt>N@j(!)%tIvKW!b)Pq*fZWW(9U1VJs=$09x^?SmC^M39d zC%PS;@T6A6EjoDa#f(@H*DPbbd3>1-Te*e0)=XB^XjfIu>GF4W-|R4pBeb)x`I=>fEZ#nYZRo=|ywlfAh=I z{>j>!MO}RV>XOR7#tQi_*BYYYOj&xf6CPcN`F%9|R_wf@kKCwnBWR{z{+5A(p$QZ} zkhm$&FUn5J&(8*>uik)zpuD;Fy|&W*$S6LJ(A+XdkKGC?Y~OVXuV?fIA5CoDe>~Py z#X?v7_tE#Zw!81v91WanQ0#HVaql_@#hp)>Wvwdi+upZrdX#kXwxM9zhh84(%~|I) z<-cpnPJPj7E#Wm&YVA#43ElOVTT~|7JPL~gln_sXNJUk*euCdurez^ zBKDK>8{V2Xaqs6lY`pQ}?F=QU850vP-&=dXU`tS%1<%aTMi(;ymrt9oyBbROaHFOJlXKAW#zVe47s(;S?1|`+UD-cEBG<#&hwVQb1pC5 zyHxXa1TC0WCBl4M?DhWTE4f6!RL;Drn;P+Kqt54yPx0|x^*b_aHnjYUT@h22xiLpR z<;%&fx8re#8y_SI|MI?xIXeQXwnb=Uw{p?tsu5iP^XGGVkv*^INOo6zcb;>e&4a+RIS}krp+j?$7WP{poU4s=S zGwh69Ov|QuoJnusHAp*{W;*{lk933W*G>PtwL8@+wT@~|=!?3!Xv5VB?ulLO#jCC4 z?WMP!n;>;mJNI*cm|ymdBfs9Sj+u+BRa`Y2ec#Ma{Kp;O&B&z34608cg)@3VogCnIZvHgFM^LR^jBKm_Ro;U$gDOh}f(nu`eS zm~IBOSU_4q_^qQM3!?pk>R|L13`ji)FKG;g>jCF;e3pSKdXOd%e(RXd3a$20Z9=d3 zL5e|mNn-~@14c6dVI?GZ&}&?fZV+D57{~^;5>fx6YelaRK-xffN#k2~uvSoJhE)#; z&6wc|Dl9#C$}aD(&!0EA558~^|S diff --git a/examples/test.xlsx b/examples/test.xlsx index a2b60d1cb1c09534a33d6358408b85cb356ed988..ac5e7f903956b8f562daadacf378b5c8505ab5bb 100644 GIT binary patch delta 1203 zcmdlbw?K|Jz?+#xgn@y9gF%UT@&`n&TQ$k4C)2AV$X7yXfscUm@HE+z%-+qwc>A~&y?*6aa z`>(qB;=U8jvJvWAr6%nVpU5C`-D%a;2|CXve`sXkn<=DoovUpLle^o^vkkMSWOL_- zNpH|N$SSua=G55u-sg_T=B-$`Eq>zJ(#BNpZ2M zfr;!ynF&qh9-FsTG7HRc;$zh)F?o0N%M#ZOt-Ehs2st;U^~k9Mp)WVySoz(r?9e8i z>eT%cfBj7UWmr4c)Sq|hsj! z)aE(t-0a%|Wwp1~zm#6#mD~FHulU62oCSBhn*XUs|8hOFy7*batNmq~jy9qD^?zxv zsMdM2B)e1m;=vbXW&(}o8o6(6!tVWk{QPH2iszI&ZG}R|`af%UO}@S5{{J<{cfYV_ z>^}VWzxutU7w<}b0Tf}A zU04)B5w$skg&z`gRjhI#fyuL3_k-xov1~KJ5}SXri-Q@Hg*m}-yV;794r0;^=gbw!2Jr|o|2Bel9uHfQpI z+sW#xT^0-G*_^h&uakCreb{8Vx#8EQOjvu(;etoBX0g`8w_jhrW9;BR6yg-!tXi|U zU3Y(Cb7}g01vVkZW799RT*%eBDdyG&cznaM*-ae$G`W!vMC5#Im7$6Kzk|Y?wrl~4Mk>Ub>S92 zT754mxo4k0S-H-ugPr5ik0zFGICz~7Gj+{A}?#K84L|0^3&!$KH zJ$hYt+kt|7vKo&Gn2zPKM#?-3dCb6~w|Jx_jaV2M;)_yq ziuKXW;Nz8M44bUQD-{5W5e6i{jKqHHn8C`xP?3YKJ@WA8XWJ$)FfgoVU|;|_45nvE zW9Q^4yvkq)?BbQS@n!>SFD|LfNiEi^$jw1>N2H*cg85qp28Jd^1_pkZDGUq@OB$a~ z7UYwYM|T}3lA5=UjFbKNB*2zs@<}ska8CB*l;KBM0ZFwC!gh$1seuWd`t`s U5=e$EX-u8W$*;|}hZ`ga0N@S5cmMzZ delta 1222 zcmZ1=w@Z#Uz?+#xgn@y9gTYF?cq4BnBeRuw@#IQI6)?Sk@hzv7cyVNvu3&ZREmZj=>eHbNiOEtnJI|hXJ7kn7v&Sm= z;6f&O)(PKyZWu-?M7gaL=hoOM&(g|wbY`LIae?|>mRn}l-@FjUH)ZLeTZcSfuDh|?JnrkERjb&p zZkXEkGx*oXPk*l2nTGA!=*4hy@k9|GQLzdAFaAmR)qVZS%e1>hzGZX4fr~r-oRq9R z&n@U{yCZDtYLoh9`U}_fOt$+X@9L;_kbAjK+x#~#+&dTO?KnRD!&YO~PwW2GUW|9V z{5WB4jk`_PHn;N4jXh0{Yc>~$SIXbB_mT6Qr+Tw;&a%QP+mmfNdy}3YKA*N{@ju%Q zckca{e{@#mW&`UU$z*>%X-3`2*?gKYpag^D6b3#d&Ra)2F0dix`9;~q8L6oy gn0|sMHYNrJ2_(Hs8nd`2H*!f$=HeG%y9V|x04|`u!vFvP diff --git a/src/book/mod.rs b/src/book/mod.rs index 8b345d6..f6dce1a 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -96,13 +96,29 @@ impl Book { Ok(&self.get_sheet()?.sheet_data) } - /// Move to a specific sheel location in the current sheet + /// Move to a specific sheet location in the current sheet pub fn move_to(&mut self, Address { row, col }: &Address) -> Result<()> { // FIXME(zaphar): Check that this is safe first. self.location.row = *row; self.location.col = *col; Ok(()) } + + /// Extend a cell to the rest of the range. + pub fn extend_to(&mut self, from: &Address, to: &Address) -> Result<()> { + for ri in from.row..=to.row { + for ci in from.col..=to.col { + if ri == from.row && ci == from.col { + continue; + } + let contents = self.model.extend_to(self.current_sheet, from.row as i32, from.col as i32, ri as i32, ci as i32).map_err(|e| anyhow!(e))?; + self.model.set_user_input(self.current_sheet, ri as i32, ci as i32, contents) + .map_err(|e| anyhow!(e))?; + } + } + self.evaluate(); + Ok(()) + } pub fn clear_current_cell(&mut self) -> Result<()> { self.clear_cell_contents(self.current_sheet as u32, self.location.clone()) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8b09161..c84a51e 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -317,6 +317,7 @@ impl<'ws> Workspace<'ws> { "Edit Mode:".to_string(), "* ENTER/RETURN: Exit edit mode and save changes".to_string(), "* Ctrl-r: Enter Range Selection mode".to_string(), + "* v: Enter Range Selection mode with the start of the range already selected".to_string(), "* ESC: Exit edit mode and discard changes".to_string(), "Otherwise edit as normal".to_string(), ], @@ -380,7 +381,7 @@ impl<'ws> Workspace<'ws> { return Ok(None); } KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => { - self.enter_range_select_mode(); + self.enter_range_select_mode(false); return Ok(None); } KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => { @@ -579,6 +580,12 @@ impl<'ws> Workspace<'ws> { self.copy_range(false)?; } KeyCode::Char('y') => self.copy_range(false)?, + KeyCode::Char('x') => { + if let (Some(from), Some(to)) = (self.state.range_select.start.as_ref(), self.state.range_select.end.as_ref()) { + self.book.extend_to(from, to)?; + } + self.exit_range_select_mode()?; + } _ => { // moop } @@ -670,7 +677,7 @@ impl<'ws> Workspace<'ws> { self.enter_edit_mode(); } KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => { - self.enter_range_select_mode(); + self.enter_range_select_mode(false); } KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => { self.state.clipboard = Some(ClipboardContents::Cell( @@ -697,7 +704,7 @@ impl<'ws> Workspace<'ws> { )); } KeyCode::Char('v') if key.modifiers != KeyModifiers::CONTROL => { - self.enter_range_select_mode() + self.enter_range_select_mode(true) } KeyCode::Char('p') if key.modifiers != KeyModifiers::CONTROL => { self.paste_range()?; @@ -810,6 +817,7 @@ impl<'ws> Workspace<'ws> { })?; } KeyCode::Char('g') => { + // TODO(zaphar): This really needs a better state machine. if self.state.char_queue.first().map(|c| *c == 'g').unwrap_or(false) { self.state.char_queue.pop(); self.move_to_top()?; @@ -819,6 +827,7 @@ impl<'ws> Workspace<'ws> { } _ => { // noop + self.state.char_queue.clear(); } } } @@ -829,6 +838,7 @@ impl<'ws> Workspace<'ws> { match &self.state.clipboard { Some(ClipboardContents::Cell(contents)) => { self.book.edit_current_cell(contents)?; + self.book.evaluate(); } Some(ClipboardContents::Range(ref rows)) => { let Address { row, col } = self.book.location.clone(); @@ -846,6 +856,7 @@ impl<'ws> Workspace<'ws> { )?; } } + self.book.evaluate(); } None => { // NOOP @@ -878,11 +889,15 @@ impl<'ws> Workspace<'ws> { self.state.modality_stack.push(Modality::Dialog); } - fn enter_range_select_mode(&mut self) { + fn enter_range_select_mode(&mut self, init_start: bool) { self.state.range_select.sheet = Some(self.book.current_sheet); self.state.range_select.original_sheet = Some(self.book.current_sheet); self.state.range_select.original_location = Some(self.book.location.clone()); - self.state.range_select.start = None; + if init_start { + self.state.range_select.start = Some(self.book.location.clone()); + } else { + self.state.range_select.start = None; + } self.state.range_select.end = None; self.state.modality_stack.push(Modality::RangeSelect); }