use super::{ inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot}, Highlights,};use gpui::{AnyElement, App, ElementId, Window};use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary};use multi_buffer::{ Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,};use std::{ any::TypeId, cmp::{self, Ordering}, fmt, iter, ops::{Add, AddAssign, Deref, DerefMut, Range, Sub}, sync::Arc,};use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary};use ui::IntoElement as _;use util::post_inc; #[derive(Clone)]pub struct FoldPlaceholder { /// Creates an element to represent this fold's placeholder. pub render: Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement>, /// If true, the element is constrained to the shaped width of an ellipsis. pub constrain_width: bool, /// If true, merges the fold with an adjacent one. pub merge_adjacent: bool, /// Category of the fold. Useful for carefully removing from overlapping folds. pub type_tag: Option<TypeId>,} impl Default for FoldPlaceholder { fn default() -> Self { Self { render: Arc::new(|_, _, _, _| gpui::Empty.into_any_element()), constrain_width: true, merge_adjacent: true, type_tag: None, } }} impl FoldPlaceholder { #[cfg(any(test, feature = "test-support"))] pub fn test() -> Self { Self { render: Arc::new(|_id, _range, _window, _cx| gpui::Empty.into_any_element()), constrain_width: true, merge_adjacent: true, type_tag: None, } }} impl fmt::Debug for FoldPlaceholder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FoldPlaceholder") .field("constrain_width", &self.constrain_width) .finish() }} impl Eq for FoldPlaceholder {} impl PartialEq for FoldPlaceholder { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.render, &other.render) && self.constrain_width == other.constrain_width }} #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]pub struct FoldPoint(pub Point); impl FoldPoint { pub fn new(row: u32, column: u32) -> Self { Self(Point::new(row, column)) } pub fn row(self) -> u32 { self.0.row } pub fn column(self) -> u32 { self.0.column } pub fn row_mut(&mut self) -> &mut u32 { &mut self.0.row } #[cfg(test)] pub fn column_mut(&mut self) -> &mut u32 { &mut self.0.column } pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint { let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>(&()); cursor.seek(&self, Bias::Right, &()); let overshoot = self.0 - cursor.start().0 .0; InlayPoint(cursor.start().1 .0 + overshoot) } pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset { let mut cursor = snapshot .transforms .cursor::<(FoldPoint, TransformSummary)>(&()); cursor.seek(&self, Bias::Right, &()); let overshoot = self.0 - cursor.start().1.output.lines; let mut offset = cursor.start().1.output.len; if !overshoot.is_zero() { let transform = cursor.item().expect("display point out of range"); assert!(transform.placeholder.is_none()); let end_inlay_offset = snapshot .inlay_snapshot .to_offset(InlayPoint(cursor.start().1.input.lines + overshoot)); offset += end_inlay_offset.0 - cursor.start().1.input.len; } FoldOffset(offset) }} impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint { fn zero(_cx: &()) -> Self { Default::default() } fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) { self.0 += &summary.output.lines; }} pub(crate) struct FoldMapWriter<'a>(&'a mut FoldMap); impl<'a> FoldMapWriter<'a> { pub(crate) fn fold<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let mut folds = Vec::new(); let snapshot = self.0.snapshot.inlay_snapshot.clone(); for (range, fold_text) in ranges.into_iter() { let buffer = &snapshot.buffer; let range = range.start.to_offset(buffer)..range.end.to_offset(buffer); // Ignore any empty ranges. if range.start == range.end { continue; } // For now, ignore any ranges that span an excerpt boundary. let fold_range = FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end)); if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id { continue; } folds.push(Fold { id: FoldId(post_inc(&mut self.0.next_fold_id.0)), range: fold_range, placeholder: fold_text, }); let inlay_range = snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range, }); } let buffer = &snapshot.buffer; folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer)); self.0.snapshot.folds = { let mut new_tree = SumTree::new(buffer); let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>(buffer); for fold in folds { new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer); new_tree.push(fold, buffer); } new_tree.append(cursor.suffix(buffer), buffer); new_tree }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) } /// Removes any folds with the given ranges. pub(crate) fn remove_folds<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, type_id: TypeId, ) -> (FoldSnapshot, Vec<FoldEdit>) { self.remove_folds_with( ranges, |fold| fold.placeholder.type_tag == Some(type_id), false, ) } /// Removes any folds whose ranges intersect the given ranges. pub(crate) fn unfold_intersecting<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, inclusive: bool, ) -> (FoldSnapshot, Vec<FoldEdit>) { self.remove_folds_with(ranges, |_| true, inclusive) } /// Removes any folds that intersect the given ranges and for which the given predicate /// returns true. fn remove_folds_with<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, should_unfold: impl Fn(&Fold) -> bool, inclusive: bool, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let mut fold_ixs_to_delete = Vec::new(); let snapshot = self.0.snapshot.inlay_snapshot.clone(); let buffer = &snapshot.buffer; for range in ranges.into_iter() { let range = range.start.to_offset(buffer)..range.end.to_offset(buffer); let mut folds_cursor = intersecting_folds(&snapshot, &self.0.snapshot.folds, range.clone(), inclusive); while let Some(fold) = folds_cursor.item() { let offset_range = fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer); if should_unfold(fold) { if offset_range.end > offset_range.start { let inlay_range = snapshot.to_inlay_offset(offset_range.start) ..snapshot.to_inlay_offset(offset_range.end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range, }); } fold_ixs_to_delete.push(*folds_cursor.start()); } folds_cursor.next(buffer); } } fold_ixs_to_delete.sort_unstable(); fold_ixs_to_delete.dedup(); self.0.snapshot.folds = { let mut cursor = self.0.snapshot.folds.cursor::<usize>(buffer); let mut folds = SumTree::new(buffer); for fold_ix in fold_ixs_to_delete { folds.append(cursor.slice(&fold_ix, Bias::Right, buffer), buffer); cursor.next(buffer); } folds.append(cursor.suffix(buffer), buffer); folds }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) }} /// Decides where the fold indicators should be; also tracks parts of a source file that are currently folded.////// See the [`display_map` module documentation](crate::display_map) for more information.pub(crate) struct FoldMap { snapshot: FoldSnapshot, next_fold_id: FoldId,} impl FoldMap { pub(crate) fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) { let this = Self { snapshot: FoldSnapshot { folds: SumTree::new(&inlay_snapshot.buffer), transforms: SumTree::from_item( Transform { summary: TransformSummary { input: inlay_snapshot.text_summary(), output: inlay_snapshot.text_summary(), }, placeholder: None, }, &(), ), inlay_snapshot: inlay_snapshot.clone(), version: 0, }, next_fold_id: FoldId::default(), }; let snapshot = this.snapshot.clone(); (this, snapshot) } pub fn read( &mut self, inlay_snapshot: InlaySnapshot, edits: Vec<InlayEdit>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let edits = self.sync(inlay_snapshot, edits); self.check_invariants(); (self.snapshot.clone(), edits) } pub fn write( &mut self, inlay_snapshot: InlaySnapshot, edits: Vec<InlayEdit>, ) -> (FoldMapWriter, FoldSnapshot, Vec<FoldEdit>) { let (snapshot, edits) = self.read(inlay_snapshot, edits); (FoldMapWriter(self), snapshot, edits) } fn check_invariants(&self) { if cfg!(test) { assert_eq!( self.snapshot.transforms.summary().input.len, self.snapshot.inlay_snapshot.len().0, "transform tree does not match inlay snapshot's length" ); let mut prev_transform_isomorphic = false; for transform in self.snapshot.transforms.iter() { if !transform.is_fold() && prev_transform_isomorphic { panic!( "found adjacent isomorphic transforms: {:?}", self.snapshot.transforms.items(&()) ); } prev_transform_isomorphic = !transform.is_fold(); } let mut folds = self.snapshot.folds.iter().peekable(); while let Some(fold) = folds.next() { if let Some(next_fold) = folds.peek() { let comparison = fold.range.cmp(&next_fold.range, self.snapshot.buffer()); assert!(comparison.is_le()); } } } } fn sync( &mut self, inlay_snapshot: InlaySnapshot, inlay_edits: Vec<InlayEdit>, ) -> Vec<FoldEdit> { if inlay_edits.is_empty() { if self.snapshot.inlay_snapshot.version != inlay_snapshot.version { self.snapshot.version += 1; } self.snapshot.inlay_snapshot = inlay_snapshot; Vec::new() } else { let mut inlay_edits_iter = inlay_edits.iter().cloned().peekable(); let mut new_transforms = SumTree::<Transform>::default(); let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>(&()); cursor.seek(&InlayOffset(0), Bias::Right, &()); while let Some(mut edit) = inlay_edits_iter.next() { if let Some(item) = cursor.item() { if !item.is_fold() { new_transforms.update_last( |transform| { if !transform.is_fold() { transform.summary.add_summary(&item.summary, &()); cursor.next(&()); } }, &(), ); } } new_transforms.append(cursor.slice(&edit.old.start, Bias::Left, &()), &()); edit.new.start -= edit.old.start - *cursor.start(); edit.old.start = *cursor.start(); cursor.seek(&edit.old.end, Bias::Right, &()); cursor.next(&()); let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize; loop { edit.old.end = *cursor.start(); if let Some(next_edit) = inlay_edits_iter.peek() { if next_edit.old.start > edit.old.end { break; } let next_edit = inlay_edits_iter.next().unwrap(); delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize; if next_edit.old.end >= edit.old.end { edit.old.end = next_edit.old.end; cursor.seek(&edit.old.end, Bias::Right, &()); cursor.next(&()); } } else { break; } } edit.new.end = InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize); let anchor = inlay_snapshot .buffer .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start)); let mut folds_cursor = self .snapshot .folds .cursor::<FoldRange>(&inlay_snapshot.buffer); folds_cursor.seek( &FoldRange(anchor..Anchor::max()), Bias::Left, &inlay_snapshot.buffer, ); let mut folds = iter::from_fn({ let inlay_snapshot = &inlay_snapshot; move || { let item = folds_cursor.item().map(|fold| { let buffer_start = fold.range.start.to_offset(&inlay_snapshot.buffer); let buffer_end = fold.range.end.to_offset(&inlay_snapshot.buffer); ( fold.clone(), inlay_snapshot.to_inlay_offset(buffer_start) ..inlay_snapshot.to_inlay_offset(buffer_end), ) }); folds_cursor.next(&inlay_snapshot.buffer); item } }) .peekable(); while folds .peek() .map_or(false, |(_, fold_range)| fold_range.start < edit.new.end) { let (fold, mut fold_range) = folds.next().unwrap(); let sum = new_transforms.summary(); assert!(fold_range.start.0 >= sum.input.len); while folds.peek().map_or(false, |(next_fold, next_fold_range)| { next_fold_range.start < fold_range.end || (next_fold_range.start == fold_range.end && fold.placeholder.merge_adjacent && next_fold.placeholder.merge_adjacent) }) { let (_, next_fold_range) = folds.next().unwrap(); if next_fold_range.end > fold_range.end { fold_range.end = next_fold_range.end; } } if fold_range.start.0 > sum.input.len { let text_summary = inlay_snapshot .text_summary_for_range(InlayOffset(sum.input.len)..fold_range.start); push_isomorphic(&mut new_transforms, text_summary); } if fold_range.end > fold_range.start { const ELLIPSIS: &str = "⋯"; let fold_id = fold.id; new_transforms.push( Transform { summary: TransformSummary { output: TextSummary::from(ELLIPSIS), input: inlay_snapshot .text_summary_for_range(fold_range.start..fold_range.end), }, placeholder: Some(TransformPlaceholder { text: ELLIPSIS, renderer: ChunkRenderer { render: Arc::new(move |cx| { (fold.placeholder.render)( fold_id, fold.range.0.clone(), cx.window, cx.context, ) }), constrain_width: fold.placeholder.constrain_width, }, }), }, &(), ); } }Lines 1–500 of 1993. Load more
use super::{ inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot}, Highlights, }; use gpui::{AnyElement, App, ElementId, Window}; use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary}; use multi_buffer::{ Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, }; use std::{ any::TypeId, cmp::{self, Ordering}, fmt, iter, ops::{Add, AddAssign, Deref, DerefMut, Range, Sub}, sync::Arc, }; use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary}; use ui::IntoElement as _; use util::post_inc; #[derive(Clone)] pub struct FoldPlaceholder { /// Creates an element to represent this fold's placeholder. pub render: Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut Window, &mut App) -> AnyElement>, /// If true, the element is constrained to the shaped width of an ellipsis. pub constrain_width: bool, /// If true, merges the fold with an adjacent one. pub merge_adjacent: bool, /// Category of the fold. Useful for carefully removing from overlapping folds. pub type_tag: Option<TypeId>, } impl Default for FoldPlaceholder { fn default() -> Self { Self { render: Arc::new(|_, _, _, _| gpui::Empty.into_any_element()), constrain_width: true, merge_adjacent: true, type_tag: None, } } } impl FoldPlaceholder { #[cfg(any(test, feature = "test-support"))] pub fn test() -> Self { Self { render: Arc::new(|_id, _range, _window, _cx| gpui::Empty.into_any_element()), constrain_width: true, merge_adjacent: true, type_tag: None, } } } impl fmt::Debug for FoldPlaceholder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FoldPlaceholder") .field("constrain_width", &self.constrain_width) .finish() } } impl Eq for FoldPlaceholder {} impl PartialEq for FoldPlaceholder { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.render, &other.render) && self.constrain_width == other.constrain_width } } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct FoldPoint(pub Point); impl FoldPoint { pub fn new(row: u32, column: u32) -> Self { Self(Point::new(row, column)) } pub fn row(self) -> u32 { self.0.row } pub fn column(self) -> u32 { self.0.column } pub fn row_mut(&mut self) -> &mut u32 { &mut self.0.row } #[cfg(test)] pub fn column_mut(&mut self) -> &mut u32 { &mut self.0.column } pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint { let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>(&()); cursor.seek(&self, Bias::Right, &()); let overshoot = self.0 - cursor.start().0 .0; InlayPoint(cursor.start().1 .0 + overshoot) } pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset { let mut cursor = snapshot .transforms .cursor::<(FoldPoint, TransformSummary)>(&()); cursor.seek(&self, Bias::Right, &()); let overshoot = self.0 - cursor.start().1.output.lines; let mut offset = cursor.start().1.output.len; if !overshoot.is_zero() { let transform = cursor.item().expect("display point out of range"); assert!(transform.placeholder.is_none()); let end_inlay_offset = snapshot .inlay_snapshot .to_offset(InlayPoint(cursor.start().1.input.lines + overshoot)); offset += end_inlay_offset.0 - cursor.start().1.input.len; } FoldOffset(offset) } } impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint { fn zero(_cx: &()) -> Self { Default::default() } fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) { self.0 += &summary.output.lines; } } pub(crate) struct FoldMapWriter<'a>(&'a mut FoldMap); impl<'a> FoldMapWriter<'a> { pub(crate) fn fold<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let mut folds = Vec::new(); let snapshot = self.0.snapshot.inlay_snapshot.clone(); for (range, fold_text) in ranges.into_iter() { let buffer = &snapshot.buffer; let range = range.start.to_offset(buffer)..range.end.to_offset(buffer); // Ignore any empty ranges. if range.start == range.end { continue; } // For now, ignore any ranges that span an excerpt boundary. let fold_range = FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end)); if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id { continue; } folds.push(Fold { id: FoldId(post_inc(&mut self.0.next_fold_id.0)), range: fold_range, placeholder: fold_text, }); let inlay_range = snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range, }); } let buffer = &snapshot.buffer; folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer)); self.0.snapshot.folds = { let mut new_tree = SumTree::new(buffer); let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>(buffer); for fold in folds { new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer); new_tree.push(fold, buffer); } new_tree.append(cursor.suffix(buffer), buffer); new_tree }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) } /// Removes any folds with the given ranges. pub(crate) fn remove_folds<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, type_id: TypeId, ) -> (FoldSnapshot, Vec<FoldEdit>) { self.remove_folds_with( ranges, |fold| fold.placeholder.type_tag == Some(type_id), false, ) } /// Removes any folds whose ranges intersect the given ranges. pub(crate) fn unfold_intersecting<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, inclusive: bool, ) -> (FoldSnapshot, Vec<FoldEdit>) { self.remove_folds_with(ranges, |_| true, inclusive) } /// Removes any folds that intersect the given ranges and for which the given predicate /// returns true. fn remove_folds_with<T: ToOffset>( &mut self, ranges: impl IntoIterator<Item = Range<T>>, should_unfold: impl Fn(&Fold) -> bool, inclusive: bool, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let mut fold_ixs_to_delete = Vec::new(); let snapshot = self.0.snapshot.inlay_snapshot.clone(); let buffer = &snapshot.buffer; for range in ranges.into_iter() { let range = range.start.to_offset(buffer)..range.end.to_offset(buffer); let mut folds_cursor = intersecting_folds(&snapshot, &self.0.snapshot.folds, range.clone(), inclusive); while let Some(fold) = folds_cursor.item() { let offset_range = fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer); if should_unfold(fold) { if offset_range.end > offset_range.start { let inlay_range = snapshot.to_inlay_offset(offset_range.start) ..snapshot.to_inlay_offset(offset_range.end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range, }); } fold_ixs_to_delete.push(*folds_cursor.start()); } folds_cursor.next(buffer); } } fold_ixs_to_delete.sort_unstable(); fold_ixs_to_delete.dedup(); self.0.snapshot.folds = { let mut cursor = self.0.snapshot.folds.cursor::<usize>(buffer); let mut folds = SumTree::new(buffer); for fold_ix in fold_ixs_to_delete { folds.append(cursor.slice(&fold_ix, Bias::Right, buffer), buffer); cursor.next(buffer); } folds.append(cursor.suffix(buffer), buffer); folds }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) } } /// Decides where the fold indicators should be; also tracks parts of a source file that are currently folded. /// /// See the [`display_map` module documentation](crate::display_map) for more information. pub(crate) struct FoldMap { snapshot: FoldSnapshot, next_fold_id: FoldId, } impl FoldMap { pub(crate) fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) { let this = Self { snapshot: FoldSnapshot { folds: SumTree::new(&inlay_snapshot.buffer), transforms: SumTree::from_item( Transform { summary: TransformSummary { input: inlay_snapshot.text_summary(), output: inlay_snapshot.text_summary(), }, placeholder: None, }, &(), ), inlay_snapshot: inlay_snapshot.clone(), version: 0, }, next_fold_id: FoldId::default(), }; let snapshot = this.snapshot.clone(); (this, snapshot) } pub fn read( &mut self, inlay_snapshot: InlaySnapshot, edits: Vec<InlayEdit>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let edits = self.sync(inlay_snapshot, edits); self.check_invariants(); (self.snapshot.clone(), edits) } pub fn write( &mut self, inlay_snapshot: InlaySnapshot, edits: Vec<InlayEdit>, ) -> (FoldMapWriter, FoldSnapshot, Vec<FoldEdit>) { let (snapshot, edits) = self.read(inlay_snapshot, edits); (FoldMapWriter(self), snapshot, edits) } fn check_invariants(&self) { if cfg!(test) { assert_eq!( self.snapshot.transforms.summary().input.len, self.snapshot.inlay_snapshot.len().0, "transform tree does not match inlay snapshot's length" ); let mut prev_transform_isomorphic = false; for transform in self.snapshot.transforms.iter() { if !transform.is_fold() && prev_transform_isomorphic { panic!( "found adjacent isomorphic transforms: {:?}", self.snapshot.transforms.items(&()) ); } prev_transform_isomorphic = !transform.is_fold(); } let mut folds = self.snapshot.folds.iter().peekable(); while let Some(fold) = folds.next() { if let Some(next_fold) = folds.peek() { let comparison = fold.range.cmp(&next_fold.range, self.snapshot.buffer()); assert!(comparison.is_le()); } } } } fn sync( &mut self, inlay_snapshot: InlaySnapshot, inlay_edits: Vec<InlayEdit>, ) -> Vec<FoldEdit> { if inlay_edits.is_empty() { if self.snapshot.inlay_snapshot.version != inlay_snapshot.version { self.snapshot.version += 1; } self.snapshot.inlay_snapshot = inlay_snapshot; Vec::new() } else { let mut inlay_edits_iter = inlay_edits.iter().cloned().peekable(); let mut new_transforms = SumTree::<Transform>::default(); let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>(&()); cursor.seek(&InlayOffset(0), Bias::Right, &()); while let Some(mut edit) = inlay_edits_iter.next() { if let Some(item) = cursor.item() { if !item.is_fold() { new_transforms.update_last( |transform| { if !transform.is_fold() { transform.summary.add_summary(&item.summary, &()); cursor.next(&()); } }, &(), ); } } new_transforms.append(cursor.slice(&edit.old.start, Bias::Left, &()), &()); edit.new.start -= edit.old.start - *cursor.start(); edit.old.start = *cursor.start(); cursor.seek(&edit.old.end, Bias::Right, &()); cursor.next(&()); let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize; loop { edit.old.end = *cursor.start(); if let Some(next_edit) = inlay_edits_iter.peek() { if next_edit.old.start > edit.old.end { break; } let next_edit = inlay_edits_iter.next().unwrap(); delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize; if next_edit.old.end >= edit.old.end { edit.old.end = next_edit.old.end; cursor.seek(&edit.old.end, Bias::Right, &()); cursor.next(&()); } } else { break; } } edit.new.end = InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize); let anchor = inlay_snapshot .buffer .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start)); let mut folds_cursor = self .snapshot .folds .cursor::<FoldRange>(&inlay_snapshot.buffer); folds_cursor.seek( &FoldRange(anchor..Anchor::max()), Bias::Left, &inlay_snapshot.buffer, ); let mut folds = iter::from_fn({ let inlay_snapshot = &inlay_snapshot; move || { let item = folds_cursor.item().map(|fold| { let buffer_start = fold.range.start.to_offset(&inlay_snapshot.buffer); let buffer_end = fold.range.end.to_offset(&inlay_snapshot.buffer); ( fold.clone(), inlay_snapshot.to_inlay_offset(buffer_start) ..inlay_snapshot.to_inlay_offset(buffer_end), ) }); folds_cursor.next(&inlay_snapshot.buffer); item } }) .peekable(); while folds .peek() .map_or(false, |(_, fold_range)| fold_range.start < edit.new.end) { let (fold, mut fold_range) = folds.next().unwrap(); let sum = new_transforms.summary(); assert!(fold_range.start.0 >= sum.input.len); while folds.peek().map_or(false, |(next_fold, next_fold_range)| { next_fold_range.start < fold_range.end || (next_fold_range.start == fold_range.end && fold.placeholder.merge_adjacent && next_fold.placeholder.merge_adjacent) }) { let (_, next_fold_range) = folds.next().unwrap(); if next_fold_range.end > fold_range.end { fold_range.end = next_fold_range.end; } } if fold_range.start.0 > sum.input.len { let text_summary = inlay_snapshot .text_summary_for_range(InlayOffset(sum.input.len)..fold_range.start); push_isomorphic(&mut new_transforms, text_summary); } if fold_range.end > fold_range.start { const ELLIPSIS: &str = "⋯"; let fold_id = fold.id; new_transforms.push( Transform { summary: TransformSummary { output: TextSummary::from(ELLIPSIS), input: inlay_snapshot .text_summary_for_range(fold_range.start..fold_range.end), }, placeholder: Some(TransformPlaceholder { text: ELLIPSIS, renderer: ChunkRenderer { render: Arc::new(move |cx| { (fold.placeholder.render)( fold_id, fold.range.0.clone(), cx.window, cx.context, ) }), constrain_width: fold.placeholder.constrain_width, }, }), }, &(), ); } }