Fix inconsistent selection start when dragging outside of terminal bounds

Max Brunsfeld and Mikayla created

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

crates/terminal/src/mappings/mouse.rs | 50 +++++++++++++++-------------
crates/terminal/src/terminal.rs       | 15 ++-----
2 files changed, 32 insertions(+), 33 deletions(-)

Detailed changes

crates/terminal/src/mappings/mouse.rs 🔗

@@ -158,37 +158,41 @@ pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode)
     }
 }
 
-pub fn mouse_side(
+pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
+    grid_point_and_side(pos, cur_size, display_offset).0
+}
+
+pub fn grid_point_and_side(
     pos: Point<Pixels>,
     cur_size: TerminalSize,
-) -> alacritty_terminal::index::Direction {
-    let cell_width = cur_size.cell_width;
-    if cell_width == px(0.) {
-        return Side::Right;
-    }
-
-    let cell_x = cmp::max(px(0.), pos.x) % cell_width;
+    display_offset: usize,
+) -> (AlacPoint, Side) {
+    let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
+    let cell_x = cmp::max(px(0.), pos.x) % cur_size.cell_width;
     let half_cell_width = cur_size.cell_width / 2.0;
-    let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
-    let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
-
-    //Width: Pixels or columns?
-    if cell_x > half_cell_width
-    // Edge case when mouse leaves the window.
-    || pos.x >= end_of_grid
-    {
+    let mut side = if cell_x > half_cell_width {
         Side::Right
     } else {
         Side::Left
-    }
-}
+    };
 
-pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
-    let col = GridCol((pos.x / cur_size.cell_width) as usize);
+    if col > cur_size.last_column() {
+        col = cur_size.last_column();
+        side = Side::Right;
+    }
     let col = min(col, cur_size.last_column());
-    let line = (pos.y / cur_size.line_height) as i32;
-    let line = min(line, cur_size.bottommost_line().0);
-    AlacPoint::new(GridLine(line - display_offset as i32), col)
+    let mut line = (pos.y / cur_size.line_height) as i32;
+    if line > cur_size.bottommost_line() {
+        line = cur_size.bottommost_line().0 as i32;
+        side = Side::Right;
+    } else if line < 0 {
+        side = Side::Left;
+    }
+
+    (
+        AlacPoint::new(GridLine(line - display_offset as i32), col),
+        side,
+    )
 }
 
 ///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode

crates/terminal/src/terminal.rs 🔗

@@ -28,7 +28,8 @@ use futures::{
 };
 
 use mappings::mouse::{
-    alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
+    alt_scroll, grid_point, grid_point_and_side, mouse_button_report, mouse_moved_report,
+    scroll_report,
 };
 
 use procinfo::LocalProcessInfo;
@@ -704,14 +705,12 @@ impl Terminal {
             }
             InternalEvent::UpdateSelection(position) => {
                 if let Some(mut selection) = term.selection.take() {
-                    let point = grid_point(
+                    let (point, side) = grid_point_and_side(
                         *position,
                         self.last_content.size,
                         term.grid().display_offset(),
                     );
 
-                    let side = mouse_side(*position, self.last_content.size);
-
                     selection.update(point, side);
                     term.selection = Some(selection);
 
@@ -1088,12 +1087,11 @@ impl Terminal {
         let position = e.position - origin;
         self.last_mouse_position = Some(position);
         if self.mouse_mode(e.modifiers.shift) {
-            let point = grid_point(
+            let (point, side) = grid_point_and_side(
                 position,
                 self.last_content.size,
                 self.last_content.display_offset,
             );
-            let side = mouse_side(position, self.last_content.size);
 
             if self.mouse_changed(point, side) {
                 if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
@@ -1175,15 +1173,12 @@ impl Terminal {
             }
         } else if e.button == MouseButton::Left {
             let position = e.position - origin;
-            let point = grid_point(
+            let (point, side) = grid_point_and_side(
                 position,
                 self.last_content.size,
                 self.last_content.display_offset,
             );
 
-            // Use .opposite so that selection is inclusive of the cell clicked.
-            let side = mouse_side(position, self.last_content.size);
-
             let selection_type = match e.click_count {
                 0 => return, //This is a release
                 1 => Some(SelectionType::Simple),