diff --git a/crates/buffer/src/anchor.rs b/crates/buffer/src/anchor.rs index 1ac82727df7485bb6d098a66b251ecb465cc1cc6..c8a7eaed24db47698fb970345206201341c5d76a 100644 --- a/crates/buffer/src/anchor.rs +++ b/crates/buffer/src/anchor.rs @@ -1,13 +1,13 @@ -use crate::Point; +use crate::{Point, ToOffset}; use super::{Buffer, Content}; use anyhow::Result; use std::{cmp::Ordering, ops::Range}; -use sum_tree::Bias; +use sum_tree::{Bias, SumTree}; #[derive(Clone, Eq, PartialEq, Debug, Hash)] pub struct Anchor { - pub offset: usize, + pub full_offset: usize, pub bias: Bias, pub version: clock::Global, } @@ -30,10 +30,38 @@ pub struct AnchorRangeMap { #[derive(Clone)] pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>); +pub struct AnchorRangeMultimap { + entries: SumTree>, + pub(crate) version: clock::Global, + pub(crate) start_bias: Bias, + pub(crate) end_bias: Bias, +} + +#[derive(Clone)] +struct AnchorRangeMultimapEntry { + range: FullOffsetRange, + value: T, +} + +#[derive(Clone, Debug)] +struct FullOffsetRange { + start: usize, + end: usize, +} + +#[derive(Clone, Debug)] +struct AnchorRangeMultimapSummary { + start: usize, + end: usize, + min_start: usize, + max_end: usize, + count: usize, +} + impl Anchor { pub fn min() -> Self { Self { - offset: 0, + full_offset: 0, bias: Bias::Left, version: Default::default(), } @@ -41,7 +69,7 @@ impl Anchor { pub fn max() -> Self { Self { - offset: usize::MAX, + full_offset: usize::MAX, bias: Bias::Right, version: Default::default(), } @@ -55,7 +83,7 @@ impl Anchor { } let offset_comparison = if self.version == other.version { - self.offset.cmp(&other.offset) + self.full_offset.cmp(&other.full_offset) } else { buffer .full_offset_for_anchor(self) @@ -136,6 +164,129 @@ impl AnchorRangeSet { } } +impl AnchorRangeMultimap { + fn intersecting_point_ranges<'a, O: ToOffset>( + &'a self, + range: Range, + content: impl Into>, + inclusive: bool, + ) -> impl Iterator, &T)> + 'a { + use super::ToPoint as _; + + let content = content.into(); + let start = range.start.to_full_offset(&content, self.start_bias); + let end = range.end.to_full_offset(&content, self.end_bias); + let mut cursor = self.entries.filter::<_, usize>( + move |summary: &AnchorRangeMultimapSummary| { + if inclusive { + start <= summary.max_end && end >= summary.min_start + } else { + start < summary.max_end && end > summary.min_start + } + }, + &(), + ); + let mut anchor = Anchor { + full_offset: 0, + bias: Bias::Left, + version: self.version.clone(), + }; + std::iter::from_fn(move || { + if let Some(item) = cursor.item() { + let ix = *cursor.start(); + anchor.full_offset = item.range.start; + anchor.bias = self.start_bias; + let start = anchor.to_point(&content); + anchor.full_offset = item.range.end; + anchor.bias = self.end_bias; + let end = anchor.to_point(&content); + let value = &item.value; + cursor.next(&()); + Some((ix, start..end, value)) + } else { + None + } + }) + } +} + +impl sum_tree::Item for AnchorRangeMultimapEntry { + type Summary = AnchorRangeMultimapSummary; + + fn summary(&self) -> Self::Summary { + AnchorRangeMultimapSummary { + start: self.range.start, + end: self.range.end, + min_start: self.range.start, + max_end: self.range.end, + count: 1, + } + } +} + +impl Default for AnchorRangeMultimapSummary { + fn default() -> Self { + Self { + start: 0, + end: usize::MAX, + min_start: usize::MAX, + max_end: 0, + count: 0, + } + } +} + +impl sum_tree::Summary for AnchorRangeMultimapSummary { + type Context = (); + + fn add_summary(&mut self, other: &Self, _: &Self::Context) { + self.min_start = self.min_start.min(other.min_start); + self.max_end = self.max_end.max(other.max_end); + + #[cfg(debug_assertions)] + { + let start_comparison = self.start.cmp(&other.start); + assert!(start_comparison <= Ordering::Equal); + if start_comparison == Ordering::Equal { + assert!(self.end.cmp(&other.end) >= Ordering::Equal); + } + } + + self.start = other.start; + self.end = other.end; + self.count += other.count; + } +} + +impl Default for FullOffsetRange { + fn default() -> Self { + Self { + start: 0, + end: usize::MAX, + } + } +} + +impl<'a> sum_tree::Dimension<'a, AnchorRangeMultimapSummary> for usize { + fn add_summary(&mut self, summary: &'a AnchorRangeMultimapSummary, _: &()) { + *self += summary.count; + } +} + +impl<'a> sum_tree::Dimension<'a, AnchorRangeMultimapSummary> for FullOffsetRange { + fn add_summary(&mut self, summary: &'a AnchorRangeMultimapSummary, _: &()) { + self.start = summary.start; + self.end = summary.end; + } +} + +impl<'a> sum_tree::SeekTarget<'a, AnchorRangeMultimapSummary, FullOffsetRange> for FullOffsetRange { + fn cmp(&self, cursor_location: &FullOffsetRange, _: &()) -> Ordering { + Ord::cmp(&self.start, &cursor_location.start) + .then_with(|| Ord::cmp(&cursor_location.end, &self.end)) + } +} + pub trait AnchorRangeExt { fn cmp<'a>(&self, b: &Range, buffer: impl Into>) -> Result; } diff --git a/crates/buffer/src/lib.rs b/crates/buffer/src/lib.rs index a5771ad4c0f55508bad6a93d73b674230a627e29..7203e0b1d71edb4b1daaffbafc740a7539f1dd30 100644 --- a/crates/buffer/src/lib.rs +++ b/crates/buffer/src/lib.rs @@ -1696,9 +1696,13 @@ impl<'a> Content<'a> { fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary { let cx = Some(anchor.version.clone()); let mut cursor = self.fragments.cursor::<(VersionedOffset, usize)>(); - cursor.seek(&VersionedOffset::Offset(anchor.offset), anchor.bias, &cx); + cursor.seek( + &VersionedOffset::Offset(anchor.full_offset), + anchor.bias, + &cx, + ); let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) { - anchor.offset - cursor.start().0.offset() + anchor.full_offset - cursor.start().0.offset() } else { 0 }; @@ -1766,13 +1770,8 @@ impl<'a> Content<'a> { } fn anchor_at(&self, position: T, bias: Bias) -> Anchor { - let offset = position.to_offset(self); - let max_offset = self.len(); - assert!(offset <= max_offset, "offset is out of range"); - let mut cursor = self.fragments.cursor::(); - cursor.seek(&offset, bias, &None); Anchor { - offset: offset + cursor.start().deleted, + full_offset: position.to_full_offset(self, bias), bias, version: self.version.clone(), } @@ -1842,9 +1841,13 @@ impl<'a> Content<'a> { let mut cursor = self .fragments .cursor::<(VersionedOffset, FragmentTextSummary)>(); - cursor.seek(&VersionedOffset::Offset(anchor.offset), anchor.bias, &cx); + cursor.seek( + &VersionedOffset::Offset(anchor.full_offset), + anchor.bias, + &cx, + ); let overshoot = if cursor.item().is_some() { - anchor.offset - cursor.start().0.offset() + anchor.full_offset - cursor.start().0.offset() } else { 0 }; @@ -2239,7 +2242,7 @@ impl<'a> Into for &'a Anchor { fn into(self) -> proto::Anchor { proto::Anchor { version: (&self.version).into(), - offset: self.offset as u64, + offset: self.full_offset as u64, bias: match self.bias { Bias::Left => proto::anchor::Bias::Left as i32, Bias::Right => proto::anchor::Bias::Right as i32, @@ -2373,7 +2376,7 @@ impl TryFrom for Anchor { } Ok(Self { - offset: message.offset as usize, + full_offset: message.offset as usize, bias: if message.bias == proto::anchor::Bias::Left as i32 { Bias::Left } else if message.bias == proto::anchor::Bias::Right as i32 { @@ -2408,6 +2411,14 @@ impl TryFrom for Selection { pub trait ToOffset { fn to_offset<'a>(&self, content: impl Into>) -> usize; + + fn to_full_offset<'a>(&self, content: impl Into>, bias: Bias) -> usize { + let content = content.into(); + let offset = self.to_offset(&content); + let mut cursor = content.fragments.cursor::(); + cursor.seek(&offset, bias, &None); + offset + cursor.start().deleted + } } impl ToOffset for Point { @@ -2417,7 +2428,8 @@ impl ToOffset for Point { } impl ToOffset for usize { - fn to_offset<'a>(&self, _: impl Into>) -> usize { + fn to_offset<'a>(&self, content: impl Into>) -> usize { + assert!(*self <= content.into().len(), "offset is out of range"); *self } } @@ -2426,6 +2438,10 @@ impl ToOffset for Anchor { fn to_offset<'a>(&self, content: impl Into>) -> usize { content.into().summary_for_anchor(self).bytes } + + fn to_full_offset<'a>(&self, _: impl Into>, _: Bias) -> usize { + self.full_offset + } } impl<'a> ToOffset for &'a Anchor {