Start work on following in multi-buffers

This commit is contained in:
Max Brunsfeld 2022-11-29 14:50:43 -08:00
parent 82abf31ef1
commit 6120d6488b
8 changed files with 241 additions and 56 deletions

View File

@ -160,7 +160,7 @@ impl ProjectDiagnosticsEditor {
editor.set_vertical_scroll_margin(5, cx);
editor
});
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event))
cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()))
.detach();
let project = project_handle.read(cx);

View File

@ -6587,8 +6587,16 @@ fn compute_scroll_position(
scroll_position
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
ExcerptsAdded {
buffer: ModelHandle<Buffer>,
predecessor: ExcerptId,
excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
},
ExcerptsRemoved {
ids: Vec<ExcerptId>,
},
BufferEdited,
Edited,
Reparsed,
@ -6596,8 +6604,12 @@ pub enum Event {
DirtyChanged,
Saved,
TitleChanged,
SelectionsChanged { local: bool },
ScrollPositionChanged { local: bool },
SelectionsChanged {
local: bool,
},
ScrollPositionChanged {
local: bool,
},
Closed,
IgnoredInput,
}

View File

@ -38,7 +38,7 @@ fn test_edit_events(cx: &mut MutableAppContext) {
event,
Event::Edited | Event::BufferEdited | Event::DirtyChanged
) {
events.borrow_mut().push(("editor1", *event));
events.borrow_mut().push(("editor1", event.clone()));
}
})
.detach();
@ -53,7 +53,7 @@ fn test_edit_events(cx: &mut MutableAppContext) {
event,
Event::Edited | Event::BufferEdited | Event::DirtyChanged
) {
events.borrow_mut().push(("editor2", *event));
events.borrow_mut().push(("editor2", event.clone()));
}
})
.detach();

View File

