Emit events from a multibuffer when adding/removing excerpts
This commit is contained in:
parent
3adc0b947f
commit
3eac3e20d5
2 changed files with 175 additions and 31 deletions
|
@ -9,9 +9,9 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
|||
pub use language::Completion;
|
||||
use language::{
|
||||
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Outline,
|
||||
OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _,
|
||||
ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
DiagnosticEntry, File, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem,
|
||||
Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _,
|
||||
ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -833,6 +833,30 @@ impl MultiBuffer {
|
|||
) -> Vec<ExcerptId>
|
||||
where
|
||||
O: text::ToOffset,
|
||||
{
|
||||
let mut ids = Vec::new();
|
||||
let mut next_excerpt_id = self.next_excerpt_id;
|
||||
self.insert_excerpts_with_ids_after(
|
||||
prev_excerpt_id,
|
||||
buffer,
|
||||
ranges.into_iter().map(|range| {
|
||||
let id = ExcerptId(post_inc(&mut next_excerpt_id));
|
||||
ids.push(id);
|
||||
(id, range)
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
ids
|
||||
}
|
||||
|
||||
pub fn insert_excerpts_with_ids_after<O>(
|
||||
&mut self,
|
||||
prev_excerpt_id: ExcerptId,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) where
|
||||
O: text::ToOffset,
|
||||
{
|
||||
assert_eq!(self.history.transaction_depth, 0);
|
||||
let mut ranges = ranges.into_iter().peekable();
|
||||
|
@ -858,7 +882,7 @@ impl MultiBuffer {
|
|||
cx.observe(&buffer, |_, _, cx| cx.notify()),
|
||||
cx.subscribe(&buffer, Self::on_buffer_event),
|
||||
],
|
||||
buffer,
|
||||
buffer: buffer.clone(),
|
||||
});
|
||||
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
|
@ -883,8 +907,8 @@ impl MultiBuffer {
|
|||
Locator::max()
|
||||
};
|
||||
|
||||
let mut ids = Vec::new();
|
||||
while let Some(range) = ranges.next() {
|
||||
let mut excerpts = Vec::new();
|
||||
while let Some((id, range)) = ranges.next() {
|
||||
let locator = Locator::between(&prev_locator, &next_locator);
|
||||
if let Err(ix) = buffer_state.excerpts.binary_search(&locator) {
|
||||
buffer_state.excerpts.insert(ix, locator.clone());
|
||||
|
@ -897,7 +921,10 @@ impl MultiBuffer {
|
|||
..buffer_snapshot.anchor_after(&primary.end)
|
||||
}),
|
||||
};
|
||||
let id = ExcerptId(post_inc(&mut self.next_excerpt_id));
|
||||
if id.0 >= self.next_excerpt_id {
|
||||
self.next_excerpt_id = id.0 + 1;
|
||||
}
|
||||
excerpts.push((id, range.clone()));
|
||||
let excerpt = Excerpt::new(
|
||||
id,
|
||||
locator.clone(),
|
||||
|
@ -909,7 +936,6 @@ impl MultiBuffer {
|
|||
new_excerpts.push(excerpt, &());
|
||||
prev_locator = locator.clone();
|
||||
new_excerpt_ids.push(ExcerptIdMapping { id, locator }, &());
|
||||
ids.push(id);
|
||||
}
|
||||
|
||||
let edit_end = new_excerpts.summary().text.len;
|
||||
|
@ -929,12 +955,17 @@ impl MultiBuffer {
|
|||
new: edit_start..edit_end,
|
||||
}]);
|
||||
cx.emit(Event::Edited);
|
||||
cx.emit(Event::ExcerptsAdded {
|
||||
buffer,
|
||||
predecessor: prev_excerpt_id,
|
||||
excerpts,
|
||||
});
|
||||
cx.notify();
|
||||
ids
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, cx: &mut ModelContext<Self>) {
|
||||
self.sync(cx);
|
||||
let ids = self.excerpt_ids();
|
||||
self.buffers.borrow_mut().clear();
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let prev_len = snapshot.len();
|
||||
|
@ -948,6 +979,7 @@ impl MultiBuffer {
|
|||
new: 0..0,
|
||||
}]);
|
||||
cx.emit(Event::Edited);
|
||||
cx.emit(Event::ExcerptsRemoved { ids });
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1071,12 +1103,14 @@ impl MultiBuffer {
|
|||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.sync(cx);
|
||||
let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
|
||||
|
||||
let mut buffers = self.buffers.borrow_mut();
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let mut new_excerpts = SumTree::new();
|
||||
let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
|
||||
let mut edits = Vec::new();
|
||||
let mut excerpt_ids = excerpt_ids.into_iter().peekable();
|
||||
let mut excerpt_ids = ids.iter().copied().peekable();
|
||||
|
||||
while let Some(excerpt_id) = excerpt_ids.next() {
|
||||
// Seek to the next excerpt to remove, preserving any preceding excerpts.
|
||||
|
@ -1143,6 +1177,7 @@ impl MultiBuffer {
|
|||
|
||||
self.subscriptions.publish_mut(edits);
|
||||
cx.emit(Event::Edited);
|
||||
cx.emit(Event::ExcerptsRemoved { ids });
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -1165,10 +1200,22 @@ impl MultiBuffer {
|
|||
fn on_buffer_event(
|
||||
&mut self,
|
||||
_: ModelHandle<Buffer>,
|
||||
event: &Event,
|
||||
event: &language::Event,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
cx.emit(event.clone());
|
||||
cx.emit(match event {
|
||||
language::Event::Edited => Event::Edited,
|
||||
language::Event::DirtyChanged => Event::DirtyChanged,
|
||||
language::Event::Saved => Event::Saved,
|
||||
language::Event::FileHandleChanged => Event::FileHandleChanged,
|
||||
language::Event::Reloaded => Event::Reloaded,
|
||||
language::Event::Reparsed => Event::Reparsed,
|
||||
language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
|
||||
language::Event::Closed => Event::Closed,
|
||||
|
||||
//
|
||||
language::Event::Operation(_) => return,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn all_buffers(&self) -> HashSet<ModelHandle<Buffer>> {
|
||||
|
@ -1603,8 +1650,28 @@ impl MultiBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
ExcerptsAdded {
|
||||
buffer: ModelHandle<Buffer>,
|
||||
predecessor: ExcerptId,
|
||||
excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
|
||||
},
|
||||
ExcerptsRemoved {
|
||||
ids: Vec<ExcerptId>,
|
||||
},
|
||||
Edited,
|
||||
Reloaded,
|
||||
Reparsed,
|
||||
Saved,
|
||||
FileHandleChanged,
|
||||
Closed,
|
||||
DirtyChanged,
|
||||
DiagnosticsUpdated,
|
||||
}
|
||||
|
||||
impl Entity for MultiBuffer {
|
||||
type Event = language::Event;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl MultiBufferSnapshot {
|
||||
|
@ -3468,7 +3535,7 @@ mod tests {
|
|||
use util::test::sample_text;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
|
||||
fn test_singleton(cx: &mut MutableAppContext) {
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
|
||||
let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
|
||||
|
||||
|
@ -3495,7 +3562,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_remote_multibuffer(cx: &mut MutableAppContext) {
|
||||
fn test_remote(cx: &mut MutableAppContext) {
|
||||
let host_buffer = cx.add_model(|cx| Buffer::new(0, "a", cx));
|
||||
let guest_buffer = cx.add_model(|cx| {
|
||||
let state = host_buffer.read(cx).to_proto();
|
||||
|
@ -3526,7 +3593,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_excerpt_buffer(cx: &mut MutableAppContext) {
|
||||
fn test_excerpt_boundaries_and_clipping(cx: &mut MutableAppContext) {
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
|
||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
|
@ -3535,7 +3602,9 @@ mod tests {
|
|||
multibuffer.update(cx, |_, cx| {
|
||||
let events = events.clone();
|
||||
cx.subscribe(&multibuffer, move |_, _, event, _| {
|
||||
events.borrow_mut().push(event.clone())
|
||||
if let Event::Edited = event {
|
||||
events.borrow_mut().push(event.clone())
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
@ -3748,7 +3817,84 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_excerpts_with_context_lines(cx: &mut MutableAppContext) {
|
||||
fn test_excerpt_events(cx: &mut MutableAppContext) {
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(10, 3, 'a'), cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(10, 3, 'm'), cx));
|
||||
|
||||
let leader_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
let follower_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
|
||||
follower_multibuffer.update(cx, |_, cx| {
|
||||
cx.subscribe(&leader_multibuffer, |follower, _, event, cx| {
|
||||
match event.clone() {
|
||||
Event::ExcerptsAdded {
|
||||
buffer,
|
||||
predecessor,
|
||||
excerpts,
|
||||
} => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
|
||||
Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
leader.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[
|
||||
ExcerptRange {
|
||||
context: 0..8,
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: 12..16,
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
);
|
||||
leader.insert_excerpts_after(
|
||||
leader.excerpt_ids()[0],
|
||||
buffer_2.clone(),
|
||||
[
|
||||
ExcerptRange {
|
||||
context: 0..5,
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: 10..15,
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
)
|
||||
});
|
||||
assert_eq!(
|
||||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
let excerpt_ids = leader.excerpt_ids();
|
||||
leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
|
||||
});
|
||||
assert_eq!(
|
||||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
leader.clear(cx);
|
||||
});
|
||||
assert_eq!(
|
||||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_push_excerpts_with_context_lines(cx: &mut MutableAppContext) {
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(20, 3, 'a'), cx));
|
||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
|
||||
|
@ -3784,7 +3930,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_empty_excerpt_buffer(cx: &mut MutableAppContext) {
|
||||
fn test_empty_multibuffer(cx: &mut MutableAppContext) {
|
||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
|
||||
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||
|
@ -3872,9 +4018,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_multibuffer_resolving_anchors_after_replacing_their_excerpts(
|
||||
cx: &mut MutableAppContext,
|
||||
) {
|
||||
fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut MutableAppContext) {
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "ABCDEFGHIJKLMNOP", cx));
|
||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue