From 924e1578ea237095d3f9c4053f4d3d05fcfb523b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 30 Nov 2021 13:29:04 -0800 Subject: [PATCH] Use `&Snapshot` directly instead of `impl Into>` The text::Buffer and its snapshot already used the same representation for their content, so we can just make Buffer deref to a Snapshot. --- crates/editor/src/display_map/fold_map.rs | 8 +- crates/editor/src/editor.rs | 12 +- crates/editor/src/movement.rs | 4 +- crates/language/src/buffer.rs | 52 +-- crates/language/src/tests.rs | 13 +- crates/project/src/worktree.rs | 2 +- crates/text/src/anchor.rs | 62 ++- crates/text/src/selection.rs | 21 +- crates/text/src/tests.rs | 12 +- crates/text/src/text.rs | 447 ++++++---------------- 10 files changed, 197 insertions(+), 436 deletions(-) diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 840497447b..16ce634e1d 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -359,7 +359,8 @@ impl FoldMap { } if fold.start > sum.input.bytes { - let text_summary = buffer.text_summary_for_range(sum.input.bytes..fold.start); + let text_summary = buffer + .text_summary_for_range::(sum.input.bytes..fold.start); new_transforms.push( Transform { summary: TransformSummary { @@ -400,7 +401,8 @@ impl FoldMap { let sum = new_transforms.summary(); if sum.input.bytes < edit.new.end { - let text_summary = buffer.text_summary_for_range(sum.input.bytes..edit.new.end); + let text_summary = + buffer.text_summary_for_range::(sum.input.bytes..edit.new.end); new_transforms.push( Transform { summary: TransformSummary { @@ -539,7 +541,7 @@ impl Snapshot { let buffer_end = cursor.start().1 + end_in_transform; summary += self .buffer_snapshot - .text_summary_for_range(buffer_start..buffer_end); + .text_summary_for_range::(buffer_start..buffer_end); } } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 27a31addb3..05e97a1a37 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1358,7 +1358,7 @@ impl Editor { let buffer = self.buffer.read(cx); if old_selections .iter() - .zip(autoclose_pair_state.ranges.ranges::(buffer)) + .zip(autoclose_pair_state.ranges.ranges::(buffer)) .all(|(selection, autoclose_range)| { let autoclose_range_end = autoclose_range.end.to_offset(buffer); selection.is_empty() && selection.start == autoclose_range_end @@ -3072,7 +3072,7 @@ impl Editor { .read(cx) .selection_set(set_id) .unwrap() - .intersecting_selections::(range, buffer); + .intersecting_selections::(range, buffer); selections .map(move |s| Selection { @@ -3090,7 +3090,7 @@ impl Editor { D: 'a + TextDimension<'a> + Ord, { let buffer = self.buffer.read(cx); - let mut selections = self.selection_set(cx).selections::(buffer).peekable(); + let mut selections = self.selection_set(cx).selections::(buffer).peekable(); let mut pending_selection = self.pending_selection(cx); iter::from_fn(move || { if let Some(pending) = pending_selection.as_mut() { @@ -3124,8 +3124,8 @@ impl Editor { let buffer = self.buffer.read(cx); self.pending_selection.as_ref().map(|pending| Selection { id: pending.selection.id, - start: pending.selection.start.summary::(buffer), - end: pending.selection.end.summary::(buffer), + start: pending.selection.start.summary::(buffer), + end: pending.selection.end.summary::(buffer), reversed: pending.selection.reversed, goal: pending.selection.goal, }) @@ -3201,7 +3201,7 @@ impl Editor { if selections.len() == autoclose_pair_state.ranges.len() { selections .iter() - .zip(autoclose_pair_state.ranges.ranges::(buffer)) + .zip(autoclose_pair_state.ranges.ranges::(buffer)) .all(|(selection, autoclose_range)| { let head = selection.head().to_point(&*buffer); autoclose_range.start <= head && autoclose_range.end >= head diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index dbf0f0f7e2..bffd21be2a 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -183,7 +183,7 @@ pub fn next_word_boundary(map: &DisplayMapSnapshot, mut point: DisplayPoint) -> pub fn is_inside_word(map: &DisplayMapSnapshot, point: DisplayPoint) -> bool { let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left); - let text = map.buffer_snapshot.text(); + let text = &map.buffer_snapshot; let next_char_kind = text.chars_at(ix).next().map(char_kind); let prev_char_kind = text.reversed_chars_at(ix).next().map(char_kind); prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word)) @@ -193,7 +193,7 @@ pub fn surrounding_word(map: &DisplayMapSnapshot, point: DisplayPoint) -> Range< let mut start = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left); let mut end = start; - let text = map.buffer_snapshot.text(); + let text = &map.buffer_snapshot; let mut next_chars = text.chars_at(start).peekable(); let mut prev_chars = text.reversed_chars_at(start).peekable(); let word_kind = cmp::max( diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index d71662d0d9..7e7cff890c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -692,9 +692,9 @@ impl Buffer { .pending_snapshots .get(&version) .ok_or_else(|| anyhow!("missing snapshot"))?; - snapshot.buffer_snapshot.content() + &snapshot.buffer_snapshot } else { - self.content() + self.deref() }; let abs_path = self.file.as_ref().and_then(|f| f.abs_path()); @@ -816,9 +816,8 @@ impl Buffer { T: 'a + ToOffset, O: 'a + FromAnchor, { - let content = self.content(); self.diagnostics - .intersecting_ranges(search_range, content, true) + .intersecting_ranges(search_range, self, true) .map(move |(_, range, diagnostic)| (range, diagnostic)) } @@ -829,9 +828,8 @@ impl Buffer { where O: 'a + FromAnchor, { - let content = self.content(); self.diagnostics - .filter(content, move |diagnostic| diagnostic.group_id == group_id) + .filter(self, move |diagnostic| diagnostic.group_id == group_id) .map(move |(_, range, diagnostic)| (range, diagnostic)) } @@ -875,12 +873,12 @@ impl Buffer { for request in autoindent_requests { let old_to_new_rows = request .edited - .iter::(&request.before_edit) + .iter::(&request.before_edit) .map(|point| point.row) .zip( request .edited - .iter::(&snapshot) + .iter::(&snapshot) .map(|point| point.row), ) .collect::>(); @@ -943,7 +941,7 @@ impl Buffer { if let Some(inserted) = request.inserted.as_ref() { let inserted_row_ranges = contiguous_ranges( inserted - .ranges::(&snapshot) + .ranges::(&snapshot) .flat_map(|range| range.start.row..range.end.row + 1), max_rows_between_yields, ); @@ -991,7 +989,7 @@ impl Buffer { for selection_set_id in &selection_set_ids { if let Ok(set) = self.selection_set(*selection_set_id) { let new_selections = set - .selections::(&*self) + .selections::(&*self) .map(|selection| { if selection.start.column == 0 { let delta = Point::new( @@ -1023,10 +1021,6 @@ impl Buffer { .unwrap(); } - pub fn indent_column_for_line(&self, row: u32) -> u32 { - self.content().indent_column_for_line(row) - } - fn set_indent_column_for_line(&mut self, row: u32, column: u32, cx: &mut ModelContext) { let current_column = self.indent_column_for_line(row); if column > current_column { @@ -1239,7 +1233,7 @@ impl Buffer { // Skip invalid ranges and coalesce contiguous ones. let mut ranges: Vec> = Vec::new(); for range in ranges_iter { - let range = range.start.to_offset(&*self)..range.end.to_offset(&*self); + let range = range.start.to_offset(self)..range.end.to_offset(self); if !new_text.is_empty() || !range.is_empty() { if let Some(prev_range) = ranges.last_mut() { if prev_range.end >= range.start { @@ -1260,10 +1254,10 @@ impl Buffer { self.pending_autoindent.take(); let autoindent_request = if autoindent && self.language.is_some() { let before_edit = self.snapshot(); - let edited = self.content().anchor_set( + let edited = self.anchor_set( Bias::Left, ranges.iter().filter_map(|range| { - let start = range.start.to_point(&*self); + let start = range.start.to_point(self); if new_text.starts_with('\n') && start.column == self.line_len(start.row) { None } else { @@ -1285,7 +1279,7 @@ impl Buffer { let mut inserted = None; if let Some(first_newline_ix) = first_newline_ix { let mut delta = 0isize; - inserted = Some(self.content().anchor_range_set( + inserted = Some(self.anchor_range_set( Bias::Left, Bias::Right, ranges.iter().map(|range| { @@ -1524,24 +1518,6 @@ impl Deref for Buffer { } } -impl<'a> From<&'a Buffer> for Content<'a> { - fn from(buffer: &'a Buffer) -> Self { - Self::from(&buffer.text) - } -} - -impl<'a> From<&'a mut Buffer> for Content<'a> { - fn from(buffer: &'a mut Buffer) -> Self { - Self::from(&buffer.text) - } -} - -impl<'a> From<&'a Snapshot> for Content<'a> { - fn from(snapshot: &'a Snapshot) -> Self { - Self::from(&snapshot.text) - } -} - impl Snapshot { fn suggest_autoindents<'a>( &'a self, @@ -1657,14 +1633,14 @@ impl Snapshot { range: Range, theme: Option<&'a SyntaxTheme>, ) -> Chunks<'a> { - let range = range.start.to_offset(&*self)..range.end.to_offset(&*self); + let range = range.start.to_offset(self)..range.end.to_offset(self); let mut highlights = None; let mut diagnostic_endpoints = Vec::::new(); if let Some(theme) = theme { for (_, range, diagnostic) in self.diagnostics - .intersecting_ranges(range.clone(), self.content(), true) + .intersecting_ranges(range.clone(), self, true) { diagnostic_endpoints.push(DiagnosticEndpoint { offset: range.start, diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 800bd66290..cff74af1e3 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -1,6 +1,15 @@ use super::*; use gpui::{ModelHandle, MutableAppContext, Task}; -use std::{any::Any, cell::RefCell, ffi::OsString, iter::FromIterator, ops::Range, path::PathBuf, rc::Rc, time::{Duration, Instant, SystemTime}}; +use std::{ + any::Any, + cell::RefCell, + ffi::OsString, + iter::FromIterator, + ops::Range, + path::PathBuf, + rc::Rc, + time::{Duration, Instant, SystemTime}, +}; use unindent::Unindent as _; #[test] @@ -359,7 +368,7 @@ fn test_autoindent_moves_selections(cx: &mut MutableAppContext) { let selection_ranges = buffer .selection_set(selection_set_id) .unwrap() - .selections::(&buffer) + .selections::(&buffer) .map(|selection| selection.point_range(&buffer)) .collect::>(); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 42ded059a4..65ef0ea4db 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -3630,7 +3630,7 @@ mod tests { let cursor_positions = buffer .selection_set(selection_set_id) .unwrap() - .selections::(&*buffer) + .selections::(&*buffer) .map(|selection| { assert_eq!(selection.start, selection.end); selection.start diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index c158cc36e1..846c57274b 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,6 +1,6 @@ -use crate::rope::TextDimension; +use crate::{rope::TextDimension, Snapshot}; -use super::{Buffer, Content, FromAnchor, FullOffset, Point, ToOffset}; +use super::{Buffer, FromAnchor, FullOffset, Point, ToOffset}; use anyhow::Result; use std::{ cmp::Ordering, @@ -83,9 +83,7 @@ impl Anchor { } } - pub fn cmp<'a>(&self, other: &Anchor, buffer: impl Into>) -> Result { - let buffer = buffer.into(); - + pub fn cmp<'a>(&self, other: &Anchor, buffer: &Snapshot) -> Result { if self == other { return Ok(Ordering::Equal); } @@ -117,12 +115,11 @@ impl Anchor { } } - pub fn summary<'a, D, C>(&self, content: C) -> D + pub fn summary<'a, D>(&self, content: &'a Snapshot) -> D where D: TextDimension<'a>, - C: Into>, { - content.into().summary_for_anchor(self) + content.summary_for_anchor(self) } } @@ -135,13 +132,11 @@ impl AnchorMap { self.entries.len() } - pub fn iter<'a, D, C>(&'a self, content: C) -> impl Iterator + 'a + pub fn iter<'a, D>(&'a self, snapshot: &'a Snapshot) -> impl Iterator + 'a where D: 'a + TextDimension<'a>, - C: 'a + Into>, { - let content = content.into(); - content + snapshot .summaries_for_anchors( self.version.clone(), self.bias, @@ -160,10 +155,9 @@ impl AnchorSet { self.0.len() } - pub fn iter<'a, D, C>(&'a self, content: C) -> impl Iterator + 'a + pub fn iter<'a, D>(&'a self, content: &'a Snapshot) -> impl Iterator + 'a where D: 'a + TextDimension<'a>, - C: 'a + Into>, { self.0.iter(content).map(|(position, _)| position) } @@ -194,12 +188,11 @@ impl AnchorRangeMap { pub fn ranges<'a, D>( &'a self, - content: impl Into> + 'a, + content: &'a Snapshot, ) -> impl Iterator, &'a T)> + 'a where D: 'a + TextDimension<'a>, { - let content = content.into(); content .summaries_for_anchor_ranges( self.version.clone(), @@ -213,13 +206,12 @@ impl AnchorRangeMap { pub fn intersecting_ranges<'a, D, I>( &'a self, range: Range<(I, Bias)>, - content: impl Into> + 'a, + content: &'a Snapshot, ) -> impl Iterator, &'a T)> + 'a where D: 'a + TextDimension<'a>, I: ToOffset, { - let content = content.into(); let range = content.anchor_at(range.start.0, range.start.1) ..content.anchor_at(range.end.0, range.end.1); @@ -249,43 +241,39 @@ impl AnchorRangeMap { self.entries.iter() } - pub fn min_by_key<'a, C, D, F, K>( + pub fn min_by_key<'a, D, F, K>( &self, - content: C, + content: &'a Snapshot, mut extract_key: F, ) -> Option<(Range, &T)> where - C: Into>, D: 'a + TextDimension<'a>, F: FnMut(&T) -> K, K: Ord, { - let content = content.into(); 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, C, D, F, K>( + pub fn max_by_key<'a, D, F, K>( &self, - content: C, + content: &'a Snapshot, mut extract_key: F, ) -> Option<(Range, &T)> where - C: Into>, D: 'a + TextDimension<'a>, F: FnMut(&T) -> K, K: Ord, { - let content = content.into(); 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: &Content<'a>) -> Range + fn resolve_range<'a, D>(&self, range: &Range, content: &'a Snapshot) -> Range where D: 'a + TextDimension<'a>, { @@ -342,10 +330,9 @@ impl AnchorRangeSet { self.0.version() } - pub fn ranges<'a, D, C>(&'a self, content: C) -> impl 'a + Iterator> + pub fn ranges<'a, D>(&'a self, content: &'a Snapshot) -> impl 'a + Iterator> where D: 'a + TextDimension<'a>, - C: 'a + Into>, { self.0.ranges(content).map(|(range, _)| range) } @@ -370,7 +357,7 @@ impl AnchorRangeMultimap { pub fn intersecting_ranges<'a, I, O>( &'a self, range: Range, - content: Content<'a>, + content: &'a Snapshot, inclusive: bool, ) -> impl Iterator, &T)> + 'a where @@ -382,7 +369,6 @@ impl AnchorRangeMultimap { ..range.end.to_full_offset(&content, end_bias); let mut cursor = self.entries.filter::<_, usize>( { - let content = content.clone(); let mut endpoint = Anchor { full_offset: FullOffset(0), bias: Bias::Right, @@ -465,7 +451,7 @@ impl AnchorRangeMultimap { pub fn filter<'a, O, F>( &'a self, - content: Content<'a>, + content: &'a Snapshot, mut f: F, ) -> impl 'a + Iterator, &T)> where @@ -574,21 +560,19 @@ impl<'a> sum_tree::SeekTarget<'a, AnchorRangeMultimapSummary, FullOffsetRange> f } pub trait AnchorRangeExt { - fn cmp<'a>(&self, b: &Range, buffer: impl Into>) -> Result; - fn to_offset<'a>(&self, content: impl Into>) -> Range; + fn cmp(&self, b: &Range, buffer: &Snapshot) -> Result; + fn to_offset(&self, content: &Snapshot) -> Range; } impl AnchorRangeExt for Range { - fn cmp<'a>(&self, other: &Range, buffer: impl Into>) -> Result { - let buffer = buffer.into(); - Ok(match self.start.cmp(&other.start, &buffer)? { + fn cmp(&self, other: &Range, buffer: &Snapshot) -> Result { + Ok(match self.start.cmp(&other.start, buffer)? { Ordering::Equal => other.end.cmp(&self.end, buffer)?, ord @ _ => ord, }) } - fn to_offset<'a>(&self, content: impl Into>) -> Range { - let content = content.into(); + fn to_offset(&self, content: &Snapshot) -> Range { self.start.to_offset(&content)..self.end.to_offset(&content) } } diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index bca0b7663f..eaa2409772 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; +use crate::{rope::TextDimension, Snapshot}; -use super::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint}; +use super::{AnchorRangeMap, Buffer, Point, ToOffset, ToPoint}; use std::{cmp::Ordering, ops::Range, sync::Arc}; pub type SelectionSetId = clock::Lamport; @@ -103,10 +103,12 @@ impl SelectionSet { self.selections.len() } - pub fn selections<'a, D, C>(&'a self, content: C) -> impl 'a + Iterator> + pub fn selections<'a, D>( + &'a self, + content: &'a Snapshot, + ) -> impl 'a + Iterator> where D: 'a + TextDimension<'a>, - C: 'a + Into>, { self.selections .ranges(content) @@ -119,15 +121,14 @@ impl SelectionSet { }) } - pub fn intersecting_selections<'a, D, I, C>( + pub fn intersecting_selections<'a, D, I>( &'a self, range: Range<(I, Bias)>, - content: C, + content: &'a Snapshot, ) -> impl 'a + Iterator> where D: 'a + TextDimension<'a>, I: 'a + ToOffset, - C: 'a + Into>, { self.selections .intersecting_ranges(range, content) @@ -140,10 +141,9 @@ impl SelectionSet { }) } - pub fn oldest_selection<'a, D, C>(&'a self, content: C) -> Option> + pub fn oldest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option> where D: 'a + TextDimension<'a>, - C: 'a + Into>, { self.selections .min_by_key(content, |selection| selection.id) @@ -156,10 +156,9 @@ impl SelectionSet { }) } - pub fn newest_selection<'a, D, C>(&'a self, content: C) -> Option> + pub fn newest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option> where D: 'a + TextDimension<'a>, - C: 'a + Into>, { self.selections .max_by_key(content, |selection| selection.id) diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index 2a8b938114..9c681b1071 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -67,7 +67,7 @@ fn test_random_edits(mut rng: StdRng) { let range = buffer.random_byte_range(0, &mut rng); assert_eq!( - buffer.text_summary_for_range(range.clone()), + buffer.text_summary_for_range::(range.clone()), TextSummary::from(&reference_string[range]) ); @@ -119,7 +119,7 @@ fn test_line_len() { fn test_text_summary_for_range() { let buffer = Buffer::new(0, 0, History::new("ab\nefg\nhklm\nnopqrs\ntuvwxyz".into())); assert_eq!( - buffer.text_summary_for_range(1..3), + buffer.text_summary_for_range::(1..3), TextSummary { bytes: 2, lines: Point::new(1, 0), @@ -131,7 +131,7 @@ fn test_text_summary_for_range() { } ); assert_eq!( - buffer.text_summary_for_range(1..12), + buffer.text_summary_for_range::(1..12), TextSummary { bytes: 11, lines: Point::new(3, 0), @@ -143,7 +143,7 @@ fn test_text_summary_for_range() { } ); assert_eq!( - buffer.text_summary_for_range(0..20), + buffer.text_summary_for_range::(0..20), TextSummary { bytes: 20, lines: Point::new(4, 1), @@ -155,7 +155,7 @@ fn test_text_summary_for_range() { } ); assert_eq!( - buffer.text_summary_for_range(0..22), + buffer.text_summary_for_range::(0..22), TextSummary { bytes: 22, lines: Point::new(4, 3), @@ -167,7 +167,7 @@ fn test_text_summary_for_range() { } ); assert_eq!( - buffer.text_summary_for_range(7..22), + buffer.text_summary_for_range::(7..22), TextSummary { bytes: 15, lines: Point::new(2, 3), diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 8c04fac9a6..8d941d6d02 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -24,7 +24,7 @@ pub use selection::*; use std::{ cmp::{self, Reverse}, iter::Iterator, - ops::{self, Range}, + ops::{self, Deref, Range}, str, sync::Arc, time::{Duration, Instant}, @@ -34,12 +34,8 @@ use sum_tree::{FilterCursor, SumTree}; #[derive(Clone)] pub struct Buffer { - fragments: SumTree, - visible_text: Rope, - deleted_text: Rope, - pub version: clock::Global, + snapshot: Snapshot, last_edit: clock::Local, - undo_map: UndoMap, history: History, selections: HashMap, deferred_ops: OperationQueue, @@ -50,6 +46,15 @@ pub struct Buffer { lamport_clock: clock::Lamport, } +#[derive(Clone)] +pub struct Snapshot { + visible_text: Rope, + deleted_text: Rope, + undo_map: UndoMap, + fragments: SumTree, + pub version: clock::Global, +} + #[derive(Clone, Debug)] pub struct Transaction { start: clock::Global, @@ -440,12 +445,14 @@ impl Buffer { } Buffer { - visible_text, - deleted_text: Rope::new(), - fragments, - version, + snapshot: Snapshot { + visible_text, + deleted_text: Rope::new(), + fragments, + version, + undo_map: Default::default(), + }, last_edit: clock::Local::default(), - undo_map: Default::default(), history, selections: HashMap::default(), deferred_ops: OperationQueue::new(), @@ -471,55 +478,6 @@ impl Buffer { } } - pub fn content<'a>(&'a self) -> Content<'a> { - self.into() - } - - pub fn as_rope(&self) -> &Rope { - &self.visible_text - } - - pub fn text_summary_for_range(&self, range: Range) -> TextSummary { - self.content().text_summary_for_range(range) - } - - pub fn anchor_before(&self, position: T) -> Anchor { - self.anchor_at(position, Bias::Left) - } - - pub fn anchor_after(&self, position: T) -> Anchor { - self.anchor_at(position, Bias::Right) - } - - pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { - self.content().anchor_at(position, bias) - } - - pub fn anchor_range_set( - &self, - start_bias: Bias, - end_bias: Bias, - entries: E, - ) -> AnchorRangeSet - where - E: IntoIterator>, - { - self.content() - .anchor_range_set(start_bias, end_bias, entries) - } - - pub fn point_for_offset(&self, offset: usize) -> Result { - self.content().point_for_offset(offset) - } - - pub fn clip_point(&self, point: Point, bias: Bias) -> Point { - self.content().clip_point(point, bias) - } - - pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { - self.visible_text.clip_offset(offset, bias) - } - pub fn replica_id(&self) -> ReplicaId { self.local_clock.replica_id } @@ -528,78 +486,6 @@ impl Buffer { self.remote_id } - pub fn text_summary(&self) -> TextSummary { - self.visible_text.summary() - } - - pub fn len(&self) -> usize { - self.content().len() - } - - pub fn line_len(&self, row: u32) -> u32 { - self.content().line_len(row) - } - - pub fn is_line_blank(&self, row: u32) -> bool { - self.content().is_line_blank(row) - } - - pub fn max_point(&self) -> Point { - self.visible_text.max_point() - } - - pub fn row_count(&self) -> u32 { - self.max_point().row + 1 - } - - pub fn text(&self) -> String { - self.text_for_range(0..self.len()).collect() - } - - pub fn text_for_range<'a, T: ToOffset>(&'a self, range: Range) -> Chunks<'a> { - self.content().text_for_range(range) - } - - pub fn chars(&self) -> impl Iterator + '_ { - self.chars_at(0) - } - - pub fn chars_at<'a, T: 'a + ToOffset>( - &'a self, - position: T, - ) -> impl Iterator + 'a { - self.content().chars_at(position) - } - - pub fn reversed_chars_at<'a, T: 'a + ToOffset>( - &'a self, - position: T, - ) -> impl Iterator + 'a { - self.content().reversed_chars_at(position) - } - - pub fn chars_for_range(&self, range: Range) -> impl Iterator + '_ { - self.text_for_range(range).flat_map(str::chars) - } - - pub fn bytes_in_range(&self, range: Range) -> rope::Bytes { - self.content().bytes_in_range(range) - } - - pub fn contains_str_at(&self, position: T, needle: &str) -> bool - where - T: ToOffset, - { - let position = position.to_offset(self); - position == self.clip_offset(position, Bias::Left) - && self - .bytes_in_range(position..self.len()) - .flatten() - .copied() - .take(needle.len()) - .eq(needle.bytes()) - } - pub fn deferred_ops_len(&self) -> usize { self.deferred_ops.len() } @@ -630,7 +516,7 @@ impl Buffer { self.history.push(edit.clone()); self.history.push_undo(edit.timestamp.local()); self.last_edit = edit.timestamp.local(); - self.version.observe(edit.timestamp.local()); + self.snapshot.version.observe(edit.timestamp.local()); self.end_transaction(None); edit } @@ -755,9 +641,9 @@ impl Buffer { let (visible_text, deleted_text) = new_ropes.finish(); drop(old_fragments); - self.fragments = new_fragments; - self.visible_text = visible_text; - self.deleted_text = deleted_text; + self.snapshot.fragments = new_fragments; + self.snapshot.visible_text = visible_text; + self.snapshot.deleted_text = deleted_text; edit.new_text = new_text; edit } @@ -787,7 +673,7 @@ impl Buffer { edit.new_text.as_deref(), edit.timestamp, ); - self.version.observe(edit.timestamp.local()); + self.snapshot.version.observe(edit.timestamp.local()); self.history.push(edit); } } @@ -797,7 +683,7 @@ impl Buffer { } => { if !self.version.observed(undo.id) { self.apply_undo(&undo)?; - self.version.observe(undo.id); + self.snapshot.version.observe(undo.id); self.lamport_clock.observe(lamport_timestamp); } } @@ -989,15 +875,15 @@ impl Buffer { let (visible_text, deleted_text) = new_ropes.finish(); drop(old_fragments); - self.fragments = new_fragments; - self.visible_text = visible_text; - self.deleted_text = deleted_text; + self.snapshot.fragments = new_fragments; + self.snapshot.visible_text = visible_text; + self.snapshot.deleted_text = deleted_text; self.local_clock.observe(timestamp.local()); self.lamport_clock.observe(timestamp.lamport()); } fn apply_undo(&mut self, undo: &UndoOperation) -> Result<()> { - self.undo_map.insert(undo); + self.snapshot.undo_map.insert(undo); let mut cx = undo.version.clone(); for edit_id in undo.counts.keys().copied() { @@ -1065,9 +951,9 @@ impl Buffer { drop(old_fragments); let (visible_text, deleted_text) = new_ropes.finish(); - self.fragments = new_fragments; - self.visible_text = visible_text; - self.deleted_text = deleted_text; + self.snapshot.fragments = new_fragments; + self.snapshot.visible_text = visible_text; + self.snapshot.deleted_text = deleted_text; Ok(()) } @@ -1216,7 +1102,7 @@ impl Buffer { version: transaction.start.clone(), }; self.apply_undo(&undo)?; - self.version.observe(undo.id); + self.snapshot.version.observe(undo.id); Ok(Operation::Undo { undo, @@ -1238,7 +1124,7 @@ impl Buffer { &self, selections: &[Selection], ) -> Arc> { - Arc::new(self.content().anchor_range_map( + Arc::new(self.anchor_range_map( Bias::Left, Bias::Left, selections.iter().map(|selection| { @@ -1345,16 +1231,6 @@ impl Buffer { lamport_timestamp: self.lamport_clock.tick(), }) } - - pub fn edits_since<'a, D>( - &'a self, - since: &'a clock::Global, - ) -> impl 'a + Iterator> - where - D: 'a + TextDimension<'a> + Ord, - { - self.content().edits_since(since) - } } #[cfg(any(test, feature = "test-support"))] @@ -1516,13 +1392,12 @@ impl Buffer { } } -#[derive(Clone)] -pub struct Snapshot { - visible_text: Rope, - deleted_text: Rope, - undo_map: UndoMap, - fragments: SumTree, - version: clock::Global, +impl Deref for Buffer { + type Target = Snapshot; + + fn deref(&self) -> &Self::Target { + &self.snapshot + } } impl Snapshot { @@ -1530,24 +1405,38 @@ impl Snapshot { &self.visible_text } + pub fn row_count(&self) -> u32 { + self.max_point().row + 1 + } + pub fn len(&self) -> usize { self.visible_text.len() } - pub fn line_len(&self, row: u32) -> u32 { - self.content().line_len(row) + pub fn chars(&self) -> impl Iterator + '_ { + self.chars_at(0) } - pub fn is_line_blank(&self, row: u32) -> bool { - self.content().is_line_blank(row) + pub fn chars_for_range(&self, range: Range) -> impl Iterator + '_ { + self.text_for_range(range).flat_map(str::chars) } - pub fn indent_column_for_line(&self, row: u32) -> u32 { - self.content().indent_column_for_line(row) + pub fn contains_str_at(&self, position: T, needle: &str) -> bool + where + T: ToOffset, + { + let position = position.to_offset(self); + position == self.clip_offset(position, Bias::Left) + && self + .bytes_in_range(position..self.len()) + .flatten() + .copied() + .take(needle.len()) + .eq(needle.bytes()) } - pub fn text(&self) -> Rope { - self.visible_text.clone() + pub fn text(&self) -> String { + self.text_for_range(0..self.len()).collect() } pub fn text_summary(&self) -> TextSummary { @@ -1558,34 +1447,6 @@ impl Snapshot { self.visible_text.max_point() } - pub fn bytes_in_range(&self, range: Range) -> rope::Bytes { - self.content().bytes_in_range(range) - } - - pub fn text_for_range(&self, range: Range) -> Chunks { - self.content().text_for_range(range) - } - - pub fn text_summary_for_range(&self, range: Range) -> TextSummary - where - T: ToOffset, - { - let range = range.start.to_offset(self.content())..range.end.to_offset(self.content()); - self.content().text_summary_for_range(range) - } - - pub fn point_for_offset(&self, offset: usize) -> Result { - self.content().point_for_offset(offset) - } - - pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { - self.visible_text.clip_offset(offset, bias) - } - - pub fn clip_point(&self, point: Point, bias: Bias) -> Point { - self.visible_text.clip_point(point, bias) - } - pub fn to_offset(&self, point: Point) -> usize { self.visible_text.point_to_offset(point) } @@ -1594,122 +1455,36 @@ impl Snapshot { self.visible_text.offset_to_point(offset) } - pub fn anchor_before(&self, position: T) -> Anchor { - self.content().anchor_at(position, Bias::Left) - } - - pub fn anchor_after(&self, position: T) -> Anchor { - self.content().anchor_at(position, Bias::Right) - } - - pub fn edits_since<'a, D>( - &'a self, - since: &'a clock::Global, - ) -> impl 'a + Iterator> - where - D: 'a + TextDimension<'a> + Ord, - { - self.content().edits_since(since) - } - pub fn version(&self) -> &clock::Global { &self.version } - pub fn content(&self) -> Content { - self.into() - } -} - -#[derive(Clone)] -pub struct Content<'a> { - visible_text: &'a Rope, - deleted_text: &'a Rope, - undo_map: &'a UndoMap, - fragments: &'a SumTree, - version: &'a clock::Global, -} - -impl<'a> From<&'a Snapshot> for Content<'a> { - fn from(snapshot: &'a Snapshot) -> Self { - Self { - visible_text: &snapshot.visible_text, - deleted_text: &snapshot.deleted_text, - undo_map: &snapshot.undo_map, - fragments: &snapshot.fragments, - version: &snapshot.version, - } - } -} - -impl<'a> From<&'a Buffer> for Content<'a> { - fn from(buffer: &'a Buffer) -> Self { - Self { - visible_text: &buffer.visible_text, - deleted_text: &buffer.deleted_text, - undo_map: &buffer.undo_map, - fragments: &buffer.fragments, - version: &buffer.version, - } - } -} - -impl<'a> From<&'a mut Buffer> for Content<'a> { - fn from(buffer: &'a mut Buffer) -> Self { - Self { - visible_text: &buffer.visible_text, - deleted_text: &buffer.deleted_text, - undo_map: &buffer.undo_map, - fragments: &buffer.fragments, - version: &buffer.version, - } - } -} - -impl<'a> From<&'a Content<'a>> for Content<'a> { - fn from(content: &'a Content) -> Self { - Self { - visible_text: &content.visible_text, - deleted_text: &content.deleted_text, - undo_map: &content.undo_map, - fragments: &content.fragments, - version: &content.version, - } - } -} - -impl<'a> Content<'a> { - fn max_point(&self) -> Point { - self.visible_text.max_point() - } - - fn len(&self) -> usize { - self.fragments.extent::(&None) - } - - pub fn chars_at(&self, position: T) -> impl Iterator + 'a { + pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator + 'a { let offset = position.to_offset(self); self.visible_text.chars_at(offset) } - pub fn reversed_chars_at(&self, position: T) -> impl Iterator + 'a { + pub fn reversed_chars_at<'a, T: ToOffset>( + &'a self, + position: T, + ) -> impl Iterator + 'a { let offset = position.to_offset(self); self.visible_text.reversed_chars_at(offset) } - pub fn bytes_in_range(&self, range: Range) -> rope::Bytes<'a> { + pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range) -> rope::Bytes<'a> { let start = range.start.to_offset(self); let end = range.end.to_offset(self); self.visible_text.bytes_in_range(start..end) } - pub fn text_for_range(&self, range: Range) -> Chunks<'a> { + pub fn text_for_range<'a, T: ToOffset>(&'a self, range: Range) -> Chunks<'a> { let start = range.start.to_offset(self); let end = range.end.to_offset(self); self.visible_text.chunks_in_range(start..end) } - fn line_len(&self, row: u32) -> u32 { + pub fn line_len(&self, row: u32) -> u32 { let row_start_offset = Point::new(row, 0).to_offset(self); let row_end_offset = if row >= self.max_point().row { self.len() @@ -1719,7 +1494,7 @@ impl<'a> Content<'a> { (row_end_offset - row_start_offset) as u32 } - fn is_line_blank(&self, row: u32) -> bool { + pub fn is_line_blank(&self, row: u32) -> bool { self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row))) .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none()) } @@ -1736,7 +1511,7 @@ impl<'a> Content<'a> { result } - fn summary_for_anchor(&self, anchor: &Anchor) -> D + fn summary_for_anchor<'a, D>(&'a self, anchor: &Anchor) -> D where D: TextDimension<'a>, { @@ -1755,15 +1530,17 @@ impl<'a> Content<'a> { self.text_summary_for_range(0..cursor.start().1 + overshoot) } - fn text_summary_for_range(&self, range: Range) -> D + pub fn text_summary_for_range<'a, D, O: ToOffset>(&'a self, range: Range) -> D where D: TextDimension<'a>, { - self.visible_text.cursor(range.start).summary(range.end) + self.visible_text + .cursor(range.start.to_offset(self)) + .summary(range.end.to_offset(self)) } - fn summaries_for_anchors( - &self, + fn summaries_for_anchors<'a, D, I>( + &'a self, version: clock::Global, bias: Bias, ranges: I, @@ -1788,8 +1565,8 @@ impl<'a> Content<'a> { }) } - fn summaries_for_anchor_ranges( - &self, + fn summaries_for_anchor_ranges<'a, D, I>( + &'a self, version: clock::Global, start_bias: Bias, end_bias: Bias, @@ -1826,7 +1603,15 @@ impl<'a> Content<'a> { }) } - fn anchor_at(&self, position: T, bias: Bias) -> Anchor { + pub fn anchor_before(&self, position: T) -> Anchor { + self.anchor_at(position, Bias::Left) + } + + pub fn anchor_after(&self, position: T) -> Anchor { + self.anchor_at(position, Bias::Right) + } + + pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { Anchor { full_offset: position.to_full_offset(self, bias), bias, @@ -1962,6 +1747,10 @@ impl<'a> Content<'a> { FullOffset(summary.visible + summary.deleted + overshoot) } + pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { + self.visible_text.clip_offset(offset, bias) + } + pub fn clip_point(&self, point: Point, bias: Bias) -> Point { self.visible_text.clip_point(point, bias) } @@ -1970,7 +1759,7 @@ impl<'a> Content<'a> { self.visible_text.clip_point_utf16(point, bias) } - fn point_for_offset(&self, offset: usize) -> Result { + pub fn point_for_offset(&self, offset: usize) -> Result { if offset <= self.len() { Ok(self.text_summary_for_range(0..offset)) } else { @@ -1978,11 +1767,14 @@ impl<'a> Content<'a> { } } - pub fn edits_since(&self, since: &'a clock::Global) -> impl 'a + Iterator> + pub fn edits_since<'a, D>( + &'a self, + since: &'a clock::Global, + ) -> impl 'a + Iterator> where D: 'a + TextDimension<'a> + Ord, { - let fragments_cursor = if since == self.version { + let fragments_cursor = if *since == self.version { None } else { Some( @@ -2324,10 +2116,9 @@ impl Operation { } pub trait ToOffset { - fn to_offset<'a>(&self, content: impl Into>) -> usize; + fn to_offset<'a>(&self, content: &Snapshot) -> usize; - fn to_full_offset<'a>(&self, content: impl Into>, bias: Bias) -> FullOffset { - let content = content.into(); + fn to_full_offset<'a>(&self, content: &Snapshot, bias: Bias) -> FullOffset { let offset = self.to_offset(&content); let mut cursor = content.fragments.cursor::(); cursor.seek(&offset, bias, &None); @@ -2336,70 +2127,70 @@ pub trait ToOffset { } impl ToOffset for Point { - fn to_offset<'a>(&self, content: impl Into>) -> usize { - content.into().visible_text.point_to_offset(*self) + fn to_offset<'a>(&self, content: &Snapshot) -> usize { + content.visible_text.point_to_offset(*self) } } impl ToOffset for PointUtf16 { - fn to_offset<'a>(&self, content: impl Into>) -> usize { - content.into().visible_text.point_utf16_to_offset(*self) + fn to_offset<'a>(&self, content: &Snapshot) -> usize { + content.visible_text.point_utf16_to_offset(*self) } } impl ToOffset for usize { - fn to_offset<'a>(&self, content: impl Into>) -> usize { - assert!(*self <= content.into().len(), "offset is out of range"); + fn to_offset<'a>(&self, content: &Snapshot) -> usize { + assert!(*self <= content.len(), "offset is out of range"); *self } } impl ToOffset for Anchor { - fn to_offset<'a>(&self, content: impl Into>) -> usize { - content.into().summary_for_anchor(self) + fn to_offset<'a>(&self, content: &Snapshot) -> usize { + content.summary_for_anchor(self) } } impl<'a> ToOffset for &'a Anchor { - fn to_offset<'b>(&self, content: impl Into>) -> usize { - content.into().summary_for_anchor(self) + fn to_offset(&self, content: &Snapshot) -> usize { + content.summary_for_anchor(self) } } pub trait ToPoint { - fn to_point<'a>(&self, content: impl Into>) -> Point; + fn to_point<'a>(&self, content: &Snapshot) -> Point; } impl ToPoint for Anchor { - fn to_point<'a>(&self, content: impl Into>) -> Point { - content.into().summary_for_anchor(self) + fn to_point<'a>(&self, content: &Snapshot) -> Point { + content.summary_for_anchor(self) } } impl ToPoint for usize { - fn to_point<'a>(&self, content: impl Into>) -> Point { - content.into().visible_text.offset_to_point(*self) + fn to_point<'a>(&self, content: &Snapshot) -> Point { + content.visible_text.offset_to_point(*self) } } impl ToPoint for Point { - fn to_point<'a>(&self, _: impl Into>) -> Point { + fn to_point<'a>(&self, _: &Snapshot) -> Point { *self } } pub trait FromAnchor { - fn from_anchor<'a>(anchor: &Anchor, content: &Content<'a>) -> Self; + fn from_anchor(anchor: &Anchor, content: &Snapshot) -> Self; } impl FromAnchor for Point { - fn from_anchor<'a>(anchor: &Anchor, content: &Content<'a>) -> Self { + fn from_anchor(anchor: &Anchor, content: &Snapshot) -> Self { anchor.to_point(content) } } impl FromAnchor for usize { - fn from_anchor<'a>(anchor: &Anchor, content: &Content<'a>) -> Self { + fn from_anchor(anchor: &Anchor, content: &Snapshot) -> Self { anchor.to_offset(content) } }