diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index b67a2fe835..caef7569c5 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -28,7 +28,7 @@ pub struct Summary { } impl DiagnosticSet { - pub fn from_sorted_entries(iter: I, buffer: &text::Snapshot) -> Self + pub fn from_sorted_entries(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, { @@ -37,7 +37,7 @@ impl DiagnosticSet { } } - pub fn new(iter: I, buffer: &text::Snapshot) -> Self + pub fn new(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, { @@ -62,7 +62,7 @@ impl DiagnosticSet { pub fn range<'a, T, O>( &'a self, range: Range, - buffer: &'a text::Snapshot, + buffer: &'a text::BufferSnapshot, inclusive: bool, ) -> impl 'a + Iterator> 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> { self.iter() .filter(move |entry| entry.diagnostic.group_id == group_id) @@ -124,7 +124,7 @@ impl sum_tree::Item for DiagnosticEntry { } impl DiagnosticEntry { - pub fn resolve(&self, buffer: &text::Snapshot) -> DiagnosticEntry { + pub fn resolve(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry { 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 diff --git a/crates/language/src/multi_buffer.rs b/crates/language/src/multi_buffer.rs index eb76568856..081f4ff8e1 100644 --- a/crates/language/src/multi_buffer.rs +++ b/crates/language/src/multi_buffer.rs @@ -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, @@ -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( - &self, - start_bias: Bias, - end_bias: Bias, - entries: E, - ) -> AnchorRangeMap - where - E: IntoIterator, 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::(); - 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(&self, start_bias: Bias, end_bias: Bias, ranges: E) -> AnchorRangeSet - where - E: IntoIterator>, - { - 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(&self, anchor: &Anchor) -> D + where + D: TextDimension + Ord + Sub, + { + let mut cursor = self.excerpts.cursor::(); + 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::(&excerpt.buffer); + let buffer_point = anchor.text_anchor.summary::(&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 + where + D: TextDimension + Ord + Sub, + I: 'a + IntoIterator, + { + let mut anchors = anchors.into_iter().peekable(); + let mut cursor = self.excerpts.cursor::(); + 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::(&excerpt.buffer); + summaries.extend( + excerpt + .buffer + .summaries_for_anchors::(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(&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::(); - cursor.seek(excerpt_id, Bias::Left, &()); + let mut cursor = self.excerpts.cursor::>(); + 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); } } diff --git a/crates/language/src/multi_buffer/anchor.rs b/crates/language/src/multi_buffer/anchor.rs index 5f5173925f..033ed009b0 100644 --- a/crates/language/src/multi_buffer/anchor.rs +++ b/crates/language/src/multi_buffer/anchor.rs @@ -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 { - pub(crate) child_maps: SmallVec<[(ExcerptId, text::AnchorRangeMap); 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(&self, snapshot: &MultiBufferSnapshot) -> D where D: TextDimension + Ord + Sub, { - let mut cursor = snapshot.excerpts.cursor::(); - 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::(&excerpt.buffer); - let buffer_point = self.text_anchor.summary::(&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 AnchorRangeMap { - 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, &'a T)> + 'a - where - D: TextDimension + Clone, - { - let mut cursor = snapshot.excerpts.cursor::(); - 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::(&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, &'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::(); - 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, &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, &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, &T)> - where - D: TextDimension, - F: FnMut(&T) -> K, - K: Ord, - { - let mut cursor = snapshot.excerpts.cursor::(); - 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> - where - D: TextDimension, - { - self.0.ranges(content).map(|(range, _)| range) + snapshot.summary_for_anchor(self) } } diff --git a/crates/language/src/multi_buffer/location.rs b/crates/language/src/multi_buffer/location.rs deleted file mode 100644 index a61b2a7630..0000000000 --- a/crates/language/src/multi_buffer/location.rs +++ /dev/null @@ -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) - ); - } - } -} diff --git a/crates/language/src/multi_buffer/selection.rs b/crates/language/src/multi_buffer/selection.rs index 825b6a27b8..3a4369b3da 100644 --- a/crates/language/src/multi_buffer/selection.rs +++ b/crates/language/src/multi_buffer/selection.rs @@ -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>, + pub selections: Arc<[Selection]>, } impl SelectionSet { @@ -17,75 +20,102 @@ impl SelectionSet { pub fn selections<'a, D>( &'a self, - content: &'a MultiBufferSnapshot, + snapshot: &'a MultiBufferSnapshot, ) -> impl 'a + Iterator> where - D: TextDimension, + D: TextDimension + Ord + Sub, { - 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> where - D: TextDimension, + D: TextDimension + Ord + Sub, 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> where - D: TextDimension, + D: TextDimension + Ord + Sub, { 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> where - D: TextDimension, + D: TextDimension + Ord + Sub, { 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, + snapshot: &'a MultiBufferSnapshot, +) -> Selection +where + D: TextDimension + Ord + Sub, +{ + Selection { + id: selection.id, + start: selection.start.summary::(snapshot), + end: selection.end.summary::(snapshot), + reversed: selection.reversed, + goal: selection.goal, + } +} + +fn resolve_selections<'a, D>( + selections: &'a [Selection], + snapshot: &'a MultiBufferSnapshot, +) -> impl 'a + Iterator> +where + D: TextDimension + Ord + Sub, +{ + let mut summaries = snapshot + .summaries_for_anchors::( + 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, + }) +} diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 6a27d41ca2..cb271e6937 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1,5 +1,5 @@ mod anchor; -mod locator; +pub mod locator; pub mod operation_queue; mod patch; mod point;