Start work on AnchorRangeMultimap
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
50c77daa0b
commit
64445c7d1c
2 changed files with 186 additions and 19 deletions
|
@ -1,13 +1,13 @@
|
||||||
use crate::Point;
|
use crate::{Point, ToOffset};
|
||||||
|
|
||||||
use super::{Buffer, Content};
|
use super::{Buffer, Content};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{cmp::Ordering, ops::Range};
|
use std::{cmp::Ordering, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::{Bias, SumTree};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
||||||
pub struct Anchor {
|
pub struct Anchor {
|
||||||
pub offset: usize,
|
pub full_offset: usize,
|
||||||
pub bias: Bias,
|
pub bias: Bias,
|
||||||
pub version: clock::Global,
|
pub version: clock::Global,
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,38 @@ pub struct AnchorRangeMap<T> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>);
|
pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>);
|
||||||
|
|
||||||
|
pub struct AnchorRangeMultimap<T: Clone> {
|
||||||
|
entries: SumTree<AnchorRangeMultimapEntry<T>>,
|
||||||
|
pub(crate) version: clock::Global,
|
||||||
|
pub(crate) start_bias: Bias,
|
||||||
|
pub(crate) end_bias: Bias,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct AnchorRangeMultimapEntry<T> {
|
||||||
|
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 {
|
impl Anchor {
|
||||||
pub fn min() -> Self {
|
pub fn min() -> Self {
|
||||||
Self {
|
Self {
|
||||||
offset: 0,
|
full_offset: 0,
|
||||||
bias: Bias::Left,
|
bias: Bias::Left,
|
||||||
version: Default::default(),
|
version: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -41,7 +69,7 @@ impl Anchor {
|
||||||
|
|
||||||
pub fn max() -> Self {
|
pub fn max() -> Self {
|
||||||
Self {
|
Self {
|
||||||
offset: usize::MAX,
|
full_offset: usize::MAX,
|
||||||
bias: Bias::Right,
|
bias: Bias::Right,
|
||||||
version: Default::default(),
|
version: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -55,7 +83,7 @@ impl Anchor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset_comparison = if self.version == other.version {
|
let offset_comparison = if self.version == other.version {
|
||||||
self.offset.cmp(&other.offset)
|
self.full_offset.cmp(&other.full_offset)
|
||||||
} else {
|
} else {
|
||||||
buffer
|
buffer
|
||||||
.full_offset_for_anchor(self)
|
.full_offset_for_anchor(self)
|
||||||
|
@ -136,6 +164,129 @@ impl AnchorRangeSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> AnchorRangeMultimap<T> {
|
||||||
|
fn intersecting_point_ranges<'a, O: ToOffset>(
|
||||||
|
&'a self,
|
||||||
|
range: Range<O>,
|
||||||
|
content: impl Into<Content<'a>>,
|
||||||
|
inclusive: bool,
|
||||||
|
) -> impl Iterator<Item = (usize, Range<Point>, &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<T: Clone> sum_tree::Item for AnchorRangeMultimapEntry<T> {
|
||||||
|
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 {
|
pub trait AnchorRangeExt {
|
||||||
fn cmp<'a>(&self, b: &Range<Anchor>, buffer: impl Into<Content<'a>>) -> Result<Ordering>;
|
fn cmp<'a>(&self, b: &Range<Anchor>, buffer: impl Into<Content<'a>>) -> Result<Ordering>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1696,9 +1696,13 @@ impl<'a> Content<'a> {
|
||||||
fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary {
|
fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary {
|
||||||
let cx = Some(anchor.version.clone());
|
let cx = Some(anchor.version.clone());
|
||||||
let mut cursor = self.fragments.cursor::<(VersionedOffset, usize)>();
|
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) {
|
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 {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -1766,13 +1770,8 @@ impl<'a> Content<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
|
fn anchor_at<T: ToOffset>(&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::<FragmentTextSummary>();
|
|
||||||
cursor.seek(&offset, bias, &None);
|
|
||||||
Anchor {
|
Anchor {
|
||||||
offset: offset + cursor.start().deleted,
|
full_offset: position.to_full_offset(self, bias),
|
||||||
bias,
|
bias,
|
||||||
version: self.version.clone(),
|
version: self.version.clone(),
|
||||||
}
|
}
|
||||||
|
@ -1842,9 +1841,13 @@ impl<'a> Content<'a> {
|
||||||
let mut cursor = self
|
let mut cursor = self
|
||||||
.fragments
|
.fragments
|
||||||
.cursor::<(VersionedOffset, FragmentTextSummary)>();
|
.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() {
|
let overshoot = if cursor.item().is_some() {
|
||||||
anchor.offset - cursor.start().0.offset()
|
anchor.full_offset - cursor.start().0.offset()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -2239,7 +2242,7 @@ impl<'a> Into<proto::Anchor> for &'a Anchor {
|
||||||
fn into(self) -> proto::Anchor {
|
fn into(self) -> proto::Anchor {
|
||||||
proto::Anchor {
|
proto::Anchor {
|
||||||
version: (&self.version).into(),
|
version: (&self.version).into(),
|
||||||
offset: self.offset as u64,
|
offset: self.full_offset as u64,
|
||||||
bias: match self.bias {
|
bias: match self.bias {
|
||||||
Bias::Left => proto::anchor::Bias::Left as i32,
|
Bias::Left => proto::anchor::Bias::Left as i32,
|
||||||
Bias::Right => proto::anchor::Bias::Right as i32,
|
Bias::Right => proto::anchor::Bias::Right as i32,
|
||||||
|
@ -2373,7 +2376,7 @@ impl TryFrom<proto::Anchor> for Anchor {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
offset: message.offset as usize,
|
full_offset: message.offset as usize,
|
||||||
bias: if message.bias == proto::anchor::Bias::Left as i32 {
|
bias: if message.bias == proto::anchor::Bias::Left as i32 {
|
||||||
Bias::Left
|
Bias::Left
|
||||||
} else if message.bias == proto::anchor::Bias::Right as i32 {
|
} else if message.bias == proto::anchor::Bias::Right as i32 {
|
||||||
|
@ -2408,6 +2411,14 @@ impl TryFrom<proto::Selection> for Selection {
|
||||||
|
|
||||||
pub trait ToOffset {
|
pub trait ToOffset {
|
||||||
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
||||||
|
|
||||||
|
fn to_full_offset<'a>(&self, content: impl Into<Content<'a>>, bias: Bias) -> usize {
|
||||||
|
let content = content.into();
|
||||||
|
let offset = self.to_offset(&content);
|
||||||
|
let mut cursor = content.fragments.cursor::<FragmentTextSummary>();
|
||||||
|
cursor.seek(&offset, bias, &None);
|
||||||
|
offset + cursor.start().deleted
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToOffset for Point {
|
impl ToOffset for Point {
|
||||||
|
@ -2417,7 +2428,8 @@ impl ToOffset for Point {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToOffset for usize {
|
impl ToOffset for usize {
|
||||||
fn to_offset<'a>(&self, _: impl Into<Content<'a>>) -> usize {
|
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize {
|
||||||
|
assert!(*self <= content.into().len(), "offset is out of range");
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2426,6 +2438,10 @@ impl ToOffset for Anchor {
|
||||||
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize {
|
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize {
|
||||||
content.into().summary_for_anchor(self).bytes
|
content.into().summary_for_anchor(self).bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_full_offset<'a>(&self, _: impl Into<Content<'a>>, _: Bias) -> usize {
|
||||||
|
self.full_offset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToOffset for &'a Anchor {
|
impl<'a> ToOffset for &'a Anchor {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue