1use std::convert::TryFrom;
22use std::mem;
23use std::ops::Range;
24
25use crate::index::{Column, Line, Point, Side};
26use crate::term::cell::Flags;
27use crate::term::{Search, Term};
28
29#[derive(Debug, Copy, Clone, PartialEq)]
31pub struct Anchor {
32    point: Point<usize>,
33    side: Side,
34}
35
36impl Anchor {
37    fn new(point: Point<usize>, side: Side) -> Anchor {
38        Anchor { point, side }
39    }
40}
41
42#[derive(Copy, Clone, Debug, Eq, PartialEq)]
44pub struct SelectionRange<L = usize> {
45    pub start: Point<L>,
47    pub end: Point<L>,
49    pub is_block: bool,
51}
52
53impl<L> SelectionRange<L> {
54    pub fn new(start: Point<L>, end: Point<L>, is_block: bool) -> Self {
55        Self { start, end, is_block }
56    }
57
58    pub fn contains(&self, col: Column, line: L) -> bool
59    where
60        L: PartialEq + PartialOrd,
61    {
62        self.start.line <= line
63            && self.end.line >= line
64            && (self.start.col <= col || (self.start.line != line && !self.is_block))
65            && (self.end.col >= col || (self.end.line != line && !self.is_block))
66    }
67}
68
69#[derive(Debug, Copy, Clone, PartialEq)]
71enum SelectionType {
72    Simple,
73    Block,
74    Semantic,
75    Lines,
76}
77
78#[derive(Debug, Clone, PartialEq)]
97pub struct Selection {
98    region: Range<Anchor>,
99    ty: SelectionType,
100}
101
102impl Selection {
103    pub fn simple(location: Point<usize>, side: Side) -> Selection {
104        Self {
105            region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) },
106            ty: SelectionType::Simple,
107        }
108    }
109
110    pub fn block(location: Point<usize>, side: Side) -> Selection {
111        Self {
112            region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) },
113            ty: SelectionType::Block,
114        }
115    }
116
117    pub fn semantic(location: Point<usize>) -> Selection {
118        Self {
119            region: Range {
120                start: Anchor::new(location, Side::Left),
121                end: Anchor::new(location, Side::Right),
122            },
123            ty: SelectionType::Semantic,
124        }
125    }
126
127    pub fn lines(location: Point<usize>) -> Selection {
128        Self {
129            region: Range {
130                start: Anchor::new(location, Side::Left),
131                end: Anchor::new(location, Side::Right),
132            },
133            ty: SelectionType::Lines,
134        }
135    }
136
137    pub fn update(&mut self, location: Point<usize>, side: Side) {
138        self.region.end.point = location;
139        self.region.end.side = side;
140    }
141
142    pub fn rotate(
143        mut self,
144        num_lines: usize,
145        num_cols: usize,
146        scrolling_region: &Range<Line>,
147        offset: isize,
148    ) -> Option<Selection> {
149        let region_start = num_lines - scrolling_region.start.0;
151        let region_end = num_lines - scrolling_region.end.0;
152
153        let (mut start, mut end) = (&mut self.region.start, &mut self.region.end);
154        if Self::points_need_swap(start.point, end.point) {
155            mem::swap(&mut start, &mut end);
156        }
157
158        if (start.point.line < region_start || region_start == num_lines)
160            && start.point.line >= region_end
161        {
162            start.point.line = usize::try_from(start.point.line as isize + offset).unwrap_or(0);
163
164            if start.point.line < region_end && end.point.line >= region_end {
166                return None;
167            }
168
169            if start.point.line >= region_start && region_start != num_lines {
171                if self.ty != SelectionType::Block {
172                    start.point.col = Column(0);
173                    start.side = Side::Left;
174                }
175                start.point.line = region_start - 1;
176            }
177        }
178
179        if (end.point.line < region_start || region_start == num_lines)
181            && end.point.line >= region_end
182        {
183            end.point.line = usize::try_from(end.point.line as isize + offset).unwrap_or(0);
184
185            if end.point.line > start.point.line {
187                return None;
188            }
189
190            if end.point.line < region_end {
192                if self.ty != SelectionType::Block {
193                    end.point.col = Column(num_cols - 1);
194                    end.side = Side::Right;
195                }
196                end.point.line = region_end;
197            }
198        }
199
200        Some(self)
201    }
202
203    pub fn is_empty(&self) -> bool {
204        match self.ty {
205            SelectionType::Simple => {
206                let (mut start, mut end) = (self.region.start, self.region.end);
207                if Selection::points_need_swap(start.point, end.point) {
208                    mem::swap(&mut start, &mut end);
209                }
210
211                start == end
214                    || (start.side == Side::Right
215                        && end.side == Side::Left
216                        && (start.point.line == end.point.line)
217                        && start.point.col + 1 == end.point.col)
218            }
219            SelectionType::Block => {
220                let (start, end) = (self.region.start, self.region.end);
221
222                (start.point.col == end.point.col && start.side == end.side)
226                    || (start.point.col + 1 == end.point.col
227                        && start.side == Side::Right
228                        && end.side == Side::Left)
229                    || (end.point.col + 1 == start.point.col
230                        && start.side == Side::Left
231                        && end.side == Side::Right)
232            }
233            SelectionType::Semantic | SelectionType::Lines => false,
234        }
235    }
236
237    pub fn to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange> {
239        let grid = term.grid();
240        let num_cols = grid.num_cols();
241
242        let (mut start, mut end) = (self.region.start, self.region.end);
244        if Self::points_need_swap(start.point, end.point) {
245            mem::swap(&mut start, &mut end);
246        }
247
248        let is_block = self.ty == SelectionType::Block;
250        let (start, end) = Self::grid_clamp(start, end, is_block, grid.len()).ok()?;
251
252        let range = match self.ty {
253            SelectionType::Simple => self.range_simple(start, end, num_cols),
254            SelectionType::Block => self.range_block(start, end),
255            SelectionType::Semantic => Self::range_semantic(term, start.point, end.point),
256            SelectionType::Lines => Self::range_lines(term, start.point, end.point),
257        };
258
259        range.map(|range| Self::range_expand_fullwidth(term, range))
261    }
262
263    fn range_expand_fullwidth<T>(term: &Term<T>, mut range: SelectionRange) -> SelectionRange {
265        let grid = term.grid();
266        let num_cols = grid.num_cols();
267
268        let flag_at = |point: Point<usize>, flag: Flags| -> bool {
270            grid[point.line][point.col].flags.contains(flag)
271        };
272
273        if range.start.col < num_cols {
275            if range.start.line + 1 != grid.len() || range.start.col.0 != 0 {
277                let prev = range.start.sub(num_cols.0, 1, true);
278                if flag_at(range.start, Flags::WIDE_CHAR_SPACER) && flag_at(prev, Flags::WIDE_CHAR)
279                {
280                    range.start = prev;
281                }
282            }
283
284            if range.start.line + 1 != grid.len() || range.start.col.0 != 0 {
286                let prev = range.start.sub(num_cols.0, 1, true);
287                if (prev.line + 1 != grid.len() || prev.col.0 != 0)
288                    && flag_at(prev, Flags::WIDE_CHAR_SPACER)
289                    && !flag_at(prev.sub(num_cols.0, 1, true), Flags::WIDE_CHAR)
290                {
291                    range.start = prev;
292                }
293            }
294        }
295
296        if range.end.line != 0 || range.end.col < num_cols {
298            if (range.end.line + 1 != grid.len() || range.end.col.0 != 0)
300                && flag_at(range.end, Flags::WIDE_CHAR_SPACER)
301                && !flag_at(range.end.sub(num_cols.0, 1, true), Flags::WIDE_CHAR)
302            {
303                range.end = range.end.add(num_cols.0, 1, true);
304            }
305
306            if flag_at(range.end, Flags::WIDE_CHAR) {
308                range.end = range.end.add(num_cols.0, 1, true);
309            }
310        }
311
312        range
313    }
314
315    fn points_need_swap(start: Point<usize>, end: Point<usize>) -> bool {
317        start.line < end.line || start.line == end.line && start.col > end.col
318    }
319
320    fn grid_clamp(
322        mut start: Anchor,
323        end: Anchor,
324        is_block: bool,
325        lines: usize,
326    ) -> Result<(Anchor, Anchor), ()> {
327        if start.point.line >= lines {
329            if end.point.line >= lines {
331                return Err(());
332            }
333
334            if !is_block {
336                start.side = Side::Left;
337                start.point.col = Column(0);
338            }
339            start.point.line = lines - 1;
340        }
341
342        Ok((start, end))
343    }
344
345    fn range_semantic<T>(
346        term: &Term<T>,
347        mut start: Point<usize>,
348        mut end: Point<usize>,
349    ) -> Option<SelectionRange> {
350        if start == end {
351            if let Some(matching) = term.bracket_search(start) {
352                if (matching.line == start.line && matching.col < start.col)
353                    || (matching.line > start.line)
354                {
355                    start = matching;
356                } else {
357                    end = matching;
358                }
359
360                return Some(SelectionRange { start, end, is_block: false });
361            }
362        }
363
364        start = term.semantic_search_left(start);
365        end = term.semantic_search_right(end);
366
367        Some(SelectionRange { start, end, is_block: false })
368    }
369
370    fn range_lines<T>(
371        term: &Term<T>,
372        mut start: Point<usize>,
373        mut end: Point<usize>,
374    ) -> Option<SelectionRange> {
375        start = term.line_search_left(start);
376        end = term.line_search_right(end);
377
378        Some(SelectionRange { start, end, is_block: false })
379    }
380
381    fn range_simple(
382        &self,
383        mut start: Anchor,
384        mut end: Anchor,
385        num_cols: Column,
386    ) -> Option<SelectionRange> {
387        if self.is_empty() {
388            return None;
389        }
390
391        if end.side == Side::Left && start.point != end.point {
393            if end.point.col == Column(0) {
395                end.point.col = num_cols - 1;
396                end.point.line += 1;
397            } else {
398                end.point.col -= 1;
399            }
400        }
401
402        if start.side == Side::Right && start.point != end.point {
404            start.point.col += 1;
405        }
406
407        Some(SelectionRange { start: start.point, end: end.point, is_block: false })
408    }
409
410    fn range_block(&self, mut start: Anchor, mut end: Anchor) -> Option<SelectionRange> {
411        if self.is_empty() {
412            return None;
413        }
414
415        if start.point.col > end.point.col {
417            mem::swap(&mut start.side, &mut end.side);
418            mem::swap(&mut start.point.col, &mut end.point.col);
419        }
420
421        if end.side == Side::Left && start.point != end.point && end.point.col.0 > 0 {
423            end.point.col -= 1;
424        }
425
426        if start.side == Side::Right && start.point != end.point {
428            start.point.col += 1;
429        }
430
431        Some(SelectionRange { start: start.point, end: end.point, is_block: true })
432    }
433}
434
435#[cfg(test)]
445mod test {
446    use std::mem;
447
448    use super::{Selection, SelectionRange};
449    use crate::clipboard::Clipboard;
450    use crate::config::MockConfig;
451    use crate::event::{Event, EventListener};
452    use crate::grid::Grid;
453    use crate::index::{Column, Line, Point, Side};
454    use crate::term::cell::{Cell, Flags};
455    use crate::term::{SizeInfo, Term};
456
457    struct Mock;
458    impl EventListener for Mock {
459        fn send_event(&self, _event: Event) {}
460    }
461
462    fn term(width: usize, height: usize) -> Term<Mock> {
463        let size = SizeInfo {
464            width: width as f32,
465            height: height as f32,
466            cell_width: 1.0,
467            cell_height: 1.0,
468            padding_x: 0.0,
469            padding_y: 0.0,
470            dpr: 1.0,
471        };
472        Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock)
473    }
474
475    #[test]
481    fn single_cell_left_to_right() {
482        let location = Point { line: 0, col: Column(0) };
483        let mut selection = Selection::simple(location, Side::Left);
484        selection.update(location, Side::Right);
485
486        assert_eq!(
487            selection.to_range(&term(1, 1)).unwrap(),
488            SelectionRange { start: location, end: location, is_block: false }
489        );
490    }
491
492    #[test]
498    fn single_cell_right_to_left() {
499        let location = Point { line: 0, col: Column(0) };
500        let mut selection = Selection::simple(location, Side::Right);
501        selection.update(location, Side::Left);
502
503        assert_eq!(
504            selection.to_range(&term(1, 1)).unwrap(),
505            SelectionRange { start: location, end: location, is_block: false }
506        );
507    }
508
509    #[test]
515    fn between_adjacent_cells_left_to_right() {
516        let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
517        selection.update(Point::new(0, Column(1)), Side::Left);
518
519        assert_eq!(selection.to_range(&term(2, 1)), None);
520    }
521
522    #[test]
528    fn between_adjacent_cells_right_to_left() {
529        let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left);
530        selection.update(Point::new(0, Column(0)), Side::Right);
531
532        assert_eq!(selection.to_range(&term(2, 1)), None);
533    }
534
535    #[test]
544    fn across_adjacent_lines_upward_final_cell_exclusive() {
545        let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right);
546        selection.update(Point::new(0, Column(1)), Side::Right);
547
548        assert_eq!(
549            selection.to_range(&term(5, 2)).unwrap(),
550            SelectionRange {
551                start: Point::new(1, Column(2)),
552                end: Point::new(0, Column(1)),
553                is_block: false,
554            }
555        );
556    }
557
558    #[test]
569    fn selection_bigger_then_smaller() {
570        let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right);
571        selection.update(Point::new(1, Column(1)), Side::Right);
572        selection.update(Point::new(1, Column(0)), Side::Right);
573
574        assert_eq!(
575            selection.to_range(&term(5, 2)).unwrap(),
576            SelectionRange {
577                start: Point::new(1, Column(1)),
578                end: Point::new(0, Column(1)),
579                is_block: false,
580            }
581        );
582    }
583
584    #[test]
585    fn line_selection() {
586        let num_lines = 10;
587        let num_cols = 5;
588        let mut selection = Selection::lines(Point::new(0, Column(1)));
589        selection.update(Point::new(5, Column(1)), Side::Right);
590        selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
591
592        assert_eq!(
593            selection.to_range(&term(num_cols, num_lines)).unwrap(),
594            SelectionRange {
595                start: Point::new(9, Column(0)),
596                end: Point::new(7, Column(4)),
597                is_block: false,
598            }
599        );
600    }
601
602    #[test]
603    fn semantic_selection() {
604        let num_lines = 10;
605        let num_cols = 5;
606        let mut selection = Selection::semantic(Point::new(0, Column(3)));
607        selection.update(Point::new(5, Column(1)), Side::Right);
608        selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
609
610        assert_eq!(
611            selection.to_range(&term(num_cols, num_lines)).unwrap(),
612            SelectionRange {
613                start: Point::new(9, Column(0)),
614                end: Point::new(7, Column(3)),
615                is_block: false,
616            }
617        );
618    }
619
620    #[test]
621    fn simple_selection() {
622        let num_lines = 10;
623        let num_cols = 5;
624        let mut selection = Selection::simple(Point::new(0, Column(3)), Side::Right);
625        selection.update(Point::new(5, Column(1)), Side::Right);
626        selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
627
628        assert_eq!(
629            selection.to_range(&term(num_cols, num_lines)).unwrap(),
630            SelectionRange {
631                start: Point::new(9, Column(0)),
632                end: Point::new(7, Column(3)),
633                is_block: false,
634            }
635        );
636    }
637
638    #[test]
639    fn block_selection() {
640        let num_lines = 10;
641        let num_cols = 5;
642        let mut selection = Selection::block(Point::new(0, Column(3)), Side::Right);
643        selection.update(Point::new(5, Column(1)), Side::Right);
644        selection = selection.rotate(num_lines, num_cols, &(Line(0)..Line(num_lines)), 7).unwrap();
645
646        assert_eq!(
647            selection.to_range(&term(num_cols, num_lines)).unwrap(),
648            SelectionRange {
649                start: Point::new(9, Column(2)),
650                end: Point::new(7, Column(3)),
651                is_block: true
652            }
653        );
654    }
655
656    #[test]
657    fn double_width_expansion() {
658        let mut term = term(10, 1);
659        let mut grid = Grid::new(Line(1), Column(10), 0, Cell::default());
660        grid[Line(0)][Column(0)].flags.insert(Flags::WIDE_CHAR);
661        grid[Line(0)][Column(1)].flags.insert(Flags::WIDE_CHAR_SPACER);
662        grid[Line(0)][Column(8)].flags.insert(Flags::WIDE_CHAR);
663        grid[Line(0)][Column(9)].flags.insert(Flags::WIDE_CHAR_SPACER);
664        mem::swap(term.grid_mut(), &mut grid);
665
666        let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left);
667        selection.update(Point::new(0, Column(8)), Side::Right);
668
669        assert_eq!(
670            selection.to_range(&term).unwrap(),
671            SelectionRange {
672                start: Point::new(0, Column(0)),
673                end: Point::new(0, Column(9)),
674                is_block: false,
675            }
676        );
677    }
678
679    #[test]
680    fn simple_is_empty() {
681        let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
682        assert!(selection.is_empty());
683        selection.update(Point::new(0, Column(1)), Side::Left);
684        assert!(selection.is_empty());
685        selection.update(Point::new(1, Column(0)), Side::Right);
686        assert!(!selection.is_empty());
687    }
688
689    #[test]
690    fn block_is_empty() {
691        let mut selection = Selection::block(Point::new(0, Column(0)), Side::Right);
692        assert!(selection.is_empty());
693        selection.update(Point::new(0, Column(1)), Side::Left);
694        assert!(selection.is_empty());
695        selection.update(Point::new(0, Column(1)), Side::Right);
696        assert!(!selection.is_empty());
697        selection.update(Point::new(1, Column(0)), Side::Right);
698        assert!(selection.is_empty());
699        selection.update(Point::new(1, Column(1)), Side::Left);
700        assert!(selection.is_empty());
701        selection.update(Point::new(1, Column(1)), Side::Right);
702        assert!(!selection.is_empty());
703    }
704
705    #[test]
706    fn rotate_in_region_up() {
707        let num_lines = 10;
708        let num_cols = 5;
709        let mut selection = Selection::simple(Point::new(2, Column(3)), Side::Right);
710        selection.update(Point::new(5, Column(1)), Side::Right);
711        selection =
712            selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap();
713
714        assert_eq!(
715            selection.to_range(&term(num_cols, num_lines)).unwrap(),
716            SelectionRange {
717                start: Point::new(8, Column(0)),
718                end: Point::new(6, Column(3)),
719                is_block: false,
720            }
721        );
722    }
723
724    #[test]
725    fn rotate_in_region_down() {
726        let num_lines = 10;
727        let num_cols = 5;
728        let mut selection = Selection::simple(Point::new(5, Column(3)), Side::Right);
729        selection.update(Point::new(8, Column(1)), Side::Left);
730        selection =
731            selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), -5).unwrap();
732
733        assert_eq!(
734            selection.to_range(&term(num_cols, num_lines)).unwrap(),
735            SelectionRange {
736                start: Point::new(3, Column(1)),
737                end: Point::new(1, Column(num_cols - 1)),
738                is_block: false,
739            }
740        );
741    }
742
743    #[test]
744    fn rotate_in_region_up_block() {
745        let num_lines = 10;
746        let num_cols = 5;
747        let mut selection = Selection::block(Point::new(2, Column(3)), Side::Right);
748        selection.update(Point::new(5, Column(1)), Side::Right);
749        selection =
750            selection.rotate(num_lines, num_cols, &(Line(1)..Line(num_lines - 1)), 4).unwrap();
751
752        assert_eq!(
753            selection.to_range(&term(num_cols, num_lines)).unwrap(),
754            SelectionRange {
755                start: Point::new(8, Column(2)),
756                end: Point::new(6, Column(3)),
757                is_block: true,
758            }
759        );
760    }
761}