Replicate pending selections separately from other selections

This fixes a panic that would occur when a leader created
a pending selection that overlapped another selection,
because the follower would attempt to treat that pending
selection as non-pending, which would violate the invariant
that selections are sorted and disjoint.
This commit is contained in:
Max Brunsfeld 2022-12-17 14:00:53 -08:00
parent 8c64514570
commit 1e02ebbd11
4 changed files with 37 additions and 12 deletions

View File

@ -5453,11 +5453,17 @@ impl Editor {
pub fn set_selections_from_remote(
&mut self,
selections: Vec<Selection<Anchor>>,
pending_selection: Option<Selection<Anchor>>,
cx: &mut ViewContext<Self>,
) {
let old_cursor_position = self.selections.newest_anchor().head();
self.selections.change_with(cx, |s| {
s.select_anchors(selections);
if let Some(pending_selection) = pending_selection {
s.set_pending(pending_selection, SelectMode::Character);
} else {
s.clear_pending();
}
});
self.selections_did_change(false, &old_cursor_position, cx);
}

View File

@ -130,13 +130,17 @@ impl FollowableItem for Editor {
.ok_or_else(|| anyhow!("invalid selection"))
})
.collect::<Result<Vec<_>>>()?;
let pending_selection = state
.pending_selection
.map(|selection| deserialize_selection(&buffer, selection))
.flatten();
let scroll_top_anchor = state
.scroll_top_anchor
.and_then(|anchor| deserialize_anchor(&buffer, anchor));
drop(buffer);
if !selections.is_empty() {
editor.set_selections_from_remote(selections, cx);
if !selections.is_empty() || pending_selection.is_some() {
editor.set_selections_from_remote(selections, pending_selection, cx);
}
if let Some(scroll_top_anchor) = scroll_top_anchor {
@ -216,6 +220,11 @@ impl FollowableItem for Editor {
.iter()
.map(serialize_selection)
.collect(),
pending_selection: self
.selections
.pending_anchor()
.as_ref()
.map(serialize_selection),
}))
}
@ -269,9 +278,13 @@ impl FollowableItem for Editor {
.selections
.disjoint_anchors()
.iter()
.chain(self.selections.pending_anchor().as_ref())
.map(serialize_selection)
.collect();
update.pending_selection = self
.selections
.pending_anchor()
.as_ref()
.map(serialize_selection);
true
}
_ => false,
@ -307,6 +320,10 @@ impl FollowableItem for Editor {
.into_iter()
.filter_map(|selection| deserialize_selection(&multibuffer, selection))
.collect::<Vec<_>>();
let pending_selection = message
.pending_selection
.and_then(|selection| deserialize_selection(&multibuffer, selection));
let scroll_top_anchor = message
.scroll_top_anchor
.and_then(|anchor| deserialize_anchor(&multibuffer, anchor));
@ -361,8 +378,8 @@ impl FollowableItem for Editor {
multibuffer.remove_excerpts(removals, cx);
});
if !selections.is_empty() {
this.set_selections_from_remote(selections, cx);
if !selections.is_empty() || pending_selection.is_some() {
this.set_selections_from_remote(selections, pending_selection, cx);
this.request_autoscroll_remotely(Autoscroll::newest(), cx);
} else if let Some(anchor) = scroll_top_anchor {
this.set_scroll_anchor_remote(ScrollAnchor {

View File

@ -853,9 +853,10 @@ message UpdateView {
repeated ExcerptInsertion inserted_excerpts = 1;
repeated uint64 deleted_excerpts = 2;
repeated Selection selections = 3;
EditorAnchor scroll_top_anchor = 4;
float scroll_x = 5;
float scroll_y = 6;
optional Selection pending_selection = 4;
EditorAnchor scroll_top_anchor = 5;
float scroll_x = 6;
float scroll_y = 7;
}
}
@ -872,9 +873,10 @@ message View {
optional string title = 2;
repeated Excerpt excerpts = 3;
repeated Selection selections = 4;
EditorAnchor scroll_top_anchor = 5;
float scroll_x = 6;
float scroll_y = 7;
optional Selection pending_selection = 5;
EditorAnchor scroll_top_anchor = 6;
float scroll_x = 7;
float scroll_y = 8;
}
}

View File

@ -6,4 +6,4 @@ pub use conn::Connection;
pub use peer::*;
mod macros;
pub const PROTOCOL_VERSION: u32 = 43;
pub const PROTOCOL_VERSION: u32 = 44;