use crate::display_map::inlay_map::InlayChunk; use super::{ Highlights, inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},};use gpui::{AnyElement, App, ElementId, HighlightStyle, Pixels, Window};use language::{Edit, HighlightId, Point};use multi_buffer::{ Anchor, AnchorRangeExt, MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset,};use project::InlayId;use std::{ any::TypeId, cmp::{self, Ordering}, fmt, iter, ops::{Add, AddAssign, Deref, DerefMut, Range, Sub, SubAssign}, sync::Arc, usize,};use sum_tree::{Bias, Cursor, Dimensions, FilterCursor, SumTree, Summary, TreeMap};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 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, _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 } #[ztracing::instrument(skip_all)] pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint { let (start, _, _) = snapshot .transforms .find::<Dimensions<FoldPoint, InlayPoint>, _>((), &self, Bias::Right); let overshoot = self.0 - start.0.0; InlayPoint(start.1.0 + overshoot) } #[ztracing::instrument(skip_all)] pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset { let (start, _, item) = snapshot .transforms .find::<Dimensions<FoldPoint, TransformSummary>, _>((), &self, Bias::Right); let overshoot = self.0 - start.1.output.lines; let mut offset = start.1.output.len; if !overshoot.is_zero() { let transform = item.expect("display point out of range"); assert!(transform.placeholder.is_none()); let end_inlay_offset = snapshot .inlay_snapshot .to_offset(InlayPoint(start.1.input.lines + overshoot)); offset += end_inlay_offset.0 - 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 FoldMapWriter<'_> { #[ztracing::instrument(skip_all)] 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 { self.0.snapshot.fold_metadata_by_id.insert( fold.id, FoldMetadata { range: fold.range.clone(), width: None, }, ); new_tree.append(cursor.slice(&fold.range, Bias::Right), buffer); new_tree.push(fold, buffer); } new_tree.append(cursor.suffix(), 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. #[ztracing::instrument(skip_all)] 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. #[ztracing::instrument(skip_all)] 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. #[ztracing::instrument(skip_all)] 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()); self.0.snapshot.fold_metadata_by_id.remove(&fold.id); } folds_cursor.next(); } } fold_ixs_to_delete.sort_unstable(); fold_ixs_to_delete.dedup(); self.0.snapshot.folds = { let mut cursor = self.0.snapshot.folds.cursor::<MultiBufferOffset>(buffer); let mut folds = SumTree::new(buffer); for fold_ix in fold_ixs_to_delete { folds.append(cursor.slice(&fold_ix, Bias::Right), buffer); cursor.next(); } folds.append(cursor.suffix(), buffer); folds }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) } #[ztracing::instrument(skip_all)] pub(crate) fn update_fold_widths( &mut self, new_widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone(); let buffer = &inlay_snapshot.buffer; for (id, new_width) in new_widths { let ChunkRendererId::Fold(id) = id else { continue; }; if let Some(metadata) = self.0.snapshot.fold_metadata_by_id.get(&id).cloned() && Some(new_width) != metadata.width { let buffer_start = metadata.range.start.to_offset(buffer); let buffer_end = metadata.range.end.to_offset(buffer); let inlay_range = inlay_snapshot.to_inlay_offset(buffer_start) ..inlay_snapshot.to_inlay_offset(buffer_end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range.clone(), }); self.0.snapshot.fold_metadata_by_id.insert( id, FoldMetadata { range: metadata.range, width: Some(new_width), }, ); } } let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(inlay_snapshot, 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 struct FoldMap { snapshot: FoldSnapshot, next_fold_id: FoldId,} impl FoldMap { #[ztracing::instrument(skip_all)] pub 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, version: 0, fold_metadata_by_id: TreeMap::default(), }, next_fold_id: FoldId::default(), }; let snapshot = this.snapshot.clone(); (this, snapshot) } #[ztracing::instrument(skip_all)] 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) } #[ztracing::instrument(skip_all)] pub(crate) 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) } #[ztracing::instrument(skip_all)] 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()); } } } } #[ztracing::instrument(skip_all)] 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(MultiBufferOffset(0)), Bias::Right); while let Some(mut edit) = inlay_edits_iter.next() { if let Some(item) = cursor.item() && !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() as isize - edit.old_len() 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() as isize - next_edit.old_len() 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(MultiBufferOffset( ((edit.new.start + edit.old_len()).0.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); 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(); itemLines 1–500 of 2407. Load more
use crate::display_map::inlay_map::InlayChunk; use super::{ Highlights, inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot}, }; use gpui::{AnyElement, App, ElementId, HighlightStyle, Pixels, Window}; use language::{Edit, HighlightId, Point}; use multi_buffer::{ Anchor, AnchorRangeExt, MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, }; use project::InlayId; use std::{ any::TypeId, cmp::{self, Ordering}, fmt, iter, ops::{Add, AddAssign, Deref, DerefMut, Range, Sub, SubAssign}, sync::Arc, usize, }; use sum_tree::{Bias, Cursor, Dimensions, FilterCursor, SumTree, Summary, TreeMap}; 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 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, _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 } #[ztracing::instrument(skip_all)] pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint { let (start, _, _) = snapshot .transforms .find::<Dimensions<FoldPoint, InlayPoint>, _>((), &self, Bias::Right); let overshoot = self.0 - start.0.0; InlayPoint(start.1.0 + overshoot) } #[ztracing::instrument(skip_all)] pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset { let (start, _, item) = snapshot .transforms .find::<Dimensions<FoldPoint, TransformSummary>, _>((), &self, Bias::Right); let overshoot = self.0 - start.1.output.lines; let mut offset = start.1.output.len; if !overshoot.is_zero() { let transform = item.expect("display point out of range"); assert!(transform.placeholder.is_none()); let end_inlay_offset = snapshot .inlay_snapshot .to_offset(InlayPoint(start.1.input.lines + overshoot)); offset += end_inlay_offset.0 - 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 FoldMapWriter<'_> { #[ztracing::instrument(skip_all)] 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 { self.0.snapshot.fold_metadata_by_id.insert( fold.id, FoldMetadata { range: fold.range.clone(), width: None, }, ); new_tree.append(cursor.slice(&fold.range, Bias::Right), buffer); new_tree.push(fold, buffer); } new_tree.append(cursor.suffix(), 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. #[ztracing::instrument(skip_all)] 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. #[ztracing::instrument(skip_all)] 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. #[ztracing::instrument(skip_all)] 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()); self.0.snapshot.fold_metadata_by_id.remove(&fold.id); } folds_cursor.next(); } } fold_ixs_to_delete.sort_unstable(); fold_ixs_to_delete.dedup(); self.0.snapshot.folds = { let mut cursor = self.0.snapshot.folds.cursor::<MultiBufferOffset>(buffer); let mut folds = SumTree::new(buffer); for fold_ix in fold_ixs_to_delete { folds.append(cursor.slice(&fold_ix, Bias::Right), buffer); cursor.next(); } folds.append(cursor.suffix(), buffer); folds }; let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(snapshot.clone(), edits); (self.0.snapshot.clone(), edits) } #[ztracing::instrument(skip_all)] pub(crate) fn update_fold_widths( &mut self, new_widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>, ) -> (FoldSnapshot, Vec<FoldEdit>) { let mut edits = Vec::new(); let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone(); let buffer = &inlay_snapshot.buffer; for (id, new_width) in new_widths { let ChunkRendererId::Fold(id) = id else { continue; }; if let Some(metadata) = self.0.snapshot.fold_metadata_by_id.get(&id).cloned() && Some(new_width) != metadata.width { let buffer_start = metadata.range.start.to_offset(buffer); let buffer_end = metadata.range.end.to_offset(buffer); let inlay_range = inlay_snapshot.to_inlay_offset(buffer_start) ..inlay_snapshot.to_inlay_offset(buffer_end); edits.push(InlayEdit { old: inlay_range.clone(), new: inlay_range.clone(), }); self.0.snapshot.fold_metadata_by_id.insert( id, FoldMetadata { range: metadata.range, width: Some(new_width), }, ); } } let edits = consolidate_inlay_edits(edits); let edits = self.0.sync(inlay_snapshot, 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 struct FoldMap { snapshot: FoldSnapshot, next_fold_id: FoldId, } impl FoldMap { #[ztracing::instrument(skip_all)] pub 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, version: 0, fold_metadata_by_id: TreeMap::default(), }, next_fold_id: FoldId::default(), }; let snapshot = this.snapshot.clone(); (this, snapshot) } #[ztracing::instrument(skip_all)] 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) } #[ztracing::instrument(skip_all)] pub(crate) 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) } #[ztracing::instrument(skip_all)] 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()); } } } } #[ztracing::instrument(skip_all)] 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(MultiBufferOffset(0)), Bias::Right); while let Some(mut edit) = inlay_edits_iter.next() { if let Some(item) = cursor.item() && !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() as isize - edit.old_len() 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() as isize - next_edit.old_len() 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(MultiBufferOffset( ((edit.new.start + edit.old_len()).0.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); 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(); item