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.set_vertical_scroll_margin(5, cx);
editor editor
}); });
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event)) cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()))
.detach(); .detach();
let project = project_handle.read(cx); let project = project_handle.read(cx);

View file

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

View file

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

View file

@ -1,14 +1,16 @@
use crate::{ use crate::{
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition, display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, MultiBuffer, movement::surrounding_word, Anchor, Autoscroll, Editor, Event, ExcerptId, ExcerptRange,
MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, FORMAT_TIMEOUT,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::HashSet;
use futures::FutureExt; use futures::FutureExt;
use gpui::{ use gpui::{
elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext, elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
RenderContext, Subscription, Task, View, ViewContext, ViewHandle, 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 language::{Bias, Buffer, File as _, OffsetRangeExt, Point, SelectionGoal};
use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath}; use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath};
use rpc::proto::{self, update_view}; use rpc::proto::{self, update_view};
@ -18,6 +20,7 @@ use std::{
borrow::Cow, borrow::Cow,
cmp::{self, Ordering}, cmp::{self, Ordering},
fmt::Write, fmt::Write,
iter,
ops::Range, ops::Range,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -48,22 +51,75 @@ impl FollowableItem for Editor {
return None; return None;
}; };
let buffer = project.update(cx, |project, cx| { let replica_id = project.read(cx).replica_id();
project.open_buffer_by_id(state.buffer_id, cx) 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 { Some(cx.spawn(|mut cx| async move {
let buffer = buffer.await?; let mut buffers = futures::future::try_join_all(buffers).await?;
let editor = pane let editor = pane.read_with(&cx, |pane, cx| {
.read_with(&cx, |pane, cx| { let mut editors = pane.items_of_type::<Self>();
pane.items_of_type::<Self>().find(|editor| { if state.singleton && buffers.len() == 1 {
editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffer) 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| { editor.update(&mut cx, |editor, cx| {
let buffer = editor.buffer.read(cx).read(cx); let buffer = editor.buffer.read(cx).read(cx);
let selections = state let selections = state
@ -90,8 +146,9 @@ impl FollowableItem for Editor {
); );
} }
Ok::<_, anyhow::Error>(()) anyhow::Ok(())
})?; })?;
Ok(editor) Ok(editor)
})) }))
} }
@ -122,9 +179,30 @@ impl FollowableItem for Editor {
} }
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> { 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 { 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_top_anchor: Some(serialize_anchor(&self.scroll_top_anchor)),
scroll_x: self.scroll_position.x(), scroll_x: self.scroll_position.x(),
scroll_y: self.scroll_position.y(), scroll_y: self.scroll_position.y(),
@ -141,13 +219,39 @@ impl FollowableItem for Editor {
&self, &self,
event: &Self::Event, event: &Self::Event,
update: &mut Option<proto::update_view::Variant>, update: &mut Option<proto::update_view::Variant>,
_: &AppContext, cx: &AppContext,
) -> bool { ) -> bool {
let update = let update =
update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default())); update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
match update { match update {
proto::update_view::Variant::Editor(update) => match event { 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 { .. } => { Event::ScrollPositionChanged { .. } => {
update.scroll_top_anchor = Some(serialize_anchor(&self.scroll_top_anchor)); update.scroll_top_anchor = Some(serialize_anchor(&self.scroll_top_anchor));
update.scroll_x = self.scroll_position.x(); 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 { fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
proto::Selection { proto::Selection {
id: selection.id as u64, id: selection.id as u64,
@ -225,10 +351,27 @@ fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor { fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor {
proto::EditorAnchor { proto::EditorAnchor {
excerpt_id: anchor.excerpt_id.to_proto(), 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( fn deserialize_selection(
buffer: &MultiBufferSnapshot, buffer: &MultiBufferSnapshot,
selection: proto::Selection, selection: proto::Selection,

View file

@ -50,6 +50,26 @@ pub struct MultiBuffer {
title: Option<String>, 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)] #[derive(Clone)]
struct History { struct History {
next_transaction_id: TransactionId, 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 { impl Entity for MultiBuffer {
type Event = Event; 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>( pub fn excerpt_boundaries_in_range<R, T>(
&self, &self,
range: R, range: R,

View file

@ -847,10 +847,12 @@ message UpdateView {
} }
message Editor { message Editor {
repeated Selection selections = 1; repeated ExcerptInsertion inserted_excerpts = 1;
EditorAnchor scroll_top_anchor = 2; repeated uint64 deleted_excerpts = 2;
float scroll_x = 3; repeated Selection selections = 3;
float scroll_y = 4; EditorAnchor scroll_top_anchor = 4;
float scroll_x = 5;
float scroll_y = 6;
} }
} }
@ -863,11 +865,13 @@ message View {
} }
message Editor { message Editor {
uint64 buffer_id = 1; bool singleton = 1;
repeated Selection selections = 2; optional string title = 2;
EditorAnchor scroll_top_anchor = 3; repeated Excerpt excerpts = 3;
float scroll_x = 4; repeated Selection selections = 4;
float scroll_y = 5; EditorAnchor scroll_top_anchor = 5;
float scroll_x = 6;
float scroll_y = 7;
} }
} }
@ -939,6 +943,20 @@ enum CursorShape {
CursorHollow = 3; 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 { message Anchor {
uint32 replica_id = 1; uint32 replica_id = 1;
uint32 local_timestamp = 2; 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 // Subcribe to query_editor in order to reraise editor events for workspace item activation purposes
cx.subscribe(&query_editor, |_, _, event, cx| { cx.subscribe(&query_editor, |_, _, event, cx| {
cx.emit(ViewEvent::EditorEvent(*event)) cx.emit(ViewEvent::EditorEvent(event.clone()))
}) })
.detach(); .detach();
@ -405,7 +405,7 @@ impl ProjectSearchView {
this.update_match_index(cx); this.update_match_index(cx);
} }
// Reraise editor events for workspace item activation purposes // Reraise editor events for workspace item activation purposes
cx.emit(ViewEvent::EditorEvent(*event)); cx.emit(ViewEvent::EditorEvent(event.clone()));
}) })
.detach(); .detach();

View file

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