From e72b4ae03d4251dc4fddd843d744ac7acc4e8071 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 28 Jun 2021 12:37:58 +0200 Subject: [PATCH] Maintain active selections as editors are focused and blurred --- zed-rpc/proto/zed.proto | 7 ++ zed/src/editor.rs | 12 ++- zed/src/editor/buffer.rs | 161 +++++++++++++++++++++++++++++---------- 3 files changed, 140 insertions(+), 40 deletions(-) diff --git a/zed-rpc/proto/zed.proto b/zed-rpc/proto/zed.proto index 07819e31c638b436404f691477ce1a61fa6771b5..689683747a8bb3aebfd3d2f809b4d9a0100b06e9 100644 --- a/zed-rpc/proto/zed.proto +++ b/zed-rpc/proto/zed.proto @@ -124,6 +124,7 @@ message Operation { Edit edit = 1; Undo undo = 2; UpdateSelections update_selections = 3; + SetActiveSelections set_active_selections = 4; } message Edit { @@ -150,6 +151,12 @@ message Operation { uint32 lamport_timestamp = 3; repeated Selection selections = 4; } + + message SetActiveSelections { + uint32 replica_id = 1; + optional uint32 local_timestamp = 2; + uint32 lamport_timestamp = 3; + } } message VectorClockEntry { diff --git a/zed/src/editor.rs b/zed/src/editor.rs index fdcc07f7dbf01a54b3ed38d69027ab92845de23f..a2dd1c15daa18e0bb25188e91f97313316d99348 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -1991,7 +1991,9 @@ impl Editor { } self.buffer.update(cx, |buffer, cx| { - buffer.update_selection_set(self.selection_set_id, selections, cx) + buffer + .update_selection_set(self.selection_set_id, selections, cx) + .unwrap(); }); self.pause_cursor_blinking(cx); @@ -2432,11 +2434,19 @@ impl View for Editor { fn on_focus(&mut self, cx: &mut ViewContext) { self.focused = true; self.blink_cursors(self.blink_epoch, cx); + self.buffer.update(cx, |buffer, cx| { + buffer + .set_active_selection_set(Some(self.selection_set_id), cx) + .unwrap(); + }); } fn on_blur(&mut self, cx: &mut ViewContext) { self.focused = false; self.cursors_visible = false; + self.buffer.update(cx, |buffer, cx| { + buffer.set_active_selection_set(None, cx).unwrap(); + }); cx.emit(Event::Blurred); cx.notify(); } diff --git a/zed/src/editor/buffer.rs b/zed/src/editor/buffer.rs index 4baeabe7cd94953bc003ae818fca502c66589c3d..f452df57a284ae5a4a12363adbd2b489e73379a1 100644 --- a/zed/src/editor/buffer.rs +++ b/zed/src/editor/buffer.rs @@ -121,8 +121,7 @@ pub struct Buffer { language: Option>, syntax_tree: Mutex>, is_parsing: bool, - selections: HashMap>, - pub selections_last_update: SelectionsVersion, + selections: HashMap, deferred_ops: OperationQueue, deferred_replicas: HashSet, replica_id: ReplicaId, @@ -133,6 +132,12 @@ pub struct Buffer { operations: Vec, } +#[derive(Clone, Debug, Eq, PartialEq)] +struct SelectionSet { + selections: Arc<[Selection]>, + active: bool, +} + #[derive(Clone)] struct SyntaxTree { tree: Tree, @@ -397,6 +402,10 @@ pub enum Operation { selections: Option>, lamport_timestamp: time::Lamport, }, + SetActiveSelections { + set_id: Option, + lamport_timestamp: time::Lamport, + }, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -493,7 +502,6 @@ impl Buffer { language, saved_mtime, selections: HashMap::default(), - selections_last_update: 0, deferred_ops: OperationQueue::new(), deferred_replicas: HashSet::default(), replica_id, @@ -890,10 +898,6 @@ impl Buffer { self.visible_text.chars_at(offset) } - pub fn selections_changed_since(&self, since: SelectionsVersion) -> bool { - self.selections_last_update != since - } - pub fn edits_since<'a>(&'a self, since: time::Global) -> impl 'a + Iterator { let since_2 = since.clone(); let cursor = self.fragments.filter( @@ -929,11 +933,11 @@ impl Buffer { cx: &mut ModelContext, ) -> Result<()> { let selections = if let Some(set_id) = set_id { - let selections = self + let set = self .selections .get(&set_id) .ok_or_else(|| anyhow!("invalid selection set {:?}", set_id))?; - Some((set_id, selections.clone())) + Some((set_id, set.selections.clone())) } else { None }; @@ -961,11 +965,11 @@ impl Buffer { cx: &mut ModelContext, ) -> Result<()> { let selections = if let Some(set_id) = set_id { - let selections = self + let set = self .selections .get(&set_id) .ok_or_else(|| anyhow!("invalid selection set {:?}", set_id))?; - Some((set_id, selections.clone())) + Some((set_id, set.selections.clone())) } else { None }; @@ -1049,10 +1053,13 @@ impl Buffer { ) -> SelectionSetId { let selections = selections.into(); let lamport_timestamp = self.lamport_clock.tick(); - self.selections - .insert(lamport_timestamp, Arc::clone(&selections)); - self.selections_last_update += 1; - + self.selections.insert( + lamport_timestamp, + SelectionSet { + selections: selections.clone(), + active: false, + }, + ); cx.notify(); self.send_operation( @@ -1072,15 +1079,15 @@ impl Buffer { set_id: SelectionSetId, selections: impl Into>, cx: &mut ModelContext, - ) { + ) -> Result<()> { let selections = selections.into(); - self.selections.insert(set_id, selections.clone()); - + let set = self + .selections + .get_mut(&set_id) + .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?; + set.selections = selections.clone(); let lamport_timestamp = self.lamport_clock.tick(); - self.selections_last_update += 1; - cx.notify(); - self.send_operation( Operation::UpdateSelections { set_id, @@ -1089,6 +1096,27 @@ impl Buffer { }, cx, ); + Ok(()) + } + + pub fn set_active_selection_set( + &mut self, + 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)); + } + + let lamport_timestamp = self.lamport_clock.tick(); + self.send_operation( + Operation::SetActiveSelections { + set_id, + lamport_timestamp, + }, + cx, + ); + Ok(()) } pub fn remove_selection_set( @@ -1100,8 +1128,6 @@ impl Buffer { .remove(&set_id) .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?; let lamport_timestamp = self.lamport_clock.tick(); - self.selections_last_update += 1; - cx.notify(); self.send_operation( Operation::UpdateSelections { @@ -1117,7 +1143,7 @@ impl Buffer { pub fn selections(&self, set_id: SelectionSetId) -> Result<&[Selection]> { self.selections .get(&set_id) - .map(|s| s.as_ref()) + .map(|s| s.selections.as_ref()) .ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id)) } @@ -1180,12 +1206,36 @@ impl Buffer { lamport_timestamp, } => { if let Some(selections) = selections { - self.selections.insert(set_id, selections); + if let Some(set) = self.selections.get_mut(&set_id) { + set.selections = selections; + } else { + self.selections.insert( + set_id, + SelectionSet { + selections, + active: false, + }, + ); + } } else { self.selections.remove(&set_id); } self.lamport_clock.observe(lamport_timestamp); - self.selections_last_update += 1; + } + Operation::SetActiveSelections { + set_id, + lamport_timestamp, + } => { + for (id, set) in &mut self.selections { + if id.replica_id == lamport_timestamp.replica_id { + if Some(*id) == set_id { + set.active = true; + } else { + set.active = false; + } + } + } + self.lamport_clock.observe(lamport_timestamp); } } Ok(()) @@ -1501,6 +1551,9 @@ impl Buffer { true } } + Operation::SetActiveSelections { set_id, .. } => { + set_id.map_or(true, |set_id| self.selections.contains_key(&set_id)) + } } } } @@ -1707,7 +1760,6 @@ impl Clone for Buffer { undo_map: self.undo_map.clone(), history: self.history.clone(), selections: self.selections.clone(), - selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), file: self.file.clone(), language: self.language.clone(), @@ -2186,6 +2238,9 @@ impl Operation { Operation::UpdateSelections { lamport_timestamp, .. } => *lamport_timestamp, + Operation::SetActiveSelections { + lamport_timestamp, .. + } => *lamport_timestamp, } } @@ -2235,6 +2290,16 @@ impl<'a> Into for &'a Operation { }), }, ), + Operation::SetActiveSelections { + set_id, + lamport_timestamp, + } => proto::operation::Variant::SetActiveSelections( + proto::operation::SetActiveSelections { + replica_id: lamport_timestamp.replica_id as u32, + local_timestamp: set_id.map(|set_id| set_id.value), + lamport_timestamp: lamport_timestamp.value, + }, + ), }), } } @@ -2350,6 +2415,18 @@ impl TryFrom for Operation { ), } } + proto::operation::Variant::SetActiveSelections(message) => { + Operation::SetActiveSelections { + set_id: message.local_timestamp.map(|value| time::Lamport { + replica_id: message.replica_id as ReplicaId, + value, + }), + lamport_timestamp: time::Lamport { + replica_id: message.replica_id as ReplicaId, + value: message.lamport_timestamp, + }, + } + } }, ) } @@ -3210,11 +3287,13 @@ mod tests { assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]); buffer.start_transaction_at(Some(set_id), now, cx).unwrap(); - buffer.update_selection_set( - set_id, - buffer.selections_from_ranges(vec![1..3]).unwrap(), - cx, - ); + buffer + .update_selection_set( + set_id, + buffer.selections_from_ranges(vec![1..3]).unwrap(), + cx, + ) + .unwrap(); buffer.edit(vec![4..5], "e", cx); buffer.end_transaction_at(Some(set_id), now, cx).unwrap(); assert_eq!(buffer.text(), "12cde6"); @@ -3222,11 +3301,13 @@ mod tests { now += UNDO_GROUP_INTERVAL + Duration::from_millis(1); buffer.start_transaction_at(Some(set_id), now, cx).unwrap(); - buffer.update_selection_set( - set_id, - buffer.selections_from_ranges(vec![2..2]).unwrap(), - cx, - ); + buffer + .update_selection_set( + set_id, + buffer.selections_from_ranges(vec![2..2]).unwrap(), + cx, + ) + .unwrap(); buffer.edit(vec![0..1], "a", cx); buffer.edit(vec![1..1], "b", cx); buffer.end_transaction_at(Some(set_id), now, cx).unwrap(); @@ -3649,7 +3730,8 @@ mod tests { if set_id.is_none() || rng.gen_bool(1.0 / 5.0) { self.add_selection_set(new_selections, cx); } else { - self.update_selection_set(*set_id.unwrap(), new_selections, cx); + self.update_selection_set(*set_id.unwrap(), new_selections, cx) + .unwrap(); } } @@ -3716,7 +3798,7 @@ mod tests { pub fn all_selections(&self) -> impl Iterator { self.selections .iter() - .map(|(set_id, selections)| (set_id, selections.as_ref())) + .map(|(set_id, set)| (set_id, set.selections.as_ref())) } pub fn all_selection_ranges<'a>( @@ -3745,6 +3827,7 @@ mod tests { Operation::Edit(edit) => Some(edit.timestamp.local()), Operation::Undo { undo, .. } => Some(undo.edit_id), Operation::UpdateSelections { .. } => None, + Operation::SetActiveSelections { .. } => None, } } }