diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index cd1bc3a607..8a2c958050 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -200,53 +200,27 @@ impl DisplaySnapshot { self.buffer_snapshot.max_buffer_row() } - pub fn prev_row_boundary(&self, mut display_point: DisplayPoint) -> (DisplayPoint, Point) { - loop { - *display_point.column_mut() = 0; - let mut point = display_point.to_point(self); - point.column = 0; - let next_display_point = self.point_to_display_point(point, Bias::Left); - if next_display_point == display_point { - return (display_point, point); - } - display_point = next_display_point; - } - } - - pub fn next_row_boundary(&self, mut display_point: DisplayPoint) -> (DisplayPoint, Point) { - loop { - *display_point.column_mut() = self.line_len(display_point.row()); - let mut point = display_point.to_point(self); - point.column = self.buffer_snapshot.line_len(point.row); - let next_display_point = self.point_to_display_point(point, Bias::Right); - if next_display_point == display_point { - return (display_point, point); - } - display_point = next_display_point; - } - } - - pub fn prev_line_boundary(&self, mut point: Point) -> Point { + pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) { loop { point.column = 0; let mut display_point = self.point_to_display_point(point, Bias::Left); *display_point.column_mut() = 0; let next_point = self.display_point_to_point(display_point, Bias::Left); if next_point == point { - return point; + return (point, display_point); } point = next_point; } } - pub fn next_line_boundary(&self, mut point: Point) -> Point { + pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) { loop { point.column = self.buffer_snapshot.line_len(point.row); let mut display_point = self.point_to_display_point(point, Bias::Right); *display_point.column_mut() = self.line_len(display_point.row()); let next_point = self.display_point_to_point(display_point, Bias::Right); if next_point == point { - return point; + return (point, display_point); } point = next_point; } @@ -631,23 +605,21 @@ mod tests { log::info!("display text: {:?}", snapshot.text()); // Line boundaries + let buffer = &snapshot.buffer_snapshot; for _ in 0..5 { - let row = rng.gen_range(0..=snapshot.max_point().row()); - let column = rng.gen_range(0..=snapshot.line_len(row)); - let point = snapshot.clip_point(DisplayPoint::new(row, column), Left); + let row = rng.gen_range(0..=buffer.max_point().row); + let column = rng.gen_range(0..=buffer.line_len(row)); + let point = buffer.clip_point(Point::new(row, column), Left); - let (prev_display_bound, prev_buffer_bound) = snapshot.prev_row_boundary(point); - let (next_display_bound, next_buffer_bound) = snapshot.next_row_boundary(point); + let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point); + let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point); - assert!(prev_display_bound <= point); - assert!(next_display_bound >= point); + assert!(prev_buffer_bound <= point); + assert!(next_buffer_bound >= point); assert_eq!(prev_buffer_bound.column, 0); assert_eq!(prev_display_bound.column(), 0); - if next_buffer_bound < snapshot.buffer_snapshot.max_point() { - assert_eq!( - snapshot.buffer_snapshot.chars_at(next_buffer_bound).next(), - Some('\n') - ); + if next_buffer_bound < buffer.max_point() { + assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n')); } assert_eq!( diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9fb7995f7b..028706b193 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1702,7 +1702,7 @@ impl Editor { contiguous_row_selections.push(selection.clone()); let start_row = selection.start.row; let mut end_row = if selection.end.column > 0 || selection.is_empty() { - display_map.next_line_boundary(selection.end).row + 1 + display_map.next_line_boundary(selection.end).0.row + 1 } else { selection.end.row }; @@ -1710,7 +1710,7 @@ impl Editor { while let Some(next_selection) = selections.peek() { if next_selection.start.row <= end_row { end_row = if next_selection.end.column > 0 || next_selection.is_empty() { - display_map.next_line_boundary(next_selection.end).row + 1 + display_map.next_line_boundary(next_selection.end).0.row + 1 } else { next_selection.end.row }; @@ -1724,7 +1724,9 @@ impl Editor { if start_row > 0 { let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1)) ..Point::new(end_row - 1, buffer.line_len(end_row - 1)); - let insertion_point = display_map.prev_line_boundary(Point::new(start_row - 1, 0)); + let insertion_point = display_map + .prev_line_boundary(Point::new(start_row - 1, 0)) + .0; // Don't move lines across excerpts if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) { @@ -1798,7 +1800,7 @@ impl Editor { contiguous_row_selections.push(selection.clone()); let start_row = selection.start.row; let mut end_row = if selection.end.column > 0 || selection.is_empty() { - display_map.next_line_boundary(selection.end).row + 1 + display_map.next_line_boundary(selection.end).0.row + 1 } else { selection.end.row }; @@ -1806,7 +1808,7 @@ impl Editor { while let Some(next_selection) = selections.peek() { if next_selection.start.row <= end_row { end_row = if next_selection.end.column > 0 || next_selection.is_empty() { - display_map.next_line_boundary(next_selection.end).row + 1 + display_map.next_line_boundary(next_selection.end).0.row + 1 } else { next_selection.end.row }; @@ -1819,7 +1821,7 @@ impl Editor { // Move the text spanned by the row range to be after the last line of the row range if end_row <= buffer.max_point().row { let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0); - let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)); + let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0; // Don't move lines across excerpt boundaries if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) { @@ -3017,82 +3019,51 @@ impl Editor { } } - pub fn visible_selections<'a>( - &'a self, - display_rows: Range, - cx: &'a mut MutableAppContext, - ) -> HashMap>> { - let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + pub fn local_selections_in_range( + &self, + range: Range, + display_map: &DisplaySnapshot, + ) -> Vec> { let buffer = &display_map.buffer_snapshot; - let start = if display_rows.start == 0 { - Anchor::min() - } else { - buffer.anchor_before( - DisplayPoint::new(display_rows.start, 0).to_offset(&display_map, Bias::Left), - ) - }; - let end = if display_rows.end > display_map.max_point().row() { - Anchor::max() - } else { - buffer.anchor_before( - DisplayPoint::new(display_rows.end, 0).to_offset(&display_map, Bias::Right), - ) - }; - let start_ix = match self .selections - .binary_search_by(|probe| probe.end.cmp(&start, &buffer).unwrap()) + .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer).unwrap()) { Ok(ix) | Err(ix) => ix, }; let end_ix = match self .selections - .binary_search_by(|probe| probe.start.cmp(&end, &buffer).unwrap()) + .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer).unwrap()) { Ok(ix) => ix + 1, Err(ix) => ix, }; - fn display_selection( + fn point_selection( selection: &Selection, - display_map: &DisplaySnapshot, - ) -> Selection { + buffer: &MultiBufferSnapshot, + ) -> Selection { + let start = selection.start.to_point(&buffer); + let end = selection.end.to_point(&buffer); Selection { id: selection.id, - start: selection.start.to_display_point(&display_map), - end: selection.end.to_display_point(&display_map), + start, + end, reversed: selection.reversed, goal: selection.goal, } } - let mut result = HashMap::default(); - - result.insert( - self.replica_id(cx), - self.selections[start_ix..end_ix] - .iter() - .chain( - self.pending_selection - .as_ref() - .map(|pending| &pending.selection), - ) - .map(|s| display_selection(s, &display_map)) - .collect(), - ); - - for (replica_id, selection) in display_map - .buffer_snapshot - .remote_selections_in_range(&(start..end)) - { - result - .entry(replica_id) - .or_insert(Vec::new()) - .push(display_selection(&selection, &display_map)); - } - - result + self.selections[start_ix..end_ix] + .iter() + .chain( + self.pending_selection + .as_ref() + .map(|pending| &pending.selection), + ) + .map(|s| point_selection(s, &buffer)) + .collect() } pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec> @@ -3792,8 +3763,8 @@ impl SelectionExt for Selection { end.row -= 1; } - let buffer_start = map.prev_line_boundary(start); - let buffer_end = map.next_line_boundary(end); + let buffer_start = map.prev_line_boundary(start).0; + let buffer_end = map.next_line_boundary(end).0; buffer_start.row..buffer_end.row + 1 } } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index cfe4a99dc4..9f80eed2fb 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1,7 +1,7 @@ use super::{ display_map::{BlockContext, ToDisplayPoint}, - DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, Scroll, - Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN, + Anchor, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, + Scroll, Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN, }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; @@ -19,7 +19,7 @@ use gpui::{ MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, }; use json::json; -use language::Chunk; +use language::{Bias, Chunk}; use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, @@ -731,28 +731,70 @@ impl Element for EditorElement { let scroll_top = scroll_position.y() * line_height; let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen + let start_anchor = if start_row == 0 { + Anchor::min() + } else { + snapshot + .buffer_snapshot + .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left)) + }; + let end_anchor = if end_row > snapshot.max_point().row() { + Anchor::max() + } else { + snapshot + .buffer_snapshot + .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right)) + }; + + let mut selections = HashMap::default(); let mut active_rows = BTreeMap::new(); let mut highlighted_row = None; - let selections = self.update_view(cx.app, |view, cx| { + self.update_view(cx.app, |view, cx| { highlighted_row = view.highlighted_row(); - let selections = view.visible_selections(start_row..end_row, cx); - for (replica_id, selections) in &selections { - if *replica_id == view.replica_id(cx) { - for selection in selections { - let is_empty = selection.start == selection.end; - let selection_start = snapshot.prev_row_boundary(selection.start).0; - let selection_end = snapshot.next_row_boundary(selection.end).0; - for row in cmp::max(selection_start.row(), start_row) - ..=cmp::min(selection_end.row(), end_row) - { - let contains_non_empty_selection = - active_rows.entry(row).or_insert(!is_empty); - *contains_non_empty_selection |= !is_empty; - } - } + let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); + + let local_selections = view + .local_selections_in_range(start_anchor.clone()..end_anchor.clone(), &display_map); + for selection in &local_selections { + let is_empty = selection.start == selection.end; + let selection_start = snapshot.prev_line_boundary(selection.start).1; + let selection_end = snapshot.next_line_boundary(selection.end).1; + for row in cmp::max(selection_start.row(), start_row) + ..=cmp::min(selection_end.row(), end_row) + { + let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty); + *contains_non_empty_selection |= !is_empty; } } - selections + selections.insert( + view.replica_id(cx), + local_selections + .into_iter() + .map(|selection| crate::Selection { + id: selection.id, + goal: selection.goal, + reversed: selection.reversed, + start: selection.start.to_display_point(&display_map), + end: selection.end.to_display_point(&display_map), + }) + .collect(), + ); + + for (replica_id, selection) in display_map + .buffer_snapshot + .remote_selections_in_range(&(start_anchor..end_anchor)) + { + selections + .entry(replica_id) + .or_insert(Vec::new()) + .push(crate::Selection { + id: selection.id, + goal: selection.goal, + reversed: selection.reversed, + start: selection.start.to_display_point(&display_map), + end: selection.end.to_display_point(&display_map), + }); + } }); let line_number_layouts = self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);