From cbe136c0cb4ef310dd2ab8a89198e8e25887eeee Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Dec 2021 11:18:01 +0100 Subject: [PATCH] Implement anchor resolution using locators --- crates/text/src/anchor.rs | 589 ++++------------------------------- crates/text/src/selection.rs | 87 +++--- crates/text/src/tests.rs | 12 +- crates/text/src/text.rs | 370 ++++++++-------------- 4 files changed, 244 insertions(+), 814 deletions(-) diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 846c57274b..1123bd2104 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,117 +1,88 @@ use crate::{rope::TextDimension, Snapshot}; -use super::{Buffer, FromAnchor, FullOffset, Point, ToOffset}; +use super::{Buffer, ToOffset}; use anyhow::Result; -use std::{ - cmp::Ordering, - fmt::{Debug, Formatter}, - ops::Range, -}; -use sum_tree::{Bias, SumTree}; +use std::{cmp::Ordering, fmt::Debug, ops::Range}; +use sum_tree::Bias; #[derive(Clone, Eq, PartialEq, Debug, Hash)] -pub struct Anchor { - pub full_offset: FullOffset, - pub bias: Bias, - pub version: clock::Global, -} - -#[derive(Clone)] -pub struct AnchorMap { - pub(crate) version: clock::Global, - pub(crate) bias: Bias, - pub(crate) entries: Vec<(FullOffset, T)>, -} - -#[derive(Clone)] -pub struct AnchorSet(pub(crate) AnchorMap<()>); - -#[derive(Clone)] -pub struct AnchorRangeMap { - pub(crate) version: clock::Global, - pub(crate) entries: Vec<(Range, T)>, - pub(crate) start_bias: Bias, - pub(crate) end_bias: Bias, -} - -#[derive(Clone)] -pub struct AnchorRangeSet(pub(crate) AnchorRangeMap<()>); - -#[derive(Clone)] -pub struct AnchorRangeMultimap { - pub(crate) entries: SumTree>, - pub(crate) version: clock::Global, - pub(crate) start_bias: Bias, - pub(crate) end_bias: Bias, -} - -#[derive(Clone)] -pub(crate) struct AnchorRangeMultimapEntry { - pub(crate) range: FullOffsetRange, - pub(crate) value: T, -} - -#[derive(Clone, Debug)] -pub(crate) struct FullOffsetRange { - pub(crate) start: FullOffset, - pub(crate) end: FullOffset, -} - -#[derive(Clone, Debug)] -pub(crate) struct AnchorRangeMultimapSummary { - start: FullOffset, - end: FullOffset, - min_start: FullOffset, - max_end: FullOffset, - count: usize, +pub enum Anchor { + Min, + Insertion { + timestamp: clock::Local, + offset: usize, + bias: Bias, + }, + Max, } impl Anchor { pub fn min() -> Self { - Self { - full_offset: FullOffset(0), - bias: Bias::Left, - version: Default::default(), - } + Self::Min } pub fn max() -> Self { - Self { - full_offset: FullOffset::MAX, - bias: Bias::Right, - version: Default::default(), - } + Self::Max } pub fn cmp<'a>(&self, other: &Anchor, buffer: &Snapshot) -> Result { - if self == other { - return Ok(Ordering::Equal); + match (self, other) { + (Self::Min, Self::Min) => Ok(Ordering::Equal), + (Self::Min, _) => Ok(Ordering::Less), + (_, Self::Min) => Ok(Ordering::Greater), + (Self::Max, Self::Max) => Ok(Ordering::Equal), + (Self::Max, _) => Ok(Ordering::Greater), + (_, Self::Max) => Ok(Ordering::Less), + ( + Self::Insertion { + timestamp: lhs_id, + bias: lhs_bias, + offset: lhs_offset, + }, + Self::Insertion { + timestamp: rhs_id, + bias: rhs_bias, + offset: rhs_offset, + }, + ) => { + let offset_comparison = if lhs_id == rhs_id { + lhs_offset.cmp(&rhs_offset) + } else { + buffer + .full_offset_for_anchor(self) + .cmp(&buffer.full_offset_for_anchor(other)) + }; + + Ok(offset_comparison.then_with(|| lhs_bias.cmp(&rhs_bias))) + } } - - let offset_comparison = if self.version == other.version { - self.full_offset.cmp(&other.full_offset) - } else { - buffer - .full_offset_for_anchor(self) - .cmp(&buffer.full_offset_for_anchor(other)) - }; - - Ok(offset_comparison.then_with(|| self.bias.cmp(&other.bias))) } pub fn bias_left(&self, buffer: &Buffer) -> Anchor { - if self.bias == Bias::Left { - self.clone() - } else { - buffer.anchor_before(self) + match self { + Anchor::Min => Anchor::Min, + Anchor::Insertion { bias, .. } => { + if *bias == Bias::Left { + self.clone() + } else { + buffer.anchor_before(self) + } + } + Anchor::Max => buffer.anchor_before(self), } } pub fn bias_right(&self, buffer: &Buffer) -> Anchor { - if self.bias == Bias::Right { - self.clone() - } else { - buffer.anchor_after(self) + match self { + Anchor::Min => buffer.anchor_after(self), + Anchor::Insertion { bias, .. } => { + if *bias == Bias::Right { + self.clone() + } else { + buffer.anchor_after(self) + } + } + Anchor::Max => Anchor::Max, } } @@ -123,442 +94,6 @@ impl Anchor { } } -impl AnchorMap { - pub fn version(&self) -> &clock::Global { - &self.version - } - - pub fn len(&self) -> usize { - self.entries.len() - } - - pub fn iter<'a, D>(&'a self, snapshot: &'a Snapshot) -> impl Iterator + 'a - where - D: 'a + TextDimension<'a>, - { - snapshot - .summaries_for_anchors( - self.version.clone(), - self.bias, - self.entries.iter().map(|e| &e.0), - ) - .zip(self.entries.iter().map(|e| &e.1)) - } -} - -impl AnchorSet { - pub fn version(&self) -> &clock::Global { - &self.0.version - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn iter<'a, D>(&'a self, content: &'a Snapshot) -> impl Iterator + 'a - where - D: 'a + TextDimension<'a>, - { - self.0.iter(content).map(|(position, _)| position) - } -} - -impl AnchorRangeMap { - pub fn version(&self) -> &clock::Global { - &self.version - } - - pub fn len(&self) -> usize { - self.entries.len() - } - - pub fn from_full_offset_ranges( - version: clock::Global, - start_bias: Bias, - end_bias: Bias, - entries: Vec<(Range, T)>, - ) -> Self { - Self { - version, - start_bias, - end_bias, - entries, - } - } - - pub fn ranges<'a, D>( - &'a self, - content: &'a Snapshot, - ) -> impl Iterator, &'a T)> + 'a - where - D: 'a + TextDimension<'a>, - { - content - .summaries_for_anchor_ranges( - self.version.clone(), - self.start_bias, - self.end_bias, - self.entries.iter().map(|e| &e.0), - ) - .zip(self.entries.iter().map(|e| &e.1)) - } - - pub fn intersecting_ranges<'a, D, I>( - &'a self, - range: Range<(I, Bias)>, - content: &'a Snapshot, - ) -> impl Iterator, &'a T)> + 'a - where - D: 'a + TextDimension<'a>, - I: ToOffset, - { - let range = content.anchor_at(range.start.0, range.start.1) - ..content.anchor_at(range.end.0, range.end.1); - - let mut probe_anchor = Anchor { - full_offset: Default::default(), - bias: self.start_bias, - version: self.version.clone(), - }; - let start_ix = self.entries.binary_search_by(|probe| { - probe_anchor.full_offset = probe.0.end; - probe_anchor.cmp(&range.start, &content).unwrap() - }); - - match start_ix { - Ok(start_ix) | Err(start_ix) => content - .summaries_for_anchor_ranges( - self.version.clone(), - self.start_bias, - self.end_bias, - self.entries[start_ix..].iter().map(|e| &e.0), - ) - .zip(self.entries.iter().map(|e| &e.1)), - } - } - - pub fn full_offset_ranges(&self) -> impl Iterator, T)> { - self.entries.iter() - } - - pub fn min_by_key<'a, D, F, K>( - &self, - content: &'a Snapshot, - mut extract_key: F, - ) -> Option<(Range, &T)> - where - D: 'a + TextDimension<'a>, - F: FnMut(&T) -> K, - K: Ord, - { - self.entries - .iter() - .min_by_key(|(_, value)| extract_key(value)) - .map(|(range, value)| (self.resolve_range(range, &content), value)) - } - - pub fn max_by_key<'a, D, F, K>( - &self, - content: &'a Snapshot, - mut extract_key: F, - ) -> Option<(Range, &T)> - where - D: 'a + TextDimension<'a>, - F: FnMut(&T) -> K, - K: Ord, - { - self.entries - .iter() - .max_by_key(|(_, value)| extract_key(value)) - .map(|(range, value)| (self.resolve_range(range, &content), value)) - } - - fn resolve_range<'a, D>(&self, range: &Range, content: &'a Snapshot) -> Range - where - D: 'a + TextDimension<'a>, - { - let mut anchor = Anchor { - full_offset: range.start, - bias: self.start_bias, - version: self.version.clone(), - }; - let start = content.summary_for_anchor(&anchor); - - anchor.full_offset = range.end; - anchor.bias = self.end_bias; - let end = content.summary_for_anchor(&anchor); - - start..end - } -} - -impl PartialEq for AnchorRangeMap { - fn eq(&self, other: &Self) -> bool { - self.version == other.version && self.entries == other.entries - } -} - -impl Eq for AnchorRangeMap {} - -impl Debug for AnchorRangeMap { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - let mut f = f.debug_map(); - for (range, value) in &self.entries { - f.key(range); - f.value(value); - } - f.finish() - } -} - -impl Debug for AnchorRangeSet { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut f = f.debug_set(); - for (range, _) in &self.0.entries { - f.entry(range); - } - f.finish() - } -} - -impl AnchorRangeSet { - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn version(&self) -> &clock::Global { - self.0.version() - } - - pub fn ranges<'a, D>(&'a self, content: &'a Snapshot) -> impl 'a + Iterator> - where - D: 'a + TextDimension<'a>, - { - self.0.ranges(content).map(|(range, _)| range) - } -} - -impl Default for AnchorRangeMultimap { - fn default() -> Self { - Self { - entries: Default::default(), - version: Default::default(), - start_bias: Bias::Left, - end_bias: Bias::Left, - } - } -} - -impl AnchorRangeMultimap { - pub fn version(&self) -> &clock::Global { - &self.version - } - - pub fn intersecting_ranges<'a, I, O>( - &'a self, - range: Range, - content: &'a Snapshot, - inclusive: bool, - ) -> impl Iterator, &T)> + 'a - where - I: ToOffset, - O: FromAnchor, - { - let end_bias = if inclusive { Bias::Right } else { Bias::Left }; - let range = range.start.to_full_offset(&content, Bias::Left) - ..range.end.to_full_offset(&content, end_bias); - let mut cursor = self.entries.filter::<_, usize>( - { - let mut endpoint = Anchor { - full_offset: FullOffset(0), - bias: Bias::Right, - version: self.version.clone(), - }; - move |summary: &AnchorRangeMultimapSummary| { - endpoint.full_offset = summary.max_end; - endpoint.bias = self.end_bias; - let max_end = endpoint.to_full_offset(&content, self.end_bias); - let start_cmp = range.start.cmp(&max_end); - - endpoint.full_offset = summary.min_start; - endpoint.bias = self.start_bias; - let min_start = endpoint.to_full_offset(&content, self.start_bias); - let end_cmp = range.end.cmp(&min_start); - - if inclusive { - start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal - } else { - start_cmp == Ordering::Less && end_cmp == Ordering::Greater - } - } - }, - &(), - ); - - std::iter::from_fn({ - let mut endpoint = Anchor { - full_offset: FullOffset(0), - bias: Bias::Left, - version: self.version.clone(), - }; - move || { - if let Some(item) = cursor.item() { - let ix = *cursor.start(); - endpoint.full_offset = item.range.start; - endpoint.bias = self.start_bias; - let start = O::from_anchor(&endpoint, &content); - endpoint.full_offset = item.range.end; - endpoint.bias = self.end_bias; - let end = O::from_anchor(&endpoint, &content); - let value = &item.value; - cursor.next(&()); - Some((ix, start..end, value)) - } else { - None - } - } - }) - } - - pub fn from_full_offset_ranges( - version: clock::Global, - start_bias: Bias, - end_bias: Bias, - entries: impl Iterator, T)>, - ) -> Self { - Self { - version, - start_bias, - end_bias, - entries: SumTree::from_iter( - entries.map(|(range, value)| AnchorRangeMultimapEntry { - range: FullOffsetRange { - start: range.start, - end: range.end, - }, - value, - }), - &(), - ), - } - } - - pub fn full_offset_ranges(&self) -> impl Iterator, &T)> { - self.entries - .cursor::<()>() - .map(|entry| (entry.range.start..entry.range.end, &entry.value)) - } - - pub fn filter<'a, O, F>( - &'a self, - content: &'a Snapshot, - mut f: F, - ) -> impl 'a + Iterator, &T)> - where - O: FromAnchor, - F: 'a + FnMut(&'a T) -> bool, - { - let mut endpoint = Anchor { - full_offset: FullOffset(0), - bias: Bias::Left, - version: self.version.clone(), - }; - self.entries - .cursor::<()>() - .enumerate() - .filter_map(move |(ix, entry)| { - if f(&entry.value) { - endpoint.full_offset = entry.range.start; - endpoint.bias = self.start_bias; - let start = O::from_anchor(&endpoint, &content); - endpoint.full_offset = entry.range.end; - endpoint.bias = self.end_bias; - let end = O::from_anchor(&endpoint, &content); - Some((ix, start..end, &entry.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: FullOffset(0), - end: FullOffset::MAX, - min_start: FullOffset::MAX, - max_end: FullOffset(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: FullOffset(0), - end: FullOffset::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(&self, b: &Range, buffer: &Snapshot) -> Result; fn to_offset(&self, content: &Snapshot) -> Range; diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index eaa2409772..e9e7dd1f22 100644 --- a/crates/text/src/selection.rs +++ b/crates/text/src/selection.rs @@ -1,8 +1,8 @@ use sum_tree::Bias; -use crate::{rope::TextDimension, Snapshot}; +use crate::{rope::TextDimension, Anchor, Snapshot}; -use super::{AnchorRangeMap, Buffer, Point, ToOffset, ToPoint}; +use super::{Buffer, Point, ToOffset, ToPoint}; use std::{cmp::Ordering, ops::Range, sync::Arc}; pub type SelectionSetId = clock::Lamport; @@ -28,7 +28,7 @@ pub struct Selection { pub struct SelectionSet { pub id: SelectionSetId, pub active: bool, - pub selections: Arc>, + pub selections: Arc<[Selection]>, } #[derive(Debug, Eq, PartialEq)] @@ -98,6 +98,21 @@ impl Selection { } } +impl Selection { + pub fn resolve<'a, D: 'a + TextDimension<'a>>( + &'a self, + snapshot: &'a Snapshot, + ) -> Selection { + Selection { + id: self.id, + start: snapshot.summary_for_anchor(&self.start), + end: snapshot.summary_for_anchor(&self.end), + reversed: self.reversed, + goal: self.goal, + } + } +} + impl SelectionSet { pub fn len(&self) -> usize { self.selections.len() @@ -105,69 +120,59 @@ impl SelectionSet { pub fn selections<'a, D>( &'a self, - content: &'a Snapshot, + snapshot: &'a Snapshot, ) -> impl 'a + Iterator> where D: 'a + TextDimension<'a>, { - self.selections - .ranges(content) - .map(|(range, state)| Selection { - id: state.id, - start: range.start, - end: range.end, - reversed: state.reversed, - goal: state.goal, - }) + self.selections.iter().map(|s| s.resolve(snapshot)) } pub fn intersecting_selections<'a, D, I>( &'a self, range: Range<(I, Bias)>, - content: &'a Snapshot, + snapshot: &'a Snapshot, ) -> impl 'a + Iterator> where D: 'a + TextDimension<'a>, 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.start.cmp(&start, snapshot).unwrap()) + { + Ok(ix) | Err(ix) => ix, + }; + let end_ix = match self + .selections + .binary_search_by(|probe| probe.end.cmp(&end, snapshot).unwrap()) + { + Ok(ix) | Err(ix) => ix, + }; + self.selections[start_ix..end_ix] + .iter() + .map(|s| s.resolve(snapshot)) } - pub fn oldest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option> + pub fn oldest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option> where D: 'a + TextDimension<'a>, { 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(|s| s.id) + .map(|s| s.resolve(snapshot)) } - pub fn newest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option> + pub fn newest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option> where D: 'a + TextDimension<'a>, { 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(|s| s.id) + .map(|s| s.resolve(snapshot)) } } diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index dafbd9604c..f7f307049c 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -645,11 +645,13 @@ impl Buffer { assert_eq!(insertion_fragment.fragment_id, fragment.id); } - let insertions = self.snapshot.insertions.items(&()); - assert_eq!( - HashSet::from_iter(insertions.iter().map(|i| &i.fragment_id)).len(), - insertions.len() - ); + let mut cursor = self.snapshot.fragments.cursor::(); + for insertion_fragment in self.snapshot.insertions.cursor::<()>() { + cursor.seek(&insertion_fragment.fragment_id, Bias::Left, &None); + let fragment = cursor.item().unwrap(); + assert_eq!(insertion_fragment.fragment_id, fragment.id); + assert_eq!(insertion_fragment.split_offset, fragment.insertion_offset); + } } } diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 0137a25bbe..5f54c4b8b9 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -27,7 +27,7 @@ use rope::TextDimension; pub use rope::{Chunks, Rope, TextSummary}; pub use selection::*; use std::{ - cmp::{self, Reverse}, + cmp::{self, Ordering}, iter::Iterator, ops::{self, Deref, Range, Sub}, str, @@ -67,8 +67,8 @@ pub struct Transaction { end: clock::Global, edits: Vec, ranges: Vec>, - selections_before: HashMap>>, - selections_after: HashMap>>, + selections_before: HashMap]>>, + selections_after: HashMap]>>, first_edit_at: Instant, last_edit_at: Instant, } @@ -155,7 +155,7 @@ impl History { fn start_transaction( &mut self, start: clock::Global, - selections_before: HashMap>>, + selections_before: HashMap]>>, now: Instant, ) { self.transaction_depth += 1; @@ -175,7 +175,7 @@ impl History { fn end_transaction( &mut self, - selections_after: HashMap>>, + selections_after: HashMap]>>, now: Instant, ) -> Option<&Transaction> { assert_ne!(self.transaction_depth, 0); @@ -430,7 +430,7 @@ pub enum Operation { }, UpdateSelections { set_id: SelectionSetId, - selections: Arc>, + selections: Arc<[Selection]>, lamport_timestamp: clock::Lamport, }, RemoveSelections { @@ -1122,9 +1122,9 @@ impl Buffer { match op { Operation::Edit(edit) => self.version.ge(&edit.version), Operation::Undo { undo, .. } => self.version.ge(&undo.version), - Operation::UpdateSelections { selections, .. } => { - self.version.ge(selections.version()) - } + Operation::UpdateSelections { selections, .. } => selections + .iter() + .all(|s| self.can_resolve(&s.start) && self.can_resolve(&s.end)), Operation::RemoveSelections { .. } => true, Operation::SetActiveSelections { set_id, .. } => { set_id.map_or(true, |set_id| self.selections.contains_key(&set_id)) @@ -1135,6 +1135,14 @@ impl Buffer { } } + fn can_resolve(&self, anchor: &Anchor) -> bool { + match anchor { + Anchor::Min => true, + Anchor::Insertion { timestamp, .. } => self.version.observed(*timestamp), + Anchor::Max => true, + } + } + pub fn peek_undo_stack(&self) -> Option<&Transaction> { self.history.undo_stack.last() } @@ -1280,25 +1288,22 @@ impl Buffer { self.selections.iter() } - fn build_selection_anchor_range_map( + fn build_anchor_selection_set( &self, selections: &[Selection], - ) -> Arc> { - Arc::new(self.anchor_range_map( - Bias::Left, - Bias::Left, - selections.iter().map(|selection| { - let start = selection.start.to_offset(self); - let end = selection.end.to_offset(self); - let range = start..end; - let state = SelectionState { + ) -> Arc<[Selection]> { + Arc::from( + selections + .iter() + .map(|selection| Selection { id: selection.id, + start: self.anchor_before(&selection.start), + end: self.anchor_before(&selection.end), reversed: selection.reversed, goal: selection.goal, - }; - (range, state) - }), - )) + }) + .collect::>(), + ) } pub fn update_selection_set( @@ -1306,7 +1311,7 @@ impl Buffer { set_id: SelectionSetId, selections: &[Selection], ) -> Result { - let selections = self.build_selection_anchor_range_map(selections); + let selections = self.build_anchor_selection_set(selections); let set = self .selections .get_mut(&set_id) @@ -1322,7 +1327,7 @@ impl Buffer { pub fn restore_selection_set( &mut self, set_id: SelectionSetId, - selections: Arc>, + selections: Arc<[Selection]>, ) -> Result { let set = self .selections @@ -1337,7 +1342,7 @@ impl Buffer { } pub fn add_selection_set(&mut self, selections: &[Selection]) -> Operation { - let selections = self.build_selection_anchor_range_map(selections); + let selections = self.build_anchor_selection_set(selections); let set_id = self.lamport_clock.tick(); self.selections.insert( set_id, @@ -1675,19 +1680,81 @@ impl Snapshot { where D: TextDimension<'a>, { - let cx = Some(anchor.version.clone()); - let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>(); - cursor.seek( - &VersionedFullOffset::Offset(anchor.full_offset), - anchor.bias, - &cx, - ); - let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) { - anchor.full_offset - cursor.start().0.full_offset() - } else { - 0 - }; - self.text_summary_for_range(0..cursor.start().1 + overshoot) + match anchor { + Anchor::Min => D::default(), + Anchor::Insertion { + timestamp, + offset, + bias, + } => { + let anchor_key = InsertionFragmentKey { + timestamp: *timestamp, + split_offset: *offset, + }; + let mut insertion_cursor = self.insertions.cursor::(); + insertion_cursor.seek(&anchor_key, *bias, &()); + if let Some(insertion) = insertion_cursor.item() { + let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key); + if comparison == Ordering::Greater + || (*bias == Bias::Left && comparison == Ordering::Equal && *offset > 0) + { + insertion_cursor.prev(&()); + } + } else { + insertion_cursor.prev(&()); + } + let insertion = insertion_cursor.item().expect("invalid insertion"); + debug_assert_eq!(insertion.timestamp, *timestamp, "invalid insertion"); + + let mut fragment_cursor = self.fragments.cursor::<(Locator, usize)>(); + fragment_cursor.seek(&insertion.fragment_id, Bias::Left, &None); + let fragment = fragment_cursor.item().unwrap(); + let mut fragment_offset = fragment_cursor.start().1; + if fragment.visible { + fragment_offset += *offset - insertion.split_offset; + } + self.text_summary_for_range(0..fragment_offset) + } + Anchor::Max => D::from_text_summary(&self.visible_text.summary()), + } + } + + fn full_offset_for_anchor(&self, anchor: &Anchor) -> FullOffset { + match anchor { + Anchor::Min => Default::default(), + Anchor::Insertion { + timestamp, + offset, + bias, + } => { + let anchor_key = InsertionFragmentKey { + timestamp: *timestamp, + split_offset: *offset, + }; + let mut insertion_cursor = self.insertions.cursor::(); + insertion_cursor.seek(&anchor_key, *bias, &()); + if let Some(insertion) = insertion_cursor.item() { + let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key); + if comparison == Ordering::Greater + || (*bias == Bias::Left && comparison == Ordering::Equal && *offset > 0) + { + insertion_cursor.prev(&()); + } + } else { + insertion_cursor.prev(&()); + } + let insertion = insertion_cursor.item().expect("invalid insertion"); + debug_assert_eq!(insertion.timestamp, *timestamp, "invalid insertion"); + + let mut fragment_cursor = self.fragments.cursor::<(Locator, FullOffset)>(); + fragment_cursor.seek(&insertion.fragment_id, Bias::Left, &None); + fragment_cursor.start().1 + (*offset - insertion.split_offset) + } + Anchor::Max => { + let text = self.fragments.summary().text; + FullOffset(text.visible + text.deleted) + } + } } pub fn text_summary_for_range<'a, D, O: ToOffset>(&'a self, range: Range) -> D @@ -1699,70 +1766,6 @@ impl Snapshot { .summary(range.end.to_offset(self)) } - fn summaries_for_anchors<'a, D, I>( - &'a self, - version: clock::Global, - bias: Bias, - ranges: I, - ) -> impl 'a + Iterator - where - D: 'a + TextDimension<'a>, - I: 'a + IntoIterator, - { - let cx = Some(version.clone()); - let mut summary = D::default(); - let mut rope_cursor = self.visible_text.cursor(0); - let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>(); - ranges.into_iter().map(move |offset| { - cursor.seek_forward(&VersionedFullOffset::Offset(*offset), bias, &cx); - let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) { - *offset - cursor.start().0.full_offset() - } else { - 0 - }; - summary.add_assign(&rope_cursor.summary(cursor.start().1 + overshoot)); - summary.clone() - }) - } - - fn summaries_for_anchor_ranges<'a, D, I>( - &'a self, - version: clock::Global, - start_bias: Bias, - end_bias: Bias, - ranges: I, - ) -> impl 'a + Iterator> - where - D: 'a + TextDimension<'a>, - I: 'a + IntoIterator>, - { - let cx = Some(version); - let mut summary = D::default(); - let mut rope_cursor = self.visible_text.cursor(0); - let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>(); - ranges.into_iter().map(move |range| { - cursor.seek_forward(&VersionedFullOffset::Offset(range.start), start_bias, &cx); - let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) { - range.start - cursor.start().0.full_offset() - } else { - 0 - }; - summary.add_assign(&rope_cursor.summary::(cursor.start().1 + overshoot)); - let start_summary = summary.clone(); - - cursor.seek_forward(&VersionedFullOffset::Offset(range.end), end_bias, &cx); - let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) { - range.end - cursor.start().0.full_offset() - } else { - 0 - }; - summary.add_assign(&rope_cursor.summary::(cursor.start().1 + overshoot)); - let end_summary = summary.clone(); - - start_summary..end_summary - }) - } - pub fn anchor_before(&self, position: T) -> Anchor { self.anchor_at(position, Bias::Left) } @@ -1772,139 +1775,22 @@ impl Snapshot { } pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { - Anchor { - full_offset: position.to_full_offset(self, bias), - bias, - version: self.version.clone(), - } - } - - pub fn anchor_map(&self, bias: Bias, entries: E) -> AnchorMap - where - E: IntoIterator, - { - let version = self.version.clone(); - let mut cursor = self.fragments.cursor::(); - let entries = entries - .into_iter() - .map(|(offset, value)| { - cursor.seek_forward(&offset, bias, &None); - let full_offset = FullOffset(cursor.start().deleted + offset); - (full_offset, value) - }) - .collect(); - - AnchorMap { - version, - bias, - entries, - } - } - - pub fn anchor_range_map( - &self, - start_bias: Bias, - end_bias: Bias, - entries: E, - ) -> AnchorRangeMap - where - E: IntoIterator, T)>, - { - let version = self.version.clone(); - let mut cursor = self.fragments.cursor::(); - let entries = entries - .into_iter() - .map(|(range, value)| { - let Range { - start: start_offset, - end: end_offset, - } = range; - cursor.seek_forward(&start_offset, start_bias, &None); - let full_start_offset = FullOffset(cursor.start().deleted + start_offset); - cursor.seek_forward(&end_offset, end_bias, &None); - let full_end_offset = FullOffset(cursor.start().deleted + end_offset); - (full_start_offset..full_end_offset, value) - }) - .collect(); - - AnchorRangeMap { - version, - start_bias, - end_bias, - entries, - } - } - - pub fn anchor_set(&self, bias: Bias, entries: E) -> AnchorSet - where - E: IntoIterator, - { - AnchorSet(self.anchor_map(bias, entries.into_iter().map(|range| (range, ())))) - } - - pub fn anchor_range_set( - &self, - start_bias: Bias, - end_bias: Bias, - entries: E, - ) -> AnchorRangeSet - where - E: IntoIterator>, - { - AnchorRangeSet(self.anchor_range_map( - start_bias, - end_bias, - entries.into_iter().map(|range| (range, ())), - )) - } - - pub fn anchor_range_multimap( - &self, - start_bias: Bias, - end_bias: Bias, - entries: E, - ) -> AnchorRangeMultimap - where - T: Clone, - E: IntoIterator, T)>, - O: ToOffset, - { - let mut entries = entries - .into_iter() - .map(|(range, value)| AnchorRangeMultimapEntry { - range: FullOffsetRange { - start: range.start.to_full_offset(self, start_bias), - end: range.end.to_full_offset(self, end_bias), - }, - value, - }) - .collect::>(); - entries.sort_unstable_by_key(|i| (i.range.start, Reverse(i.range.end))); - AnchorRangeMultimap { - entries: SumTree::from_iter(entries, &()), - version: self.version.clone(), - start_bias, - end_bias, - } - } - - fn full_offset_for_anchor(&self, anchor: &Anchor) -> FullOffset { - let cx = Some(anchor.version.clone()); - let mut cursor = self - .fragments - .cursor::<(VersionedFullOffset, FragmentTextSummary)>(); - cursor.seek( - &VersionedFullOffset::Offset(anchor.full_offset), - anchor.bias, - &cx, - ); - let overshoot = if cursor.item().is_some() { - anchor.full_offset - cursor.start().0.full_offset() + let offset = position.to_offset(self); + if bias == Bias::Left && offset == 0 { + Anchor::Min + } else if bias == Bias::Right && offset == self.len() { + Anchor::Max } else { - 0 - }; - let summary = cursor.start().1; - FullOffset(summary.visible + summary.deleted + overshoot) + let mut fragment_cursor = self.fragments.cursor::<(usize, Locator)>(); + fragment_cursor.seek(&offset, bias, &None); + let fragment = fragment_cursor.item().unwrap(); + let overshoot = offset - fragment_cursor.start().0; + Anchor::Insertion { + timestamp: fragment.insertion_timestamp.local(), + offset: fragment.insertion_offset + overshoot, + bias, + } + } } pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { @@ -2200,10 +2086,6 @@ impl sum_tree::Summary for InsertionFragmentKey { #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FullOffset(pub usize); -impl FullOffset { - const MAX: Self = FullOffset(usize::MAX); -} - impl ops::AddAssign for FullOffset { fn add_assign(&mut self, rhs: usize) { self.0 += rhs; @@ -2239,6 +2121,12 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FullOffset { } } +impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Locator { + fn add_summary(&mut self, summary: &FragmentSummary, _: &Option) { + *self = summary.max_id.clone(); + } +} + impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, FragmentTextSummary> for usize { fn cmp( &self, @@ -2363,9 +2251,9 @@ impl ToOffset for Anchor { } } -impl<'a> ToOffset for &'a Anchor { +impl<'a, T: ToOffset> ToOffset for &'a T { fn to_offset(&self, content: &Snapshot) -> usize { - content.summary_for_anchor(self) + (*self).to_offset(content) } }