From 40c861c249dd4290a73a647f03d8902c64a890c2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 1 Nov 2021 14:05:19 -0700 Subject: [PATCH] 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 --- Cargo.lock | 1 - crates/buffer/Cargo.toml | 1 - crates/buffer/src/lib.rs | 299 +++------------------------------ crates/buffer/src/selection.rs | 54 +----- crates/language/src/lib.rs | 38 ++++- crates/language/src/proto.rs | 261 ++++++++++++++++++++++++++++ crates/project/src/worktree.rs | 6 +- 7 files changed, 322 insertions(+), 338 deletions(-) create mode 100644 crates/language/src/proto.rs diff --git a/Cargo.lock b/Cargo.lock index 0e01793175..b0a7d6988c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -761,7 +761,6 @@ dependencies = [ "gpui", "log", "rand 0.8.3", - "rpc", "seahash", "smallvec", "sum_tree", diff --git a/crates/buffer/Cargo.toml b/crates/buffer/Cargo.toml index f6d949c05f..3d2cc8eec0 100644 --- a/crates/buffer/Cargo.toml +++ b/crates/buffer/Cargo.toml @@ -8,7 +8,6 @@ test-support = ["rand", "seahash"] [dependencies] clock = { path = "../clock" } -rpc = { path = "../rpc" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" arrayvec = "0.7.1" diff --git a/crates/buffer/src/lib.rs b/crates/buffer/src/lib.rs index fad83e901a..bf97574d34 100644 --- a/crates/buffer/src/lib.rs +++ b/crates/buffer/src/lib.rs @@ -19,11 +19,9 @@ pub use point_utf16::*; pub use random_char_iter::*; use rope::TextDimension; pub use rope::{Chunks, Rope, TextSummary}; -use rpc::proto; pub use selection::*; use std::{ cmp::{self, Reverse}, - convert::TryFrom, iter::Iterator, ops::{self, Range}, str, @@ -35,7 +33,7 @@ use sum_tree::{FilterCursor, SumTree}; #[cfg(any(test, feature = "test-support"))] #[derive(Clone, Default)] -struct DeterministicState; +pub struct DeterministicState; #[cfg(any(test, feature = "test-support"))] impl std::hash::BuildHasher for DeterministicState { @@ -344,10 +342,10 @@ impl Edit<(D1, D2)> { } #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -struct InsertionTimestamp { - replica_id: ReplicaId, - local: clock::Seq, - lamport: clock::Seq, +pub struct InsertionTimestamp { + pub replica_id: ReplicaId, + pub local: clock::Seq, + pub lamport: clock::Seq, } impl InsertionTimestamp { @@ -422,18 +420,18 @@ pub enum Operation { #[derive(Clone, Debug, Eq, PartialEq)] pub struct EditOperation { - timestamp: InsertionTimestamp, - version: clock::Global, - ranges: Vec>, - new_text: Option, + pub timestamp: InsertionTimestamp, + pub version: clock::Global, + pub ranges: Vec>, + pub new_text: Option, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct UndoOperation { - id: clock::Local, - counts: HashMap, - ranges: Vec>, - version: clock::Global, + pub id: clock::Local, + pub counts: HashMap, + pub ranges: Vec>, + pub version: clock::Global, } impl Buffer { @@ -472,34 +470,6 @@ impl Buffer { } } - pub fn from_proto(replica_id: u16, message: proto::Buffer) -> Result { - 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::>()?; - 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 { self.version.clone() } @@ -1203,6 +1173,14 @@ impl Buffer { .retain(|set_id, _| set_id.replica_id != replica_id) } + pub fn base_text(&self) -> &Arc { + &self.history.base_text + } + + pub fn history(&self) -> impl Iterator { + self.history.ops.values() + } + pub fn undo(&mut self) -> Vec { let mut ops = Vec::new(); 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( &mut self, set_id: Option, @@ -2157,18 +2139,10 @@ impl Default for FragmentSummary { } #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FullOffset(usize); +pub struct FullOffset(pub usize); impl FullOffset { 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 for FullOffset { @@ -2298,227 +2272,6 @@ impl Operation { } } -impl<'a> Into 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 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 for Operation { - type Error = anyhow::Error; - - fn try_from(message: proto::Operation) -> Result { - 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 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 { fn to_offset<'a>(&self, content: impl Into>) -> usize; diff --git a/crates/buffer/src/selection.rs b/crates/buffer/src/selection.rs index 3dc84f6647..c55a5f423b 100644 --- a/crates/buffer/src/selection.rs +++ b/crates/buffer/src/selection.rs @@ -1,7 +1,5 @@ -use super::{AnchorRangeMap, Buffer, Content, FullOffset, Point, ToOffset, ToPoint}; -use rpc::proto; +use super::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint}; use std::{cmp::Ordering, ops::Range, sync::Arc}; -use sum_tree::Bias; pub type SelectionSetId = clock::Lamport; pub type SelectionsVersion = usize; @@ -129,53 +127,3 @@ impl SelectionSet { }) } } - -impl<'a> Into 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 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(), - )), - } - } -} diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 911f8ea1fd..726afd7617 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -1,5 +1,6 @@ mod highlight_map; mod language; +pub mod proto; #[cfg(test)] mod tests; @@ -16,7 +17,6 @@ use lazy_static::lazy_static; use lsp::LanguageServer; use parking_lot::Mutex; use postage::{prelude::Stream, sink::Sink, watch}; -use rpc::proto; use similar::{ChangeTag, TextDiff}; use smol::future::yield_now; use std::{ @@ -251,10 +251,34 @@ impl Buffer { message: proto::Buffer, file: Option>, ) -> Result { - Ok(Self::build( - TextBuffer::from_proto(replica_id, message)?, - file, - )) + let mut buffer = + buffer::Buffer::new(replica_id, message.id, History::new(message.content.into())); + 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( @@ -319,7 +343,7 @@ impl Buffer { .as_ref() .ok_or_else(|| anyhow!("buffer has no file"))?; 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()); Ok(cx.spawn(|this, mut cx| async move { let (version, mtime) = save.await?; @@ -494,7 +518,7 @@ impl Buffer { .await; this.update(&mut cx, |this, cx| { if this.apply_diff(diff, cx) { - this.saved_version = this.version.clone(); + this.saved_version = this.version(); this.saved_mtime = new_mtime; cx.emit(Event::Reloaded); } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs new file mode 100644 index 0000000000..769373b8c8 --- /dev/null +++ b/crates/language/src/proto.rs @@ -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 { + 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(), + )), + } +} diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 7c9273a8cf..166913b1c0 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -430,8 +430,8 @@ impl Worktree { let ops = payload .operations .into_iter() - .map(|op| op.try_into()) - .collect::>>()?; + .map(|op| language::proto::deserialize_operation(op)) + .collect::, _>>()?; match self { Worktree::Local(worktree) => { @@ -1944,7 +1944,7 @@ impl language::File for File { .request(proto::UpdateBuffer { worktree_id: remote_id, buffer_id, - operations: vec![(&operation).into()], + operations: vec![language::proto::serialize_operation(&operation)], }) .await {