@ -1,14 +1,16 @@
use crate::{
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, MultiBuffer,
MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, ExcerptRange,
MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
};
use anyhow::{anyhow, Result};
use collections::HashSet;
use futures::FutureExt;
use gpui::{
elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
};
use language::proto::serialize_anchor as serialize_text_anchor;
use language::{Bias, Buffer, File as _, OffsetRangeExt, Point, SelectionGoal};
use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath};
use rpc::proto::{self, update_view};
@ -18,6 +20,7 @@ use std::{
borrow::Cow,
cmp::{self, Ordering},
fmt::Write,
iter,
ops::Range,
path::{Path, PathBuf},
};
@ -48,22 +51,75 @@ impl FollowableItem for Editor {
return None;
};
let buffer = project.update(cx, |project, cx| {
project.open_buffer_by_id(state.buffer_id, cx)
let replica_id = project.read(cx).replica_id();
let buffer_ids = state
.excerpts
.iter()
.map(|excerpt| excerpt.buffer_id)
.collect::<HashSet<_>>();
let buffers = project.update(cx, |project, cx| {
buffer_ids
.iter()
.map(|id| project.open_buffer_by_id(*id, cx))
.collect::<Vec<_>>()
});
Some(cx.spawn(|mut cx| async move {
let buffer = buffer.await?;
let editor = pane
.read_with(&cx, |pane, cx| {
pane.items_of_type::<Self>().find(|editor| {
editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffer)
let mut buffers = futures::future::try_join_all(buffers).await?;
let editor = pane.read_with(&cx, |pane, cx| {
let mut editors = pane.items_of_type::<Self>();
if state.singleton && buffers.len() == 1 {
editors.find(|editor| {
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 {
None
}
});
let editor = editor.unwrap_or_else(|| {
pane.update(&mut cx, |_, cx| {
let multibuffer = cx.add_model(|cx| {
let mut multibuffer;
if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else {
multibuffer = MultiBuffer::new(replica_id);
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let buffer_id = excerpt.buffer_id;
let buffer_excerpts = iter::from_fn(|| {
let excerpt = excerpts.peek()?;
(excerpt.buffer_id == buffer_id)
.then(|| excerpts.next().unwrap())
});
let buffer =
buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
if let Some(buffer) = buffer {
multibuffer.push_excerpts(
buffer.clone(),
buffer_excerpts.filter_map(deserialize_excerpt_range),
cx,
);
}
}
};
if let Some(title) = &state.title {
multibuffer = multibuffer.with_title(title.clone())
}
multibuffer
});
cx.add_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), cx))
})
.unwrap_or_else(|| {
pane.update(&mut cx, |_, cx| {
cx.add_view(|cx| Editor::for_buffer(buffer, Some(project), cx))
})
});
});
editor.update(&mut cx, |editor, cx| {
let buffer = editor.buffer.read(cx).read(cx);
let selections = state
@ -90,8 +146,9 @@ impl FollowableItem for Editor {
);
}
Ok::<_, anyhow::Error>(())
anyhow::Ok(())
})?;
Ok(editor)
}))
}
@ -122,9 +179,30 @@ impl FollowableItem for Editor {
}
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
let buffer_id = self.buffer.read(cx).as_singleton()?.read(cx).remote_id();
let buffer = self.buffer.read(cx);
let excerpts = buffer
.read(cx)
.excerpts()
.map(|(id, buffer, range)| proto::Excerpt {
id: id.to_proto(),
buffer_id: buffer.remote_id(),
context_start: Some(serialize_text_anchor(&range.context.start)),
context_end: Some(serialize_text_anchor(&range.context.end)),
primary_start: range
.primary
.as_ref()
.map(|range| serialize_text_anchor(&range.start)),
primary_end: range
.primary
.as_ref()
.map(|range| serialize_text_anchor(&range.end)),
})
.collect();
Some(proto::view::Variant::Editor(proto::view::Editor {
buffer_id,
singleton: buffer.is_singleton(),
title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
excerpts,
scroll_top_anchor: Some(serialize_anchor(&self.scroll_top_anchor)),
scroll_x: self.scroll_position.x(),
scroll_y: self.scroll_position.y(),
@ -141,13 +219,39 @@ impl FollowableItem for Editor {
&self,
event: &Self::Event,
update: &mut Option<proto::update_view::Variant>,
_: &AppContext,
cx: &AppContext,
) -> bool {
let update =
update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
match update {
proto::update_view::Variant::Editor(update) => match event {
Event::ExcerptsAdded {
buffer,
predecessor,
excerpts,
} => {
let mut excerpts = excerpts.iter();
if let Some((id, range)) = excerpts.next() {
update.inserted_excerpts.push(proto::ExcerptInsertion {
previous_excerpt_id: Some(predecessor.to_proto()),
excerpt: serialize_excerpt(buffer, id, range, cx),
});
update.inserted_excerpts.extend(excerpts.map(|(id, range)| {
proto::ExcerptInsertion {
previous_excerpt_id: None,
excerpt: serialize_excerpt(buffer, id, range, cx),
}
}))
}
true
}
Event::ExcerptsRemoved { ids } => {
update
.deleted_excerpts
.extend(ids.iter().map(ExcerptId::to_proto));
true
}
Event::ScrollPositionChanged { .. } => {
update.scroll_top_anchor = Some(serialize_anchor(&self.scroll_top_anchor));
update.scroll_x = self.scroll_position.x();
@ -213,6 +317,28 @@ impl FollowableItem for Editor {
}
}
fn serialize_excerpt(
buffer: &ModelHandle<Buffer>,
id: &ExcerptId,
range: &ExcerptRange<language::Anchor>,
cx: &AppContext,
) -> Option<proto::Excerpt> {
Some(proto::Excerpt {
id: id.to_proto(),
buffer_id: buffer.read(cx).remote_id(),
context_start: Some(serialize_text_anchor(&range.context.start)),
context_end: Some(serialize_text_anchor(&range.context.end)),
primary_start: range
.primary
.as_ref()
.map(|r| serialize_text_anchor(&r.start)),
primary_end: range
.primary
.as_ref()
.map(|r| serialize_text_anchor(&r.end)),
})
}
fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
proto::Selection {
id: selection.id as u64,
@ -225,10 +351,27 @@ fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor {
proto::EditorAnchor {
excerpt_id: anchor.excerpt_id.to_proto(),
anchor: Some(language::proto::serialize_anchor(&anchor.text_anchor)),
anchor: Some(serialize_text_anchor(&anchor.text_anchor)),
}
}
fn deserialize_excerpt_range(excerpt: proto::Excerpt) -> Option<ExcerptRange<language::Anchor>> {
let context = {
let start = language::proto::deserialize_anchor(excerpt.context_start?)?;
let end = language::proto::deserialize_anchor(excerpt.context_end?)?;
start..end
};
let primary = excerpt
.primary_start
.zip(excerpt.primary_end)
.and_then(|(start, end)| {
let start = language::proto::deserialize_anchor(start)?;
let end = language::proto::deserialize_anchor(end)?;
Some(start..end)
});
Some(ExcerptRange { context, primary })
}
fn deserialize_selection(
buffer: &MultiBufferSnapshot,
selection: proto::Selection,

View File

@ -50,6 +50,26 @@ pub struct MultiBuffer {
title: Option<String>,
}
#[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,
}
#[derive(Clone)]
struct History {
next_transaction_id: TransactionId,
@ -1650,26 +1670,6 @@ 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 = Event;
}
@ -2517,6 +2517,14 @@ impl MultiBufferSnapshot {
}
}
pub fn excerpts(
&self,
) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
self.excerpts
.iter()
.map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
}
pub fn excerpt_boundaries_in_range<R, T>(
&self,
range: R,

View File

@ -847,10 +847,12 @@ message UpdateView {
}
message Editor {
repeated Selection selections = 1;
EditorAnchor scroll_top_anchor = 2;
float scroll_x = 3;
float scroll_y = 4;
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;
}
}
@ -863,11 +865,13 @@ message View {
}
message Editor {
uint64 buffer_id = 1;
repeated Selection selections = 2;
EditorAnchor scroll_top_anchor = 3;
float scroll_x = 4;
float scroll_y = 5;
bool singleton = 1;
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;
}
}
@ -939,6 +943,20 @@ enum CursorShape {
CursorHollow = 3;
}
message ExcerptInsertion {
Excerpt excerpt = 1;
optional uint64 previous_excerpt_id = 2;
}
message Excerpt {
uint64 id = 1;
uint64 buffer_id = 2;
Anchor context_start = 3;
Anchor context_end = 4;
Anchor primary_start = 5;
Anchor primary_end = 6;
}
message Anchor {
uint32 replica_id = 1;
uint32 local_timestamp = 2;

View File

@ -388,7 +388,7 @@ impl ProjectSearchView {
});
// Subcribe to query_editor in order to reraise editor events for workspace item activation purposes
cx.subscribe(&query_editor, |_, _, event, cx| {
cx.emit(ViewEvent::EditorEvent(*event))
cx.emit(ViewEvent::EditorEvent(event.clone()))
})
.detach();
@ -405,7 +405,7 @@ impl ProjectSearchView {
this.update_match_index(cx);
}
// Reraise editor events for workspace item activation purposes
cx.emit(ViewEvent::EditorEvent(*event));
cx.emit(ViewEvent::EditorEvent(event.clone()));
})
.detach();

View File

@ -1496,6 +1496,10 @@ impl BufferSnapshot {
&self.visible_text
}
pub fn remote_id(&self) -> u64 {
self.remote_id
}
pub fn replica_id(&self) -> ReplicaId {
self.replica_id
}