From c881c7f30fef5aa16629cd97d6e9dd55b8eafc72 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 1 Jul 2021 11:21:43 +0200 Subject: [PATCH] Start on rendering remote selections --- zed/src/editor.rs | 111 ++++++++++++++---------------- zed/src/editor/buffer.rs | 51 ++++++++------ zed/src/editor/element.rs | 137 ++++++++++++++++++++++---------------- 3 files changed, 161 insertions(+), 138 deletions(-) diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 107098ecb278eccf7d9f075d9f458448fb4bea95..a844b03f920b72ac52e0cf1223202a925af6a7af 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -622,8 +622,8 @@ impl Editor { fn end_selection(&mut self, cx: &mut ViewContext) { if let Some(selection) = self.pending_selection.take() { - let ix = self.selection_insertion_index(&selection.start, cx.as_ref()); let mut selections = self.selections(cx.as_ref()).to_vec(); + let ix = self.selection_insertion_index(&selections, &selection.start, cx.as_ref()); selections.insert(ix, selection); self.update_selections(selections, false, cx); } else { @@ -1916,31 +1916,53 @@ impl Editor { } } + pub fn active_selection_sets<'a>( + &'a self, + cx: &'a AppContext, + ) -> impl 'a + Iterator { + self.buffer + .read(cx) + .selection_sets() + .filter(|(_, set)| set.active) + .map(|(set_id, _)| *set_id) + } + pub fn selections_in_range<'a>( &'a self, + set_id: SelectionSetId, range: Range, cx: &'a AppContext, ) -> impl 'a + Iterator> { + let buffer = self.buffer.read(cx); + let selections = &buffer.selection_set(set_id).unwrap().selections; let start = self.display_map.anchor_before(range.start, Bias::Left, cx); - let start_index = self.selection_insertion_index(&start, cx); - let pending_selection = self.pending_selection.as_ref().and_then(|s| { - let selection_range = s.display_range(&self.display_map, cx); - if selection_range.start <= range.end || selection_range.end <= range.end { - Some(selection_range) - } else { - None - } - }); - self.selections(cx)[start_index..] + let start_index = self.selection_insertion_index(selections, &start, cx); + let pending_selection = if set_id.replica_id == self.buffer.read(cx).replica_id() { + self.pending_selection.as_ref().and_then(|s| { + let selection_range = s.display_range(&self.display_map, cx); + if selection_range.start <= range.end || selection_range.end <= range.end { + Some(selection_range) + } else { + None + } + }) + } else { + None + }; + selections[start_index..] .iter() .map(move |s| s.display_range(&self.display_map, cx)) .take_while(move |r| r.start <= range.end || r.end <= range.end) .chain(pending_selection) } - fn selection_insertion_index(&self, start: &Anchor, cx: &AppContext) -> usize { + fn selection_insertion_index( + &self, + selections: &[Selection], + start: &Anchor, + cx: &AppContext, + ) -> usize { let buffer = self.buffer.read(cx); - let selections = self.selections(cx); match selections.binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap()) { Ok(index) => index, Err(index) => { @@ -1956,10 +1978,11 @@ impl Editor { } fn selections<'a>(&self, cx: &'a AppContext) -> &'a [Selection] { - self.buffer - .read(cx) - .selections(self.selection_set_id) + let buffer = self.buffer.read(cx); + &buffer + .selection_set(self.selection_set_id) .unwrap() + .selections } fn update_selections( @@ -2551,14 +2574,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] ); @@ -2567,14 +2584,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] ); @@ -2583,14 +2594,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -2600,14 +2605,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] ); @@ -2617,14 +2616,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [ DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) @@ -2636,14 +2629,8 @@ mod tests { }); let view = buffer_view.read(cx); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(cx.as_ref()), - cx.as_ref(), - ) - .collect::>(); assert_eq!( - selections, + view.selection_ranges(cx.as_ref()), [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] ); } @@ -4141,8 +4128,12 @@ mod tests { impl Editor { fn selection_ranges(&self, cx: &AppContext) -> Vec> { - self.selections_in_range(DisplayPoint::zero()..self.max_point(cx), cx) - .collect::>() + self.selections_in_range( + self.selection_set_id, + DisplayPoint::zero()..self.max_point(cx), + cx, + ) + .collect::>() } } diff --git a/zed/src/editor/buffer.rs b/zed/src/editor/buffer.rs index dadafa0fc63f8d9b51d648ec1ccb7999e09912ff..5404f276c7ad69e1ea6640b5a3f32090020b32aa 100644 --- a/zed/src/editor/buffer.rs +++ b/zed/src/editor/buffer.rs @@ -131,9 +131,9 @@ pub struct Buffer { } #[derive(Clone, Debug, Eq, PartialEq)] -struct SelectionSet { - selections: Arc<[Selection]>, - active: bool, +pub struct SelectionSet { + pub selections: Arc<[Selection]>, + pub active: bool, } #[derive(Clone)] @@ -583,6 +583,10 @@ impl Buffer { result } + pub fn replica_id(&self) -> ReplicaId { + self.local_clock.replica_id + } + pub fn snapshot(&self) -> Snapshot { Snapshot { text: self.visible_text.clone(), @@ -1200,8 +1204,18 @@ impl Buffer { set_id: Option, cx: &mut ModelContext, ) -> Result<()> { - if set_id.is_some() && !self.selections.contains_key(set_id.as_ref().unwrap()) { - return Err(anyhow!("invalid selection set id {:?}", set_id)); + if let Some(set_id) = set_id { + assert_eq!(set_id.replica_id, self.replica_id()); + } + + for (id, set) in &mut self.selections { + if id.replica_id == self.local_clock.replica_id { + if Some(*id) == set_id { + set.active = true; + } else { + set.active = false; + } + } } let lamport_timestamp = self.lamport_clock.tick(); @@ -1236,13 +1250,16 @@ impl Buffer { Ok(()) } - pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> { + pub fn selection_set(&self, set_id: SelectionSetId) -> Result<&SelectionSet> { self.selections .get(&set_id) - .map(|s| s.selections.as_ref()) .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } + pub fn selection_sets(&self) -> impl Iterator { + self.selections.iter() + } + pub fn apply_ops>( &mut self, ops: I, @@ -3313,8 +3330,9 @@ mod tests { assert!(!buffer.is_dirty()); assert!(!buffer.has_conflict()); - let selections = buffer.selections(selection_set_id).unwrap(); - let cursor_positions = selections + let set = buffer.selection_set(selection_set_id).unwrap(); + let cursor_positions = set + .selections .iter() .map(|selection| { assert_eq!(selection.start, selection.end); @@ -3595,8 +3613,8 @@ mod tests { buffer.replica_id ); assert_eq!( - buffer.all_selections().collect::>(), - first_buffer.all_selections().collect::>() + buffer.selection_sets().collect::>(), + first_buffer.selection_sets().collect::>() ); assert_eq!( buffer.all_selection_ranges().collect::>(), @@ -3843,7 +3861,7 @@ mod tests { // Randomly add, remove or mutate selection sets. let replica_selection_sets = &self - .all_selections() + .selection_sets() .map(|(set_id, _)| *set_id) .filter(|set_id| self.replica_id == set_id.replica_id) .collect::>(); @@ -3915,7 +3933,8 @@ mod tests { pub fn selection_ranges<'a>(&'a self, set_id: SelectionSetId) -> Result>> { Ok(self - .selections(set_id)? + .selection_set(set_id)? + .selections .iter() .map(move |selection| { let start = selection.start.to_offset(self); @@ -3929,12 +3948,6 @@ mod tests { .collect()) } - pub fn all_selections(&self) -> impl Iterator { - self.selections - .iter() - .map(|(set_id, set)| (set_id, set.selections.as_ref())) - } - pub fn all_selection_ranges<'a>( &'a self, ) -> impl 'a + Iterator>)> { diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index 47080b8137846acd6e649968ea6e02bcf8b1982c..2921b2ce6afc3bdcec7beb4a1ddcfcbd8299a47a 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -1,6 +1,6 @@ use super::{DisplayPoint, Editor, SelectAction}; use gpui::{ - color::{ColorF, ColorU}, + color::ColorU, geometry::{ rect::RectF, vector::{vec2f, Vector2F}, @@ -217,66 +217,83 @@ impl EditorElement { // Draw selections let corner_radius = 2.5; + let colors = [ + (ColorU::from_u32(0xa3d6ffff), ColorU::from_u32(0x000000ff)), + (ColorU::from_u32(0xffaf87ff), ColorU::from_u32(0xff8e72ff)), + (ColorU::from_u32(0x86eaccff), ColorU::from_u32(0x377771ff)), + (ColorU::from_u32(0xb8b8ffff), ColorU::from_u32(0x9381ffff)), + (ColorU::from_u32(0xf5cce8ff), ColorU::from_u32(0x4a2040ff)), + ]; let mut cursors = SmallVec::<[Cursor; 32]>::new(); let content_origin = bounds.origin() + vec2f(-descent, 0.0); - - for selection in view.selections_in_range( - DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0), - cx.app, - ) { - if selection.start != selection.end { - let range_start = cmp::min(selection.start, selection.end); - let range_end = cmp::max(selection.start, selection.end); - let row_range = if range_end.column() == 0 { - cmp::max(range_start.row(), start_row)..cmp::min(range_end.row(), end_row) - } else { - cmp::max(range_start.row(), start_row)..cmp::min(range_end.row() + 1, end_row) - }; - - let selection = Selection { - line_height, - start_y: content_origin.y() + row_range.start as f32 * line_height - scroll_top, - lines: row_range - .into_iter() - .map(|row| { - let line_layout = &layout.line_layouts[(row - start_row) as usize]; - SelectionLine { - start_x: if row == range_start.row() { - content_origin.x() - + line_layout.x_for_index(range_start.column() as usize) - - scroll_left - } else { - content_origin.x() - scroll_left - }, - end_x: if row == range_end.row() { - content_origin.x() - + line_layout.x_for_index(range_end.column() as usize) - - scroll_left - } else { - content_origin.x() + line_layout.width() + corner_radius * 2.0 - - scroll_left - }, - } - }) - .collect(), - }; - - selection.paint(bounds, cx.scene); - } - - if view.cursors_visible() { - let cursor_position = selection.end; - if (start_row..end_row).contains(&cursor_position.row()) { - let cursor_row_layout = - &layout.line_layouts[(selection.end.row() - start_row) as usize]; - let x = cursor_row_layout.x_for_index(selection.end.column() as usize) - - scroll_left; - let y = selection.end.row() as f32 * line_height - scroll_top; - cursors.push(Cursor { - origin: content_origin + vec2f(x, y), + for selection_set_id in view.active_selection_sets(cx.app) { + let (selection_color, cursor_color) = + colors[selection_set_id.replica_id as usize % colors.len()]; + for selection in view.selections_in_range( + selection_set_id, + DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0), + cx.app, + ) { + if selection.start != selection.end { + let range_start = cmp::min(selection.start, selection.end); + let range_end = cmp::max(selection.start, selection.end); + let row_range = if range_end.column() == 0 { + cmp::max(range_start.row(), start_row)..cmp::min(range_end.row(), end_row) + } else { + cmp::max(range_start.row(), start_row) + ..cmp::min(range_end.row() + 1, end_row) + }; + + let selection = Selection { + color: selection_color, line_height, - }); + start_y: content_origin.y() + row_range.start as f32 * line_height + - scroll_top, + lines: row_range + .into_iter() + .map(|row| { + let line_layout = &layout.line_layouts[(row - start_row) as usize]; + SelectionLine { + start_x: if row == range_start.row() { + content_origin.x() + + line_layout.x_for_index(range_start.column() as usize) + - scroll_left + } else { + content_origin.x() - scroll_left + }, + end_x: if row == range_end.row() { + content_origin.x() + + line_layout.x_for_index(range_end.column() as usize) + - scroll_left + } else { + content_origin.x() + + line_layout.width() + + corner_radius * 2.0 + - scroll_left + }, + } + }) + .collect(), + }; + + selection.paint(bounds, cx.scene); + } + + if view.cursors_visible() { + let cursor_position = selection.end; + if (start_row..end_row).contains(&cursor_position.row()) { + let cursor_row_layout = + &layout.line_layouts[(selection.end.row() - start_row) as usize]; + let x = cursor_row_layout.x_for_index(selection.end.column() as usize) + - scroll_left; + let y = selection.end.row() as f32 * line_height - scroll_top; + cursors.push(Cursor { + color: cursor_color, + origin: content_origin + vec2f(x, y), + line_height, + }); + } } } } @@ -575,13 +592,14 @@ impl PaintState { struct Cursor { origin: Vector2F, line_height: f32, + color: ColorU, } impl Cursor { fn paint(&self, cx: &mut PaintContext) { cx.scene.push_quad(Quad { bounds: RectF::new(self.origin, vec2f(2.0, self.line_height)), - background: Some(ColorU::black()), + background: Some(self.color), border: Border::new(0., ColorU::black()), corner_radius: 0., }); @@ -593,6 +611,7 @@ struct Selection { start_y: f32, line_height: f32, lines: Vec, + color: ColorU, } #[derive(Debug)] @@ -696,7 +715,7 @@ impl Selection { path.curve_to(first_top_left + top_curve_width, first_top_left); path.line_to(first_top_right - top_curve_width); - scene.push_path(path.build(ColorF::new(0.639, 0.839, 1.0, 1.0).to_u8(), Some(bounds))); + scene.push_path(path.build(self.color, Some(bounds))); } }