Finish removing anchor collections from MultiBuffer

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-12-10 15:58:37 +01:00
parent d9da8effd4
commit 9c74deb9ec
6 changed files with 181 additions and 415 deletions

View File

@ -28,7 +28,7 @@ pub struct Summary {
}
impl DiagnosticSet {
pub fn from_sorted_entries<I>(iter: I, buffer: &text::Snapshot) -> Self
pub fn from_sorted_entries<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
where
I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
{
@ -37,7 +37,7 @@ impl DiagnosticSet {
}
}
pub fn new<I>(iter: I, buffer: &text::Snapshot) -> Self
pub fn new<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
where
I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
{
@ -62,7 +62,7 @@ impl DiagnosticSet {
pub fn range<'a, T, O>(
&'a self,
range: Range<T>,
buffer: &'a text::Snapshot,
buffer: &'a text::BufferSnapshot,
inclusive: bool,
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
where
@ -101,7 +101,7 @@ impl DiagnosticSet {
pub fn group<'a, O: FromAnchor>(
&'a self,
group_id: usize,
buffer: &'a text::Snapshot,
buffer: &'a text::BufferSnapshot,
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
self.iter()
.filter(move |entry| entry.diagnostic.group_id == group_id)
@ -124,7 +124,7 @@ impl sum_tree::Item for DiagnosticEntry<Anchor> {
}
impl DiagnosticEntry<Anchor> {
pub fn resolve<O: FromAnchor>(&self, buffer: &text::Snapshot) -> DiagnosticEntry<O> {
pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
DiagnosticEntry {
range: O::from_anchor(&self.range.start, buffer)
..O::from_anchor(&self.range.end, buffer),
@ -146,7 +146,7 @@ impl Default for Summary {
}
impl sum_tree::Summary for Summary {
type Context = text::Snapshot;
type Context = text::BufferSnapshot;
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
if other

View File

@ -1,32 +1,36 @@
mod anchor;
mod location;
mod selection;
use self::location::*;
use crate::{
buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
BufferSnapshot, Diagnostic, File, Language,
};
pub use anchor::{Anchor, AnchorRangeExt};
use anyhow::Result;
use clock::ReplicaId;
use collections::HashMap;
use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
use std::{cmp, io, ops::Range, sync::Arc, time::SystemTime};
pub use selection::SelectionSet;
use std::{
cmp, io,
ops::{Range, Sub},
sync::Arc,
time::SystemTime,
};
use sum_tree::{Bias, Cursor, SumTree};
use text::{
locator::Locator,
rope::TextDimension,
subscription::{Subscription, Topic},
AnchorRangeExt as _, Edit, Point, PointUtf16, Selection, SelectionSetId, TextSummary,
};
use theme::SyntaxTheme;
pub use anchor::{Anchor, AnchorRangeExt, AnchorRangeMap, AnchorRangeSet};
pub use selection::SelectionSet;
const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
pub type ExcerptId = Locator;
#[derive(Default)]
pub struct MultiBuffer {
snapshot: Mutex<MultiBufferSnapshot>,
@ -314,10 +318,10 @@ impl MultiBuffer {
let mut edits = Vec::new();
let mut new_excerpts = SumTree::new();
let mut cursor = snapshot.excerpts.cursor::<(ExcerptId, usize)>();
let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
for (id, buffer_state) in excerpts_to_edit {
new_excerpts.push_tree(cursor.slice(id, Bias::Left, &()), &());
new_excerpts.push_tree(cursor.slice(&Some(id), Bias::Left, &()), &());
let old_excerpt = cursor.item().unwrap();
let buffer = buffer_state.buffer.read(cx);
@ -411,36 +415,6 @@ impl MultiBuffer {
self.snapshot.lock().anchor_at(position, bias)
}
pub fn anchor_range_map<T, E>(
&self,
start_bias: Bias,
end_bias: Bias,
entries: E,
) -> AnchorRangeMap<T>
where
E: IntoIterator<Item = (Range<usize>, T)>,
{
let entries = entries.into_iter().peekable();
let mut child_maps = SmallVec::new();
if let Some((range, _)) = entries.peek() {
let mut cursor = self.snapshot.lock().excerpts.cursor::<ExcerptSummary>();
cursor.seek(&range.start, Bias::Right, &());
let mut excerpt_end = cursor.end(&());
// child_maps.push
// for entry in entries {}
}
AnchorRangeMap { child_maps }
}
pub fn anchor_range_set<E>(&self, start_bias: Bias, end_bias: Bias, ranges: E) -> AnchorRangeSet
where
E: IntoIterator<Item = Range<usize>>,
{
todo!()
}
pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
self.snapshot.lock().clip_offset(offset, bias)
}
@ -863,6 +837,77 @@ impl MultiBufferSnapshot {
summary
}
fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
where
D: TextDimension + Ord + Sub<D, Output = D>,
{
let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
if let Some(excerpt) = cursor.item() {
if excerpt.id == anchor.excerpt_id {
let mut excerpt_start = D::from_text_summary(&cursor.start().text);
excerpt_start.add_summary(&excerpt.header_summary(), &());
let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
if buffer_point > excerpt_buffer_start {
excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
}
return excerpt_start;
}
}
D::from_text_summary(&cursor.start().text)
}
fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
where
D: TextDimension + Ord + Sub<D, Output = D>,
I: 'a + IntoIterator<Item = &'a Anchor>,
{
let mut anchors = anchors.into_iter().peekable();
let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
let mut summaries = Vec::new();
while let Some(anchor) = anchors.peek() {
let excerpt_id = &anchor.excerpt_id;
cursor.seek(&Some(excerpt_id), Bias::Left, &());
if let Some(excerpt) = cursor.item() {
let excerpt_exists = excerpt.id == *excerpt_id;
let excerpt_anchors = std::iter::from_fn(|| {
let anchor = anchors.peek()?;
if anchor.excerpt_id == *excerpt_id {
Some(&anchors.next().unwrap().text_anchor)
} else {
None
}
});
if excerpt_exists {
let mut excerpt_start = D::from_text_summary(&cursor.start().text);
excerpt_start.add_summary(&excerpt.header_summary(), &());
let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
summaries.extend(
excerpt
.buffer
.summaries_for_anchors::<D, _>(excerpt_anchors)
.map(move |summary| {
let mut excerpt_start = excerpt_start.clone();
let excerpt_buffer_start = excerpt_buffer_start.clone();
if summary > excerpt_buffer_start {
excerpt_start.add_assign(&(summary - excerpt_buffer_start));
}
excerpt_start
}),
);
} else {
excerpt_anchors.for_each(drop);
}
} else {
break;
}
}
summaries
}
pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
self.anchor_at(position, Bias::Left)
}
@ -923,12 +968,12 @@ impl MultiBufferSnapshot {
fn buffer_snapshot_for_excerpt<'a>(
&'a self,
excerpt_id: &ExcerptId,
excerpt_id: &'a ExcerptId,
) -> Option<&'a BufferSnapshot> {
let mut cursor = self.excerpts.cursor::<ExcerptId>();
cursor.seek(excerpt_id, Bias::Left, &());
let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
cursor.seek(&Some(excerpt_id), Bias::Left, &());
if let Some(excerpt) = cursor.item() {
if cursor.start() == excerpt_id {
if *cursor.start() == Some(excerpt_id) {
return Some(&excerpt.buffer);
}
}
@ -1020,9 +1065,9 @@ impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
}
}
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Location {
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
Ord::cmp(self, &cursor_location.excerpt_id)
Ord::cmp(self, &Some(&cursor_location.excerpt_id))
}
}
@ -1038,10 +1083,9 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
}
}
impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Location {
impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
debug_assert!(summary.excerpt_id > *self);
*self = summary.excerpt_id.clone();
*self = Some(&summary.excerpt_id);
}
}

View File

@ -1,27 +1,18 @@
use super::{location::*, ExcerptSummary, MultiBufferSnapshot, ToOffset, ToPoint};
use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint};
use anyhow::{anyhow, Result};
use smallvec::SmallVec;
use std::{
cmp::Ordering,
ops::{Range, Sub},
};
use sum_tree::Bias;
use text::{rope::TextDimension, AnchorRangeExt as _, Point};
use text::{rope::TextDimension, Point};
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub struct Anchor {
excerpt_id: ExcerptId,
text_anchor: text::Anchor,
pub(crate) excerpt_id: ExcerptId,
pub(crate) text_anchor: text::Anchor,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AnchorRangeMap<T> {
pub(crate) child_maps: SmallVec<[(ExcerptId, text::AnchorRangeMap<T>); 1]>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AnchorRangeSet(AnchorRangeMap<()>);
impl Anchor {
pub fn min() -> Self {
Self {
@ -75,234 +66,11 @@ impl Anchor {
self.clone()
}
pub fn summary<'a, D>(&self, snapshot: &'a MultiBufferSnapshot) -> D
pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
where
D: TextDimension + Ord + Sub<D, Output = D>,
{
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 mut excerpt_start = D::from_text_summary(&cursor.start().text);
excerpt_start.add_summary(&excerpt.header_summary(), &());
let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
let buffer_point = self.text_anchor.summary::<D>(&excerpt.buffer);
if buffer_point > excerpt_buffer_start {
excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
}
return excerpt_start;
}
}
D::from_text_summary(&cursor.start().text)
}
}
impl<T> AnchorRangeMap<T> {
pub fn len(&self) -> usize {
self.child_maps
.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.child_maps
.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
.child_maps
.binary_search_by_key(&start_excerpt_id, |e| &e.0)
{
Ok(ix) | Err(ix) => ix,
};
let mut entry_ranges = None;
let mut entries = self.child_maps[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.child_maps {
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 AnchorRangeSet {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn ranges<'a, D>(
&'a self,
content: &'a MultiBufferSnapshot,
) -> impl 'a + Iterator<Item = Range<Point>>
where
D: TextDimension,
{
self.0.ranges(content).map(|(range, _)| range)
snapshot.summary_for_anchor(self)
}
}

View File

@ -1,76 +0,0 @@
use smallvec::{smallvec, SmallVec};
use std::iter;
pub type ExcerptId = Location;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Location(SmallVec<[u8; 4]>);
impl Location {
pub fn min() -> Self {
Self(smallvec![u8::MIN])
}
pub fn max() -> Self {
Self(smallvec![u8::MAX])
}
pub fn between(lhs: &Self, rhs: &Self) -> Self {
let lhs = lhs.0.iter().copied().chain(iter::repeat(u8::MIN));
let rhs = rhs.0.iter().copied().chain(iter::repeat(u8::MAX));
let mut location = SmallVec::new();
for (lhs, rhs) in lhs.zip(rhs) {
let mid = lhs + (rhs.saturating_sub(lhs)) / 2;
location.push(mid);
if mid > lhs {
break;
}
}
Self(location)
}
}
impl Default for Location {
fn default() -> Self {
Self::min()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::prelude::*;
use std::mem;
#[gpui::test(iterations = 100)]
fn test_location(mut rng: StdRng) {
let mut lhs = Default::default();
let mut rhs = Default::default();
while lhs == rhs {
lhs = Location(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
.collect(),
);
rhs = Location(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
.collect(),
);
}
if lhs > rhs {
mem::swap(&mut lhs, &mut rhs);
}
let middle = Location::between(&lhs, &rhs);
assert!(middle > lhs);
assert!(middle < rhs);
for ix in 0..middle.0.len() - 1 {
assert!(
middle.0[ix] == *lhs.0.get(ix).unwrap_or(&0)
|| middle.0[ix] == *rhs.0.get(ix).unwrap_or(&0)
);
}
}
}

View File

@ -1,13 +1,16 @@
use super::{anchor::AnchorRangeMap, MultiBufferSnapshot, ToOffset};
use std::{ops::Range, sync::Arc};
use super::{Anchor, MultiBufferSnapshot, ToOffset};
use std::{
ops::{Range, Sub},
sync::Arc,
};
use sum_tree::Bias;
use text::{rope::TextDimension, Selection, SelectionSetId, SelectionState};
use text::{rope::TextDimension, Selection, SelectionSetId};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SelectionSet {
pub id: SelectionSetId,
pub active: bool,
pub selections: Arc<AnchorRangeMap<SelectionState>>,
pub selections: Arc<[Selection<Anchor>]>,
}
impl SelectionSet {
@ -17,75 +20,102 @@ impl SelectionSet {
pub fn selections<'a, D>(
&'a self,
content: &'a MultiBufferSnapshot,
snapshot: &'a MultiBufferSnapshot,
) -> impl 'a + Iterator<Item = Selection<D>>
where
D: TextDimension,
D: TextDimension + Ord + Sub<D, Output = D>,
{
self.selections
.ranges(content)
.map(|(range, state)| Selection {
id: state.id,
start: range.start,
end: range.end,
reversed: state.reversed,
goal: state.goal,
})
resolve_selections(&self.selections, snapshot)
}
pub fn intersecting_selections<'a, D, I>(
&'a self,
range: Range<(I, Bias)>,
content: &'a MultiBufferSnapshot,
snapshot: &'a MultiBufferSnapshot,
) -> impl 'a + Iterator<Item = Selection<D>>
where
D: TextDimension,
D: TextDimension + Ord + Sub<D, Output = D>,
I: 'a + ToOffset,
{
self.selections
.intersecting_ranges(range, content)
.map(|(range, state)| Selection {
id: state.id,
start: range.start,
end: range.end,
reversed: state.reversed,
goal: state.goal,
})
let start = snapshot.anchor_at(range.start.0, range.start.1);
let end = snapshot.anchor_at(range.end.0, range.end.1);
let start_ix = match self
.selections
.binary_search_by(|probe| probe.end.cmp(&start, snapshot).unwrap())
{
Ok(ix) | Err(ix) => ix,
};
let end_ix = match self
.selections
.binary_search_by(|probe| probe.start.cmp(&end, snapshot).unwrap())
{
Ok(ix) | Err(ix) => ix,
};
resolve_selections(&self.selections[start_ix..end_ix], snapshot)
}
pub fn oldest_selection<'a, D>(
&'a self,
content: &'a MultiBufferSnapshot,
snapshot: &'a MultiBufferSnapshot,
) -> Option<Selection<D>>
where
D: TextDimension,
D: TextDimension + Ord + Sub<D, Output = D>,
{
self.selections
.min_by_key(content, |selection| selection.id)
.map(|(range, state)| Selection {
id: state.id,
start: range.start,
end: range.end,
reversed: state.reversed,
goal: state.goal,
})
.iter()
.min_by_key(|selection| selection.id)
.map(|selection| resolve_selection(selection, snapshot))
}
pub fn newest_selection<'a, D>(
&'a self,
content: &'a MultiBufferSnapshot,
snapshot: &'a MultiBufferSnapshot,
) -> Option<Selection<D>>
where
D: TextDimension,
D: TextDimension + Ord + Sub<D, Output = D>,
{
self.selections
.max_by_key(content, |selection| selection.id)
.map(|(range, state)| Selection {
id: state.id,
start: range.start,
end: range.end,
reversed: state.reversed,
goal: state.goal,
})
.iter()
.max_by_key(|selection| selection.id)
.map(|selection| resolve_selection(selection, snapshot))
}
}
fn resolve_selection<'a, D>(
selection: &'a Selection<Anchor>,
snapshot: &'a MultiBufferSnapshot,
) -> Selection<D>
where
D: TextDimension + Ord + Sub<D, Output = D>,
{
Selection {
id: selection.id,
start: selection.start.summary::<D>(snapshot),
end: selection.end.summary::<D>(snapshot),
reversed: selection.reversed,
goal: selection.goal,
}
}
fn resolve_selections<'a, D>(
selections: &'a [Selection<Anchor>],
snapshot: &'a MultiBufferSnapshot,
) -> impl 'a + Iterator<Item = Selection<D>>
where
D: TextDimension + Ord + Sub<D, Output = D>,
{
let mut summaries = snapshot
.summaries_for_anchors::<D, _>(
selections
.iter()
.flat_map(|selection| [&selection.start, &selection.end]),
)
.into_iter();
selections.iter().map(move |selection| Selection {
id: selection.id,
start: summaries.next().unwrap(),
end: summaries.next().unwrap(),
reversed: selection.reversed,
goal: selection.goal,
})
}

View File

@ -1,5 +1,5 @@
mod anchor;
mod locator;
pub mod locator;
pub mod operation_queue;
mod patch;
mod point;