1use std::sync::Mutex;
16
17use gap_buf::{GapBuffer, gap_buffer};
18
19pub use self::cursor::{Selection, VPoint};
20use crate::{
21 buffer::Change,
22 text::{Point, Strs, TextRange},
23 utils::{add_shifts, merging_range_by_guess_and_lazy_shift},
24};
25
26pub struct Selections {
42 buf: GapBuffer<Selection>,
43 main_i: usize,
44 shift: Mutex<Shift>,
45}
46
47impl Selections {
48 pub(crate) fn new(main: Selection) -> Self {
50 Self {
51 buf: gap_buffer![main],
52 main_i: 0,
53 shift: Mutex::default(),
54 }
55 }
56
57 pub(crate) const fn new_empty() -> Self {
59 Self {
60 buf: GapBuffer::new(),
61 main_i: 0,
62 shift: Mutex::new(Shift { from: 0, by: [0; 3] }),
63 }
64 }
65
66 pub fn set_main(&mut self, new: usize) {
70 self.main_i = new.min(self.buf.len().saturating_sub(1));
71 }
72
73 pub fn rotate_main(&mut self, amount: i32) {
75 self.main_i = (self.main_i as i32 + amount).rem_euclid(self.buf.len() as i32) as usize
76 }
77
78 pub fn clear(&mut self) {
80 self.buf = GapBuffer::new();
81 *self.shift.get_mut().unwrap() = Shift::default();
82 }
83
84 pub fn reset(&mut self) {
89 self.remove_extras();
90 self.buf[self.main_i] = Selection::default();
91 }
92
93 pub fn remove_extras(&mut self) {
95 if !self.is_empty() {
96 let cursor = self.buf.remove(self.main_i);
97 let shift = std::mem::take(self.shift.get_mut().unwrap());
98 if shift.from <= self.main_i && shift.by != [0; 3] {
99 cursor.shift_by(shift.by);
100 }
101 self.buf = gap_buffer![cursor];
102 }
103 self.main_i = 0;
104 }
105
106 pub(crate) fn correct_all(&mut self, strs: &Strs) {
109 for selection in &mut self.buf {
110 selection.correct(strs)
111 }
112 }
113
114 #[track_caller]
123 pub fn main(&self) -> &Selection {
124 match self.get(self.main_i) {
125 Some(main) => main,
126 None => panic!("No main selection"),
127 }
128 }
129
130 pub fn get_main(&self) -> Option<&Selection> {
135 self.get(self.main_i)
136 }
137
138 pub fn get(&self, n: usize) -> Option<&Selection> {
140 if n >= self.len() {
141 return None;
142 }
143 let mut shift = self.shift.lock().unwrap();
144 if n >= shift.from && shift.by != [0; 3] {
145 for cursor in self.buf.range(shift.from..n + 1).iter() {
146 cursor.shift_by(shift.by);
147 }
148 if n + 1 < self.buf.len() {
149 shift.from = n + 1;
150 } else {
151 *shift = Shift::default();
152 }
153 }
154
155 self.buf.get(n)
156 }
157
158 pub fn iter(&self) -> impl Iterator<Item = (&Selection, bool)> {
163 let mut shift = *self.shift.lock().unwrap();
164
165 self.buf.iter().enumerate().map(move |(i, selection)| {
166 if i >= shift.from && shift.by != [0; 3] {
167 selection.shift_by(shift.by);
168 if i + 1 < self.buf.len() {
169 self.shift.lock().unwrap().from = i + 1;
170 shift.from = i + 1;
171 } else {
172 *self.shift.lock().unwrap() = Shift::default();
173 }
174 }
175
176 (selection, i == self.main_i)
177 })
178 }
179
180 pub fn iter_within(
188 &self,
189 range: impl TextRange,
190 ) -> impl Iterator<Item = (usize, &Selection, bool)> {
191 let shift = *self.shift.lock().unwrap();
192 let range = range.to_range(u32::MAX as usize);
193
194 let m_range = merging_range_by_guess_and_lazy_shift(
195 (&self.buf, self.buf.len()),
196 (0, [range.start, range.end]),
197 (shift.from, shift.by[0], 0, |byte, shift| {
198 (byte as i32 + shift) as usize
199 }),
200 (
201 |sel| sel.start_point().byte(),
202 |sel| sel.end_point_excl().byte(),
203 ),
204 );
205
206 let (s0, s1) = self.buf.range(m_range.clone()).as_slices();
207 let iter = [s0, s1].into_iter().flatten().enumerate();
208 iter.map(move |(i, selection)| {
209 let i = i + m_range.start;
210 if i >= shift.from && shift.by != [0; 3] {
211 selection.shift_by(shift.by);
212 if i + 1 < self.buf.len() {
213 self.shift.lock().unwrap().from = i + 1;
214 } else {
215 *self.shift.lock().unwrap() = Shift::default();
216 }
217 }
218
219 (i, selection, i == self.main_i)
220 })
221 }
222
223 pub fn main_index(&self) -> usize {
225 self.main_i
226 }
227
228 pub fn len(&self) -> usize {
230 self.buf.len()
231 }
232
233 #[must_use]
235 pub fn is_empty(&self) -> bool {
236 self.len() == 0
237 }
238
239 pub(crate) fn insert(
243 &mut self,
244 guess_i: usize,
245 sel: Selection,
246 main: bool,
247 ) -> ([usize; 2], bool) {
248 let mut shift = self.shift.lock().unwrap();
249 let shift_from = shift.from.min(self.len());
250
251 let m_range = merging_range_by_guess_and_lazy_shift(
253 (&self.buf, self.buf.len()),
254 (guess_i, [sel.start_point(), sel.end_point_excl()]),
255 (shift_from, shift.by, [0; 3], Point::shift_by),
256 (Selection::start_point, Selection::end_point_excl),
257 );
258
259 if shift_from < m_range.end && shift.by != [0; 3] {
261 for cursor in self.buf.range(shift_from..m_range.end).into_iter() {
262 cursor.shift_by(shift.by);
263 }
264 }
265
266 let (caret, anchor, last_cursor_overhangs) = {
269 let mut c_range = m_range.clone();
270 let first = c_range.next().and_then(|i| self.buf.get(i));
271 let last = c_range.last().and_then(|i| self.buf.get(i)).or(first);
272 let start = first
273 .map(|first| first.lazy_v_start().min(sel.lazy_v_start()))
274 .unwrap_or(sel.lazy_v_start());
275 let (end, last_sel_overhangs) = if let Some(last) = last
276 && last.lazy_v_end() >= sel.lazy_v_end()
277 {
278 (last.lazy_v_end(), true)
279 } else {
280 (sel.lazy_v_end(), false)
281 };
282
283 if let Some(anchor) = sel.anchor() {
284 match sel.caret() < anchor {
285 true => (start, Some(end), last_sel_overhangs),
286 false => (end, Some(start), last_sel_overhangs),
287 }
288 } else {
289 (end, (start != end).then_some(start), last_sel_overhangs)
290 }
291 };
292
293 let selection = Selection::from_v(caret, anchor, sel.change_i);
294 self.buf.splice(m_range.clone(), [selection]);
295
296 if main {
297 self.main_i = m_range.start;
298 } else if self.main_i >= m_range.start {
299 self.main_i = (self.main_i + 1 - m_range.clone().count()).max(m_range.start)
300 }
301
302 let cursors_taken = m_range.clone().count();
305 let new_shift_from = shift_from.saturating_sub(cursors_taken).max(m_range.start) + 1;
306 if new_shift_from < self.buf.len() {
307 shift.from = new_shift_from;
308 } else {
309 *shift = Shift::default()
310 }
311
312 ([m_range.start, cursors_taken], last_cursor_overhangs)
313 }
314
315 pub(crate) fn apply_change(&mut self, guess_i: usize, change: Change<&str>) -> usize {
319 let mut shift = self.shift.lock().unwrap();
320 let shift_from = shift.from.min(self.len());
321
322 let c_range = merging_range_by_guess_and_lazy_shift(
324 (&self.buf, self.buf.len()),
325 (guess_i, [change.start(), change.taken_end()]),
326 (shift_from, shift.by, [0; 3], Point::shift_by),
327 (Selection::start_point, Selection::end_point_excl),
328 );
329
330 if c_range.end > shift_from && shift.by != [0; 3] {
335 for cursor in self.buf.range(shift_from..c_range.end).into_iter() {
336 cursor.shift_by(shift.by);
337 }
338 }
339 let range = c_range.start..c_range.end.max(shift_from);
340 for cursor in self.buf.range(range).into_iter() {
341 cursor.shift_by_change(change);
342 }
343
344 let (cursors_taken, cursors_added) = {
345 let mut cursors_taken = self.buf.splice(c_range.clone(), []);
346 if let Some(first) = cursors_taken.next() {
347 let last = cursors_taken.next_back().unwrap_or(first.clone());
348 let (start, end) = (first.start_point(), last.end_point_excl());
349 let merged = Selection::new(start, (start < end).then_some(end));
350 drop(cursors_taken);
351 self.buf.insert(c_range.start, merged);
352
353 (c_range.len(), 1)
354 } else {
355 (0, 0)
356 }
357 };
358
359 let from = shift_from.saturating_sub(cursors_taken).max(c_range.start) + cursors_added;
360 if from < self.buf.len() {
361 *shift = Shift {
362 from,
363 by: add_shifts(shift.by, change.shift()),
364 };
365 } else {
366 *shift = Shift::default()
367 }
368
369 cursors_taken - cursors_added
370 }
371
372 pub(crate) fn remove(&mut self, i: usize) -> Option<(Selection, bool)> {
374 if i >= self.buf.len() {
375 return None;
376 }
377 let shift = self.shift.get_mut().unwrap();
378
379 if i >= shift.from && shift.by != [0; 3] {
380 for cursor in self.buf.range(shift.from..i + 1).iter() {
381 cursor.shift_by(shift.by);
382 }
383 if i + 1 < self.buf.len() {
384 shift.from = i;
387 } else {
388 *shift = Shift::default()
389 }
390 } else if i < shift.from {
391 shift.from -= 1;
394 }
395
396 let was_main = self.main_i == i;
397 if self.main_i >= i {
398 self.main_i = self.main_i.saturating_sub(1);
399 }
400 Some((self.buf.remove(i), was_main))
401 }
402
403 pub(crate) fn populate(&mut self) {
405 if self.buf.is_empty() {
406 self.main_i = 0;
407 self.buf = gap_buffer![Selection::default()];
408 }
409 }
410}
411
412impl Clone for Selections {
413 fn clone(&self) -> Self {
414 Self {
415 buf: self.buf.clone(),
416 main_i: self.main_i,
417 shift: Mutex::new(*self.shift.lock().unwrap()),
418 }
419 }
420}
421
422mod cursor {
423 use std::{cmp::Ordering, ops::Range, sync::Mutex};
424
425 use bincode::{Decode, Encode};
426
427 use crate::{
428 buffer::Change,
429 opts::PrintOpts,
430 text::{Point, Strs, Text, TextIndex},
431 ui::Area,
432 };
433
434 #[derive(Default, Encode, Decode)]
437 pub struct Selection {
438 caret: Mutex<LazyVPoint>,
439 anchor: Mutex<Option<LazyVPoint>>,
440 pub(in crate::mode::cursor) change_i: Option<u32>,
441 }
442
443 impl Selection {
444 pub(crate) fn new(caret: Point, anchor: Option<Point>) -> Self {
446 Self {
447 caret: Mutex::new(LazyVPoint::Unknown(caret)),
448 anchor: Mutex::new(anchor.map(LazyVPoint::Unknown)),
449 change_i: None,
450 }
451 }
452
453 pub(super) fn from_v(
454 caret: LazyVPoint,
455 anchor: Option<LazyVPoint>,
456 change_i: Option<u32>,
457 ) -> Self {
458 Self {
459 caret: Mutex::new(caret),
460 anchor: Mutex::new(anchor),
461 change_i,
462 }
463 }
464
465 #[track_caller]
467 pub fn move_to(&mut self, idx: impl TextIndex, text: &Text) {
468 let byte = idx.to_byte_index();
469 if byte == self.caret().byte() {
470 return;
471 }
472 *self.caret.get_mut().unwrap() =
473 LazyVPoint::Unknown(text.point_at_byte(byte.min(text.len() - 1)));
474 }
475
476 pub fn move_hor(&mut self, by: i32, text: &Text) -> i32 {
480 let by = by as isize;
481 if by == 0 {
482 return 0;
483 };
484
485 let caret = self.caret.get_mut().unwrap();
486
487 let target_char = caret.point().char().saturating_add_signed(by);
491
492 let point = if target_char == 0 {
493 Point::default()
494 } else if target_char >= text.last_point().char() {
495 text.last_point()
496 } else if by.abs() < 500 {
497 if by > 0 {
498 text[caret.point()..]
499 .chars()
500 .take(by as usize)
501 .fold(caret.point(), |point, char| point.fwd(char))
502 } else {
503 text[..caret.point()]
504 .chars()
505 .rev()
506 .take(by.unsigned_abs())
507 .fold(caret.point(), |point, char| point.rev(char))
508 }
509 } else {
510 text.point_at_char(target_char)
511 };
512
513 let moved = point.char() as i32 - caret.point().char() as i32;
514 *caret = LazyVPoint::Unknown(point);
515 moved
516 }
517
518 pub fn move_ver(&mut self, by: i32, text: &Text, area: &Area, opts: PrintOpts) -> bool {
522 if by == 0 {
523 return false;
524 }
525 let caret = self.caret.get_mut().unwrap();
526 let point = caret.point();
527
528 let desired_col = match *caret {
529 LazyVPoint::Unknown(_) => None,
530 LazyVPoint::Known(vpoint) => Some(vpoint.desired_visual_col()),
531 LazyVPoint::Desired { dvcol, .. } => Some(dvcol as usize),
532 };
533
534 let vpoint = area.move_ver(by, text, point, desired_col, opts);
535 *caret = LazyVPoint::Known(vpoint);
536
537 vpoint.point != point
538 }
539
540 pub fn move_ver_wrapped(
544 &mut self,
545 by: i32,
546 text: &Text,
547 area: &Area,
548 opts: PrintOpts,
549 ) -> bool {
550 if by == 0 {
551 return false;
552 };
553
554 let caret = self.caret.get_mut().unwrap();
555 let point = caret.point();
556
557 let desired_col = match *caret {
558 LazyVPoint::Unknown(_) => None,
559 LazyVPoint::Known(vpoint) => Some(vpoint.desired_wrapped_col()),
560 LazyVPoint::Desired { dwcol, .. } => Some(dwcol as usize),
561 };
562
563 let vpoint = area.move_ver_wrapped(by, text, point, desired_col, opts);
564 *caret = LazyVPoint::Known(vpoint);
565
566 vpoint.point != point
567 }
568
569 pub(crate) fn shift_by_change(&self, change: Change<&str>) {
570 let mut caret = self.caret.lock().unwrap();
571
572 let (shift, taken) = (change.shift(), change.taken_end());
573 if caret.point() >= change.start() {
574 let shifted_caret = caret.point().max(taken).shift_by(shift);
575 *caret = LazyVPoint::Unknown(shifted_caret);
576 }
577
578 let mut anchor = self.anchor.lock().unwrap();
579 if let Some(anchor) = &mut *anchor
580 && anchor.point() >= change.start()
581 {
582 let shifted_anchor = anchor.point().max(taken).shift_by(shift);
583 *anchor = LazyVPoint::Unknown(shifted_anchor);
584 }
585 }
586
587 pub(crate) fn shift_by(&self, shift: [i32; 3]) {
590 let mut caret = self.caret.lock().unwrap();
591 *caret = LazyVPoint::Unknown(caret.point().shift_by(shift));
592
593 let mut anchor = self.anchor.lock().unwrap();
594 if let Some(anchor) = &mut *anchor {
595 *anchor = LazyVPoint::Unknown(anchor.point().shift_by(shift));
596 }
597 }
598
599 pub(crate) fn correct(&mut self, strs: &Strs) {
602 let mut caret = self.caret.lock().unwrap();
603 *caret = LazyVPoint::Unknown(strs.point_at_byte(caret.point().byte()));
604
605 let mut anchor = self.anchor.lock().unwrap();
606 if let Some(anchor) = &mut *anchor {
607 *anchor = LazyVPoint::Unknown(strs.point_at_byte(anchor.point().byte()));
608 }
609 }
610
611 pub fn set_anchor(&mut self) {
619 *self.anchor.get_mut().unwrap() = Some(*self.caret.get_mut().unwrap())
620 }
621
622 pub fn unset_anchor(&mut self) -> Option<Point> {
627 self.anchor.get_mut().unwrap().take().map(|a| a.point())
628 }
629
630 pub fn swap_ends(&mut self) {
632 if let Some(anchor) = self.anchor.get_mut().unwrap() {
633 std::mem::swap(self.caret.get_mut().unwrap(), anchor);
634 }
635 }
636
637 pub fn caret(&self) -> Point {
639 self.caret.lock().unwrap().point()
640 }
641
642 pub fn anchor(&self) -> Option<Point> {
645 self.anchor.lock().unwrap().map(|lazy| lazy.point())
646 }
647
648 pub fn byte_range(&self, text: &Text) -> Range<usize> {
663 self.start_point().byte()..self.end_point(text).byte()
664 }
665
666 pub fn start_point(&self) -> Point {
668 if let Some(anchor) = *self.anchor.lock().unwrap() {
669 anchor.point().min(self.caret.lock().unwrap().point())
670 } else {
671 self.caret.lock().unwrap().point()
672 }
673 }
674
675 pub fn end_point(&self, text: &Text) -> Point {
677 self.end_point_excl()
678 .fwd(text.char_at(self.end_point_excl()).unwrap())
679 }
680
681 pub(super) fn end_point_excl(&self) -> Point {
682 if let Some(anchor) = *self.anchor.lock().unwrap() {
683 anchor.point().max(self.caret.lock().unwrap().point())
684 } else {
685 self.caret.lock().unwrap().point()
686 }
687 }
688
689 pub fn point_range(&self, text: &Text) -> Range<Point> {
694 self.start_point()..self.end_point(text)
695 }
696
697 pub fn point_range_excl(&self) -> Range<Point> {
701 self.start_point()..self.end_point_excl()
702 }
703
704 pub fn set_desired_cols(&mut self, v: usize, w: usize) {
709 let (v, w) = (v as u16, w as u16);
710 let caret = self.caret.get_mut().unwrap();
711 match caret {
712 LazyVPoint::Known(vp) => {
713 vp.dvcol = v;
714 vp.dwcol = w;
715 }
716 LazyVPoint::Unknown(point) => {
717 *caret = LazyVPoint::Desired { point: *point, dvcol: v, dwcol: w }
718 }
719 LazyVPoint::Desired { dvcol, dwcol, .. } => (*dvcol, *dwcol) = (v, w),
720 }
721 }
722
723 pub fn v_caret(&self, text: &Text, area: &Area, opts: PrintOpts) -> VPoint {
729 let mut caret = self.caret.lock().unwrap();
730 let vp = caret.calculate(text, area, opts);
731 *caret = LazyVPoint::Known(vp);
732 vp
733 }
734
735 pub fn v_anchor(&self, text: &Text, area: &Area, opts: PrintOpts) -> Option<VPoint> {
741 self.anchor.lock().unwrap().as_mut().map(|anchor| {
742 let vp = anchor.calculate(text, area, opts);
743 *anchor = LazyVPoint::Known(vp);
744 vp
745 })
746 }
747
748 pub fn v_range(&self, text: &Text, area: &Area, opts: PrintOpts) -> [VPoint; 2] {
755 let v_caret = self.v_caret(text, area, opts);
756 let v_anchor = self.v_anchor(text, area, opts).unwrap_or(v_caret);
757 [v_caret.min(v_anchor), v_caret.max(v_anchor)]
758 }
759
760 pub(super) fn lazy_v_start(&self) -> LazyVPoint {
762 match *self.anchor.lock().unwrap() {
763 Some(anchor) => self.caret.lock().unwrap().min(anchor),
764 None => *self.caret.lock().unwrap(),
765 }
766 }
767
768 pub(super) fn lazy_v_end(&self) -> LazyVPoint {
770 match *self.anchor.lock().unwrap() {
771 Some(anchor) => self.caret.lock().unwrap().max(anchor),
772 None => *self.caret.lock().unwrap(),
773 }
774 }
775 }
776
777 impl Clone for Selection {
778 fn clone(&self) -> Self {
779 Self {
780 caret: Mutex::new(*self.caret.lock().unwrap()),
781 anchor: Mutex::new(*self.anchor.lock().unwrap()),
782 change_i: self.change_i,
783 }
784 }
785 }
786
787 #[derive(Clone, Copy, Eq, Encode, Decode)]
790 pub(super) enum LazyVPoint {
791 Unknown(Point),
792 Known(VPoint),
793 Desired {
794 point: Point,
795 dvcol: u16,
796 dwcol: u16,
797 },
798 }
799
800 impl LazyVPoint {
801 fn point(&self) -> Point {
802 match *self {
803 LazyVPoint::Unknown(point) | LazyVPoint::Desired { point, .. } => point,
804 LazyVPoint::Known(vp) => vp.point,
805 }
806 }
807
808 fn calculate(self, text: &Text, area: &Area, opts: PrintOpts) -> VPoint {
810 match self {
811 Self::Known(vp) => vp,
812 Self::Unknown(point) => area.move_ver(0, text, point, None, opts),
813 Self::Desired { point, dvcol, dwcol } => {
814 let mut vp = area.move_ver(0, text, point, Some(dvcol as usize), opts);
815 vp.dvcol = dvcol;
816 vp.dwcol = dwcol;
817 vp
818 }
819 }
820 }
821 }
822
823 impl Default for LazyVPoint {
824 fn default() -> Self {
825 Self::Desired {
826 point: Point::default(),
827 dvcol: 0,
828 dwcol: 0,
829 }
830 }
831 }
832
833 #[allow(clippy::non_canonical_partial_ord_impl)]
834 impl PartialOrd for LazyVPoint {
835 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
836 Some(self.point().cmp(&other.point()))
837 }
838 }
839
840 impl Ord for LazyVPoint {
841 fn cmp(&self, other: &Self) -> Ordering {
842 self.partial_cmp(other).unwrap()
843 }
844 }
845
846 impl PartialEq for LazyVPoint {
847 fn eq(&self, other: &Self) -> bool {
848 self.point() == other.point()
849 }
850 }
851
852 #[derive(Default, Clone, Copy, Debug, Eq, Encode, Decode)]
876 pub struct VPoint {
877 point: Point,
878 ccol: u16,
880 vcol: u16,
881 dvcol: u16,
882 wcol: u16,
883 dwcol: u16,
884 }
885
886 impl VPoint {
887 pub fn new(point: Point, ccol: u16, vcol: u16, dvcol: u16, wcol: u16, dwcol: u16) -> Self {
889 Self { point, ccol, vcol, dvcol, wcol, dwcol }
890 }
891
892 pub fn byte(&self) -> usize {
894 self.point.byte()
895 }
896
897 pub fn char(&self) -> usize {
899 self.point.char()
900 }
901
902 pub fn line(&self) -> usize {
904 self.point.line()
905 }
906
907 pub fn char_col(&self) -> usize {
909 self.ccol as usize
910 }
911
912 pub fn visual_col(&self) -> usize {
914 self.vcol as usize
915 }
916
917 pub fn desired_visual_col(&self) -> usize {
919 self.dvcol as usize
920 }
921
922 pub fn wrapped_col(&self) -> usize {
924 self.wcol as usize
925 }
926
927 pub fn desired_wrapped_col(&self) -> usize {
929 self.dwcol as usize
930 }
931 }
932
933 #[allow(clippy::non_canonical_partial_ord_impl)]
934 impl PartialOrd for VPoint {
935 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
936 Some(self.point.cmp(&other.point))
937 }
938 }
939
940 impl Ord for VPoint {
941 fn cmp(&self, other: &Self) -> Ordering {
942 self.partial_cmp(other).unwrap()
943 }
944 }
945
946 impl PartialEq for VPoint {
947 fn eq(&self, other: &Self) -> bool {
948 self.point == other.point
949 }
950 }
951
952 impl std::fmt::Debug for Selection {
953 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
954 f.debug_struct("Selection")
955 .field("caret", &*self.caret.lock().unwrap())
956 .field("anchor", &*self.anchor.lock().unwrap())
957 .field("change_i", &self.change_i)
958 .finish()
959 }
960 }
961
962 impl std::fmt::Debug for LazyVPoint {
963 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
964 match self {
965 Self::Known(vp) => write!(f, "Known({:?}, {})", vp.point, vp.dwcol),
966 Self::Unknown(p_or_b) => write!(f, "Unknown({p_or_b:?}"),
967 Self::Desired { point, dvcol, dwcol } => {
968 write!(f, "Desired({point:?}, {dvcol}, {dwcol})")
969 }
970 }
971 }
972 }
973}
974
975impl std::fmt::Debug for Selections {
976 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
977 struct DebugShiftState(Shift);
978 impl std::fmt::Debug for DebugShiftState {
979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
980 write!(f, "{:?}", self.0)
981 }
982 }
983
984 f.debug_struct("Selections")
985 .field("buf", &self.buf)
986 .field("main_i", &self.main_i)
987 .field("shift_sate", &DebugShiftState(*self.shift.lock().unwrap()))
988 .finish()
989 }
990}
991
992#[derive(Default, Debug, Clone, Copy)]
993struct Shift {
994 from: usize,
995 by: [i32; 3],
996}