Update editor element to use new {next,prev}_line_boundary methods

Since these methods take buffer points instead of display points, this adjusts
the logic for retrieving the visible selections, so that they are initially returned
in terms of buffer points.
This commit is contained in:
Max Brunsfeld 2021-12-28 13:47:09 -08:00
parent 7f786ca8a6
commit 137fbd0088
3 changed files with 109 additions and 124 deletions

View File

@ -200,53 +200,27 @@ impl DisplaySnapshot {
self.buffer_snapshot.max_buffer_row() self.buffer_snapshot.max_buffer_row()
} }
pub fn prev_row_boundary(&self, mut display_point: DisplayPoint) -> (DisplayPoint, Point) { pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
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 {
loop { loop {
point.column = 0; point.column = 0;
let mut display_point = self.point_to_display_point(point, Bias::Left); let mut display_point = self.point_to_display_point(point, Bias::Left);
*display_point.column_mut() = 0; *display_point.column_mut() = 0;
let next_point = self.display_point_to_point(display_point, Bias::Left); let next_point = self.display_point_to_point(display_point, Bias::Left);
if next_point == point { if next_point == point {
return point; return (point, display_point);
} }
point = next_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 { loop {
point.column = self.buffer_snapshot.line_len(point.row); point.column = self.buffer_snapshot.line_len(point.row);
let mut display_point = self.point_to_display_point(point, Bias::Right); let mut display_point = self.point_to_display_point(point, Bias::Right);
*display_point.column_mut() = self.line_len(display_point.row()); *display_point.column_mut() = self.line_len(display_point.row());
let next_point = self.display_point_to_point(display_point, Bias::Right); let next_point = self.display_point_to_point(display_point, Bias::Right);
if next_point == point { if next_point == point {
return point; return (point, display_point);
} }
point = next_point; point = next_point;
} }
@ -631,23 +605,21 @@ mod tests {
log::info!("display text: {:?}", snapshot.text()); log::info!("display text: {:?}", snapshot.text());
// Line boundaries // Line boundaries
let buffer = &snapshot.buffer_snapshot;
for _ in 0..5 { for _ in 0..5 {
let row = rng.gen_range(0..=snapshot.max_point().row()); let row = rng.gen_range(0..=buffer.max_point().row);
let column = rng.gen_range(0..=snapshot.line_len(row)); let column = rng.gen_range(0..=buffer.line_len(row));
let point = snapshot.clip_point(DisplayPoint::new(row, column), Left); let point = buffer.clip_point(Point::new(row, column), Left);
let (prev_display_bound, prev_buffer_bound) = snapshot.prev_row_boundary(point); let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
let (next_display_bound, next_buffer_bound) = snapshot.next_row_boundary(point); let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
assert!(prev_display_bound <= point); assert!(prev_buffer_bound <= point);
assert!(next_display_bound >= point); assert!(next_buffer_bound >= point);
assert_eq!(prev_buffer_bound.column, 0); assert_eq!(prev_buffer_bound.column, 0);
assert_eq!(prev_display_bound.column(), 0); assert_eq!(prev_display_bound.column(), 0);
if next_buffer_bound < snapshot.buffer_snapshot.max_point() { if next_buffer_bound < buffer.max_point() {
assert_eq!( assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
snapshot.buffer_snapshot.chars_at(next_buffer_bound).next(),
Some('\n')
);
} }
assert_eq!( assert_eq!(

View File

@ -1702,7 +1702,7 @@ impl Editor {
contiguous_row_selections.push(selection.clone()); contiguous_row_selections.push(selection.clone());
let start_row = selection.start.row; let start_row = selection.start.row;
let mut end_row = if selection.end.column > 0 || selection.is_empty() { 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 { } else {
selection.end.row selection.end.row
}; };
@ -1710,7 +1710,7 @@ impl Editor {
while let Some(next_selection) = selections.peek() { while let Some(next_selection) = selections.peek() {
if next_selection.start.row <= end_row { if next_selection.start.row <= end_row {
end_row = if next_selection.end.column > 0 || next_selection.is_empty() { 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 { } else {
next_selection.end.row next_selection.end.row
}; };
@ -1724,7 +1724,9 @@ impl Editor {
if start_row > 0 { if start_row > 0 {
let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1)) 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)); ..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 // Don't move lines across excerpts
if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) { if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) {
@ -1798,7 +1800,7 @@ impl Editor {
contiguous_row_selections.push(selection.clone()); contiguous_row_selections.push(selection.clone());
let start_row = selection.start.row; let start_row = selection.start.row;
let mut end_row = if selection.end.column > 0 || selection.is_empty() { 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 { } else {
selection.end.row selection.end.row
}; };
@ -1806,7 +1808,7 @@ impl Editor {
while let Some(next_selection) = selections.peek() { while let Some(next_selection) = selections.peek() {
if next_selection.start.row <= end_row { if next_selection.start.row <= end_row {
end_row = if next_selection.end.column > 0 || next_selection.is_empty() { 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 { } else {
next_selection.end.row 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 // 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 { if end_row <= buffer.max_point().row {
let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0); 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 // Don't move lines across excerpt boundaries
if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) { if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) {
@ -3017,82 +3019,51 @@ impl Editor {
} }
} }
pub fn visible_selections<'a>( pub fn local_selections_in_range(
&'a self, &self,
display_rows: Range<u32>, range: Range<Anchor>,
cx: &'a mut MutableAppContext, display_map: &DisplaySnapshot,
) -> HashMap<ReplicaId, Vec<Selection<DisplayPoint>>> { ) -> Vec<Selection<Point>> {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot; 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 let start_ix = match self
.selections .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, Ok(ix) | Err(ix) => ix,
}; };
let end_ix = match self let end_ix = match self
.selections .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, Ok(ix) => ix + 1,
Err(ix) => ix, Err(ix) => ix,
}; };
fn display_selection( fn point_selection(
selection: &Selection<Anchor>, selection: &Selection<Anchor>,
display_map: &DisplaySnapshot, buffer: &MultiBufferSnapshot,
) -> Selection<DisplayPoint> { ) -> Selection<Point> {
let start = selection.start.to_point(&buffer);
let end = selection.end.to_point(&buffer);
Selection { Selection {
id: selection.id, id: selection.id,
start: selection.start.to_display_point(&display_map), start,
end: selection.end.to_display_point(&display_map), end,
reversed: selection.reversed, reversed: selection.reversed,
goal: selection.goal, goal: selection.goal,
} }
} }
let mut result = HashMap::default(); self.selections[start_ix..end_ix]
.iter()
result.insert( .chain(
self.replica_id(cx), self.pending_selection
self.selections[start_ix..end_ix] .as_ref()
.iter() .map(|pending| &pending.selection),
.chain( )
self.pending_selection .map(|s| point_selection(s, &buffer))
.as_ref() .collect()
.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
} }
pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>> pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
@ -3792,8 +3763,8 @@ impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
end.row -= 1; end.row -= 1;
} }
let buffer_start = map.prev_line_boundary(start); let buffer_start = map.prev_line_boundary(start).0;
let buffer_end = map.next_line_boundary(end); let buffer_end = map.next_line_boundary(end).0;
buffer_start.row..buffer_end.row + 1 buffer_start.row..buffer_end.row + 1
} }
} }

View File

@ -1,7 +1,7 @@
use super::{ use super::{
display_map::{BlockContext, ToDisplayPoint}, display_map::{BlockContext, ToDisplayPoint},
DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, Scroll, Anchor, DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input,
Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN, Scroll, Select, SelectPhase, SoftWrap, ToPoint, MAX_LINE_LEN,
}; };
use clock::ReplicaId; use clock::ReplicaId;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
@ -19,7 +19,7 @@ use gpui::{
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
}; };
use json::json; use json::json;
use language::Chunk; use language::{Bias, Chunk};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
cmp::{self, Ordering}, cmp::{self, Ordering},
@ -731,28 +731,70 @@ impl Element for EditorElement {
let scroll_top = scroll_position.y() * line_height; 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 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 active_rows = BTreeMap::new();
let mut highlighted_row = None; 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(); highlighted_row = view.highlighted_row();
let selections = view.visible_selections(start_row..end_row, cx); let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx));
for (replica_id, selections) in &selections {
if *replica_id == view.replica_id(cx) { let local_selections = view
for selection in selections { .local_selections_in_range(start_anchor.clone()..end_anchor.clone(), &display_map);
let is_empty = selection.start == selection.end; for selection in &local_selections {
let selection_start = snapshot.prev_row_boundary(selection.start).0; let is_empty = selection.start == selection.end;
let selection_end = snapshot.next_row_boundary(selection.end).0; let selection_start = snapshot.prev_line_boundary(selection.start).1;
for row in cmp::max(selection_start.row(), start_row) let selection_end = snapshot.next_line_boundary(selection.end).1;
..=cmp::min(selection_end.row(), end_row) 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); let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
*contains_non_empty_selection |= !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); let line_number_layouts = self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);