1 //! State management for a selection in the grid. 2 //! 3 //! A selection should start when the mouse is clicked, and it should be 4 //! finalized when the button is released. The selection should be cleared 5 //! when text is added/removed/scrolled on the screen. The selection should 6 //! also be cleared if the user clicks off of the selection. 7 8 use std::cmp::min; 9 use std::mem; 10 use std::ops::{Bound, Range, RangeBounds}; 11 12 use crate::ansi::CursorShape; 13 use crate::grid::{Dimensions, GridCell, Indexed}; 14 use crate::index::{Boundary, Column, Line, Point, Side}; 15 use crate::term::cell::{Cell, Flags}; 16 use crate::term::Term; 17 18 /// A Point and side within that point. 19 #[derive(Debug, Copy, Clone, PartialEq)] 20 struct Anchor { 21 point: Point, 22 side: Side, 23 } 24 25 impl Anchor { new(point: Point, side: Side) -> Anchor26 fn new(point: Point, side: Side) -> Anchor { 27 Anchor { point, side } 28 } 29 } 30 31 /// Represents a range of selected cells. 32 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 33 pub struct SelectionRange { 34 /// Start point, top left of the selection. 35 pub start: Point, 36 /// End point, bottom right of the selection. 37 pub end: Point, 38 /// Whether this selection is a block selection. 39 pub is_block: bool, 40 } 41 42 impl SelectionRange { new(start: Point, end: Point, is_block: bool) -> Self43 pub fn new(start: Point, end: Point, is_block: bool) -> Self { 44 Self { start, end, is_block } 45 } 46 } 47 48 impl SelectionRange { 49 /// Check if a point lies within the selection. contains(&self, point: Point) -> bool50 pub fn contains(&self, point: Point) -> bool { 51 self.start.line <= point.line 52 && self.end.line >= point.line 53 && (self.start.column <= point.column 54 || (self.start.line != point.line && !self.is_block)) 55 && (self.end.column >= point.column || (self.end.line != point.line && !self.is_block)) 56 } 57 58 /// Check if the cell at a point is part of the selection. contains_cell( &self, indexed: &Indexed<&Cell>, point: Point, shape: CursorShape, ) -> bool59 pub fn contains_cell( 60 &self, 61 indexed: &Indexed<&Cell>, 62 point: Point, 63 shape: CursorShape, 64 ) -> bool { 65 // Do not invert block cursor at selection boundaries. 66 if shape == CursorShape::Block 67 && point == indexed.point 68 && (self.start == indexed.point 69 || self.end == indexed.point 70 || (self.is_block 71 && ((self.start.line == indexed.point.line 72 && self.end.column == indexed.point.column) 73 || (self.end.line == indexed.point.line 74 && self.start.column == indexed.point.column)))) 75 { 76 return false; 77 } 78 79 // Point itself is selected. 80 if self.contains(indexed.point) { 81 return true; 82 } 83 84 // Check if a wide char's trailing spacer is selected. 85 indexed.cell.flags().contains(Flags::WIDE_CHAR) 86 && self.contains(Point::new(indexed.point.line, indexed.point.column + 1)) 87 } 88 } 89 90 /// Different kinds of selection. 91 #[derive(Debug, Copy, Clone, PartialEq)] 92 pub enum SelectionType { 93 Simple, 94 Block, 95 Semantic, 96 Lines, 97 } 98 99 /// Describes a region of a 2-dimensional area. 100 /// 101 /// Used to track a text selection. There are four supported modes, each with its own constructor: 102 /// [`simple`], [`block`], [`semantic`], and [`lines`]. The [`simple`] mode precisely tracks which 103 /// cells are selected without any expansion. [`block`] will select rectangular regions. 104 /// [`semantic`] mode expands the initial selection to the nearest semantic escape char in either 105 /// direction. [`lines`] will always select entire lines. 106 /// 107 /// Calls to [`update`] operate different based on the selection kind. The [`simple`] and [`block`] 108 /// mode do nothing special, simply track points and sides. [`semantic`] will continue to expand 109 /// out to semantic boundaries as the selection point changes. Similarly, [`lines`] will always 110 /// expand the new point to encompass entire lines. 111 /// 112 /// [`simple`]: enum.Selection.html#method.simple 113 /// [`block`]: enum.Selection.html#method.block 114 /// [`semantic`]: enum.Selection.html#method.semantic 115 /// [`lines`]: enum.Selection.html#method.lines 116 /// [`update`]: enum.Selection.html#method.update 117 #[derive(Debug, Clone, PartialEq)] 118 pub struct Selection { 119 pub ty: SelectionType, 120 region: Range<Anchor>, 121 } 122 123 impl Selection { new(ty: SelectionType, location: Point, side: Side) -> Selection124 pub fn new(ty: SelectionType, location: Point, side: Side) -> Selection { 125 Self { 126 region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) }, 127 ty, 128 } 129 } 130 131 /// Update the end of the selection. update(&mut self, point: Point, side: Side)132 pub fn update(&mut self, point: Point, side: Side) { 133 self.region.end = Anchor::new(point, side); 134 } 135 rotate<D: Dimensions>( mut self, dimensions: &D, range: &Range<Line>, delta: i32, ) -> Option<Selection>136 pub fn rotate<D: Dimensions>( 137 mut self, 138 dimensions: &D, 139 range: &Range<Line>, 140 delta: i32, 141 ) -> Option<Selection> { 142 let bottommost_line = dimensions.bottommost_line(); 143 let range_bottom = range.end; 144 let range_top = range.start; 145 146 let (mut start, mut end) = (&mut self.region.start, &mut self.region.end); 147 if start.point > end.point { 148 mem::swap(&mut start, &mut end); 149 } 150 151 // Rotate start of selection. 152 if (start.point.line >= range_top || range_top == 0) && start.point.line < range_bottom { 153 start.point.line = min(start.point.line - delta, bottommost_line); 154 155 // If end is within the same region, delete selection once start rotates out. 156 if start.point.line >= range_bottom && end.point.line < range_bottom { 157 return None; 158 } 159 160 // Clamp selection to start of region. 161 if start.point.line < range_top && range_top != 0 { 162 if self.ty != SelectionType::Block { 163 start.point.column = Column(0); 164 start.side = Side::Left; 165 } 166 start.point.line = range_top; 167 } 168 } 169 170 // Rotate end of selection. 171 if (end.point.line >= range_top || range_top == 0) && end.point.line < range_bottom { 172 end.point.line = min(end.point.line - delta, bottommost_line); 173 174 // Delete selection if end has overtaken the start. 175 if end.point.line < start.point.line { 176 return None; 177 } 178 179 // Clamp selection to end of region. 180 if end.point.line >= range_bottom { 181 if self.ty != SelectionType::Block { 182 end.point.column = dimensions.last_column(); 183 end.side = Side::Right; 184 } 185 end.point.line = range_bottom - 1; 186 } 187 } 188 189 Some(self) 190 } 191 is_empty(&self) -> bool192 pub fn is_empty(&self) -> bool { 193 match self.ty { 194 SelectionType::Simple => { 195 let (mut start, mut end) = (self.region.start, self.region.end); 196 if start.point > end.point { 197 mem::swap(&mut start, &mut end); 198 } 199 200 // Simple selection is empty when the points are identical 201 // or two adjacent cells have the sides right -> left. 202 start == end 203 || (start.side == Side::Right 204 && end.side == Side::Left 205 && (start.point.line == end.point.line) 206 && start.point.column + 1 == end.point.column) 207 }, 208 SelectionType::Block => { 209 let (start, end) = (self.region.start, self.region.end); 210 211 // Block selection is empty when the points' columns and sides are identical 212 // or two cells with adjacent columns have the sides right -> left, 213 // regardless of their lines 214 (start.point.column == end.point.column && start.side == end.side) 215 || (start.point.column + 1 == end.point.column 216 && start.side == Side::Right 217 && end.side == Side::Left) 218 || (end.point.column + 1 == start.point.column 219 && start.side == Side::Left 220 && end.side == Side::Right) 221 }, 222 SelectionType::Semantic | SelectionType::Lines => false, 223 } 224 } 225 226 /// Check whether selection contains any point in a given range. intersects_range<R: RangeBounds<Line>>(&self, range: R) -> bool227 pub fn intersects_range<R: RangeBounds<Line>>(&self, range: R) -> bool { 228 let mut start = self.region.start.point.line; 229 let mut end = self.region.end.point.line; 230 231 if start > end { 232 mem::swap(&mut start, &mut end); 233 } 234 235 let range_top = match range.start_bound() { 236 Bound::Included(&range_start) => range_start, 237 Bound::Excluded(&range_start) => range_start + 1, 238 Bound::Unbounded => Line(i32::min_value()), 239 }; 240 241 let range_bottom = match range.end_bound() { 242 Bound::Included(&range_end) => range_end, 243 Bound::Excluded(&range_end) => range_end - 1, 244 Bound::Unbounded => Line(i32::max_value()), 245 }; 246 247 range_bottom >= start && range_top <= end 248 } 249 250 /// Expand selection sides to include all cells. include_all(&mut self)251 pub fn include_all(&mut self) { 252 let (start, end) = (self.region.start.point, self.region.end.point); 253 let (start_side, end_side) = match self.ty { 254 SelectionType::Block 255 if start.column > end.column 256 || (start.column == end.column && start.line > end.line) => 257 { 258 (Side::Right, Side::Left) 259 }, 260 SelectionType::Block => (Side::Left, Side::Right), 261 _ if start > end => (Side::Right, Side::Left), 262 _ => (Side::Left, Side::Right), 263 }; 264 265 self.region.start.side = start_side; 266 self.region.end.side = end_side; 267 } 268 269 /// Convert selection to grid coordinates. to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange>270 pub fn to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange> { 271 let grid = term.grid(); 272 let columns = grid.columns(); 273 274 // Order start above the end. 275 let mut start = self.region.start; 276 let mut end = self.region.end; 277 278 if start.point > end.point { 279 mem::swap(&mut start, &mut end); 280 } 281 282 // Clamp selection to within grid boundaries. 283 if end.point.line < term.topmost_line() { 284 return None; 285 } 286 start.point = start.point.grid_clamp(term, Boundary::Grid); 287 288 match self.ty { 289 SelectionType::Simple => self.range_simple(start, end, columns), 290 SelectionType::Block => self.range_block(start, end), 291 SelectionType::Semantic => Some(Self::range_semantic(term, start.point, end.point)), 292 SelectionType::Lines => Some(Self::range_lines(term, start.point, end.point)), 293 } 294 } 295 range_semantic<T>(term: &Term<T>, mut start: Point, mut end: Point) -> SelectionRange296 fn range_semantic<T>(term: &Term<T>, mut start: Point, mut end: Point) -> SelectionRange { 297 if start == end { 298 if let Some(matching) = term.bracket_search(start) { 299 if (matching.line == start.line && matching.column < start.column) 300 || (matching.line > start.line) 301 { 302 start = matching; 303 } else { 304 end = matching; 305 } 306 307 return SelectionRange { start, end, is_block: false }; 308 } 309 } 310 311 let start = term.semantic_search_left(start); 312 let end = term.semantic_search_right(end); 313 314 SelectionRange { start, end, is_block: false } 315 } 316 range_lines<T>(term: &Term<T>, start: Point, end: Point) -> SelectionRange317 fn range_lines<T>(term: &Term<T>, start: Point, end: Point) -> SelectionRange { 318 let start = term.line_search_left(start); 319 let end = term.line_search_right(end); 320 321 SelectionRange { start, end, is_block: false } 322 } 323 range_simple( &self, mut start: Anchor, mut end: Anchor, columns: usize, ) -> Option<SelectionRange>324 fn range_simple( 325 &self, 326 mut start: Anchor, 327 mut end: Anchor, 328 columns: usize, 329 ) -> Option<SelectionRange> { 330 if self.is_empty() { 331 return None; 332 } 333 334 // Remove last cell if selection ends to the left of a cell. 335 if end.side == Side::Left && start.point != end.point { 336 // Special case when selection ends to left of first cell. 337 if end.point.column == 0 { 338 end.point.column = Column(columns - 1); 339 end.point.line -= 1; 340 } else { 341 end.point.column -= 1; 342 } 343 } 344 345 // Remove first cell if selection starts at the right of a cell. 346 if start.side == Side::Right && start.point != end.point { 347 start.point.column += 1; 348 349 // Wrap to next line when selection starts to the right of last column. 350 if start.point.column == columns { 351 start.point.column = Column(0); 352 start.point.line += 1; 353 } 354 } 355 356 Some(SelectionRange { start: start.point, end: end.point, is_block: false }) 357 } 358 range_block(&self, mut start: Anchor, mut end: Anchor) -> Option<SelectionRange>359 fn range_block(&self, mut start: Anchor, mut end: Anchor) -> Option<SelectionRange> { 360 if self.is_empty() { 361 return None; 362 } 363 364 // Always go top-left -> bottom-right. 365 if start.point.column > end.point.column { 366 mem::swap(&mut start.side, &mut end.side); 367 mem::swap(&mut start.point.column, &mut end.point.column); 368 } 369 370 // Remove last cell if selection ends to the left of a cell. 371 if end.side == Side::Left && start.point != end.point && end.point.column.0 > 0 { 372 end.point.column -= 1; 373 } 374 375 // Remove first cell if selection starts at the right of a cell. 376 if start.side == Side::Right && start.point != end.point { 377 start.point.column += 1; 378 } 379 380 Some(SelectionRange { start: start.point, end: end.point, is_block: true }) 381 } 382 } 383 384 /// Tests for selection. 385 /// 386 /// There are comments on all of the tests describing the selection. Pictograms 387 /// are used to avoid ambiguity. Grid cells are represented by a [ ]. Only 388 /// cells that are completely covered are counted in a selection. Ends are 389 /// represented by `B` and `E` for begin and end, respectively. A selected cell 390 /// looks like [XX], [BX] (at the start), [XB] (at the end), [XE] (at the end), 391 /// and [EX] (at the start), or [BE] for a single cell. Partially selected cells 392 /// look like [ B] and [E ]. 393 #[cfg(test)] 394 mod tests { 395 use super::*; 396 397 use crate::config::MockConfig; 398 use crate::index::{Column, Point, Side}; 399 use crate::term::{SizeInfo, Term}; 400 term(height: usize, width: usize) -> Term<()>401 fn term(height: usize, width: usize) -> Term<()> { 402 let size = SizeInfo::new(width as f32, height as f32, 1.0, 1.0, 0.0, 0.0, false); 403 Term::new(&MockConfig::default(), size, ()) 404 } 405 406 /// Test case of single cell selection. 407 /// 408 /// 1. [ ] 409 /// 2. [B ] 410 /// 3. [BE] 411 #[test] single_cell_left_to_right()412 fn single_cell_left_to_right() { 413 let location = Point::new(Line(0), Column(0)); 414 let mut selection = Selection::new(SelectionType::Simple, location, Side::Left); 415 selection.update(location, Side::Right); 416 417 assert_eq!(selection.to_range(&term(1, 2)).unwrap(), SelectionRange { 418 start: location, 419 end: location, 420 is_block: false 421 }); 422 } 423 424 /// Test case of single cell selection. 425 /// 426 /// 1. [ ] 427 /// 2. [ B] 428 /// 3. [EB] 429 #[test] single_cell_right_to_left()430 fn single_cell_right_to_left() { 431 let location = Point::new(Line(0), Column(0)); 432 let mut selection = Selection::new(SelectionType::Simple, location, Side::Right); 433 selection.update(location, Side::Left); 434 435 assert_eq!(selection.to_range(&term(1, 2)).unwrap(), SelectionRange { 436 start: location, 437 end: location, 438 is_block: false 439 }); 440 } 441 442 /// Test adjacent cell selection from left to right. 443 /// 444 /// 1. [ ][ ] 445 /// 2. [ B][ ] 446 /// 3. [ B][E ] 447 #[test] between_adjacent_cells_left_to_right()448 fn between_adjacent_cells_left_to_right() { 449 let mut selection = 450 Selection::new(SelectionType::Simple, Point::new(Line(0), Column(0)), Side::Right); 451 selection.update(Point::new(Line(0), Column(1)), Side::Left); 452 453 assert_eq!(selection.to_range(&term(1, 2)), None); 454 } 455 456 /// Test adjacent cell selection from right to left. 457 /// 458 /// 1. [ ][ ] 459 /// 2. [ ][B ] 460 /// 3. [ E][B ] 461 #[test] between_adjacent_cells_right_to_left()462 fn between_adjacent_cells_right_to_left() { 463 let mut selection = 464 Selection::new(SelectionType::Simple, Point::new(Line(0), Column(1)), Side::Left); 465 selection.update(Point::new(Line(0), Column(0)), Side::Right); 466 467 assert_eq!(selection.to_range(&term(1, 2)), None); 468 } 469 470 /// Test selection across adjacent lines. 471 /// 472 /// 1. [ ][ ][ ][ ][ ] 473 /// [ ][ ][ ][ ][ ] 474 /// 2. [ ][ B][ ][ ][ ] 475 /// [ ][ ][ ][ ][ ] 476 /// 3. [ ][ B][XX][XX][XX] 477 /// [XX][XE][ ][ ][ ] 478 #[test] across_adjacent_lines_upward_final_cell_exclusive()479 fn across_adjacent_lines_upward_final_cell_exclusive() { 480 let mut selection = 481 Selection::new(SelectionType::Simple, Point::new(Line(0), Column(1)), Side::Right); 482 selection.update(Point::new(Line(1), Column(1)), Side::Right); 483 484 assert_eq!(selection.to_range(&term(2, 5)).unwrap(), SelectionRange { 485 start: Point::new(Line(0), Column(2)), 486 end: Point::new(Line(1), Column(1)), 487 is_block: false, 488 }); 489 } 490 491 /// Test selection across adjacent lines. 492 /// 493 /// 1. [ ][ ][ ][ ][ ] 494 /// [ ][ ][ ][ ][ ] 495 /// 2. [ ][ ][ ][ ][ ] 496 /// [ ][ B][ ][ ][ ] 497 /// 3. [ ][ E][XX][XX][XX] 498 /// [XX][XB][ ][ ][ ] 499 /// 4. [ E][XX][XX][XX][XX] 500 /// [XX][XB][ ][ ][ ] 501 #[test] selection_bigger_then_smaller()502 fn selection_bigger_then_smaller() { 503 let mut selection = 504 Selection::new(SelectionType::Simple, Point::new(Line(1), Column(1)), Side::Right); 505 selection.update(Point::new(Line(0), Column(1)), Side::Right); 506 selection.update(Point::new(Line(0), Column(0)), Side::Right); 507 508 assert_eq!(selection.to_range(&term(2, 5)).unwrap(), SelectionRange { 509 start: Point::new(Line(0), Column(1)), 510 end: Point::new(Line(1), Column(1)), 511 is_block: false, 512 }); 513 } 514 515 #[test] line_selection()516 fn line_selection() { 517 let size = (10, 5); 518 let mut selection = 519 Selection::new(SelectionType::Lines, Point::new(Line(9), Column(1)), Side::Left); 520 selection.update(Point::new(Line(4), Column(1)), Side::Right); 521 selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 4).unwrap(); 522 523 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 524 start: Point::new(Line(0), Column(0)), 525 end: Point::new(Line(5), Column(4)), 526 is_block: false, 527 }); 528 } 529 530 #[test] semantic_selection()531 fn semantic_selection() { 532 let size = (10, 5); 533 let mut selection = 534 Selection::new(SelectionType::Semantic, Point::new(Line(9), Column(3)), Side::Left); 535 selection.update(Point::new(Line(4), Column(1)), Side::Right); 536 selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 4).unwrap(); 537 538 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 539 start: Point::new(Line(0), Column(1)), 540 end: Point::new(Line(5), Column(3)), 541 is_block: false, 542 }); 543 } 544 545 #[test] simple_selection()546 fn simple_selection() { 547 let size = (10, 5); 548 let mut selection = 549 Selection::new(SelectionType::Simple, Point::new(Line(9), Column(3)), Side::Right); 550 selection.update(Point::new(Line(4), Column(1)), Side::Right); 551 selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 4).unwrap(); 552 553 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 554 start: Point::new(Line(0), Column(2)), 555 end: Point::new(Line(5), Column(3)), 556 is_block: false, 557 }); 558 } 559 560 #[test] block_selection()561 fn block_selection() { 562 let size = (10, 5); 563 let mut selection = 564 Selection::new(SelectionType::Block, Point::new(Line(9), Column(3)), Side::Right); 565 selection.update(Point::new(Line(4), Column(1)), Side::Right); 566 selection = selection.rotate(&size, &(Line(0)..Line(size.0 as i32)), 4).unwrap(); 567 568 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 569 start: Point::new(Line(0), Column(2)), 570 end: Point::new(Line(5), Column(3)), 571 is_block: true 572 }); 573 } 574 575 #[test] simple_is_empty()576 fn simple_is_empty() { 577 let mut selection = 578 Selection::new(SelectionType::Simple, Point::new(Line(1), Column(0)), Side::Right); 579 assert!(selection.is_empty()); 580 selection.update(Point::new(Line(1), Column(1)), Side::Left); 581 assert!(selection.is_empty()); 582 selection.update(Point::new(Line(0), Column(0)), Side::Right); 583 assert!(!selection.is_empty()); 584 } 585 586 #[test] block_is_empty()587 fn block_is_empty() { 588 let mut selection = 589 Selection::new(SelectionType::Block, Point::new(Line(1), Column(0)), Side::Right); 590 assert!(selection.is_empty()); 591 selection.update(Point::new(Line(1), Column(1)), Side::Left); 592 assert!(selection.is_empty()); 593 selection.update(Point::new(Line(1), Column(1)), Side::Right); 594 assert!(!selection.is_empty()); 595 selection.update(Point::new(Line(0), Column(0)), Side::Right); 596 assert!(selection.is_empty()); 597 selection.update(Point::new(Line(0), Column(1)), Side::Left); 598 assert!(selection.is_empty()); 599 selection.update(Point::new(Line(0), Column(1)), Side::Right); 600 assert!(!selection.is_empty()); 601 } 602 603 #[test] rotate_in_region_up()604 fn rotate_in_region_up() { 605 let size = (10, 5); 606 let mut selection = 607 Selection::new(SelectionType::Simple, Point::new(Line(7), Column(3)), Side::Right); 608 selection.update(Point::new(Line(4), Column(1)), Side::Right); 609 selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), 4).unwrap(); 610 611 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 612 start: Point::new(Line(1), Column(0)), 613 end: Point::new(Line(3), Column(3)), 614 is_block: false, 615 }); 616 } 617 618 #[test] rotate_in_region_down()619 fn rotate_in_region_down() { 620 let size = (10, 5); 621 let mut selection = 622 Selection::new(SelectionType::Simple, Point::new(Line(4), Column(3)), Side::Right); 623 selection.update(Point::new(Line(1), Column(1)), Side::Left); 624 selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), -5).unwrap(); 625 626 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 627 start: Point::new(Line(6), Column(1)), 628 end: Point::new(Line(8), size.last_column()), 629 is_block: false, 630 }); 631 } 632 633 #[test] rotate_in_region_up_block()634 fn rotate_in_region_up_block() { 635 let size = (10, 5); 636 let mut selection = 637 Selection::new(SelectionType::Block, Point::new(Line(7), Column(3)), Side::Right); 638 selection.update(Point::new(Line(4), Column(1)), Side::Right); 639 selection = selection.rotate(&size, &(Line(1)..Line(size.0 as i32 - 1)), 4).unwrap(); 640 641 assert_eq!(selection.to_range(&term(size.0, size.1)).unwrap(), SelectionRange { 642 start: Point::new(Line(1), Column(2)), 643 end: Point::new(Line(3), Column(3)), 644 is_block: true, 645 }); 646 } 647 648 #[test] range_intersection()649 fn range_intersection() { 650 let mut selection = 651 Selection::new(SelectionType::Lines, Point::new(Line(3), Column(1)), Side::Left); 652 selection.update(Point::new(Line(6), Column(1)), Side::Right); 653 654 assert!(selection.intersects_range(..)); 655 assert!(selection.intersects_range(Line(2)..)); 656 assert!(selection.intersects_range(Line(2)..=Line(4))); 657 assert!(selection.intersects_range(Line(2)..=Line(7))); 658 assert!(selection.intersects_range(Line(4)..=Line(5))); 659 assert!(selection.intersects_range(Line(5)..Line(8))); 660 661 assert!(!selection.intersects_range(..=Line(2))); 662 assert!(!selection.intersects_range(Line(7)..=Line(8))); 663 } 664 } 665