Implement Anchor, AnchorRangeMap, SelectionSet in multi_buffer
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
parent
a7634ccd5f
commit
daedf179b2
@@ -0,0 +1,280 @@
|
||||
use super::{location::*, ExcerptSummary, MultiBufferSnapshot, ToOffset};
|
||||
use anyhow::{anyhow, Result};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp::Ordering, ops::Range};
|
||||
use sum_tree::Bias;
|
||||
use text::{rope::TextDimension, AnchorRangeExt, ToOffset as _};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
||||
pub struct Anchor {
|
||||
excerpt_id: ExcerptId,
|
||||
text_anchor: text::Anchor,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AnchorRangeMap<T> {
|
||||
entries: SmallVec<[(ExcerptId, text::AnchorRangeMap<T>); 1]>,
|
||||
}
|
||||
|
||||
impl Anchor {
|
||||
pub fn min() -> Self {
|
||||
Self {
|
||||
excerpt_id: ExcerptId::min(),
|
||||
text_anchor: text::Anchor::min(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max() -> Self {
|
||||
Self {
|
||||
excerpt_id: ExcerptId::max(),
|
||||
text_anchor: text::Anchor::max(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmp<'a>(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Result<Ordering> {
|
||||
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id);
|
||||
if excerpt_id_cmp.is_eq() {
|
||||
self.text_anchor.cmp(
|
||||
&other.text_anchor,
|
||||
snapshot
|
||||
.buffer_snapshot_for_excerpt(&self.excerpt_id)
|
||||
.ok_or_else(|| anyhow!("excerpt {:?} not found", self.excerpt_id))?,
|
||||
)
|
||||
} else {
|
||||
return Ok(excerpt_id_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
|
||||
if self.text_anchor.bias != Bias::Left {
|
||||
if let Some(buffer_snapshot) = snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id) {
|
||||
return Self {
|
||||
excerpt_id: self.excerpt_id.clone(),
|
||||
text_anchor: self.text_anchor.bias_left(buffer_snapshot),
|
||||
};
|
||||
}
|
||||
}
|
||||
self.clone()
|
||||
}
|
||||
|
||||
pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
|
||||
if self.text_anchor.bias != Bias::Right {
|
||||
if let Some(buffer_snapshot) = snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id) {
|
||||
return Self {
|
||||
excerpt_id: self.excerpt_id.clone(),
|
||||
text_anchor: self.text_anchor.bias_right(buffer_snapshot),
|
||||
};
|
||||
}
|
||||
}
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AnchorRangeMap<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.entries
|
||||
.iter()
|
||||
.map(|(_, text_map)| text_map.len())
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn ranges<'a, D>(
|
||||
&'a self,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
|
||||
where
|
||||
D: TextDimension + Clone,
|
||||
{
|
||||
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
|
||||
self.entries
|
||||
.iter()
|
||||
.filter_map(move |(excerpt_id, text_map)| {
|
||||
cursor.seek_forward(excerpt_id, Bias::Left, &());
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
if excerpt.id == *excerpt_id {
|
||||
let mut excerpt_start = D::from_text_summary(&cursor.start().text);
|
||||
excerpt_start.add_summary(&excerpt.header_summary(), &());
|
||||
return Some(text_map.ranges::<D>(&excerpt.buffer).map(
|
||||
move |(range, value)| {
|
||||
let mut full_range = excerpt_start.clone()..excerpt_start.clone();
|
||||
full_range.start.add_assign(&range.start);
|
||||
full_range.end.add_assign(&range.end);
|
||||
(full_range, value)
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn intersecting_ranges<'a, D, I>(
|
||||
&'a self,
|
||||
range: Range<(I, Bias)>,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
|
||||
where
|
||||
D: TextDimension,
|
||||
I: ToOffset,
|
||||
{
|
||||
let start_bias = range.start.1;
|
||||
let end_bias = range.end.1;
|
||||
let start_offset = range.start.0.to_offset(snapshot);
|
||||
let end_offset = range.end.0.to_offset(snapshot);
|
||||
|
||||
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
|
||||
cursor.seek(&start_offset, start_bias, &());
|
||||
let start_excerpt_id = &cursor.start().excerpt_id;
|
||||
let start_ix = match self
|
||||
.entries
|
||||
.binary_search_by_key(&start_excerpt_id, |e| &e.0)
|
||||
{
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
|
||||
let mut entry_ranges = None;
|
||||
let mut entries = self.entries[start_ix..].iter();
|
||||
std::iter::from_fn(move || loop {
|
||||
match &mut entry_ranges {
|
||||
None => {
|
||||
let (excerpt_id, text_map) = entries.next()?;
|
||||
cursor.seek(excerpt_id, Bias::Left, &());
|
||||
if cursor.start().text.bytes >= end_offset {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
if excerpt.id == *excerpt_id {
|
||||
let mut excerpt_start = D::from_text_summary(&cursor.start().text);
|
||||
excerpt_start.add_summary(&excerpt.header_summary(), &());
|
||||
|
||||
let excerpt_start_offset = cursor.start().text.bytes;
|
||||
let excerpt_end_offset = cursor.end(&()).text.bytes;
|
||||
let excerpt_buffer_range = excerpt.range.to_offset(&excerpt.buffer);
|
||||
|
||||
let start;
|
||||
if start_offset >= excerpt_start_offset {
|
||||
start = (
|
||||
excerpt_buffer_range.start + start_offset
|
||||
- excerpt_start_offset,
|
||||
start_bias,
|
||||
);
|
||||
} else {
|
||||
start = (excerpt_buffer_range.start, Bias::Left);
|
||||
}
|
||||
|
||||
let end;
|
||||
if end_offset <= excerpt_end_offset {
|
||||
end = (
|
||||
excerpt_buffer_range.start + end_offset - excerpt_start_offset,
|
||||
end_bias,
|
||||
);
|
||||
} else {
|
||||
end = (excerpt_buffer_range.end, Bias::Right);
|
||||
}
|
||||
|
||||
entry_ranges = Some(
|
||||
text_map
|
||||
.intersecting_ranges(start..end, &excerpt.buffer)
|
||||
.map(move |(range, value)| {
|
||||
let mut full_range =
|
||||
excerpt_start.clone()..excerpt_start.clone();
|
||||
full_range.start.add_assign(&range.start);
|
||||
full_range.end.add_assign(&range.end);
|
||||
(full_range, value)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ranges) => {
|
||||
if let Some(item) = ranges.next() {
|
||||
return Some(item);
|
||||
} else {
|
||||
entry_ranges.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_by_key<'a, D, F, K>(
|
||||
&self,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
extract_key: F,
|
||||
) -> Option<(Range<D>, &T)>
|
||||
where
|
||||
D: TextDimension,
|
||||
F: FnMut(&T) -> K,
|
||||
K: Ord,
|
||||
{
|
||||
self.min_or_max_by_key(snapshot, Ordering::Less, extract_key)
|
||||
}
|
||||
|
||||
pub fn max_by_key<'a, D, F, K>(
|
||||
&self,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
extract_key: F,
|
||||
) -> Option<(Range<D>, &T)>
|
||||
where
|
||||
D: TextDimension,
|
||||
F: FnMut(&T) -> K,
|
||||
K: Ord,
|
||||
{
|
||||
self.min_or_max_by_key(snapshot, Ordering::Greater, extract_key)
|
||||
}
|
||||
|
||||
fn min_or_max_by_key<'a, D, F, K>(
|
||||
&self,
|
||||
snapshot: &'a MultiBufferSnapshot,
|
||||
target_ordering: Ordering,
|
||||
mut extract_key: F,
|
||||
) -> Option<(Range<D>, &T)>
|
||||
where
|
||||
D: TextDimension,
|
||||
F: FnMut(&T) -> K,
|
||||
K: Ord,
|
||||
{
|
||||
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
|
||||
let mut max = None;
|
||||
for (excerpt_id, text_map) in &self.entries {
|
||||
cursor.seek(excerpt_id, Bias::Left, &());
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
if excerpt.id == *excerpt_id {
|
||||
if let Some((range, value)) =
|
||||
text_map.max_by_key(&excerpt.buffer, &mut extract_key)
|
||||
{
|
||||
if max.as_ref().map_or(true, |(_, max_value)| {
|
||||
extract_key(value).cmp(&extract_key(*max_value)) == target_ordering
|
||||
}) {
|
||||
let mut excerpt_start = D::from_text_summary(&cursor.start().text);
|
||||
excerpt_start.add_summary(&excerpt.header_summary(), &());
|
||||
let mut full_range = excerpt_start.clone()..excerpt_start.clone();
|
||||
full_range.start.add_assign(&range.start);
|
||||
full_range.end.add_assign(&range.end);
|
||||
max = Some((full_range, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
max
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOffset for Anchor {
|
||||
fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
|
||||
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
|
||||
cursor.seek(&self.excerpt_id, Bias::Left, &());
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
if excerpt.id == self.excerpt_id {
|
||||
let buffer_offset = self.text_anchor.to_offset(&excerpt.buffer);
|
||||
return cursor.start().text.bytes
|
||||
+ excerpt.header_height as usize
|
||||
+ buffer_offset.saturating_sub(excerpt.range.start.to_offset(&excerpt.buffer));
|
||||
}
|
||||
}
|
||||
cursor.start().text.bytes
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user