From 4578938ea1a1aba1f4e339923ac3ec36b61ca27a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 6 Dec 2021 13:37:17 -0700 Subject: [PATCH] Implement ExcerptList::subscribe Co-Authored-By: Max Brunsfeld --- crates/language/src/buffer.rs | 1 + crates/language/src/excerpt_list.rs | 73 +++++++++++++++++++++-------- crates/text/src/patch.rs | 9 ++++ crates/text/src/subscription.rs | 48 +++++++++++++++++++ crates/text/src/text.rs | 42 ++++------------- 5 files changed, 119 insertions(+), 54 deletions(-) create mode 100644 crates/text/src/subscription.rs diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 55346fc9dd410c5617c2c1186ca839cc9c0ca32e..b8d1aad91b05a3ca76e478638e2e2257a6bfc9cb 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -28,6 +28,7 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, vec, }; +use text::subscription::Subscription; pub use text::{Buffer as TextBuffer, Operation as _, *}; use theme::SyntaxTheme; use tree_sitter::{InputEdit, Parser, QueryCursor, Tree}; diff --git a/crates/language/src/excerpt_list.rs b/crates/language/src/excerpt_list.rs index 59619f156e0416279126377a215c20f630759286..0cda89c5794f316246782ed21aea2c6898ae9c99 100644 --- a/crates/language/src/excerpt_list.rs +++ b/crates/language/src/excerpt_list.rs @@ -1,12 +1,14 @@ use crate::{buffer, Buffer, Chunk}; use collections::HashMap; use gpui::{AppContext, Entity, ModelContext, ModelHandle}; -use lsp::TextDocumentSaveReason; use parking_lot::Mutex; use smallvec::{smallvec, SmallVec}; -use std::{cmp, iter, mem, ops::Range}; +use std::{cmp, iter, ops::Range}; use sum_tree::{Bias, Cursor, SumTree}; -use text::{Anchor, AnchorRangeExt, Patch, TextSummary}; +use text::{ + subscription::{Subscription, Topic}, + Anchor, AnchorRangeExt, Edit, Patch, TextSummary, +}; use theme::SyntaxTheme; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; @@ -21,6 +23,7 @@ pub type ExcerptId = Location; pub struct ExcerptList { snapshot: Mutex, buffers: HashMap, + subscriptions: Topic, } #[derive(Debug)] @@ -77,6 +80,10 @@ impl ExcerptList { self.snapshot.lock().clone() } + pub fn subscribe(&mut self) -> Subscription { + self.subscriptions.subscribe() + } + pub fn push(&mut self, props: ExcerptProperties, cx: &mut ModelContext) -> ExcerptId where O: text::ToOffset, @@ -89,10 +96,13 @@ impl ExcerptList { let prev_id = snapshot.excerpts.last().map(|e| &e.id); let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max()); - snapshot.excerpts.push( - Excerpt::new(id.clone(), buffer.snapshot(), range, props.header_height), - &(), - ); + let edit_start = snapshot.excerpts.summary().text.bytes; + let excerpt = Excerpt::new(id.clone(), buffer.snapshot(), range, props.header_height); + let edit = Edit { + old: edit_start..edit_start, + new: edit_start..edit_start + excerpt.text_summary.bytes, + }; + snapshot.excerpts.push(excerpt, &()); self.buffers .entry(props.buffer.id()) .or_insert_with(|| BufferState { @@ -103,6 +113,8 @@ impl ExcerptList { .excerpts .push(id.clone()); + self.subscriptions.publish_mut([edit]); + id } @@ -126,8 +138,6 @@ impl ExcerptList { } excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _)| *excerpt_id); - dbg!(&excerpts_to_edit); - let mut patch = Patch::::default(); let mut new_excerpts = SumTree::new(); let mut cursor = snapshot.excerpts.cursor::<(ExcerptId, usize)>(); @@ -168,6 +178,8 @@ impl ExcerptList { drop(cursor); snapshot.excerpts = new_excerpts; + + self.subscriptions.publish(&patch); } } @@ -357,12 +369,11 @@ impl Location { #[cfg(test)] mod tests { - use std::env; - use super::*; use crate::Buffer; use gpui::MutableAppContext; use rand::prelude::*; + use std::{env, mem}; use text::{Point, RandomCharIter}; use util::test::sample_text; @@ -371,12 +382,10 @@ mod tests { let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx)); let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx)); - let list = cx.add_model(|cx| { - let mut list = ExcerptList::new(); - // aaaaaa - // bbbbbb - // cccccc - // dddddd + let list = cx.add_model(|cx| ExcerptList::new()); + + let subscription = list.update(cx, |list, cx| { + let subscription = list.subscribe(); list.push( ExcerptProperties { buffer: &buffer_1, @@ -385,6 +394,14 @@ mod tests { }, cx, ); + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 0..0, + new: 0..12 + }] + ); + list.push( ExcerptProperties { buffer: &buffer_1, @@ -401,7 +418,15 @@ mod tests { }, cx, ); - list + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 12..12, + new: 12..26 + }] + ); + + subscription }); assert_eq!( @@ -425,7 +450,7 @@ mod tests { buffer.edit( [ Point::new(0, 0)..Point::new(0, 0), - Point::new(2, 1)..Point::new(2, 2), + Point::new(2, 1)..Point::new(2, 3), ], "\n", cx, @@ -439,7 +464,7 @@ mod tests { "\n", // "bbbb\n", // "c\n", // - "ccc\n", // + "cc\n", // "\n", // "ddd\n", // "eeee\n", // @@ -449,6 +474,14 @@ mod tests { "jj" // ) ); + + assert_eq!( + subscription.consume().into_inner(), + [Edit { + old: 17..19, + new: 17..18 + }] + ); } #[gpui::test(iterations = 100)] diff --git a/crates/text/src/patch.rs b/crates/text/src/patch.rs index a21f1125ec1470ce9be0d909cc2410cf4e0b8c18..f5592cefd00f41c17b320cd7a5d55511dc4ed514 100644 --- a/crates/text/src/patch.rs +++ b/crates/text/src/patch.rs @@ -210,6 +210,15 @@ impl<'a, T: Clone> IntoIterator for &'a Patch { } } +impl<'a, T: Clone> IntoIterator for &'a mut Patch { + type Item = Edit; + type IntoIter = std::iter::Cloned>>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter().cloned() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/text/src/subscription.rs b/crates/text/src/subscription.rs new file mode 100644 index 0000000000000000000000000000000000000000..b636dfcc923654619f7ea61d87b61d7abb4dbae5 --- /dev/null +++ b/crates/text/src/subscription.rs @@ -0,0 +1,48 @@ +use crate::{Edit, Patch}; +use parking_lot::Mutex; +use std::{ + mem, + sync::{Arc, Weak}, +}; + +#[derive(Default)] +pub struct Topic(Mutex>>>>); + +pub struct Subscription(Arc>>); + +impl Topic { + pub fn subscribe(&mut self) -> Subscription { + let subscription = Subscription(Default::default()); + self.0.get_mut().push(Arc::downgrade(&subscription.0)); + subscription + } + + pub fn publish(&self, edits: impl Clone + IntoIterator>) { + publish(&mut *self.0.lock(), edits); + } + + pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator>) { + publish(self.0.get_mut(), edits); + } +} + +impl Subscription { + pub fn consume(&self) -> Patch { + mem::take(&mut *self.0.lock()) + } +} + +fn publish( + subscriptions: &mut Vec>>>, + edits: impl Clone + IntoIterator>, +) { + subscriptions.retain(|subscription| { + if let Some(subscription) = subscription.upgrade() { + let mut patch = subscription.lock(); + *patch = patch.compose(edits.clone()); + true + } else { + false + } + }); +} diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 06c92ca91efe07d22fbc30bb3eacf115d5a47304..f52a76c3e4b168822640b23cb76eeda0925e8902 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -7,6 +7,7 @@ mod point_utf16; pub mod random_char_iter; pub mod rope; mod selection; +pub mod subscription; #[cfg(test)] mod tests; @@ -15,7 +16,6 @@ use anyhow::{anyhow, Result}; use clock::ReplicaId; use collections::{HashMap, HashSet}; use operation_queue::OperationQueue; -use parking_lot::Mutex; pub use patch::Patch; pub use point::*; pub use point_utf16::*; @@ -29,9 +29,10 @@ use std::{ iter::Iterator, ops::{self, Deref, Range, Sub}, str, - sync::{Arc, Weak}, + sync::Arc, time::{Duration, Instant}, }; +use subscription::{Subscription, Topic}; pub use sum_tree::Bias; use sum_tree::{FilterCursor, SumTree}; @@ -46,7 +47,7 @@ pub struct Buffer { remote_id: u64, local_clock: clock::Local, lamport_clock: clock::Lamport, - subscriptions: Vec>>>>, + subscriptions: Topic, } #[derive(Clone, Debug)] @@ -343,20 +344,6 @@ impl Edit<(D1, D2)> { } } -#[derive(Clone, Default)] -pub struct Subscription(Arc>>>); - -impl Subscription { - pub fn consume(&self) -> Patch { - let mut patches = self.0.lock(); - let mut changes = Patch::default(); - for patch in patches.drain(..) { - changes = changes.compose(&patch); - } - changes - } -} - #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct InsertionTimestamp { pub replica_id: ReplicaId, @@ -699,7 +686,7 @@ impl Buffer { self.snapshot.fragments = new_fragments; self.snapshot.visible_text = visible_text; self.snapshot.deleted_text = deleted_text; - self.update_subscriptions(edits); + self.subscriptions.publish_mut(&edits); edit_op.new_text = new_text; edit_op } @@ -955,7 +942,7 @@ impl Buffer { self.snapshot.deleted_text = deleted_text; self.local_clock.observe(timestamp.local()); self.lamport_clock.observe(timestamp.lamport()); - self.update_subscriptions(edits); + self.subscriptions.publish_mut(&edits); } fn apply_undo(&mut self, undo: &UndoOperation) -> Result<()> { @@ -1045,7 +1032,7 @@ impl Buffer { self.snapshot.fragments = new_fragments; self.snapshot.visible_text = visible_text; self.snapshot.deleted_text = deleted_text; - self.update_subscriptions(edits); + self.subscriptions.publish_mut(&edits); Ok(()) } @@ -1203,20 +1190,7 @@ impl Buffer { } pub fn subscribe(&mut self) -> Subscription { - let subscription = Subscription(Default::default()); - self.subscriptions.push(Arc::downgrade(&subscription.0)); - subscription - } - - fn update_subscriptions(&mut self, edits: Patch) { - self.subscriptions.retain(|subscription| { - if let Some(subscription) = subscription.upgrade() { - subscription.lock().push(edits.clone()); - true - } else { - false - } - }); + self.subscriptions.subscribe() } pub fn selection_set(&self, set_id: SelectionSetId) -> Result<&SelectionSet> {