mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 02:17:35 +03:00
Start work on following in multi-buffers
This commit is contained in:
parent
82abf31ef1
commit
6120d6488b
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user