Move protobuf logic from buffer crate to language crate
This will enable us to add operations that only pertain to the language crate. Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
78d97a3db2
commit
40c861c249
7 changed files with 322 additions and 338 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -761,7 +761,6 @@ dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"rpc",
|
|
||||||
"seahash",
|
"seahash",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
|
|
|
@ -8,7 +8,6 @@ test-support = ["rand", "seahash"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
rpc = { path = "../rpc" }
|
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
arrayvec = "0.7.1"
|
arrayvec = "0.7.1"
|
||||||
|
|
|
@ -19,11 +19,9 @@ pub use point_utf16::*;
|
||||||
pub use random_char_iter::*;
|
pub use random_char_iter::*;
|
||||||
use rope::TextDimension;
|
use rope::TextDimension;
|
||||||
pub use rope::{Chunks, Rope, TextSummary};
|
pub use rope::{Chunks, Rope, TextSummary};
|
||||||
use rpc::proto;
|
|
||||||
pub use selection::*;
|
pub use selection::*;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Reverse},
|
cmp::{self, Reverse},
|
||||||
convert::TryFrom,
|
|
||||||
iter::Iterator,
|
iter::Iterator,
|
||||||
ops::{self, Range},
|
ops::{self, Range},
|
||||||
str,
|
str,
|
||||||
|
@ -35,7 +33,7 @@ use sum_tree::{FilterCursor, SumTree};
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct DeterministicState;
|
pub struct DeterministicState;
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl std::hash::BuildHasher for DeterministicState {
|
impl std::hash::BuildHasher for DeterministicState {
|
||||||
|
@ -344,10 +342,10 @@ impl<D1, D2> Edit<(D1, D2)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
struct InsertionTimestamp {
|
pub struct InsertionTimestamp {
|
||||||
replica_id: ReplicaId,
|
pub replica_id: ReplicaId,
|
||||||
local: clock::Seq,
|
pub local: clock::Seq,
|
||||||
lamport: clock::Seq,
|
pub lamport: clock::Seq,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InsertionTimestamp {
|
impl InsertionTimestamp {
|
||||||
|
@ -422,18 +420,18 @@ pub enum Operation {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct EditOperation {
|
pub struct EditOperation {
|
||||||
timestamp: InsertionTimestamp,
|
pub timestamp: InsertionTimestamp,
|
||||||
version: clock::Global,
|
pub version: clock::Global,
|
||||||
ranges: Vec<Range<FullOffset>>,
|
pub ranges: Vec<Range<FullOffset>>,
|
||||||
new_text: Option<String>,
|
pub new_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct UndoOperation {
|
pub struct UndoOperation {
|
||||||
id: clock::Local,
|
pub id: clock::Local,
|
||||||
counts: HashMap<clock::Local, u32>,
|
pub counts: HashMap<clock::Local, u32>,
|
||||||
ranges: Vec<Range<FullOffset>>,
|
pub ranges: Vec<Range<FullOffset>>,
|
||||||
version: clock::Global,
|
pub version: clock::Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
|
@ -472,34 +470,6 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_proto(replica_id: u16, message: proto::Buffer) -> Result<Self> {
|
|
||||||
let mut buffer = Buffer::new(replica_id, message.id, History::new(message.content.into()));
|
|
||||||
let ops = message
|
|
||||||
.history
|
|
||||||
.into_iter()
|
|
||||||
.map(|op| Operation::Edit(op.into()));
|
|
||||||
buffer.apply_ops(ops)?;
|
|
||||||
buffer.selections = message
|
|
||||||
.selections
|
|
||||||
.into_iter()
|
|
||||||
.map(|set| {
|
|
||||||
let set = SelectionSet::try_from(set)?;
|
|
||||||
Result::<_, anyhow::Error>::Ok((set.id, set))
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
Ok(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_proto(&self) -> proto::Buffer {
|
|
||||||
let ops = self.history.ops.values().map(Into::into).collect();
|
|
||||||
proto::Buffer {
|
|
||||||
id: self.remote_id,
|
|
||||||
content: self.history.base_text.to_string(),
|
|
||||||
history: ops,
|
|
||||||
selections: self.selections.iter().map(|(_, set)| set.into()).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version(&self) -> clock::Global {
|
pub fn version(&self) -> clock::Global {
|
||||||
self.version.clone()
|
self.version.clone()
|
||||||
}
|
}
|
||||||
|
@ -1203,6 +1173,14 @@ impl Buffer {
|
||||||
.retain(|set_id, _| set_id.replica_id != replica_id)
|
.retain(|set_id, _| set_id.replica_id != replica_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn base_text(&self) -> &Arc<str> {
|
||||||
|
&self.history.base_text
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn history(&self) -> impl Iterator<Item = &EditOperation> {
|
||||||
|
self.history.ops.values()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self) -> Vec<Operation> {
|
pub fn undo(&mut self) -> Vec<Operation> {
|
||||||
let mut ops = Vec::new();
|
let mut ops = Vec::new();
|
||||||
if let Some(transaction) = self.history.pop_undo().cloned() {
|
if let Some(transaction) = self.history.pop_undo().cloned() {
|
||||||
|
@ -1331,6 +1309,10 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_raw_selection_set(&mut self, id: SelectionSetId, selections: SelectionSet) {
|
||||||
|
self.selections.insert(id, selections);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_active_selection_set(
|
pub fn set_active_selection_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_id: Option<SelectionSetId>,
|
set_id: Option<SelectionSetId>,
|
||||||
|
@ -2157,18 +2139,10 @@ impl Default for FragmentSummary {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct FullOffset(usize);
|
pub struct FullOffset(pub usize);
|
||||||
|
|
||||||
impl FullOffset {
|
impl FullOffset {
|
||||||
const MAX: Self = FullOffset(usize::MAX);
|
const MAX: Self = FullOffset(usize::MAX);
|
||||||
|
|
||||||
fn to_proto(self) -> u64 {
|
|
||||||
self.0 as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_proto(value: u64) -> Self {
|
|
||||||
Self(value as usize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::AddAssign<usize> for FullOffset {
|
impl ops::AddAssign<usize> for FullOffset {
|
||||||
|
@ -2298,227 +2272,6 @@ impl Operation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<proto::Operation> for &'a Operation {
|
|
||||||
fn into(self) -> proto::Operation {
|
|
||||||
proto::Operation {
|
|
||||||
variant: Some(match self {
|
|
||||||
Operation::Edit(edit) => proto::operation::Variant::Edit(edit.into()),
|
|
||||||
Operation::Undo {
|
|
||||||
undo,
|
|
||||||
lamport_timestamp,
|
|
||||||
} => proto::operation::Variant::Undo(proto::operation::Undo {
|
|
||||||
replica_id: undo.id.replica_id as u32,
|
|
||||||
local_timestamp: undo.id.value,
|
|
||||||
lamport_timestamp: lamport_timestamp.value,
|
|
||||||
ranges: undo
|
|
||||||
.ranges
|
|
||||||
.iter()
|
|
||||||
.map(|r| proto::Range {
|
|
||||||
start: r.start.to_proto(),
|
|
||||||
end: r.end.to_proto(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
counts: undo
|
|
||||||
.counts
|
|
||||||
.iter()
|
|
||||||
.map(|(edit_id, count)| proto::operation::UndoCount {
|
|
||||||
replica_id: edit_id.replica_id as u32,
|
|
||||||
local_timestamp: edit_id.value,
|
|
||||||
count: *count,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
version: From::from(&undo.version),
|
|
||||||
}),
|
|
||||||
Operation::UpdateSelections {
|
|
||||||
set_id,
|
|
||||||
selections,
|
|
||||||
lamport_timestamp,
|
|
||||||
} => proto::operation::Variant::UpdateSelections(
|
|
||||||
proto::operation::UpdateSelections {
|
|
||||||
replica_id: set_id.replica_id as u32,
|
|
||||||
local_timestamp: set_id.value,
|
|
||||||
lamport_timestamp: lamport_timestamp.value,
|
|
||||||
version: selections.version().into(),
|
|
||||||
selections: selections
|
|
||||||
.raw_entries()
|
|
||||||
.iter()
|
|
||||||
.map(|(range, state)| proto::Selection {
|
|
||||||
id: state.id as u64,
|
|
||||||
start: range.start.0.to_proto(),
|
|
||||||
end: range.end.0.to_proto(),
|
|
||||||
reversed: state.reversed,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Operation::RemoveSelections {
|
|
||||||
set_id,
|
|
||||||
lamport_timestamp,
|
|
||||||
} => proto::operation::Variant::RemoveSelections(
|
|
||||||
proto::operation::RemoveSelections {
|
|
||||||
replica_id: set_id.replica_id as u32,
|
|
||||||
local_timestamp: set_id.value,
|
|
||||||
lamport_timestamp: lamport_timestamp.value,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Operation::SetActiveSelections {
|
|
||||||
set_id,
|
|
||||||
lamport_timestamp,
|
|
||||||
} => proto::operation::Variant::SetActiveSelections(
|
|
||||||
proto::operation::SetActiveSelections {
|
|
||||||
replica_id: lamport_timestamp.replica_id as u32,
|
|
||||||
local_timestamp: set_id.map(|set_id| set_id.value),
|
|
||||||
lamport_timestamp: lamport_timestamp.value,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
#[cfg(test)]
|
|
||||||
Operation::Test(_) => unimplemented!(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Into<proto::operation::Edit> for &'a EditOperation {
|
|
||||||
fn into(self) -> proto::operation::Edit {
|
|
||||||
let ranges = self
|
|
||||||
.ranges
|
|
||||||
.iter()
|
|
||||||
.map(|range| proto::Range {
|
|
||||||
start: range.start.to_proto(),
|
|
||||||
end: range.end.to_proto(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
proto::operation::Edit {
|
|
||||||
replica_id: self.timestamp.replica_id as u32,
|
|
||||||
local_timestamp: self.timestamp.local,
|
|
||||||
lamport_timestamp: self.timestamp.lamport,
|
|
||||||
version: From::from(&self.version),
|
|
||||||
ranges,
|
|
||||||
new_text: self.new_text.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<proto::Operation> for Operation {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(message: proto::Operation) -> Result<Self, Self::Error> {
|
|
||||||
Ok(
|
|
||||||
match message
|
|
||||||
.variant
|
|
||||||
.ok_or_else(|| anyhow!("missing operation variant"))?
|
|
||||||
{
|
|
||||||
proto::operation::Variant::Edit(edit) => Operation::Edit(edit.into()),
|
|
||||||
proto::operation::Variant::Undo(undo) => Operation::Undo {
|
|
||||||
lamport_timestamp: clock::Lamport {
|
|
||||||
replica_id: undo.replica_id as ReplicaId,
|
|
||||||
value: undo.lamport_timestamp,
|
|
||||||
},
|
|
||||||
undo: UndoOperation {
|
|
||||||
id: clock::Local {
|
|
||||||
replica_id: undo.replica_id as ReplicaId,
|
|
||||||
value: undo.local_timestamp,
|
|
||||||
},
|
|
||||||
counts: undo
|
|
||||||
.counts
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| {
|
|
||||||
(
|
|
||||||
clock::Local {
|
|
||||||
replica_id: c.replica_id as ReplicaId,
|
|
||||||
value: c.local_timestamp,
|
|
||||||
},
|
|
||||||
c.count,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
ranges: undo
|
|
||||||
.ranges
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| FullOffset::from_proto(r.start)..FullOffset::from_proto(r.end))
|
|
||||||
.collect(),
|
|
||||||
version: undo.version.into(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
proto::operation::Variant::UpdateSelections(message) => {
|
|
||||||
let version = message.version.into();
|
|
||||||
let entries = message
|
|
||||||
.selections
|
|
||||||
.iter()
|
|
||||||
.map(|selection| {
|
|
||||||
let range = (FullOffset::from_proto(selection.start), Bias::Left)
|
|
||||||
..(FullOffset::from_proto(selection.end), Bias::Right);
|
|
||||||
let state = SelectionState {
|
|
||||||
id: selection.id as usize,
|
|
||||||
reversed: selection.reversed,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
};
|
|
||||||
(range, state)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let selections = AnchorRangeMap::from_raw(version, entries);
|
|
||||||
|
|
||||||
Operation::UpdateSelections {
|
|
||||||
set_id: clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value: message.local_timestamp,
|
|
||||||
},
|
|
||||||
lamport_timestamp: clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value: message.lamport_timestamp,
|
|
||||||
},
|
|
||||||
selections: Arc::from(selections),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proto::operation::Variant::RemoveSelections(message) => {
|
|
||||||
Operation::RemoveSelections {
|
|
||||||
set_id: clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value: message.local_timestamp,
|
|
||||||
},
|
|
||||||
lamport_timestamp: clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value: message.lamport_timestamp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proto::operation::Variant::SetActiveSelections(message) => {
|
|
||||||
Operation::SetActiveSelections {
|
|
||||||
set_id: message.local_timestamp.map(|value| clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value,
|
|
||||||
}),
|
|
||||||
lamport_timestamp: clock::Lamport {
|
|
||||||
replica_id: message.replica_id as ReplicaId,
|
|
||||||
value: message.lamport_timestamp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<proto::operation::Edit> for EditOperation {
|
|
||||||
fn from(edit: proto::operation::Edit) -> Self {
|
|
||||||
let ranges = edit
|
|
||||||
.ranges
|
|
||||||
.into_iter()
|
|
||||||
.map(|range| FullOffset::from_proto(range.start)..FullOffset::from_proto(range.end))
|
|
||||||
.collect();
|
|
||||||
EditOperation {
|
|
||||||
timestamp: InsertionTimestamp {
|
|
||||||
replica_id: edit.replica_id as ReplicaId,
|
|
||||||
local: edit.local_timestamp,
|
|
||||||
lamport: edit.lamport_timestamp,
|
|
||||||
},
|
|
||||||
version: edit.version.into(),
|
|
||||||
ranges,
|
|
||||||
new_text: edit.new_text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ToOffset {
|
pub trait ToOffset {
|
||||||
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use super::{AnchorRangeMap, Buffer, Content, FullOffset, Point, ToOffset, ToPoint};
|
use super::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint};
|
||||||
use rpc::proto;
|
|
||||||
use std::{cmp::Ordering, ops::Range, sync::Arc};
|
use std::{cmp::Ordering, ops::Range, sync::Arc};
|
||||||
use sum_tree::Bias;
|
|
||||||
|
|
||||||
pub type SelectionSetId = clock::Lamport;
|
pub type SelectionSetId = clock::Lamport;
|
||||||
pub type SelectionsVersion = usize;
|
pub type SelectionsVersion = usize;
|
||||||
|
@ -129,53 +127,3 @@ impl SelectionSet {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<proto::SelectionSet> for &'a SelectionSet {
|
|
||||||
fn into(self) -> proto::SelectionSet {
|
|
||||||
let version = self.selections.version();
|
|
||||||
let entries = self.selections.raw_entries();
|
|
||||||
proto::SelectionSet {
|
|
||||||
replica_id: self.id.replica_id as u32,
|
|
||||||
lamport_timestamp: self.id.value as u32,
|
|
||||||
is_active: self.active,
|
|
||||||
version: version.into(),
|
|
||||||
selections: entries
|
|
||||||
.iter()
|
|
||||||
.map(|(range, state)| proto::Selection {
|
|
||||||
id: state.id as u64,
|
|
||||||
start: range.start.0.to_proto(),
|
|
||||||
end: range.end.0.to_proto(),
|
|
||||||
reversed: state.reversed,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<proto::SelectionSet> for SelectionSet {
|
|
||||||
fn from(set: proto::SelectionSet) -> Self {
|
|
||||||
Self {
|
|
||||||
id: clock::Lamport {
|
|
||||||
replica_id: set.replica_id as u16,
|
|
||||||
value: set.lamport_timestamp,
|
|
||||||
},
|
|
||||||
active: set.is_active,
|
|
||||||
selections: Arc::new(AnchorRangeMap::from_raw(
|
|
||||||
set.version.into(),
|
|
||||||
set.selections
|
|
||||||
.into_iter()
|
|
||||||
.map(|selection| {
|
|
||||||
let range = (FullOffset::from_proto(selection.start), Bias::Left)
|
|
||||||
..(FullOffset::from_proto(selection.end), Bias::Right);
|
|
||||||
let state = SelectionState {
|
|
||||||
id: selection.id as usize,
|
|
||||||
reversed: selection.reversed,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
};
|
|
||||||
(range, state)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod highlight_map;
|
mod highlight_map;
|
||||||
mod language;
|
mod language;
|
||||||
|
pub mod proto;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
@ -16,7 +17,6 @@ use lazy_static::lazy_static;
|
||||||
use lsp::LanguageServer;
|
use lsp::LanguageServer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::{prelude::Stream, sink::Sink, watch};
|
use postage::{prelude::Stream, sink::Sink, watch};
|
||||||
use rpc::proto;
|
|
||||||
use similar::{ChangeTag, TextDiff};
|
use similar::{ChangeTag, TextDiff};
|
||||||
use smol::future::yield_now;
|
use smol::future::yield_now;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -251,10 +251,34 @@ impl Buffer {
|
||||||
message: proto::Buffer,
|
message: proto::Buffer,
|
||||||
file: Option<Box<dyn File>>,
|
file: Option<Box<dyn File>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self::build(
|
let mut buffer =
|
||||||
TextBuffer::from_proto(replica_id, message)?,
|
buffer::Buffer::new(replica_id, message.id, History::new(message.content.into()));
|
||||||
file,
|
let ops = message
|
||||||
))
|
.history
|
||||||
|
.into_iter()
|
||||||
|
.map(|op| Operation::Edit(proto::deserialize_edit_operation(op)));
|
||||||
|
buffer.apply_ops(ops)?;
|
||||||
|
for set in message.selections {
|
||||||
|
let set = proto::deserialize_selection_set(set);
|
||||||
|
buffer.add_raw_selection_set(set.id, set);
|
||||||
|
}
|
||||||
|
Ok(Self::build(buffer, file))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_proto(&self) -> proto::Buffer {
|
||||||
|
proto::Buffer {
|
||||||
|
id: self.remote_id(),
|
||||||
|
content: self.text.base_text().to_string(),
|
||||||
|
history: self
|
||||||
|
.text
|
||||||
|
.history()
|
||||||
|
.map(proto::serialize_edit_operation)
|
||||||
|
.collect(),
|
||||||
|
selections: self
|
||||||
|
.selection_sets()
|
||||||
|
.map(|(_, set)| proto::serialize_selection_set(set))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_language(
|
pub fn with_language(
|
||||||
|
@ -319,7 +343,7 @@ impl Buffer {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or_else(|| anyhow!("buffer has no file"))?;
|
.ok_or_else(|| anyhow!("buffer has no file"))?;
|
||||||
let text = self.as_rope().clone();
|
let text = self.as_rope().clone();
|
||||||
let version = self.version.clone();
|
let version = self.version();
|
||||||
let save = file.save(self.remote_id(), text, version, cx.as_mut());
|
let save = file.save(self.remote_id(), text, version, cx.as_mut());
|
||||||
Ok(cx.spawn(|this, mut cx| async move {
|
Ok(cx.spawn(|this, mut cx| async move {
|
||||||
let (version, mtime) = save.await?;
|
let (version, mtime) = save.await?;
|
||||||
|
@ -494,7 +518,7 @@ impl Buffer {
|
||||||
.await;
|
.await;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
if this.apply_diff(diff, cx) {
|
if this.apply_diff(diff, cx) {
|
||||||
this.saved_version = this.version.clone();
|
this.saved_version = this.version();
|
||||||
this.saved_mtime = new_mtime;
|
this.saved_mtime = new_mtime;
|
||||||
cx.emit(Event::Reloaded);
|
cx.emit(Event::Reloaded);
|
||||||
}
|
}
|
||||||
|
|
261
crates/language/src/proto.rs
Normal file
261
crates/language/src/proto.rs
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use buffer::*;
|
||||||
|
use clock::ReplicaId;
|
||||||
|
use rpc::proto;
|
||||||
|
|
||||||
|
pub use proto::Buffer;
|
||||||
|
|
||||||
|
pub fn serialize_operation(operation: &Operation) -> proto::Operation {
|
||||||
|
proto::Operation {
|
||||||
|
variant: Some(match operation {
|
||||||
|
Operation::Edit(edit) => {
|
||||||
|
proto::operation::Variant::Edit(serialize_edit_operation(edit))
|
||||||
|
}
|
||||||
|
Operation::Undo {
|
||||||
|
undo,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => proto::operation::Variant::Undo(proto::operation::Undo {
|
||||||
|
replica_id: undo.id.replica_id as u32,
|
||||||
|
local_timestamp: undo.id.value,
|
||||||
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
|
ranges: undo
|
||||||
|
.ranges
|
||||||
|
.iter()
|
||||||
|
.map(|r| proto::Range {
|
||||||
|
start: r.start.0 as u64,
|
||||||
|
end: r.end.0 as u64,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
counts: undo
|
||||||
|
.counts
|
||||||
|
.iter()
|
||||||
|
.map(|(edit_id, count)| proto::operation::UndoCount {
|
||||||
|
replica_id: edit_id.replica_id as u32,
|
||||||
|
local_timestamp: edit_id.value,
|
||||||
|
count: *count,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
version: From::from(&undo.version),
|
||||||
|
}),
|
||||||
|
Operation::UpdateSelections {
|
||||||
|
set_id,
|
||||||
|
selections,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
|
||||||
|
replica_id: set_id.replica_id as u32,
|
||||||
|
local_timestamp: set_id.value,
|
||||||
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
|
version: selections.version().into(),
|
||||||
|
selections: selections
|
||||||
|
.raw_entries()
|
||||||
|
.iter()
|
||||||
|
.map(|(range, state)| proto::Selection {
|
||||||
|
id: state.id as u64,
|
||||||
|
start: range.start.0 .0 as u64,
|
||||||
|
end: range.end.0 .0 as u64,
|
||||||
|
reversed: state.reversed,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Operation::RemoveSelections {
|
||||||
|
set_id,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => proto::operation::Variant::RemoveSelections(proto::operation::RemoveSelections {
|
||||||
|
replica_id: set_id.replica_id as u32,
|
||||||
|
local_timestamp: set_id.value,
|
||||||
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
|
}),
|
||||||
|
Operation::SetActiveSelections {
|
||||||
|
set_id,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => proto::operation::Variant::SetActiveSelections(
|
||||||
|
proto::operation::SetActiveSelections {
|
||||||
|
replica_id: lamport_timestamp.replica_id as u32,
|
||||||
|
local_timestamp: set_id.map(|set_id| set_id.value),
|
||||||
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
|
||||||
|
let ranges = operation
|
||||||
|
.ranges
|
||||||
|
.iter()
|
||||||
|
.map(|range| proto::Range {
|
||||||
|
start: range.start.0 as u64,
|
||||||
|
end: range.end.0 as u64,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
proto::operation::Edit {
|
||||||
|
replica_id: operation.timestamp.replica_id as u32,
|
||||||
|
local_timestamp: operation.timestamp.local,
|
||||||
|
lamport_timestamp: operation.timestamp.lamport,
|
||||||
|
version: From::from(&operation.version),
|
||||||
|
ranges,
|
||||||
|
new_text: operation.new_text.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_selection_set(set: &SelectionSet) -> proto::SelectionSet {
|
||||||
|
let version = set.selections.version();
|
||||||
|
let entries = set.selections.raw_entries();
|
||||||
|
proto::SelectionSet {
|
||||||
|
replica_id: set.id.replica_id as u32,
|
||||||
|
lamport_timestamp: set.id.value as u32,
|
||||||
|
is_active: set.active,
|
||||||
|
version: version.into(),
|
||||||
|
selections: entries
|
||||||
|
.iter()
|
||||||
|
.map(|(range, state)| proto::Selection {
|
||||||
|
id: state.id as u64,
|
||||||
|
start: range.start.0 .0 as u64,
|
||||||
|
end: range.end.0 .0 as u64,
|
||||||
|
reversed: state.reversed,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_operation(message: proto::Operation) -> Result<Operation> {
|
||||||
|
Ok(
|
||||||
|
match message
|
||||||
|
.variant
|
||||||
|
.ok_or_else(|| anyhow!("missing operation variant"))?
|
||||||
|
{
|
||||||
|
proto::operation::Variant::Edit(edit) => {
|
||||||
|
Operation::Edit(deserialize_edit_operation(edit))
|
||||||
|
}
|
||||||
|
proto::operation::Variant::Undo(undo) => Operation::Undo {
|
||||||
|
lamport_timestamp: clock::Lamport {
|
||||||
|
replica_id: undo.replica_id as ReplicaId,
|
||||||
|
value: undo.lamport_timestamp,
|
||||||
|
},
|
||||||
|
undo: UndoOperation {
|
||||||
|
id: clock::Local {
|
||||||
|
replica_id: undo.replica_id as ReplicaId,
|
||||||
|
value: undo.local_timestamp,
|
||||||
|
},
|
||||||
|
counts: undo
|
||||||
|
.counts
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| {
|
||||||
|
(
|
||||||
|
clock::Local {
|
||||||
|
replica_id: c.replica_id as ReplicaId,
|
||||||
|
value: c.local_timestamp,
|
||||||
|
},
|
||||||
|
c.count,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
ranges: undo
|
||||||
|
.ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| FullOffset(r.start as usize)..FullOffset(r.end as usize))
|
||||||
|
.collect(),
|
||||||
|
version: undo.version.into(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
proto::operation::Variant::UpdateSelections(message) => {
|
||||||
|
let version = message.version.into();
|
||||||
|
let entries = message
|
||||||
|
.selections
|
||||||
|
.iter()
|
||||||
|
.map(|selection| {
|
||||||
|
let range = (FullOffset(selection.start as usize), Bias::Left)
|
||||||
|
..(FullOffset(selection.end as usize), Bias::Right);
|
||||||
|
let state = SelectionState {
|
||||||
|
id: selection.id as usize,
|
||||||
|
reversed: selection.reversed,
|
||||||
|
goal: SelectionGoal::None,
|
||||||
|
};
|
||||||
|
(range, state)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let selections = AnchorRangeMap::from_raw(version, entries);
|
||||||
|
|
||||||
|
Operation::UpdateSelections {
|
||||||
|
set_id: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.local_timestamp,
|
||||||
|
},
|
||||||
|
lamport_timestamp: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.lamport_timestamp,
|
||||||
|
},
|
||||||
|
selections: Arc::from(selections),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proto::operation::Variant::RemoveSelections(message) => Operation::RemoveSelections {
|
||||||
|
set_id: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.local_timestamp,
|
||||||
|
},
|
||||||
|
lamport_timestamp: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.lamport_timestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
proto::operation::Variant::SetActiveSelections(message) => {
|
||||||
|
Operation::SetActiveSelections {
|
||||||
|
set_id: message.local_timestamp.map(|value| clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
lamport_timestamp: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.lamport_timestamp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
|
||||||
|
let ranges = edit
|
||||||
|
.ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|range| FullOffset(range.start as usize)..FullOffset(range.end as usize))
|
||||||
|
.collect();
|
||||||
|
EditOperation {
|
||||||
|
timestamp: InsertionTimestamp {
|
||||||
|
replica_id: edit.replica_id as ReplicaId,
|
||||||
|
local: edit.local_timestamp,
|
||||||
|
lamport: edit.lamport_timestamp,
|
||||||
|
},
|
||||||
|
version: edit.version.into(),
|
||||||
|
ranges,
|
||||||
|
new_text: edit.new_text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_selection_set(set: proto::SelectionSet) -> SelectionSet {
|
||||||
|
SelectionSet {
|
||||||
|
id: clock::Lamport {
|
||||||
|
replica_id: set.replica_id as u16,
|
||||||
|
value: set.lamport_timestamp,
|
||||||
|
},
|
||||||
|
active: set.is_active,
|
||||||
|
selections: Arc::new(AnchorRangeMap::from_raw(
|
||||||
|
set.version.into(),
|
||||||
|
set.selections
|
||||||
|
.into_iter()
|
||||||
|
.map(|selection| {
|
||||||
|
let range = (FullOffset(selection.start as usize), Bias::Left)
|
||||||
|
..(FullOffset(selection.end as usize), Bias::Right);
|
||||||
|
let state = SelectionState {
|
||||||
|
id: selection.id as usize,
|
||||||
|
reversed: selection.reversed,
|
||||||
|
goal: SelectionGoal::None,
|
||||||
|
};
|
||||||
|
(range, state)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
|
@ -430,8 +430,8 @@ impl Worktree {
|
||||||
let ops = payload
|
let ops = payload
|
||||||
.operations
|
.operations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|op| op.try_into())
|
.map(|op| language::proto::deserialize_operation(op))
|
||||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Worktree::Local(worktree) => {
|
Worktree::Local(worktree) => {
|
||||||
|
@ -1944,7 +1944,7 @@ impl language::File for File {
|
||||||
.request(proto::UpdateBuffer {
|
.request(proto::UpdateBuffer {
|
||||||
worktree_id: remote_id,
|
worktree_id: remote_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
operations: vec![(&operation).into()],
|
operations: vec![language::proto::serialize_operation(&operation)],
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue