diff --git a/Cargo.lock b/Cargo.lock index c1295012c7..1c93aff3af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,6 +561,18 @@ dependencies = [ "workspace", ] +[[package]] +name = "bromberg_sl2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed88064f69518b7e3ea50ecfc1b61d43f19248618a377b95ae5c8b611134d4d" +dependencies = [ + "digest 0.9.0", + "lazy_static", + "rayon", + "seq-macro", +] + [[package]] name = "bstr" version = "0.2.17" @@ -4156,6 +4168,12 @@ dependencies = [ "pest", ] +[[package]] +name = "seq-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" + [[package]] name = "serde" version = "1.0.137" @@ -4806,9 +4824,11 @@ version = "0.1.0" dependencies = [ "anyhow", "arrayvec 0.7.2", + "bromberg_sl2", "clock", "collections", "ctor", + "digest 0.9.0", "env_logger", "gpui", "lazy_static", diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index dd4b85731a..9f20880447 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -5364,7 +5364,7 @@ impl TestClient { (buffer.version(), buffer.save(cx)) }); let save = cx.background().spawn(async move { - let (saved_version, _) = save + let (saved_version, _, _) = save .await .map_err(|err| anyhow!("save request failed: {:?}", err))?; assert!(saved_version.observed_all(&requested_version)); diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index d123e0a688..94eda67c39 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -568,7 +568,10 @@ impl workspace::Item for ProjectDiagnosticsEditor { } fn should_update_tab_on_event(event: &Event) -> bool { - matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged) + matches!( + event, + Event::Saved | Event::DirtyChanged | Event::TitleChanged + ) } fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext) { diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 3f9be8ad2d..95ca958ad6 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -5,7 +5,7 @@ use crate::{ }; use collections::BTreeMap; use gpui::fonts::HighlightStyle; -use language::{Chunk, Edit, Point, PointUtf16, TextSummary}; +use language::{Chunk, Edit, Point, TextSummary}; use parking_lot::Mutex; use std::{ any::TypeId, @@ -370,22 +370,10 @@ impl FoldMap { if fold.end > fold.start { let output_text = "…"; - let chars = output_text.chars().count() as u32; - let lines = Point::new(0, output_text.len() as u32); - let lines_utf16 = - PointUtf16::new(0, output_text.encode_utf16().count() as u32); new_transforms.push( Transform { summary: TransformSummary { - output: TextSummary { - bytes: output_text.len(), - lines, - lines_utf16, - first_line_chars: chars, - last_line_chars: chars, - longest_row: 0, - longest_row_chars: chars, - }, + output: TextSummary::from(output_text), input: new_buffer.text_summary_for_range(fold.start..fold.end), }, output_text: Some(output_text), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8a1cf4afd8..26692a3ba7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5508,7 +5508,7 @@ impl Editor { cx.emit(Event::BufferEdited); } language::Event::Reparsed => cx.emit(Event::Reparsed), - language::Event::Dirtied => cx.emit(Event::Dirtied), + language::Event::DirtyChanged => cx.emit(Event::DirtyChanged), language::Event::Saved => cx.emit(Event::Saved), language::Event::FileHandleChanged => cx.emit(Event::TitleChanged), language::Event::Reloaded => cx.emit(Event::TitleChanged), @@ -5665,7 +5665,7 @@ pub enum Event { Edited, Reparsed, Blurred, - Dirtied, + DirtyChanged, Saved, TitleChanged, SelectionsChanged { local: bool }, @@ -6181,7 +6181,10 @@ mod tests { let events = events.clone(); |cx| { cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { events.borrow_mut().push(("editor1", *event)); } }) @@ -6193,7 +6196,10 @@ mod tests { let events = events.clone(); |cx| { cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { events.borrow_mut().push(("editor2", *event)); } }) @@ -6211,8 +6217,8 @@ mod tests { ("editor1", Event::Edited), ("editor1", Event::BufferEdited), ("editor2", Event::BufferEdited), - ("editor1", Event::Dirtied), - ("editor2", Event::Dirtied) + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged) ] ); @@ -6235,6 +6241,8 @@ mod tests { ("editor1", Event::Edited), ("editor1", Event::BufferEdited), ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), ] ); @@ -6246,6 +6254,8 @@ mod tests { ("editor1", Event::Edited), ("editor1", Event::BufferEdited), ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), ] ); @@ -6257,6 +6267,8 @@ mod tests { ("editor2", Event::Edited), ("editor1", Event::BufferEdited), ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), ] ); @@ -6268,6 +6280,8 @@ mod tests { ("editor2", Event::Edited), ("editor1", Event::BufferEdited), ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), ] ); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 47337aa9a2..8e15dce83c 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -440,7 +440,10 @@ impl Item for Editor { } fn should_update_tab_on_event(event: &Event) -> bool { - matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged) + matches!( + event, + Event::Saved | Event::DirtyChanged | Event::TitleChanged + ) } } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 88bfe28a27..2c7023d37e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1923,15 +1923,7 @@ impl MultiBufferSnapshot { ); if range.end > end_before_newline { - summary.add_assign(&D::from_text_summary(&TextSummary { - bytes: 1, - lines: Point::new(1 as u32, 0), - lines_utf16: PointUtf16::new(1 as u32, 0), - first_line_chars: 0, - last_line_chars: 0, - longest_row: 0, - longest_row_chars: 0, - })); + summary.add_assign(&D::from_text_summary(&TextSummary::from("\n"))); } cursor.next(&()); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 46a658729a..69d51ce9db 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -51,7 +51,10 @@ pub struct Buffer { text: TextBuffer, file: Option>, saved_version: clock::Global, + saved_version_fingerprint: String, saved_mtime: SystemTime, + transaction_depth: usize, + was_dirty_before_starting_transaction: Option, language: Option>, autoindent_requests: Vec>, pending_autoindent: Option>, @@ -155,7 +158,7 @@ pub enum Operation { pub enum Event { Operation(Operation), Edited, - Dirtied, + DirtyChanged, Saved, FileHandleChanged, Reloaded, @@ -192,7 +195,7 @@ pub trait File: Send + Sync { text: Rope, version: clock::Global, cx: &mut MutableAppContext, - ) -> Task>; + ) -> Task>; fn as_any(&self) -> &dyn Any; @@ -209,6 +212,7 @@ pub trait LocalFile: File { &self, buffer_id: u64, version: &clock::Global, + fingerprint: String, mtime: SystemTime, cx: &mut MutableAppContext, ); @@ -426,6 +430,9 @@ impl Buffer { Self { saved_mtime, saved_version: buffer.version(), + saved_version_fingerprint: buffer.as_rope().fingerprint(), + transaction_depth: 0, + was_dirty_before_starting_transaction: None, text: buffer, file, syntax_tree: Mutex::new(None), @@ -476,7 +483,7 @@ impl Buffer { pub fn save( &mut self, cx: &mut ModelContext, - ) -> Task> { + ) -> Task> { let file = if let Some(file) = self.file.as_ref() { file } else { @@ -486,11 +493,11 @@ impl Buffer { let version = self.version(); let save = file.save(self.remote_id(), text, version, cx.as_mut()); cx.spawn(|this, mut cx| async move { - let (version, mtime) = save.await?; + let (version, fingerprint, mtime) = save.await?; this.update(&mut cx, |this, cx| { - this.did_save(version.clone(), mtime, None, cx); + this.did_save(version.clone(), fingerprint.clone(), mtime, None, cx); }); - Ok((version, mtime)) + Ok((version, fingerprint, mtime)) }) } @@ -507,12 +514,14 @@ impl Buffer { pub fn did_save( &mut self, version: clock::Global, + fingerprint: String, mtime: SystemTime, new_file: Option>, cx: &mut ModelContext, ) { - self.saved_mtime = mtime; self.saved_version = version; + self.saved_version_fingerprint = fingerprint; + self.saved_mtime = mtime; if let Some(new_file) = new_file { self.file = Some(new_file); self.file_update_count += 1; @@ -533,7 +542,12 @@ impl Buffer { .await; this.update(&mut cx, |this, cx| { if let Some(transaction) = this.apply_diff(diff, cx).cloned() { - this.did_reload(this.version(), new_mtime, cx); + this.did_reload( + this.version(), + this.as_rope().fingerprint(), + new_mtime, + cx, + ); Ok(Some(transaction)) } else { Ok(None) @@ -548,13 +562,21 @@ impl Buffer { pub fn did_reload( &mut self, version: clock::Global, + fingerprint: String, mtime: SystemTime, cx: &mut ModelContext, ) { - self.saved_mtime = mtime; self.saved_version = version; + self.saved_version_fingerprint = fingerprint; + self.saved_mtime = mtime; if let Some(file) = self.file.as_ref().and_then(|f| f.as_local()) { - file.buffer_reloaded(self.remote_id(), &self.saved_version, self.saved_mtime, cx); + file.buffer_reloaded( + self.remote_id(), + &self.saved_version, + self.saved_version_fingerprint.clone(), + self.saved_mtime, + cx, + ); } cx.emit(Event::Reloaded); cx.notify(); @@ -581,7 +603,7 @@ impl Buffer { if !old_file.is_deleted() { file_changed = true; if !self.is_dirty() { - cx.emit(Event::Dirtied); + cx.emit(Event::DirtyChanged); } } } else { @@ -972,12 +994,12 @@ impl Buffer { } pub fn is_dirty(&self) -> bool { - !self.saved_version.observed_all(&self.version) + self.saved_version_fingerprint != self.as_rope().fingerprint() || self.file.as_ref().map_or(false, |file| file.is_deleted()) } pub fn has_conflict(&self) -> bool { - !self.saved_version.observed_all(&self.version) + self.saved_version_fingerprint != self.as_rope().fingerprint() && self .file .as_ref() @@ -993,6 +1015,10 @@ impl Buffer { } pub fn start_transaction_at(&mut self, now: Instant) -> Option { + self.transaction_depth += 1; + if self.was_dirty_before_starting_transaction.is_none() { + self.was_dirty_before_starting_transaction = Some(self.is_dirty()); + } self.text.start_transaction_at(now) } @@ -1005,8 +1031,14 @@ impl Buffer { now: Instant, cx: &mut ModelContext, ) -> Option { + assert!(self.transaction_depth > 0); + self.transaction_depth -= 1; + let was_dirty = if self.transaction_depth == 0 { + self.was_dirty_before_starting_transaction.take().unwrap() + } else { + false + }; if let Some((transaction_id, start_version)) = self.text.end_transaction_at(now) { - let was_dirty = start_version != self.saved_version; self.did_edit(&start_version, was_dirty, cx); Some(transaction_id) } else { @@ -1217,8 +1249,8 @@ impl Buffer { self.reparse(cx); cx.emit(Event::Edited); - if !was_dirty { - cx.emit(Event::Dirtied); + if was_dirty != self.is_dirty() { + cx.emit(Event::DirtyChanged); } cx.notify(); } diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 8b3ba0e8b7..723e57ded4 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -91,7 +91,7 @@ fn test_edit_events(cx: &mut gpui::MutableAppContext) { }) .detach(); - // An edit emits an edited event, followed by a dirtied event, + // An edit emits an edited event, followed by a dirty changed event, // since the buffer was previously in a clean state. buffer.edit([(2..4, "XYZ")], cx); @@ -112,21 +112,46 @@ fn test_edit_events(cx: &mut gpui::MutableAppContext) { }); // Incorporating a set of remote ops emits a single edited event, - // followed by a dirtied event. + // followed by a dirty changed event. buffer2.update(cx, |buffer, cx| { buffer .apply_ops(buffer1_ops.borrow_mut().drain(..), cx) .unwrap(); }); - - let buffer_1_events = buffer_1_events.borrow(); assert_eq!( - *buffer_1_events, - vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited] + mem::take(&mut *buffer_1_events.borrow_mut()), + vec![ + Event::Edited, + Event::DirtyChanged, + Event::Edited, + Event::Edited, + ] + ); + assert_eq!( + mem::take(&mut *buffer_2_events.borrow_mut()), + vec![Event::Edited, Event::DirtyChanged] ); - let buffer_2_events = buffer_2_events.borrow(); - assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]); + buffer1.update(cx, |buffer, cx| { + // Undoing the first transaction emits edited event, followed by a + // dirty changed event, since the buffer is again in a clean state. + buffer.undo(cx); + }); + // Incorporating the remote ops again emits a single edited event, + // followed by a dirty changed event. + buffer2.update(cx, |buffer, cx| { + buffer + .apply_ops(buffer1_ops.borrow_mut().drain(..), cx) + .unwrap(); + }); + assert_eq!( + mem::take(&mut *buffer_1_events.borrow_mut()), + vec![Event::Edited, Event::DirtyChanged,] + ); + assert_eq!( + mem::take(&mut *buffer_2_events.borrow_mut()), + vec![Event::Edited, Event::DirtyChanged] + ); } #[gpui::test] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 07a53fb892..1acf5a8335 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4742,12 +4742,14 @@ impl Project { }) .await; - let (saved_version, mtime) = buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?; + let (saved_version, fingerprint, mtime) = + buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?; Ok(proto::BufferSaved { project_id, buffer_id, version: serialize_version(&saved_version), mtime: Some(mtime.into()), + fingerprint, }) } @@ -5306,7 +5308,7 @@ impl Project { .and_then(|buffer| buffer.upgrade(cx)); if let Some(buffer) = buffer { buffer.update(cx, |buffer, cx| { - buffer.did_save(version, mtime, None, cx); + buffer.did_save(version, envelope.payload.fingerprint, mtime, None, cx); }); } Ok(()) @@ -5332,7 +5334,7 @@ impl Project { .and_then(|buffer| buffer.upgrade(cx)); if let Some(buffer) = buffer { buffer.update(cx, |buffer, cx| { - buffer.did_reload(version, mtime, cx); + buffer.did_reload(version, payload.fingerprint, mtime, cx); }); } Ok(()) @@ -8105,10 +8107,16 @@ mod tests { assert!(buffer.is_dirty()); assert_eq!( *events.borrow(), - &[language::Event::Edited, language::Event::Dirtied] + &[language::Event::Edited, language::Event::DirtyChanged] ); events.borrow_mut().clear(); - buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), None, cx); + buffer.did_save( + buffer.version(), + buffer.as_rope().fingerprint(), + buffer.file().unwrap().mtime(), + None, + cx, + ); }); // after saving, the buffer is not dirty, and emits a saved event. @@ -8129,20 +8137,23 @@ mod tests { *events.borrow(), &[ language::Event::Edited, - language::Event::Dirtied, + language::Event::DirtyChanged, language::Event::Edited, ], ); events.borrow_mut().clear(); - // TODO - currently, after restoring the buffer to its - // previously-saved state, the is still considered dirty. + // After restoring the buffer to its previously-saved state, + // the buffer is not considered dirty anymore. buffer.edit([(1..3, "")], cx); assert!(buffer.text() == "ac"); - assert!(buffer.is_dirty()); + assert!(!buffer.is_dirty()); }); - assert_eq!(*events.borrow(), &[language::Event::Edited]); + assert_eq!( + *events.borrow(), + &[language::Event::Edited, language::Event::DirtyChanged] + ); // When a file is deleted, the buffer is considered dirty. let events = Rc::new(RefCell::new(Vec::new())); @@ -8164,7 +8175,10 @@ mod tests { buffer2.condition(&cx, |b, _| b.is_dirty()).await; assert_eq!( *events.borrow(), - &[language::Event::Dirtied, language::Event::FileHandleChanged] + &[ + language::Event::DirtyChanged, + language::Event::FileHandleChanged + ] ); // When a file is already dirty when deleted, we don't emit a Dirtied event. diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 1007b43d75..d5e9b392e7 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -634,6 +634,7 @@ impl LocalWorktree { ) -> Task> { let buffer = buffer_handle.read(cx); let text = buffer.as_rope().clone(); + let fingerprint = text.fingerprint(); let version = buffer.version(); let save = self.write_file(path, text, cx); let handle = cx.handle(); @@ -648,7 +649,7 @@ impl LocalWorktree { }; buffer_handle.update(&mut cx, |buffer, cx| { - buffer.did_save(version, file.mtime, Some(Arc::new(file)), cx); + buffer.did_save(version, fingerprint, file.mtime, Some(Arc::new(file)), cx); }); Ok(()) @@ -1702,11 +1703,12 @@ impl language::File for File { text: Rope, version: clock::Global, cx: &mut MutableAppContext, - ) -> Task> { + ) -> Task> { self.worktree.update(cx, |worktree, cx| match worktree { Worktree::Local(worktree) => { let rpc = worktree.client.clone(); let project_id = worktree.share.as_ref().map(|share| share.project_id); + let fingerprint = text.fingerprint(); let save = worktree.write_file(self.path.clone(), text, cx); cx.background().spawn(async move { let entry = save.await?; @@ -1716,9 +1718,10 @@ impl language::File for File { buffer_id, version: serialize_version(&version), mtime: Some(entry.mtime.into()), + fingerprint: fingerprint.clone(), })?; } - Ok((version, entry.mtime)) + Ok((version, fingerprint, entry.mtime)) }) } Worktree::Remote(worktree) => { @@ -1737,7 +1740,7 @@ impl language::File for File { .mtime .ok_or_else(|| anyhow!("missing mtime"))? .into(); - Ok((version, mtime)) + Ok((version, response.fingerprint, mtime)) }) } }) @@ -1779,6 +1782,7 @@ impl language::LocalFile for File { &self, buffer_id: u64, version: &clock::Global, + fingerprint: String, mtime: SystemTime, cx: &mut MutableAppContext, ) { @@ -1791,6 +1795,7 @@ impl language::LocalFile for File { buffer_id, version: serialize_version(&version), mtime: Some(mtime.into()), + fingerprint, }) .log_err(); } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index ed05ed7b43..1c271344df 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -364,6 +364,7 @@ message BufferSaved { uint64 buffer_id = 2; repeated VectorClockEntry version = 3; Timestamp mtime = 4; + string fingerprint = 5; } message BufferReloaded { @@ -371,6 +372,7 @@ message BufferReloaded { uint64 buffer_id = 2; repeated VectorClockEntry version = 3; Timestamp mtime = 4; + string fingerprint = 5; } message ReloadBuffers { diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index ae39b44fd5..692aa54d2c 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 24; +pub const PROTOCOL_VERSION: u32 = 25; diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 5f4bb4715d..cbbd65027e 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -16,6 +16,8 @@ collections = { path = "../collections" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" arrayvec = "0.7.1" +digest = { version = "0.9", features = ["std"] } +bromberg_sl2 = "0.6" lazy_static = "1.4" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11" diff --git a/crates/text/src/rope.rs b/crates/text/src/rope.rs index ffb3439f3e..1167006249 100644 --- a/crates/text/src/rope.rs +++ b/crates/text/src/rope.rs @@ -2,6 +2,7 @@ use crate::PointUtf16; use super::Point; use arrayvec::ArrayString; +use bromberg_sl2::{DigestString, HashMatrix}; use smallvec::SmallVec; use std::{cmp, fmt, io, mem, ops::Range, str}; use sum_tree::{Bias, Dimension, SumTree}; @@ -115,7 +116,7 @@ impl Rope { } pub fn summary(&self) -> TextSummary { - self.chunks.summary().clone() + self.chunks.summary().text.clone() } pub fn len(&self) -> usize { @@ -290,6 +291,10 @@ impl Rope { self.clip_point(Point::new(row, u32::MAX), Bias::Left) .column } + + pub fn fingerprint(&self) -> String { + self.chunks.summary().fingerprint.to_hex() + } } impl<'a> From<&'a str> for Rope { @@ -709,10 +714,34 @@ impl Chunk { } impl sum_tree::Item for Chunk { - type Summary = TextSummary; + type Summary = ChunkSummary; fn summary(&self) -> Self::Summary { - TextSummary::from(self.0.as_str()) + ChunkSummary::from(self.0.as_str()) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct ChunkSummary { + text: TextSummary, + fingerprint: HashMatrix, +} + +impl<'a> From<&'a str> for ChunkSummary { + fn from(text: &'a str) -> Self { + Self { + text: TextSummary::from(text), + fingerprint: bromberg_sl2::hash_strict(text.as_bytes()), + } + } +} + +impl sum_tree::Summary for ChunkSummary { + type Context = (); + + fn add_summary(&mut self, summary: &Self, _: &()) { + self.text += &summary.text; + self.fingerprint = self.fingerprint * summary.fingerprint; } } @@ -819,7 +848,7 @@ impl std::ops::AddAssign for TextSummary { } } -pub trait TextDimension: 'static + for<'a> Dimension<'a, TextSummary> { +pub trait TextDimension: 'static + for<'a> Dimension<'a, ChunkSummary> { fn from_text_summary(summary: &TextSummary) -> Self; fn add_assign(&mut self, other: &Self); } @@ -838,6 +867,12 @@ impl<'a, D1: TextDimension, D2: TextDimension> TextDimension for (D1, D2) { } } +impl<'a> sum_tree::Dimension<'a, ChunkSummary> for TextSummary { + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + *self += &summary.text; + } +} + impl TextDimension for TextSummary { fn from_text_summary(summary: &TextSummary) -> Self { summary.clone() @@ -848,9 +883,9 @@ impl TextDimension for TextSummary { } } -impl<'a> sum_tree::Dimension<'a, TextSummary> for usize { - fn add_summary(&mut self, summary: &'a TextSummary, _: &()) { - *self += summary.bytes; +impl<'a> sum_tree::Dimension<'a, ChunkSummary> for usize { + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + *self += summary.text.bytes; } } @@ -864,9 +899,9 @@ impl TextDimension for usize { } } -impl<'a> sum_tree::Dimension<'a, TextSummary> for Point { - fn add_summary(&mut self, summary: &'a TextSummary, _: &()) { - *self += summary.lines; +impl<'a> sum_tree::Dimension<'a, ChunkSummary> for Point { + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + *self += summary.text.lines; } } @@ -880,9 +915,9 @@ impl TextDimension for Point { } } -impl<'a> sum_tree::Dimension<'a, TextSummary> for PointUtf16 { - fn add_summary(&mut self, summary: &'a TextSummary, _: &()) { - *self += summary.lines_utf16; +impl<'a> sum_tree::Dimension<'a, ChunkSummary> for PointUtf16 { + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + *self += summary.text.lines_utf16; } }