diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index d7c1526cd486fd908da1d1c28d393bd8916edae4..4ee9526a6797a2d84bfe5776a72a940b9f71466e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -3304,7 +3304,10 @@ mod tests { fn test_remote_multibuffer(cx: &mut MutableAppContext) { let host_buffer = cx.add_model(|cx| Buffer::new(0, "a", cx)); let guest_buffer = cx.add_model(|cx| { - let (state, ops) = host_buffer.read(cx).to_proto(); + let state = host_buffer.read(cx).to_proto(); + let ops = cx + .background() + .block(host_buffer.read(cx).serialize_ops(cx)); let mut buffer = Buffer::from_proto(1, state, None).unwrap(); buffer .apply_ops( diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 99efcc55b0ac609982630fce314892403dfd2b72..1deb7f15c9df068f584b8004281d92243a7c13dd 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -371,40 +371,46 @@ impl Buffer { Ok(this) } - pub fn to_proto(&self) -> (proto::BufferState, Vec) { - let mut operations = self - .text - .history() - .map(|op| proto::serialize_operation(&Operation::Buffer(op.clone()))) - .chain(self.deferred_ops.iter().map(proto::serialize_operation)) - .chain(self.remote_selections.iter().map(|(_, set)| { - proto::serialize_operation(&Operation::UpdateSelections { - selections: set.selections.clone(), - lamport_timestamp: set.lamport_timestamp, - line_mode: set.line_mode, - }) - })) - .chain(Some(proto::serialize_operation( - &Operation::UpdateDiagnostics { - diagnostics: self.diagnostics.iter().cloned().collect(), - lamport_timestamp: self.diagnostics_timestamp, - }, - ))) - .chain(Some(proto::serialize_operation( - &Operation::UpdateCompletionTriggers { - triggers: self.completion_triggers.clone(), - lamport_timestamp: self.completion_triggers_timestamp, - }, - ))) - .collect::>(); - operations.sort_unstable_by_key(proto::lamport_timestamp_for_operation); - let state = proto::BufferState { + pub fn to_proto(&self) -> proto::BufferState { + proto::BufferState { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), base_text: self.base_text().to_string(), line_ending: proto::serialize_line_ending(self.line_ending()) as i32, - }; - (state, operations) + } + } + + pub fn serialize_ops(&self, cx: &AppContext) -> Task> { + let mut operations = Vec::new(); + operations.extend(self.deferred_ops.iter().map(proto::serialize_operation)); + operations.extend(self.remote_selections.iter().map(|(_, set)| { + proto::serialize_operation(&Operation::UpdateSelections { + selections: set.selections.clone(), + lamport_timestamp: set.lamport_timestamp, + line_mode: set.line_mode, + }) + })); + operations.push(proto::serialize_operation(&Operation::UpdateDiagnostics { + diagnostics: self.diagnostics.iter().cloned().collect(), + lamport_timestamp: self.diagnostics_timestamp, + })); + operations.push(proto::serialize_operation( + &Operation::UpdateCompletionTriggers { + triggers: self.completion_triggers.clone(), + lamport_timestamp: self.completion_triggers_timestamp, + }, + )); + + let text_operations = self.text.operations().clone(); + cx.background().spawn(async move { + operations.extend( + text_operations + .iter() + .map(|(_, op)| proto::serialize_operation(&Operation::Buffer(op.clone()))), + ); + operations.sort_unstable_by_key(proto::lamport_timestamp_for_operation); + operations + }) } pub fn with_language(mut self, language: Arc, cx: &mut ModelContext) -> Self { diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index e6686d17610370c47c90a0bcb06671a6ffa025b7..9c3d1a4509fdf44d4620b74b7d8bb3fe3f37f48c 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -1048,7 +1048,8 @@ fn test_serialization(cx: &mut gpui::MutableAppContext) { }); assert_eq!(buffer1.read(cx).text(), "abcDF"); - let (state, ops) = buffer1.read(cx).to_proto(); + let state = buffer1.read(cx).to_proto(); + let ops = cx.background().block(buffer1.read(cx).serialize_ops(cx)); let buffer2 = cx.add_model(|cx| { let mut buffer = Buffer::from_proto(1, state, None).unwrap(); buffer @@ -1086,7 +1087,10 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) { for i in 0..rng.gen_range(min_peers..=max_peers) { let buffer = cx.add_model(|cx| { - let (state, ops) = base_buffer.read(cx).to_proto(); + let state = base_buffer.read(cx).to_proto(); + let ops = cx + .background() + .block(base_buffer.read(cx).serialize_ops(cx)); let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap(); buffer .apply_ops( @@ -1181,7 +1185,8 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) { mutation_count -= 1; } 50..=59 if replica_ids.len() < max_peers => { - let (old_buffer_state, old_buffer_ops) = buffer.read(cx).to_proto(); + let old_buffer_state = buffer.read(cx).to_proto(); + let old_buffer_ops = cx.background().block(buffer.read(cx).serialize_ops(cx)); let new_replica_id = (0..=replica_ids.len() as ReplicaId) .filter(|replica_id| *replica_id != buffer.read(cx).replica_id()) .choose(&mut rng) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 89cbb868a088a72bf3262090429308acd2181a4c..eb68b1a43fc0f30e7a83b638af59ce4f0fa638c1 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5693,11 +5693,15 @@ impl Project { if let Some(project_id) = self.remote_id() { let shared_buffers = self.shared_buffers.entry(peer_id).or_default(); if shared_buffers.insert(buffer_id) { - let (state, mut operations) = buffer.read(cx).to_proto(); + let buffer = buffer.read(cx); + let state = buffer.to_proto(); + let operations = buffer.serialize_ops(cx); let client = self.client.clone(); cx.background() .spawn( async move { + let mut operations = operations.await; + client.send(proto::CreateBufferForPeer { project_id, peer_id: peer_id.0, diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 1f2e4e7c7a95321a28e2a0e46885b90923869b87..64b18ef81385b722b8aef2cb48f9e4fb402ce916 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -45,7 +45,7 @@ use std::{ }; pub use subscription::*; pub use sum_tree::Bias; -use sum_tree::{FilterCursor, SumTree}; +use sum_tree::{FilterCursor, SumTree, TreeMap}; lazy_static! { static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); @@ -109,7 +109,7 @@ impl HistoryEntry { struct History { // TODO: Turn this into a String or Rope, maybe. base_text: Arc, - operations: HashMap, + operations: TreeMap, insertion_slices: HashMap>, undo_stack: Vec, redo_stack: Vec, @@ -1213,8 +1213,8 @@ impl Buffer { &self.history.base_text } - pub fn history(&self) -> impl Iterator { - self.history.operations.values() + pub fn operations(&self) -> &TreeMap { + &self.history.operations } pub fn undo_history(&self) -> impl Iterator {