use collections::{HashMap, HashSet};use gpui::{AnyElement, IntoElement};use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};use serde::{Deserialize, Serialize};use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};use sum_tree::{Bias, SeekTarget, SumTree};use text::Point;use ui::{App, SharedString, Window}; use crate::{BlockStyle, FoldPlaceholder, RenderBlock}; #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]pub struct CreaseId(usize); pub struct CreaseMap { snapshot: CreaseSnapshot, next_id: CreaseId, id_to_range: HashMap<CreaseId, Range<Anchor>>,} impl CreaseMap { #[ztracing::instrument(skip_all)] pub fn new(snapshot: &MultiBufferSnapshot) -> Self { CreaseMap { snapshot: CreaseSnapshot::new(snapshot), next_id: CreaseId::default(), id_to_range: HashMap::default(), } }} #[derive(Clone)]pub struct CreaseSnapshot { creases: SumTree<CreaseItem>,} impl CreaseSnapshot { pub fn new(snapshot: &MultiBufferSnapshot) -> Self { CreaseSnapshot { creases: SumTree::new(snapshot), } } #[ztracing::instrument(skip_all)] pub fn creases(&self) -> impl Iterator<Item = (CreaseId, &Crease<Anchor>)> { self.creases.iter().map(|item| (item.id, &item.crease)) } /// Returns the first Crease starting on the specified buffer row. #[ztracing::instrument(skip_all)] pub fn query_row<'a>( &'a self, row: MultiBufferRow, snapshot: &'a MultiBufferSnapshot, ) -> Option<&'a Crease<Anchor>> { let start = snapshot.anchor_before(Point::new(row.0, 0)); let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); cursor.seek(&start, Bias::Left); while let Some(item) = cursor.item() { match Ord::cmp(&item.crease.range().start.to_point(snapshot).row, &row.0) { Ordering::Less => cursor.next(), Ordering::Equal => { if item.crease.range().start.is_valid(snapshot) { return Some(&item.crease); } else { cursor.next(); } } Ordering::Greater => break, } } None } #[ztracing::instrument(skip_all)] pub fn creases_in_range<'a>( &'a self, range: Range<MultiBufferRow>, snapshot: &'a MultiBufferSnapshot, ) -> impl 'a + Iterator<Item = &'a Crease<Anchor>> { let start = snapshot.anchor_before(Point::new(range.start.0, 0)); let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); cursor.seek(&start, Bias::Left); std::iter::from_fn(move || { while let Some(item) = cursor.item() { cursor.next(); let crease_range = item.crease.range(); let crease_start = crease_range.start.to_point(snapshot); let crease_end = crease_range.end.to_point(snapshot); if crease_end.row > range.end.0 { continue; } if crease_start.row >= range.start.0 && crease_end.row < range.end.0 { return Some(&item.crease); } } None }) } #[ztracing::instrument(skip_all)] pub fn crease_items_with_offsets( &self, snapshot: &MultiBufferSnapshot, ) -> Vec<(CreaseId, Range<Point>)> { let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); let mut results = Vec::new(); cursor.next(); while let Some(item) = cursor.item() { let crease_range = item.crease.range(); let start_point = crease_range.start.to_point(snapshot); let end_point = crease_range.end.to_point(snapshot); results.push((item.id, start_point..end_point)); cursor.next(); } results }} type RenderToggleFn = Arc< dyn Send + Sync + Fn( MultiBufferRow, bool, Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>, &mut Window, &mut App, ) -> AnyElement,>;type RenderTrailerFn = Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> AnyElement>; #[derive(Clone)]pub enum Crease<T> { Inline { range: Range<T>, placeholder: FoldPlaceholder, render_toggle: Option<RenderToggleFn>, render_trailer: Option<RenderTrailerFn>, metadata: Option<CreaseMetadata>, }, Block { range: Range<T>, block_height: u32, block_style: BlockStyle, render_block: RenderBlock, block_priority: usize, render_toggle: Option<RenderToggleFn>, },} /// Metadata about a [`Crease`], that is used for serialization.#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]pub struct CreaseMetadata { pub icon_path: SharedString, pub label: SharedString,} impl<T> Crease<T> { #[ztracing::instrument(skip_all)] pub fn simple(range: Range<T>, placeholder: FoldPlaceholder) -> Self { Crease::Inline { range, placeholder, render_toggle: None, render_trailer: None, metadata: None, } } #[ztracing::instrument(skip_all)] pub fn block(range: Range<T>, height: u32, style: BlockStyle, render: RenderBlock) -> Self { Self::Block { range, block_height: height, block_style: style, render_block: render, block_priority: 0, render_toggle: None, } } #[ztracing::instrument(skip_all)] pub fn inline<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>( range: Range<T>, placeholder: FoldPlaceholder, render_toggle: RenderToggle, render_trailer: RenderTrailer, ) -> Self where RenderToggle: 'static + Send + Sync + Fn( MultiBufferRow, bool, Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>, &mut Window, &mut App, ) -> ToggleElement + 'static, ToggleElement: IntoElement, RenderTrailer: 'static + Send + Sync + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> TrailerElement + 'static, TrailerElement: IntoElement, { Crease::Inline { range, placeholder, render_toggle: Some(Arc::new(move |row, folded, toggle, window, cx| { render_toggle(row, folded, toggle, window, cx).into_any_element() })), render_trailer: Some(Arc::new(move |row, folded, window, cx| { render_trailer(row, folded, window, cx).into_any_element() })), metadata: None, } } #[ztracing::instrument(skip_all)] pub fn with_metadata(self, metadata: CreaseMetadata) -> Self { match self { Crease::Inline { range, placeholder, render_toggle, render_trailer, .. } => Crease::Inline { range, placeholder, render_toggle, render_trailer, metadata: Some(metadata), }, Crease::Block { .. } => self, } } #[ztracing::instrument(skip_all)] pub fn range(&self) -> &Range<T> { match self { Crease::Inline { range, .. } => range, Crease::Block { range, .. } => range, } } #[ztracing::instrument(skip_all)] pub fn metadata(&self) -> Option<&CreaseMetadata> { match self { Self::Inline { metadata, .. } => metadata.as_ref(), Self::Block { .. } => None, } }} impl<T> std::fmt::Debug for Crease<T>where T: Debug,{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Crease::Inline { range, metadata, .. } => f .debug_struct("Crease::Inline") .field("range", range) .field("metadata", metadata) .finish_non_exhaustive(), Crease::Block { range, block_height, .. } => f .debug_struct("Crease::Block") .field("range", range) .field("height", block_height) .finish_non_exhaustive(), } }} #[derive(Clone, Debug)]struct CreaseItem { id: CreaseId, crease: Crease<Anchor>,} impl CreaseMap { pub fn snapshot(&self) -> CreaseSnapshot { self.snapshot.clone() } #[ztracing::instrument(skip_all)] pub fn insert( &mut self, creases: impl IntoIterator<Item = Crease<Anchor>>, snapshot: &MultiBufferSnapshot, ) -> Vec<CreaseId> { let mut new_ids = Vec::new(); self.snapshot.creases = { let mut new_creases = SumTree::new(snapshot); let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot); for crease in creases { let crease_range = crease.range().clone(); new_creases.append(cursor.slice(&crease_range, Bias::Left), snapshot); let id = self.next_id; self.next_id.0 += 1; self.id_to_range.insert(id, crease_range); new_creases.push(CreaseItem { crease, id }, snapshot); new_ids.push(id); } new_creases.append(cursor.suffix(), snapshot); new_creases }; new_ids } #[ztracing::instrument(skip_all)] pub fn remove( &mut self, ids: impl IntoIterator<Item = CreaseId>, snapshot: &MultiBufferSnapshot, ) -> Vec<(CreaseId, Range<Anchor>)> { let mut removals = Vec::new(); let mut ids_to_remove = HashSet::default(); for id in ids { if let Some(range) = self.id_to_range.remove(&id) { ids_to_remove.insert(id); removals.push((id, range)); } } if !ids_to_remove.is_empty() { self.snapshot.creases = { let mut new_creases = SumTree::new(snapshot); for item in self.snapshot.creases.iter() { if !ids_to_remove.contains(&item.id) { new_creases.push(item.clone(), snapshot); } } new_creases }; } removals }} #[derive(Debug, Clone)]pub struct ItemSummary { range: Range<Anchor>,} impl Default for ItemSummary { fn default() -> Self { Self { range: Anchor::Min..Anchor::Min, } }} impl sum_tree::Summary for ItemSummary { type Context<'a> = &'a MultiBufferSnapshot; fn zero(_cx: Self::Context<'_>) -> Self { Default::default() } fn add_summary(&mut self, other: &Self, _snapshot: &MultiBufferSnapshot) { self.range = other.range.clone(); }} impl sum_tree::Item for CreaseItem { type Summary = ItemSummary; #[ztracing::instrument(skip_all)] fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary { ItemSummary { range: self.crease.range().clone(), } }} /// Implements `SeekTarget` for `Range<Anchor>` to enable seeking within a `SumTree` of `CreaseItem`s.impl SeekTarget<'_, ItemSummary, ItemSummary> for Range<Anchor> { #[ztracing::instrument(skip_all)] fn cmp(&self, cursor_location: &ItemSummary, snapshot: &MultiBufferSnapshot) -> Ordering { AnchorRangeExt::cmp(self, &cursor_location.range, snapshot) }} impl SeekTarget<'_, ItemSummary, ItemSummary> for Anchor { #[ztracing::instrument(skip_all)] fn cmp(&self, other: &ItemSummary, snapshot: &MultiBufferSnapshot) -> Ordering { self.cmp(&other.range.start, snapshot) }} #[cfg(test)]mod test { use super::*; use gpui::{App, div}; use multi_buffer::MultiBuffer; #[gpui::test] fn test_insert_and_remove_creases(cx: &mut App) { let text = "line1\nline2\nline3\nline4\nline5"; let buffer = MultiBuffer::build_simple(text, cx); let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let mut crease_map = CreaseMap::new(&buffer.read(cx).read(cx)); // Insert creases let creases = [ Crease::inline( snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), ]; let crease_ids = crease_map.insert(creases, &snapshot); assert_eq!(crease_ids.len(), 2); // Verify creases are inserted let crease_snapshot = crease_map.snapshot(); assert!( crease_snapshot .query_row(MultiBufferRow(1), &snapshot) .is_some() ); assert!( crease_snapshot .query_row(MultiBufferRow(3), &snapshot) .is_some() ); // Remove creases crease_map.remove(crease_ids, &snapshot); // Verify creases are removed let crease_snapshot = crease_map.snapshot(); assert!( crease_snapshot .query_row(MultiBufferRow(1), &snapshot) .is_none() ); assert!( crease_snapshot .query_row(MultiBufferRow(3), &snapshot) .is_none() ); } #[gpui::test] #[ztracing::instrument(skip_all)] fn test_creases_in_range(cx: &mut App) { let text = "line1\nline2\nline3\nline4\nline5\nline6\nline7"; let buffer = MultiBuffer::build_simple(text, cx); let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let mut crease_map = CreaseMap::new(&snapshot); let creases = [ Crease::inline( snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), ]; crease_map.insert(creases, &snapshot); let crease_snapshot = crease_map.snapshot();Lines 1–500 of 520. Load more
use collections::{HashMap, HashSet}; use gpui::{AnyElement, IntoElement}; use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint}; use serde::{Deserialize, Serialize}; use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc}; use sum_tree::{Bias, SeekTarget, SumTree}; use text::Point; use ui::{App, SharedString, Window}; use crate::{BlockStyle, FoldPlaceholder, RenderBlock}; #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct CreaseId(usize); pub struct CreaseMap { snapshot: CreaseSnapshot, next_id: CreaseId, id_to_range: HashMap<CreaseId, Range<Anchor>>, } impl CreaseMap { #[ztracing::instrument(skip_all)] pub fn new(snapshot: &MultiBufferSnapshot) -> Self { CreaseMap { snapshot: CreaseSnapshot::new(snapshot), next_id: CreaseId::default(), id_to_range: HashMap::default(), } } } #[derive(Clone)] pub struct CreaseSnapshot { creases: SumTree<CreaseItem>, } impl CreaseSnapshot { pub fn new(snapshot: &MultiBufferSnapshot) -> Self { CreaseSnapshot { creases: SumTree::new(snapshot), } } #[ztracing::instrument(skip_all)] pub fn creases(&self) -> impl Iterator<Item = (CreaseId, &Crease<Anchor>)> { self.creases.iter().map(|item| (item.id, &item.crease)) } /// Returns the first Crease starting on the specified buffer row. #[ztracing::instrument(skip_all)] pub fn query_row<'a>( &'a self, row: MultiBufferRow, snapshot: &'a MultiBufferSnapshot, ) -> Option<&'a Crease<Anchor>> { let start = snapshot.anchor_before(Point::new(row.0, 0)); let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); cursor.seek(&start, Bias::Left); while let Some(item) = cursor.item() { match Ord::cmp(&item.crease.range().start.to_point(snapshot).row, &row.0) { Ordering::Less => cursor.next(), Ordering::Equal => { if item.crease.range().start.is_valid(snapshot) { return Some(&item.crease); } else { cursor.next(); } } Ordering::Greater => break, } } None } #[ztracing::instrument(skip_all)] pub fn creases_in_range<'a>( &'a self, range: Range<MultiBufferRow>, snapshot: &'a MultiBufferSnapshot, ) -> impl 'a + Iterator<Item = &'a Crease<Anchor>> { let start = snapshot.anchor_before(Point::new(range.start.0, 0)); let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); cursor.seek(&start, Bias::Left); std::iter::from_fn(move || { while let Some(item) = cursor.item() { cursor.next(); let crease_range = item.crease.range(); let crease_start = crease_range.start.to_point(snapshot); let crease_end = crease_range.end.to_point(snapshot); if crease_end.row > range.end.0 { continue; } if crease_start.row >= range.start.0 && crease_end.row < range.end.0 { return Some(&item.crease); } } None }) } #[ztracing::instrument(skip_all)] pub fn crease_items_with_offsets( &self, snapshot: &MultiBufferSnapshot, ) -> Vec<(CreaseId, Range<Point>)> { let mut cursor = self.creases.cursor::<ItemSummary>(snapshot); let mut results = Vec::new(); cursor.next(); while let Some(item) = cursor.item() { let crease_range = item.crease.range(); let start_point = crease_range.start.to_point(snapshot); let end_point = crease_range.end.to_point(snapshot); results.push((item.id, start_point..end_point)); cursor.next(); } results } } type RenderToggleFn = Arc< dyn Send + Sync + Fn( MultiBufferRow, bool, Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>, &mut Window, &mut App, ) -> AnyElement, >; type RenderTrailerFn = Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> AnyElement>; #[derive(Clone)] pub enum Crease<T> { Inline { range: Range<T>, placeholder: FoldPlaceholder, render_toggle: Option<RenderToggleFn>, render_trailer: Option<RenderTrailerFn>, metadata: Option<CreaseMetadata>, }, Block { range: Range<T>, block_height: u32, block_style: BlockStyle, render_block: RenderBlock, block_priority: usize, render_toggle: Option<RenderToggleFn>, }, } /// Metadata about a [`Crease`], that is used for serialization. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CreaseMetadata { pub icon_path: SharedString, pub label: SharedString, } impl<T> Crease<T> { #[ztracing::instrument(skip_all)] pub fn simple(range: Range<T>, placeholder: FoldPlaceholder) -> Self { Crease::Inline { range, placeholder, render_toggle: None, render_trailer: None, metadata: None, } } #[ztracing::instrument(skip_all)] pub fn block(range: Range<T>, height: u32, style: BlockStyle, render: RenderBlock) -> Self { Self::Block { range, block_height: height, block_style: style, render_block: render, block_priority: 0, render_toggle: None, } } #[ztracing::instrument(skip_all)] pub fn inline<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>( range: Range<T>, placeholder: FoldPlaceholder, render_toggle: RenderToggle, render_trailer: RenderTrailer, ) -> Self where RenderToggle: 'static + Send + Sync + Fn( MultiBufferRow, bool, Arc<dyn Send + Sync + Fn(bool, &mut Window, &mut App)>, &mut Window, &mut App, ) -> ToggleElement + 'static, ToggleElement: IntoElement, RenderTrailer: 'static + Send + Sync + Fn(MultiBufferRow, bool, &mut Window, &mut App) -> TrailerElement + 'static, TrailerElement: IntoElement, { Crease::Inline { range, placeholder, render_toggle: Some(Arc::new(move |row, folded, toggle, window, cx| { render_toggle(row, folded, toggle, window, cx).into_any_element() })), render_trailer: Some(Arc::new(move |row, folded, window, cx| { render_trailer(row, folded, window, cx).into_any_element() })), metadata: None, } } #[ztracing::instrument(skip_all)] pub fn with_metadata(self, metadata: CreaseMetadata) -> Self { match self { Crease::Inline { range, placeholder, render_toggle, render_trailer, .. } => Crease::Inline { range, placeholder, render_toggle, render_trailer, metadata: Some(metadata), }, Crease::Block { .. } => self, } } #[ztracing::instrument(skip_all)] pub fn range(&self) -> &Range<T> { match self { Crease::Inline { range, .. } => range, Crease::Block { range, .. } => range, } } #[ztracing::instrument(skip_all)] pub fn metadata(&self) -> Option<&CreaseMetadata> { match self { Self::Inline { metadata, .. } => metadata.as_ref(), Self::Block { .. } => None, } } } impl<T> std::fmt::Debug for Crease<T> where T: Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Crease::Inline { range, metadata, .. } => f .debug_struct("Crease::Inline") .field("range", range) .field("metadata", metadata) .finish_non_exhaustive(), Crease::Block { range, block_height, .. } => f .debug_struct("Crease::Block") .field("range", range) .field("height", block_height) .finish_non_exhaustive(), } } } #[derive(Clone, Debug)] struct CreaseItem { id: CreaseId, crease: Crease<Anchor>, } impl CreaseMap { pub fn snapshot(&self) -> CreaseSnapshot { self.snapshot.clone() } #[ztracing::instrument(skip_all)] pub fn insert( &mut self, creases: impl IntoIterator<Item = Crease<Anchor>>, snapshot: &MultiBufferSnapshot, ) -> Vec<CreaseId> { let mut new_ids = Vec::new(); self.snapshot.creases = { let mut new_creases = SumTree::new(snapshot); let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot); for crease in creases { let crease_range = crease.range().clone(); new_creases.append(cursor.slice(&crease_range, Bias::Left), snapshot); let id = self.next_id; self.next_id.0 += 1; self.id_to_range.insert(id, crease_range); new_creases.push(CreaseItem { crease, id }, snapshot); new_ids.push(id); } new_creases.append(cursor.suffix(), snapshot); new_creases }; new_ids } #[ztracing::instrument(skip_all)] pub fn remove( &mut self, ids: impl IntoIterator<Item = CreaseId>, snapshot: &MultiBufferSnapshot, ) -> Vec<(CreaseId, Range<Anchor>)> { let mut removals = Vec::new(); let mut ids_to_remove = HashSet::default(); for id in ids { if let Some(range) = self.id_to_range.remove(&id) { ids_to_remove.insert(id); removals.push((id, range)); } } if !ids_to_remove.is_empty() { self.snapshot.creases = { let mut new_creases = SumTree::new(snapshot); for item in self.snapshot.creases.iter() { if !ids_to_remove.contains(&item.id) { new_creases.push(item.clone(), snapshot); } } new_creases }; } removals } } #[derive(Debug, Clone)] pub struct ItemSummary { range: Range<Anchor>, } impl Default for ItemSummary { fn default() -> Self { Self { range: Anchor::Min..Anchor::Min, } } } impl sum_tree::Summary for ItemSummary { type Context<'a> = &'a MultiBufferSnapshot; fn zero(_cx: Self::Context<'_>) -> Self { Default::default() } fn add_summary(&mut self, other: &Self, _snapshot: &MultiBufferSnapshot) { self.range = other.range.clone(); } } impl sum_tree::Item for CreaseItem { type Summary = ItemSummary; #[ztracing::instrument(skip_all)] fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary { ItemSummary { range: self.crease.range().clone(), } } } /// Implements `SeekTarget` for `Range<Anchor>` to enable seeking within a `SumTree` of `CreaseItem`s. impl SeekTarget<'_, ItemSummary, ItemSummary> for Range<Anchor> { #[ztracing::instrument(skip_all)] fn cmp(&self, cursor_location: &ItemSummary, snapshot: &MultiBufferSnapshot) -> Ordering { AnchorRangeExt::cmp(self, &cursor_location.range, snapshot) } } impl SeekTarget<'_, ItemSummary, ItemSummary> for Anchor { #[ztracing::instrument(skip_all)] fn cmp(&self, other: &ItemSummary, snapshot: &MultiBufferSnapshot) -> Ordering { self.cmp(&other.range.start, snapshot) } } #[cfg(test)] mod test { use super::*; use gpui::{App, div}; use multi_buffer::MultiBuffer; #[gpui::test] fn test_insert_and_remove_creases(cx: &mut App) { let text = "line1\nline2\nline3\nline4\nline5"; let buffer = MultiBuffer::build_simple(text, cx); let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let mut crease_map = CreaseMap::new(&buffer.read(cx).read(cx)); // Insert creases let creases = [ Crease::inline( snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), ]; let crease_ids = crease_map.insert(creases, &snapshot); assert_eq!(crease_ids.len(), 2); // Verify creases are inserted let crease_snapshot = crease_map.snapshot(); assert!( crease_snapshot .query_row(MultiBufferRow(1), &snapshot) .is_some() ); assert!( crease_snapshot .query_row(MultiBufferRow(3), &snapshot) .is_some() ); // Remove creases crease_map.remove(crease_ids, &snapshot); // Verify creases are removed let crease_snapshot = crease_map.snapshot(); assert!( crease_snapshot .query_row(MultiBufferRow(1), &snapshot) .is_none() ); assert!( crease_snapshot .query_row(MultiBufferRow(3), &snapshot) .is_none() ); } #[gpui::test] #[ztracing::instrument(skip_all)] fn test_creases_in_range(cx: &mut App) { let text = "line1\nline2\nline3\nline4\nline5\nline6\nline7"; let buffer = MultiBuffer::build_simple(text, cx); let snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let mut crease_map = CreaseMap::new(&snapshot); let creases = [ Crease::inline( snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), Crease::inline( snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)), FoldPlaceholder::test(), |_row, _folded, _toggle, _window, _cx| div(), |_row, _folded, _window, _cx| div(), ), ]; crease_map.insert(creases, &snapshot); let crease_snapshot = crease_map.snapshot();