Fine tune

Conrad Irwin created

Change summary

crates/channel/src/channel_buffer.rs    | 12 ++++
crates/client/src/user.rs               |  1 
crates/collab/src/db/queries/buffers.rs | 17 ++++++
crates/editor/src/editor.rs             | 13 ++++-
crates/editor/src/element.rs            | 67 ++++++++++++++------------
5 files changed, 73 insertions(+), 37 deletions(-)

Detailed changes

crates/channel/src/channel_buffer.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{Channel, ChannelId, ChannelStore};
 use anyhow::Result;
-use client::{Client, Collaborator, UserStore};
+use client::{Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
 use collections::HashMap;
 use gpui::{AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task};
 use language::proto::serialize_version;
@@ -181,6 +181,16 @@ impl ChannelBuffer {
     ) {
         match event {
             language::Event::Operation(operation) => {
+                if *ZED_ALWAYS_ACTIVE {
+                    match operation {
+                        language::Operation::UpdateSelections { selections, .. } => {
+                            if selections.is_empty() {
+                                return;
+                            }
+                        }
+                        _ => {}
+                    }
+                }
                 let operation = language::proto::serialize_operation(operation);
                 self.client
                     .send(proto::UpdateChannelBuffer {

crates/client/src/user.rs 🔗

@@ -635,7 +635,6 @@ impl UserStore {
         cx.spawn(|this, mut cx| async move {
             if let Some(rpc) = client.upgrade() {
                 let response = rpc.request(request).await.context("error loading users")?;
-                dbg!(&response.users);
                 let users = response
                     .users
                     .into_iter()

crates/collab/src/db/queries/buffers.rs 🔗

@@ -450,8 +450,21 @@ impl Database {
     )> {
         self.transaction(move |tx| async move {
             let channel = self.get_channel_internal(channel_id, &*tx).await?;
-            self.check_user_is_channel_member(&channel, user, &*tx)
-                .await?;
+
+            let mut requires_write_permission = false;
+            for op in operations.iter() {
+                match op.variant {
+                    None | Some(proto::operation::Variant::UpdateSelections(_)) => {}
+                    Some(_) => requires_write_permission = true,
+                }
+            }
+            if requires_write_permission {
+                self.check_user_is_channel_member(&channel, user, &*tx)
+                    .await?;
+            } else {
+                self.check_user_is_channel_participant(&channel, user, &*tx)
+                    .await?;
+            }
 
             let buffer = buffer::Entity::find()
                 .filter(buffer::Column::ChannelId.eq(channel_id))

crates/editor/src/editor.rs 🔗

@@ -368,7 +368,7 @@ pub struct Editor {
     collaboration_hub: Option<Box<dyn CollaborationHub>>,
     blink_manager: Model<BlinkManager>,
     recently_focused: bool,
-    hovered_selections: HashSet<(ReplicaId, usize)>,
+    hovered_cursor: Option<HoveredCursor>,
     pub show_local_selections: bool,
     mode: EditorMode,
     show_gutter: bool,
@@ -420,6 +420,7 @@ pub struct EditorSnapshot {
     ongoing_scroll: OngoingScroll,
 }
 
+#[derive(Debug)]
 pub struct RemoteSelection {
     pub replica_id: ReplicaId,
     pub selection: Selection<Anchor>,
@@ -444,6 +445,11 @@ enum SelectionHistoryMode {
     Redoing,
 }
 
+struct HoveredCursor {
+    replica_id: u16,
+    selection_id: usize,
+}
+
 impl Default for SelectionHistoryMode {
     fn default() -> Self {
         Self::Normal
@@ -1608,7 +1614,7 @@ impl Editor {
             gutter_width: Default::default(),
             style: None,
             recently_focused: false,
-            hovered_selections: Default::default(),
+            hovered_cursor: Default::default(),
             editor_actions: Default::default(),
             show_copilot_suggestions: mode == EditorMode::Full,
             _subscriptions: vec![
@@ -8998,8 +9004,9 @@ impl Editor {
         } else {
             self.blink_manager.update(cx, BlinkManager::enable);
             self.recently_focused = true;
+            cx.notify();
             cx.spawn(|this, mut cx| async move {
-                cx.background_executor().timer(Duration::from_secs(5)).await;
+                cx.background_executor().timer(Duration::from_secs(2)).await;
                 this.update(&mut cx, |this, cx| {
                     this.recently_focused = false;
                     cx.notify()

crates/editor/src/element.rs 🔗

@@ -567,6 +567,7 @@ impl EditorElement {
                         cx,
                     );
                     hover_at(editor, Some(point), cx);
+                    Self::update_visible_cursor(editor, point, cx);
                 }
                 None => {
                     update_inlay_link_and_hover_points(
@@ -594,24 +595,28 @@ impl EditorElement {
         cx: &mut ViewContext<Editor>,
     ) {
         let snapshot = editor.snapshot(cx);
-        if let Some(hub) = editor.collaboration_hub() {
-            let range = if point.column() > 0 {
-                DisplayPoint::new(point.row(), point.column() - 1)..point
-            } else {
-                point..DisplayPoint::new(point.row(), point.column() + 1)
-            };
-            let range = snapshot
+        let Some(hub) = editor.collaboration_hub() else {
+            return;
+        };
+        let range = DisplayPoint::new(point.row(), point.column().saturating_sub(1))
+            ..DisplayPoint::new(point.row(), point.column() + 1);
+
+        let range = snapshot
+            .buffer_snapshot
+            .anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
+            ..snapshot
                 .buffer_snapshot
-                .anchor_at(range.start.to_point(&snapshot.display_snapshot), Bias::Left)
-                ..snapshot
-                    .buffer_snapshot
-                    .anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
-            for selection in snapshot.remote_selections_in_range(&range, hub, cx) {
-                let key = (selection.replica_id, selection.selection.id);
-                editor.hovered_selections.insert(key);
-            }
-        }
-        editor.hovered_selections.clear();
+                .anchor_at(range.end.to_point(&snapshot.display_snapshot), Bias::Right);
+
+        let Some(selection) = snapshot.remote_selections_in_range(&range, hub, cx).next() else {
+            editor.hovered_cursor.take();
+            return;
+        };
+        editor.hovered_cursor.replace(crate::HoveredCursor {
+            replica_id: selection.replica_id,
+            selection_id: selection.selection.id,
+        });
+        cx.notify()
     }
 
     fn paint_background(
@@ -1990,7 +1995,7 @@ impl EditorElement {
                     if Some(selection.peer_id) == editor.leader_peer_id {
                         continue;
                     }
-                    let id = (selection.replica_id, selection.selection.id);
+                    let is_shown = editor.recently_focused || editor.hovered_cursor.as_ref().is_some_and(|c| c.replica_id == selection.replica_id && c.selection_id == selection.selection.id);
 
                     remote_selections
                         .entry(selection.replica_id)
@@ -2003,7 +2008,7 @@ impl EditorElement {
                             &snapshot.display_snapshot,
                             false,
                             false,
-                            if editor.recently_focused || editor.hovered_selections.contains(&id) {
+                            if is_shown {
                                 selection.user_name
                             } else {
                                 None
@@ -3209,26 +3214,20 @@ impl Cursor {
             fill(bounds, self.color)
         };
 
-        cx.paint_quad(cursor);
-
-        if let Some(block_text) = &self.block_text {
-            block_text
-                .paint(self.origin + origin, self.line_height, cx)
-                .log_err();
-        }
-
         if let Some(name) = &self.cursor_name {
+            let text_size = self.line_height / 1.5;
+
             let name_origin = if name.is_top_row {
                 point(bounds.right() - px(1.), bounds.top())
             } else {
-                point(bounds.left(), bounds.top() - self.line_height / 4. - px(1.))
+                point(bounds.left(), bounds.top() - text_size / 2. - px(1.))
             };
             cx.with_z_index(name.z_index, |cx| {
                 div()
                     .bg(self.color)
-                    .text_size(self.line_height / 2.)
+                    .text_size(text_size)
                     .px_0p5()
-                    .line_height(self.line_height / 2. + px(1.))
+                    .line_height(text_size + px(2.))
                     .text_color(name.color)
                     .child(name.string.clone())
                     .into_any_element()
@@ -3239,6 +3238,14 @@ impl Cursor {
                     )
             })
         }
+
+        cx.paint_quad(cursor);
+
+        if let Some(block_text) = &self.block_text {
+            block_text
+                .paint(self.origin + origin, self.line_height, cx)
+                .log_err();
+        }
     }
 
     pub fn shape(&self) -> CursorShape {