Implement FoldMap's folds using a SumTree

This required passing a `Context` object to `Summary` and introducing a
new `SeekDimension` trait that allows comparing two dimensions and pass
an additional context object.
This commit is contained in:
Antonio Scandurra
2021-05-06 16:20:23 +02:00
parent 7fb7a6adfc
commit 901525bf77
9 changed files with 354 additions and 151 deletions
+179 -50
View File
@@ -1,10 +1,10 @@
use super::{
buffer, Anchor, AnchorRangeExt, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
buffer::{self, AnchorRangeExt},
Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
};
use crate::{
sum_tree::{self, Cursor, SumTree},
sum_tree::{self, Cursor, SeekBias, SumTree},
time,
util::find_insertion_index,
};
use anyhow::{anyhow, Result};
use gpui::{AppContext, ModelHandle};
@@ -14,12 +14,11 @@ use std::{
iter::Take,
ops::Range,
};
use sum_tree::{Dimension, SeekBias};
pub struct FoldMap {
buffer: ModelHandle<Buffer>,
transforms: Mutex<SumTree<Transform>>,
folds: Vec<Range<Anchor>>,
folds: SumTree<Fold>,
last_sync: Mutex<time::Global>,
}
@@ -29,7 +28,7 @@ impl FoldMap {
let text_summary = buffer.text_summary();
Self {
buffer: buffer_handle,
folds: Vec::new(),
folds: Default::default(),
transforms: Mutex::new(SumTree::from_item(Transform {
summary: TransformSummary {
buffer: text_summary.clone(),
@@ -76,17 +75,20 @@ impl FoldMap {
pub fn folds_in_range<'a, T>(
&'a self,
range: Range<T>,
app: &'a AppContext,
ctx: &'a AppContext,
) -> Result<impl Iterator<Item = &'a Range<Anchor>>>
where
T: ToOffset,
{
let buffer = self.buffer.read(app);
let buffer = self.buffer.read(ctx);
let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
Ok(self.folds.iter().filter(move |fold| {
range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less
&& range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater
}))
Ok(self
.folds
.filter::<_, usize>(move |summary| {
range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal
&& range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal
})
.map(|f| &f.0))
}
pub fn fold<T: ToOffset>(
@@ -99,16 +101,31 @@ impl FoldMap {
let mut edits = Vec::new();
let buffer = self.buffer.read(ctx);
for range in ranges.into_iter() {
let start = range.start.to_offset(buffer)?;
let end = range.end.to_offset(buffer)?;
let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?;
let fold = if range.start == range.end {
Fold(buffer.anchor_after(range.start)?..buffer.anchor_after(range.end)?)
} else {
Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?)
};
edits.push(Edit {
old_range: start..end,
new_range: start..end,
old_range: range.clone(),
new_range: range.clone(),
});
let fold = buffer.anchor_after(start)?..buffer.anchor_before(end)?;
let ix = find_insertion_index(&self.folds, |probe| probe.cmp(&fold, buffer))?;
self.folds.insert(ix, fold);
self.folds = {
let mut new_tree = SumTree::new();
let mut cursor = self.folds.cursor::<_, ()>();
new_tree.push_tree_with_ctx(
cursor.slice_with_ctx(
&FoldRange(fold.0.clone()),
SeekBias::Right,
Some(buffer),
),
Some(buffer),
);
new_tree.push_with_ctx(fold, Some(buffer));
new_tree.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer));
new_tree
};
}
edits.sort_unstable_by(|a, b| {
a.old_range
@@ -131,27 +148,45 @@ impl FoldMap {
let buffer = self.buffer.read(ctx);
let mut edits = Vec::new();
let mut fold_ixs_to_delete = Vec::new();
for range in ranges.into_iter() {
let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
let range = start..end;
// Remove intersecting folds and add their ranges to edits that are passed to apply_edits
self.folds.retain(|fold| {
if fold.start.cmp(&end, buffer).unwrap() > Ordering::Equal
|| fold.end.cmp(&start, buffer).unwrap() < Ordering::Equal
{
true
} else {
let offset_range =
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap();
edits.push(Edit {
old_range: offset_range.clone(),
new_range: offset_range,
});
false
}
let mut cursor = self.folds.filter::<_, usize>(|summary| {
range.start.cmp(&summary.max_end, buffer).unwrap() < Ordering::Equal
&& range.end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal
});
while let Some(fold) = cursor.item() {
let offset_range =
fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap();
edits.push(Edit {
old_range: offset_range.clone(),
new_range: offset_range,
});
fold_ixs_to_delete.push(*cursor.start());
cursor.next();
}
}
fold_ixs_to_delete.sort_unstable();
fold_ixs_to_delete.dedup();
self.folds = {
let mut cursor = self.folds.cursor::<_, ()>();
let mut folds = SumTree::new();
for fold_ix in fold_ixs_to_delete {
folds.push_tree_with_ctx(
cursor.slice_with_ctx(&fold_ix, SeekBias::Right, Some(buffer)),
Some(buffer),
);
cursor.next();
}
folds.push_tree_with_ctx(cursor.suffix_with_ctx(Some(buffer)), Some(buffer));
folds
};
self.apply_edits(edits, ctx);
Ok(())
@@ -262,14 +297,14 @@ impl FoldMap {
((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
let anchor = buffer.anchor_before(edit.new_range.start).unwrap();
let folds_start =
find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))
.unwrap();
let mut folds = self.folds[folds_start..]
.iter()
.map(|fold| {
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
})
let mut folds_cursor = self.folds.cursor::<_, ()>();
folds_cursor.seek_with_ctx(
&FoldRange(anchor..Anchor::End),
SeekBias::Left,
Some(buffer),
);
let mut folds = folds_cursor
.map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap())
.peekable();
while folds
@@ -422,18 +457,112 @@ impl sum_tree::Item for Transform {
}
impl sum_tree::Summary for TransformSummary {
type Context = ();
fn add_summary(&mut self, other: &Self) {
self.buffer += &other.buffer;
self.display += &other.display;
}
}
impl<'a> Dimension<'a, TransformSummary> for TransformSummary {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
fn add_summary(&mut self, summary: &'a TransformSummary) {
sum_tree::Summary::add_summary(self, summary);
}
}
#[derive(Clone, Debug)]
struct Fold(Range<Anchor>);
impl sum_tree::Item for Fold {
type Summary = FoldSummary;
fn summary(&self) -> Self::Summary {
FoldSummary {
start: self.0.start.clone(),
end: self.0.end.clone(),
min_start: self.0.start.clone(),
max_end: self.0.end.clone(),
count: 1,
}
}
}
#[derive(Clone, Debug)]
struct FoldSummary {
start: Anchor,
end: Anchor,
min_start: Anchor,
max_end: Anchor,
count: usize,
}
impl Default for FoldSummary {
fn default() -> Self {
Self {
start: Anchor::Start,
end: Anchor::End,
min_start: Anchor::Start,
max_end: Anchor::Start,
count: 0,
}
}
}
impl sum_tree::Summary for FoldSummary {
type Context = Buffer;
fn add_summary_with_ctx(&mut self, other: &Self, buffer: Option<&Self::Context>) {
let buffer = buffer.unwrap();
if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
self.min_start = other.min_start.clone();
}
if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
self.max_end = other.max_end.clone();
}
if cfg!(debug_assertions) {
let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
let end_comparison = self.end.cmp(&other.end, buffer).unwrap();
assert!(start_comparison <= Ordering::Equal);
if start_comparison == Ordering::Equal {
assert!(end_comparison >= Ordering::Equal);
}
}
self.start = other.start.clone();
self.end = other.end.clone();
self.count += other.count;
}
}
#[derive(Clone, Debug)]
struct FoldRange(Range<Anchor>);
impl Default for FoldRange {
fn default() -> Self {
Self(Anchor::Start..Anchor::End)
}
}
impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
fn add_summary(&mut self, summary: &'a FoldSummary) {
self.0.start = summary.start.clone();
self.0.end = summary.end.clone();
}
}
impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for FoldRange {
fn cmp(&self, other: &Self, buffer: Option<&Buffer>) -> Ordering {
self.0.cmp(&other.0, buffer.unwrap()).unwrap()
}
}
impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
fn add_summary(&mut self, summary: &'a FoldSummary) {
*self += summary.count;
}
}
pub struct BufferRows<'a> {
cursor: Cursor<'a, Transform, DisplayPoint, TransformSummary>,
display_point: Point,
@@ -498,7 +627,7 @@ impl<'a> Iterator for Chars<'a> {
}
}
impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
fn add_summary(&mut self, summary: &'a TransformSummary) {
self.0 += &summary.display.lines;
}
@@ -507,19 +636,19 @@ impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
pub struct DisplayOffset(usize);
impl<'a> Dimension<'a, TransformSummary> for DisplayOffset {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
fn add_summary(&mut self, summary: &'a TransformSummary) {
self.0 += &summary.display.chars;
}
}
impl<'a> Dimension<'a, TransformSummary> for Point {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
fn add_summary(&mut self, summary: &'a TransformSummary) {
*self += &summary.buffer.lines;
}
}
impl<'a> Dimension<'a, TransformSummary> for usize {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
fn add_summary(&mut self, summary: &'a TransformSummary) {
*self += &summary.buffer.chars;
}
@@ -894,13 +1023,13 @@ mod tests {
fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
let buffer = self.buffer.read(app);
let mut folds = self.folds.clone();
let mut folds = self.folds.items();
// Ensure sorting doesn't change how folds get merged and displayed.
folds.sort_by(|a, b| a.cmp(b, buffer).unwrap());
folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
let mut fold_ranges = folds
.iter()
.map(|fold| {
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap()
})
.peekable();
+1 -1
View File
@@ -1,6 +1,6 @@
mod fold_map;
use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
use anyhow::Result;
pub use fold_map::BufferRows;
use fold_map::{FoldMap, FoldMapSnapshot};