Replicate multibuffer excerpt additions and removals to followers
This commit is contained in:
parent
a48cd9125b
commit
9314c0e313
5 changed files with 254 additions and 41 deletions
|
@ -6377,6 +6377,18 @@ impl Editor {
|
||||||
self.refresh_code_actions(cx);
|
self.refresh_code_actions(cx);
|
||||||
cx.emit(Event::BufferEdited);
|
cx.emit(Event::BufferEdited);
|
||||||
}
|
}
|
||||||
|
multi_buffer::Event::ExcerptsAdded {
|
||||||
|
buffer,
|
||||||
|
predecessor,
|
||||||
|
excerpts,
|
||||||
|
} => cx.emit(Event::ExcerptsAdded {
|
||||||
|
buffer: buffer.clone(),
|
||||||
|
predecessor: *predecessor,
|
||||||
|
excerpts: excerpts.clone(),
|
||||||
|
}),
|
||||||
|
multi_buffer::Event::ExcerptsRemoved { ids } => {
|
||||||
|
cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
|
||||||
|
}
|
||||||
multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
|
multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
|
||||||
multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
|
multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
|
||||||
multi_buffer::Event::Saved => cx.emit(Event::Saved),
|
multi_buffer::Event::Saved => cx.emit(Event::Saved),
|
||||||
|
@ -6386,7 +6398,6 @@ impl Editor {
|
||||||
multi_buffer::Event::DiagnosticsUpdated => {
|
multi_buffer::Event::DiagnosticsUpdated => {
|
||||||
self.refresh_active_diagnostics(cx);
|
self.refresh_active_diagnostics(cx);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4967,19 +4967,27 @@ fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_following(cx: &mut gpui::MutableAppContext) {
|
async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
Settings::test_async(cx);
|
||||||
|
let fs = FakeFs::new(cx.background());
|
||||||
|
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||||
|
|
||||||
cx.set_global(Settings::test(cx));
|
let buffer = project.update(cx, |project, cx| {
|
||||||
|
let buffer = project
|
||||||
let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
|
.create_buffer(&sample_text(16, 8, 'a'), None, cx)
|
||||||
let (_, follower) = cx.add_window(
|
.unwrap();
|
||||||
WindowOptions {
|
cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
|
||||||
bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
|
});
|
||||||
..Default::default()
|
let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
|
||||||
},
|
let (_, follower) = cx.update(|cx| {
|
||||||
|cx| build_editor(buffer.clone(), cx),
|
cx.add_window(
|
||||||
);
|
WindowOptions {
|
||||||
|
bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|cx| build_editor(buffer.clone(), cx),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let pending_update = Rc::new(RefCell::new(None));
|
let pending_update = Rc::new(RefCell::new(None));
|
||||||
follower.update(cx, {
|
follower.update(cx, {
|
||||||
|
@ -5000,10 +5008,12 @@ fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
});
|
});
|
||||||
follower.update(cx, |follower, cx| {
|
follower.update(cx, |follower, cx| {
|
||||||
follower
|
follower
|
||||||
.apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
|
.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
|
follower.read_with(cx, |follower, cx| {
|
||||||
|
assert_eq!(follower.selections.ranges(cx), vec![1..1]);
|
||||||
|
});
|
||||||
|
|
||||||
// Update the scroll position only
|
// Update the scroll position only
|
||||||
leader.update(cx, |leader, cx| {
|
leader.update(cx, |leader, cx| {
|
||||||
|
@ -5011,7 +5021,7 @@ fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
});
|
});
|
||||||
follower.update(cx, |follower, cx| {
|
follower.update(cx, |follower, cx| {
|
||||||
follower
|
follower
|
||||||
.apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
|
.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -5028,12 +5038,14 @@ fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
follower.update(cx, |follower, cx| {
|
follower.update(cx, |follower, cx| {
|
||||||
let initial_scroll_position = follower.scroll_position(cx);
|
let initial_scroll_position = follower.scroll_position(cx);
|
||||||
follower
|
follower
|
||||||
.apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
|
.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(follower.scroll_position(cx), initial_scroll_position);
|
assert_eq!(follower.scroll_position(cx), initial_scroll_position);
|
||||||
assert!(follower.autoscroll_request.is_some());
|
assert!(follower.autoscroll_request.is_some());
|
||||||
});
|
});
|
||||||
assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
|
follower.read_with(cx, |follower, cx| {
|
||||||
|
assert_eq!(follower.selections.ranges(cx), vec![0..0]);
|
||||||
|
});
|
||||||
|
|
||||||
// Creating a pending selection that precedes another selection
|
// Creating a pending selection that precedes another selection
|
||||||
leader.update(cx, |leader, cx| {
|
leader.update(cx, |leader, cx| {
|
||||||
|
@ -5042,10 +5054,12 @@ fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
});
|
});
|
||||||
follower.update(cx, |follower, cx| {
|
follower.update(cx, |follower, cx| {
|
||||||
follower
|
follower
|
||||||
.apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
|
.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
|
follower.read_with(cx, |follower, cx| {
|
||||||
|
assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
|
||||||
|
});
|
||||||
|
|
||||||
// Extend the pending selection so that it surrounds another selection
|
// Extend the pending selection so that it surrounds another selection
|
||||||
leader.update(cx, |leader, cx| {
|
leader.update(cx, |leader, cx| {
|
||||||
|
@ -5053,10 +5067,143 @@ fn test_following(cx: &mut gpui::MutableAppContext) {
|
||||||
});
|
});
|
||||||
follower.update(cx, |follower, cx| {
|
follower.update(cx, |follower, cx| {
|
||||||
follower
|
follower
|
||||||
.apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
|
.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
|
follower.read_with(cx, |follower, cx| {
|
||||||
|
assert_eq!(follower.selections.ranges(cx), vec![0..2]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
|
||||||
|
Settings::test_async(cx);
|
||||||
|
let fs = FakeFs::new(cx.background());
|
||||||
|
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||||
|
let (_, pane) = cx.add_window(|cx| Pane::new(None, cx));
|
||||||
|
|
||||||
|
let leader = pane.update(cx, |_, cx| {
|
||||||
|
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||||
|
cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start following the editor when it has no excerpts.
|
||||||
|
let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
|
||||||
|
let follower_1 = cx
|
||||||
|
.update(|cx| {
|
||||||
|
Editor::from_state_proto(pane.clone(), project.clone(), &mut state_message, cx)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let follower_1_update = Rc::new(RefCell::new(None));
|
||||||
|
follower_1.update(cx, {
|
||||||
|
let update = follower_1_update.clone();
|
||||||
|
|_, cx| {
|
||||||
|
cx.subscribe(&leader, move |_, leader, event, cx| {
|
||||||
|
leader
|
||||||
|
.read(cx)
|
||||||
|
.add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
|
||||||
|
(
|
||||||
|
project
|
||||||
|
.create_buffer("abc\ndef\nghi\njkl\n", None, cx)
|
||||||
|
.unwrap(),
|
||||||
|
project
|
||||||
|
.create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert some excerpts.
|
||||||
|
leader.update(cx, |leader, cx| {
|
||||||
|
leader.buffer.update(cx, |multibuffer, cx| {
|
||||||
|
let excerpt_ids = multibuffer.push_excerpts(
|
||||||
|
buffer_1.clone(),
|
||||||
|
[
|
||||||
|
ExcerptRange {
|
||||||
|
context: 1..6,
|
||||||
|
primary: None,
|
||||||
|
},
|
||||||
|
ExcerptRange {
|
||||||
|
context: 12..15,
|
||||||
|
primary: None,
|
||||||
|
},
|
||||||
|
ExcerptRange {
|
||||||
|
context: 0..3,
|
||||||
|
primary: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
multibuffer.insert_excerpts_after(
|
||||||
|
excerpt_ids[0],
|
||||||
|
buffer_2.clone(),
|
||||||
|
[
|
||||||
|
ExcerptRange {
|
||||||
|
context: 8..12,
|
||||||
|
primary: None,
|
||||||
|
},
|
||||||
|
ExcerptRange {
|
||||||
|
context: 0..6,
|
||||||
|
primary: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start following separately after it already has excerpts.
|
||||||
|
let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
|
||||||
|
let follower_2 = cx
|
||||||
|
.update(|cx| {
|
||||||
|
Editor::from_state_proto(pane.clone(), project.clone(), &mut state_message, cx)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
follower_2.read_with(cx, Editor::text),
|
||||||
|
leader.read_with(cx, Editor::text)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply the update of adding the excerpts.
|
||||||
|
follower_1.update(cx, |follower, cx| {
|
||||||
|
follower
|
||||||
|
.apply_update_proto(&project, follower_1_update.borrow_mut().take().unwrap(), cx)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
follower_1.read_with(cx, Editor::text),
|
||||||
|
leader.read_with(cx, Editor::text)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove some excerpts.
|
||||||
|
leader.update(cx, |leader, cx| {
|
||||||
|
leader.buffer.update(cx, |multibuffer, cx| {
|
||||||
|
let excerpt_ids = multibuffer.excerpt_ids();
|
||||||
|
multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
|
||||||
|
multibuffer.remove_excerpts([excerpt_ids[0]], cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply the update of removing the excerpts.
|
||||||
|
follower_1.update(cx, |follower, cx| {
|
||||||
|
follower
|
||||||
|
.apply_update_proto(&project, follower_1_update.borrow_mut().take().unwrap(), cx)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
follower_1.read_with(cx, Editor::text),
|
||||||
|
leader.read_with(cx, Editor::text)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -72,10 +72,6 @@ impl FollowableItem for Editor {
|
||||||
editors.find(|editor| {
|
editors.find(|editor| {
|
||||||
editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffers[0])
|
editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffers[0])
|
||||||
})
|
})
|
||||||
} else if let Some(title) = &state.title {
|
|
||||||
editors.find(|editor| {
|
|
||||||
editor.read(cx).buffer().read(cx).title(cx).as_ref() == title
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -231,16 +227,17 @@ impl FollowableItem for Editor {
|
||||||
predecessor,
|
predecessor,
|
||||||
excerpts,
|
excerpts,
|
||||||
} => {
|
} => {
|
||||||
|
let buffer_id = buffer.read(cx).remote_id();
|
||||||
let mut excerpts = excerpts.iter();
|
let mut excerpts = excerpts.iter();
|
||||||
if let Some((id, range)) = excerpts.next() {
|
if let Some((id, range)) = excerpts.next() {
|
||||||
update.inserted_excerpts.push(proto::ExcerptInsertion {
|
update.inserted_excerpts.push(proto::ExcerptInsertion {
|
||||||
previous_excerpt_id: Some(predecessor.to_proto()),
|
previous_excerpt_id: Some(predecessor.to_proto()),
|
||||||
excerpt: serialize_excerpt(buffer, id, range, cx),
|
excerpt: serialize_excerpt(buffer_id, id, range),
|
||||||
});
|
});
|
||||||
update.inserted_excerpts.extend(excerpts.map(|(id, range)| {
|
update.inserted_excerpts.extend(excerpts.map(|(id, range)| {
|
||||||
proto::ExcerptInsertion {
|
proto::ExcerptInsertion {
|
||||||
previous_excerpt_id: None,
|
previous_excerpt_id: None,
|
||||||
excerpt: serialize_excerpt(buffer, id, range, cx),
|
excerpt: serialize_excerpt(buffer_id, id, range),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -275,22 +272,69 @@ impl FollowableItem for Editor {
|
||||||
|
|
||||||
fn apply_update_proto(
|
fn apply_update_proto(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
message: update_view::Variant,
|
message: update_view::Variant,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match message {
|
match message {
|
||||||
update_view::Variant::Editor(message) => {
|
update_view::Variant::Editor(message) => {
|
||||||
let buffer = self.buffer.read(cx);
|
let multibuffer = self.buffer.read(cx);
|
||||||
let buffer = buffer.read(cx);
|
let multibuffer = multibuffer.read(cx);
|
||||||
|
let mut removals = message
|
||||||
|
.deleted_excerpts
|
||||||
|
.into_iter()
|
||||||
|
.map(ExcerptId::from_proto)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
removals.sort_by(|a, b| a.cmp(&b, &multibuffer));
|
||||||
|
|
||||||
let selections = message
|
let selections = message
|
||||||
.selections
|
.selections
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|selection| deserialize_selection(&buffer, selection))
|
.filter_map(|selection| deserialize_selection(&multibuffer, selection))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let scroll_top_anchor = message
|
let scroll_top_anchor = message
|
||||||
.scroll_top_anchor
|
.scroll_top_anchor
|
||||||
.and_then(|anchor| deserialize_anchor(&buffer, anchor));
|
.and_then(|anchor| deserialize_anchor(&multibuffer, anchor));
|
||||||
drop(buffer);
|
drop(multibuffer);
|
||||||
|
|
||||||
|
self.buffer.update(cx, |multibuffer, cx| {
|
||||||
|
let mut insertions = message.inserted_excerpts.into_iter().peekable();
|
||||||
|
while let Some(insertion) = insertions.next() {
|
||||||
|
let Some(excerpt) = insertion.excerpt else { continue };
|
||||||
|
let Some(previous_excerpt_id) = insertion.previous_excerpt_id else { continue };
|
||||||
|
let buffer_id = excerpt.buffer_id;
|
||||||
|
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else { continue };
|
||||||
|
|
||||||
|
let adjacent_excerpts = iter::from_fn(|| {
|
||||||
|
let insertion = insertions.peek()?;
|
||||||
|
if insertion.previous_excerpt_id.is_none()
|
||||||
|
&& insertion.excerpt.as_ref()?.buffer_id == buffer_id
|
||||||
|
{
|
||||||
|
insertions.next()?.excerpt
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
multibuffer.insert_excerpts_with_ids_after(
|
||||||
|
ExcerptId::from_proto(previous_excerpt_id),
|
||||||
|
buffer,
|
||||||
|
[excerpt]
|
||||||
|
.into_iter()
|
||||||
|
.chain(adjacent_excerpts)
|
||||||
|
.filter_map(|excerpt| {
|
||||||
|
Some((
|
||||||
|
ExcerptId::from_proto(excerpt.id),
|
||||||
|
deserialize_excerpt_range(excerpt)?,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
multibuffer.remove_excerpts(removals, cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
if !selections.is_empty() {
|
if !selections.is_empty() {
|
||||||
self.set_selections_from_remote(selections, cx);
|
self.set_selections_from_remote(selections, cx);
|
||||||
|
@ -318,14 +362,13 @@ impl FollowableItem for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_excerpt(
|
fn serialize_excerpt(
|
||||||
buffer: &ModelHandle<Buffer>,
|
buffer_id: u64,
|
||||||
id: &ExcerptId,
|
id: &ExcerptId,
|
||||||
range: &ExcerptRange<language::Anchor>,
|
range: &ExcerptRange<language::Anchor>,
|
||||||
cx: &AppContext,
|
|
||||||
) -> Option<proto::Excerpt> {
|
) -> Option<proto::Excerpt> {
|
||||||
Some(proto::Excerpt {
|
Some(proto::Excerpt {
|
||||||
id: id.to_proto(),
|
id: id.to_proto(),
|
||||||
buffer_id: buffer.read(cx).remote_id(),
|
buffer_id,
|
||||||
context_start: Some(serialize_text_anchor(&range.context.start)),
|
context_start: Some(serialize_text_anchor(&range.context.start)),
|
||||||
context_end: Some(serialize_text_anchor(&range.context.end)),
|
context_end: Some(serialize_text_anchor(&range.context.end)),
|
||||||
primary_start: range
|
primary_start: range
|
||||||
|
@ -390,7 +433,7 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
|
||||||
Some(Anchor {
|
Some(Anchor {
|
||||||
excerpt_id,
|
excerpt_id,
|
||||||
text_anchor: language::proto::deserialize_anchor(anchor.anchor?)?,
|
text_anchor: language::proto::deserialize_anchor(anchor.anchor?)?,
|
||||||
buffer_id: Some(buffer.buffer_id_for_excerpt(excerpt_id)?),
|
buffer_id: buffer.buffer_id_for_excerpt(excerpt_id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -463,6 +463,7 @@ pub trait FollowableItem: Item {
|
||||||
) -> bool;
|
) -> bool;
|
||||||
fn apply_update_proto(
|
fn apply_update_proto(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
message: proto::update_view::Variant,
|
message: proto::update_view::Variant,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
@ -482,6 +483,7 @@ pub trait FollowableItemHandle: ItemHandle {
|
||||||
) -> bool;
|
) -> bool;
|
||||||
fn apply_update_proto(
|
fn apply_update_proto(
|
||||||
&self,
|
&self,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
message: proto::update_view::Variant,
|
message: proto::update_view::Variant,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
@ -514,10 +516,11 @@ impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
|
||||||
|
|
||||||
fn apply_update_proto(
|
fn apply_update_proto(
|
||||||
&self,
|
&self,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
message: proto::update_view::Variant,
|
message: proto::update_view::Variant,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.update(cx, |this, cx| this.apply_update_proto(message, cx))
|
self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
|
fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
|
||||||
|
@ -2477,6 +2480,7 @@ impl Workspace {
|
||||||
let variant = update_view
|
let variant = update_view
|
||||||
.variant
|
.variant
|
||||||
.ok_or_else(|| anyhow!("missing update view variant"))?;
|
.ok_or_else(|| anyhow!("missing update view variant"))?;
|
||||||
|
let project = this.project.clone();
|
||||||
this.update_leader_state(leader_id, cx, |state, cx| {
|
this.update_leader_state(leader_id, cx, |state, cx| {
|
||||||
let variant = variant.clone();
|
let variant = variant.clone();
|
||||||
match state
|
match state
|
||||||
|
@ -2485,7 +2489,7 @@ impl Workspace {
|
||||||
.or_insert(FollowerItem::Loading(Vec::new()))
|
.or_insert(FollowerItem::Loading(Vec::new()))
|
||||||
{
|
{
|
||||||
FollowerItem::Loaded(item) => {
|
FollowerItem::Loaded(item) => {
|
||||||
item.apply_update_proto(variant, cx).log_err();
|
item.apply_update_proto(&project, variant, cx).log_err();
|
||||||
}
|
}
|
||||||
FollowerItem::Loading(updates) => updates.push(variant),
|
FollowerItem::Loading(updates) => updates.push(variant),
|
||||||
}
|
}
|
||||||
|
@ -2576,7 +2580,7 @@ impl Workspace {
|
||||||
let e = e.into_mut();
|
let e = e.into_mut();
|
||||||
if let FollowerItem::Loading(updates) = e {
|
if let FollowerItem::Loading(updates) = e {
|
||||||
for update in updates.drain(..) {
|
for update in updates.drain(..) {
|
||||||
item.apply_update_proto(update, cx)
|
item.apply_update_proto(&this.project, update, cx)
|
||||||
.context("failed to apply view update")
|
.context("failed to apply view update")
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,16 @@ function isStyleSet(key: any): key is StyleSets {
|
||||||
"negative",
|
"negative",
|
||||||
].includes(key);
|
].includes(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStyle(key: any): key is Styles {
|
function isStyle(key: any): key is Styles {
|
||||||
return ["default", "active", "disabled", "hovered", "pressed", "inverted"].includes(key);
|
return [
|
||||||
|
"default",
|
||||||
|
"active",
|
||||||
|
"disabled",
|
||||||
|
"hovered",
|
||||||
|
"pressed",
|
||||||
|
"inverted",
|
||||||
|
].includes(key);
|
||||||
}
|
}
|
||||||
function getStyle(
|
function getStyle(
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue