1use std::cmp::{max, min};
22use std::ops::Range;
23
24use crate::index::{Column, Point, Side};
25use crate::term::Search;
26
27#[derive(Debug, Clone, PartialEq)]
43pub enum Selection {
44 Simple {
45 region: Range<Anchor>,
47 },
48 Semantic {
49 region: Range<Point<isize>>,
51 },
52 Lines {
53 region: Range<Point<isize>>,
55
56 initial_line: isize,
59 },
60}
61
62#[derive(Debug, Clone, PartialEq)]
64pub struct Anchor {
65 point: Point<isize>,
66 side: Side,
67}
68
69impl Anchor {
70 fn new(point: Point<isize>, side: Side) -> Anchor {
71 Anchor { point, side }
72 }
73}
74
75pub trait Dimensions {
77 fn dimensions(&self) -> Point;
79}
80
81impl Selection {
82 pub fn simple(location: Point<usize>, side: Side) -> Selection {
83 Selection::Simple {
84 region: Range {
85 start: Anchor::new(location.into(), side),
86 end: Anchor::new(location.into(), side),
87 },
88 }
89 }
90
91 pub fn rotate(&mut self, offset: isize) {
92 match *self {
93 Selection::Simple { ref mut region } => {
94 region.start.point.line += offset;
95 region.end.point.line += offset;
96 }
97 Selection::Semantic { ref mut region } => {
98 region.start.line += offset;
99 region.end.line += offset;
100 }
101 Selection::Lines {
102 ref mut region,
103 ref mut initial_line,
104 } => {
105 region.start.line += offset;
106 region.end.line += offset;
107 *initial_line += offset;
108 }
109 }
110 }
111
112 pub fn semantic(point: Point<usize>) -> Selection {
113 Selection::Semantic {
114 region: Range {
115 start: point.into(),
116 end: point.into(),
117 },
118 }
119 }
120
121 pub fn lines(point: Point<usize>) -> Selection {
122 Selection::Lines {
123 region: Range {
124 start: point.into(),
125 end: point.into(),
126 },
127 initial_line: point.line as isize,
128 }
129 }
130
131 pub fn update(&mut self, location: Point<usize>, side: Side) {
132 match *self {
134 Selection::Simple { ref mut region } => {
135 region.end = Anchor::new(location.into(), side);
136 }
137 Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => {
138 region.end = location.into();
139 }
140 }
141 }
142
143 pub fn to_span<G>(&self, grid: &G, alt_screen: bool) -> Option<Span>
144 where
145 G: Search + Dimensions,
146 {
147 match *self {
148 Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen),
149 Selection::Semantic { ref region } => {
150 Selection::span_semantic(grid, region, alt_screen)
151 }
152 Selection::Lines {
153 ref region,
154 initial_line,
155 } => Selection::span_lines(grid, region, initial_line, alt_screen),
156 }
157 }
158
159 pub fn is_empty(&self) -> bool {
160 match *self {
161 Selection::Simple { ref region } => {
162 region.start == region.end && region.start.side == region.end.side
163 }
164 Selection::Semantic { .. } | Selection::Lines { .. } => false,
165 }
166 }
167
168 fn span_semantic<G>(grid: &G, region: &Range<Point<isize>>, alt_screen: bool) -> Option<Span>
169 where
170 G: Search + Dimensions,
171 {
172 let cols = grid.dimensions().col;
173 let lines = grid.dimensions().line.0 as isize;
174
175 let (mut front, mut tail) = if region.start < region.end {
177 (region.start, region.end)
178 } else {
179 (region.end, region.start)
180 };
181
182 if alt_screen {
183 Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?;
184 }
185
186 let (mut start, mut end) = if front < tail && front.line == tail.line {
187 (
188 grid.semantic_search_left(front.into()),
189 grid.semantic_search_right(tail.into()),
190 )
191 } else {
192 (
193 grid.semantic_search_right(front.into()),
194 grid.semantic_search_left(tail.into()),
195 )
196 };
197
198 if start > end {
199 ::std::mem::swap(&mut start, &mut end);
200 }
201
202 Some(Span {
203 cols,
204 front: start,
205 tail: end,
206 ty: SpanType::Inclusive,
207 })
208 }
209
210 fn span_lines<G>(
211 grid: &G,
212 region: &Range<Point<isize>>,
213 initial_line: isize,
214 alt_screen: bool,
215 ) -> Option<Span>
216 where
217 G: Dimensions,
218 {
219 let cols = grid.dimensions().col;
220 let lines = grid.dimensions().line.0 as isize;
221
222 let mut start = Point {
225 col: cols - 1,
226 line: initial_line,
227 };
228 let mut end = Point {
229 col: Column(0),
230 line: initial_line,
231 };
232
233 if region.start.line < region.end.line {
235 start.line = min(start.line, region.start.line);
237 end.line = max(end.line, region.end.line);
238 } else {
239 start.line = min(start.line, region.end.line);
241 end.line = max(end.line, region.start.line);
242 }
243
244 if alt_screen {
245 Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
246 }
247
248 Some(Span {
249 cols,
250 front: start.into(),
251 tail: end.into(),
252 ty: SpanType::Inclusive,
253 })
254 }
255
256 fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
257 where
258 G: Dimensions,
259 {
260 let start = region.start.point;
261 let start_side = region.start.side;
262 let end = region.end.point;
263 let end_side = region.end.side;
264 let cols = grid.dimensions().col;
265 let lines = grid.dimensions().line.0 as isize;
266
267 let (mut front, mut tail, front_side, tail_side) =
269 if start.line > end.line || start.line == end.line && start.col <= end.col {
270 (end, start, end_side, start_side)
272 } else {
273 (start, end, start_side, end_side)
275 };
276
277 if (front == tail && front_side == tail_side)
279 || (tail_side == Side::Right
280 && front_side == Side::Left
281 && front.line == tail.line
282 && front.col == tail.col + 1)
283 {
284 return None;
285 }
286
287 if front_side == Side::Left && start != end {
289 if front.col == Column(0) {
291 front.col = cols - 1;
292 front.line += 1;
293 } else {
294 front.col -= 1;
295 }
296 }
297
298 if tail_side == Side::Right && front != tail {
300 tail.col += 1;
301 }
302
303 if alt_screen {
304 Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?;
305 }
306
307 Some(Span {
309 cols,
310 front: front.into(),
311 tail: tail.into(),
312 ty: SpanType::Inclusive,
313 })
314 }
315
316 fn alt_screen_clamp(
318 front: &mut Point<isize>,
319 tail: &mut Point<isize>,
320 lines: isize,
321 cols: Column,
322 ) -> Option<()> {
323 if tail.line >= lines {
324 if front.line >= lines {
326 return None;
327 }
328
329 tail.line = lines - 1;
331 tail.col = Column(0);
332 }
333
334 if front.line < 0 {
335 if tail.line < 0 {
337 return None;
338 }
339
340 front.line = 0;
342 front.col = cols - 1;
343 }
344
345 Some(())
346 }
347}
348
349#[derive(Debug, Eq, PartialEq)]
351pub enum SpanType {
352 Inclusive,
354
355 Exclusive,
357
358 ExcludeTail,
360
361 ExcludeFront,
363}
364
365#[derive(Debug, Eq, PartialEq)]
367pub struct Span {
368 front: Point<usize>,
369 tail: Point<usize>,
370 cols: Column,
371
372 ty: SpanType,
374}
375
376#[derive(Debug)]
377pub struct Locations {
378 pub start: Point<usize>,
380 pub end: Point<usize>,
382}
383
384impl Span {
385 pub fn to_locations(&self) -> Locations {
386 let (start, end) = match self.ty {
387 SpanType::Inclusive => (self.front, self.tail),
388 SpanType::Exclusive => (
389 Span::wrap_start(self.front, self.cols),
390 Span::wrap_end(self.tail, self.cols),
391 ),
392 SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail),
393 SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)),
394 };
395
396 Locations { start, end }
397 }
398
399 fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> {
400 if start.col == cols - 1 {
401 Point {
402 line: start.line + 1,
403 col: Column(0),
404 }
405 } else {
406 start.col += 1;
407 start
408 }
409 }
410
411 fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> {
412 if end.col == Column(0) && end.line != 0 {
413 Point {
414 line: end.line - 1,
415 col: cols,
416 }
417 } else {
418 Point {
419 line: end.line,
420 col: end.col - 1,
421 }
422 }
423 }
424}
425
426#[cfg(test)]
436mod test {
437 use super::{Selection, Span, SpanType};
438 use crate::index::{Column, Line, Point, Side};
439
440 struct Dimensions(Point);
441 impl super::Dimensions for Dimensions {
442 fn dimensions(&self) -> Point {
443 self.0
444 }
445 }
446
447 impl Dimensions {
448 pub fn new(line: usize, col: usize) -> Self {
449 Dimensions(Point {
450 line: Line(line),
451 col: Column(col),
452 })
453 }
454 }
455
456 impl super::Search for Dimensions {
457 fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> {
458 point
459 }
460 fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> {
461 point
462 }
463 fn url_search(&self, _: Point<usize>) -> Option<String> {
464 None
465 }
466 }
467
468 #[test]
474 fn single_cell_left_to_right() {
475 let location = Point {
476 line: 0,
477 col: Column(0),
478 };
479 let mut selection = Selection::simple(location, Side::Left);
480 selection.update(location, Side::Right);
481
482 assert_eq!(
483 selection.to_span(&Dimensions::new(1, 1), false).unwrap(),
484 Span {
485 cols: Column(1),
486 ty: SpanType::Inclusive,
487 front: location,
488 tail: location
489 }
490 );
491 }
492
493 #[test]
499 fn single_cell_right_to_left() {
500 let location = Point {
501 line: 0,
502 col: Column(0),
503 };
504 let mut selection = Selection::simple(location, Side::Right);
505 selection.update(location, Side::Left);
506
507 assert_eq!(
508 selection.to_span(&Dimensions::new(1, 1), false).unwrap(),
509 Span {
510 cols: Column(1),
511 ty: SpanType::Inclusive,
512 front: location,
513 tail: location
514 }
515 );
516 }
517
518 #[test]
524 fn between_adjacent_cells_left_to_right() {
525 let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
526 selection.update(Point::new(0, Column(1)), Side::Left);
527
528 assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None);
529 }
530
531 #[test]
537 fn between_adjacent_cells_right_to_left() {
538 let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left);
539 selection.update(Point::new(0, Column(0)), Side::Right);
540
541 assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None);
542 }
543
544 #[test]
554 fn across_adjacent_lines_upward_final_cell_exclusive() {
555 let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right);
556 selection.update(Point::new(0, Column(1)), Side::Right);
557
558 assert_eq!(
559 selection.to_span(&Dimensions::new(2, 5), false).unwrap(),
560 Span {
561 cols: Column(5),
562 front: Point::new(0, Column(1)),
563 tail: Point::new(1, Column(2)),
564 ty: SpanType::Inclusive,
565 }
566 );
567 }
568
569 #[test]
581 fn selection_bigger_then_smaller() {
582 let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right);
583 selection.update(Point::new(1, Column(1)), Side::Right);
584 selection.update(Point::new(1, Column(0)), Side::Right);
585
586 assert_eq!(
587 selection.to_span(&Dimensions::new(2, 5), false).unwrap(),
588 Span {
589 cols: Column(5),
590 front: Point::new(0, Column(1)),
591 tail: Point::new(1, Column(1)),
592 ty: SpanType::Inclusive,
593 }
594 );
595 }
596
597 #[test]
598 fn alt_scren_lines() {
599 let mut selection = Selection::lines(Point::new(0, Column(0)));
600 selection.update(Point::new(5, Column(3)), Side::Right);
601 selection.rotate(-3);
602
603 assert_eq!(
604 selection.to_span(&Dimensions::new(10, 5), true).unwrap(),
605 Span {
606 cols: Column(5),
607 front: Point::new(0, Column(4)),
608 tail: Point::new(2, Column(0)),
609 ty: SpanType::Inclusive,
610 }
611 );
612 }
613
614 #[test]
615 fn alt_screen_semantic() {
616 let mut selection = Selection::semantic(Point::new(0, Column(0)));
617 selection.update(Point::new(5, Column(3)), Side::Right);
618 selection.rotate(-3);
619
620 assert_eq!(
621 selection.to_span(&Dimensions::new(10, 5), true).unwrap(),
622 Span {
623 cols: Column(5),
624 front: Point::new(0, Column(4)),
625 tail: Point::new(2, Column(3)),
626 ty: SpanType::Inclusive,
627 }
628 );
629 }
630
631 #[test]
632 fn alt_screen_simple() {
633 let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
634 selection.update(Point::new(5, Column(3)), Side::Right);
635 selection.rotate(-3);
636
637 assert_eq!(
638 selection.to_span(&Dimensions::new(10, 5), true).unwrap(),
639 Span {
640 cols: Column(5),
641 front: Point::new(0, Column(4)),
642 tail: Point::new(2, Column(4)),
643 ty: SpanType::Inclusive,
644 }
645 );
646 }
647}