From 6444fcd4428e35f28129f6484e3a15227021e659 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 16 Dec 2021 13:53:32 -0800 Subject: [PATCH] Integrate MultiBuffer::buffer_rows into the display map --- crates/editor/src/display_map/block_map.rs | 7 +- crates/editor/src/display_map/fold_map.rs | 88 +++++++++++++----- crates/editor/src/display_map/wrap_map.rs | 15 ++- crates/editor/src/multi_buffer.rs | 103 ++++++++++++++------- 4 files changed, 146 insertions(+), 67 deletions(-) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index a7183fa474..620ac97e06 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1263,6 +1263,7 @@ mod tests { .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id)); let mut sorted_blocks = sorted_blocks.into_iter().peekable(); + let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::>(); let mut expected_buffer_rows = Vec::new(); let mut expected_text = String::new(); let input_text = wraps_snapshot.text(); @@ -1272,9 +1273,9 @@ mod tests { expected_text.push('\n'); } - let buffer_row = wraps_snapshot + let buffer_row = input_buffer_rows[wraps_snapshot .to_point(WrapPoint::new(row, 0), Bias::Left) - .row; + .row as usize]; while let Some((_, block)) = sorted_blocks.peek() { if block.position.row == row && block.disposition == BlockDisposition::Above { @@ -1290,7 +1291,7 @@ mod tests { } let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0; - expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) }); + expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row }); expected_text.push_str(input_line); while let Some((_, block)) = sorted_blocks.peek() { diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 4f7837a2eb..60dd40c853 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1,4 +1,7 @@ -use crate::{Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot, ToOffset}; +use crate::{ + multi_buffer::MultiBufferRows, Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot, + ToOffset, +}; use language::{Chunk, Edit, Point, PointUtf16, TextSummary}; use parking_lot::Mutex; use std::{ @@ -563,9 +566,18 @@ impl FoldSnapshot { } let fold_point = FoldPoint::new(start_row, 0); - let mut cursor = self.transforms.cursor(); + let mut cursor = self.transforms.cursor::<(FoldPoint, Point)>(); cursor.seek(&fold_point, Bias::Left, &()); - FoldBufferRows { fold_point, cursor } + + let overshoot = fold_point.0 - cursor.start().0 .0; + let buffer_point = cursor.start().1 + overshoot; + let input_buffer_rows = self.buffer_snapshot.buffer_rows(buffer_point.row); + + FoldBufferRows { + fold_point, + input_buffer_rows, + cursor, + } } pub fn max_point(&self) -> FoldPoint { @@ -897,26 +909,30 @@ impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize { pub struct FoldBufferRows<'a> { cursor: Cursor<'a, Transform, (FoldPoint, Point)>, + input_buffer_rows: MultiBufferRows<'a>, fold_point: FoldPoint, } impl<'a> Iterator for FoldBufferRows<'a> { - type Item = u32; + type Item = Option; fn next(&mut self) -> Option { + let mut traversed_fold = false; while self.fold_point > self.cursor.end(&()).0 { self.cursor.next(&()); + traversed_fold = true; if self.cursor.item().is_none() { - // TODO: Return a bool from next? break; } } if self.cursor.item().is_some() { - let overshoot = self.fold_point.0 - self.cursor.start().0 .0; - let buffer_point = self.cursor.start().1 + overshoot; + if traversed_fold { + self.input_buffer_rows.seek(self.cursor.start().1.row); + self.input_buffer_rows.next(); + } *self.fold_point.row_mut() += 1; - Some(buffer_point.row) + self.input_buffer_rows.next() } else { None } @@ -1282,20 +1298,38 @@ mod tests { snapshot_edits.push((snapshot.clone(), edits)); let mut expected_text: String = buffer_snapshot.text().to_string(); - let mut expected_buffer_rows = Vec::new(); - let mut next_row = buffer_snapshot.max_point().row; for fold_range in map.merged_fold_ranges().into_iter().rev() { - let fold_start = buffer_snapshot.offset_to_point(fold_range.start); - let fold_end = buffer_snapshot.offset_to_point(fold_range.end); - expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); - next_row = fold_start.row; - expected_text.replace_range(fold_range.start..fold_range.end, "…"); } - expected_buffer_rows.extend((0..=next_row).rev()); - expected_buffer_rows.reverse(); assert_eq!(snapshot.text(), expected_text); + log::info!( + "fold text {:?} ({} lines)", + expected_text, + expected_text.matches('\n').count() + 1 + ); + + let mut prev_row = 0; + let mut expected_buffer_rows = Vec::new(); + for fold_range in map.merged_fold_ranges().into_iter() { + let fold_start = buffer_snapshot.offset_to_point(fold_range.start).row; + let fold_end = buffer_snapshot.offset_to_point(fold_range.end).row; + expected_buffer_rows.extend( + buffer_snapshot + .buffer_rows(prev_row) + .take((1 + fold_start - prev_row) as usize), + ); + prev_row = 1 + fold_end; + } + expected_buffer_rows.extend(buffer_snapshot.buffer_rows(prev_row)); + + assert_eq!( + expected_buffer_rows.len(), + expected_text.matches('\n').count() + 1, + "wrong expected buffer rows {:?}. text: {:?}", + expected_buffer_rows, + expected_text + ); for (output_row, line) in expected_text.lines().enumerate() { let line_len = snapshot.line_len(output_row as u32); @@ -1373,14 +1407,19 @@ mod tests { ); } - for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { - let fold_row = Point::new(*buffer_row, 0) - .to_fold_point(&snapshot, Right) + let mut fold_row = 0; + while fold_row < expected_buffer_rows.len() as u32 { + fold_row = snapshot + .clip_point(FoldPoint::new(fold_row, 0), Bias::Right) .row(); + eprintln!("fold_row: {} of {}", fold_row, expected_buffer_rows.len()); assert_eq!( snapshot.buffer_rows(fold_row).collect::>(), - expected_buffer_rows[idx..], + expected_buffer_rows[(fold_row as usize)..], + "wrong buffer rows starting at fold row {}", + fold_row, ); + fold_row += 1; } for fold_range in map.merged_fold_ranges() { @@ -1470,8 +1509,11 @@ mod tests { let (snapshot, _) = map.read(buffer_snapshot.clone(), vec![]); assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee\nffffff\n"); - assert_eq!(snapshot.buffer_rows(0).collect::>(), [0, 3, 5, 6]); - assert_eq!(snapshot.buffer_rows(3).collect::>(), [6]); + assert_eq!( + snapshot.buffer_rows(0).collect::>(), + [Some(0), Some(3), Some(5), Some(6)] + ); + assert_eq!(snapshot.buffer_rows(3).collect::>(), [Some(6)]); } impl FoldMap { diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index bee2a7f8de..4a2510fb82 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -64,7 +64,7 @@ pub struct WrapChunks<'a> { pub struct WrapBufferRows<'a> { input_buffer_rows: fold_map::FoldBufferRows<'a>, - input_buffer_row: u32, + input_buffer_row: Option, output_row: u32, soft_wrapped: bool, max_output_row: u32, @@ -751,22 +751,19 @@ impl WrapSnapshot { } } + let input_buffer_rows = self.buffer_snapshot().buffer_rows(0).collect::>(); let mut expected_buffer_rows = Vec::new(); - let mut buffer_row = 0; let mut prev_tab_row = 0; for display_row in 0..=self.max_point().row() { let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0)); - let soft_wrapped; - if tab_point.row() == prev_tab_row { - soft_wrapped = display_row != 0; + if tab_point.row() == prev_tab_row && display_row != 0 { + expected_buffer_rows.push(None); } else { let fold_point = self.tab_snapshot.to_fold_point(tab_point, Bias::Left).0; let buffer_point = fold_point.to_buffer_point(&self.tab_snapshot.fold_snapshot); - buffer_row = buffer_point.row; + expected_buffer_rows.push(input_buffer_rows[buffer_point.row as usize]); prev_tab_row = tab_point.row(); - soft_wrapped = false; } - expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) }); } for start_display_row in 0..expected_buffer_rows.len() { @@ -866,7 +863,7 @@ impl<'a> Iterator for WrapBufferRows<'a> { self.soft_wrapped = true; } - Some(if soft_wrapped { None } else { Some(buffer_row) }) + Some(if soft_wrapped { None } else { buffer_row }) } } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index deb476d6c7..d907e5dbf4 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1059,29 +1059,13 @@ impl MultiBufferSnapshot { } pub fn buffer_rows<'a>(&'a self, start_row: u32) -> MultiBufferRows<'a> { - let mut excerpts = self.excerpts.cursor::(); - excerpts.seek(&Point::new(start_row, 0), Bias::Right, &()); - if excerpts.item().is_none() { - excerpts.prev(&()); - } - - let mut header_height = 0; - let mut buffer_row_range = 0..0; - if let Some(excerpt) = excerpts.item() { - let overshoot = start_row - excerpts.start().row; - let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer).row; - let excerpt_header_height = excerpt.header_height as u32; - header_height = excerpt_header_height.saturating_sub(overshoot); - buffer_row_range.start = - excerpt_start + overshoot.saturating_sub(excerpt_header_height); - buffer_row_range.end = - excerpt_start + excerpt.text_summary.lines.row + 1 - excerpt_header_height; - } - MultiBufferRows { - header_height, - buffer_row_range, - excerpts, - } + let mut result = MultiBufferRows { + header_height: 0, + buffer_row_range: 0..0, + excerpts: self.excerpts.cursor(), + }; + result.seek(start_row); + result } pub fn chunks<'a, T: ToOffset>( @@ -1853,6 +1837,36 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> { } } +impl<'a> MultiBufferRows<'a> { + pub fn seek(&mut self, row: u32) { + self.header_height = 0; + self.buffer_row_range = 0..0; + + self.excerpts + .seek_forward(&Point::new(row, 0), Bias::Right, &()); + if self.excerpts.item().is_none() { + self.excerpts.prev(&()); + + if self.excerpts.item().is_none() && row == 0 { + self.buffer_row_range = 0..1; + return; + } + } + + if let Some(excerpt) = self.excerpts.item() { + let overshoot = row - self.excerpts.start().row; + let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer).row; + let excerpt_header_height = excerpt.header_height as u32; + + self.header_height = excerpt_header_height.saturating_sub(overshoot); + self.buffer_row_range.start = + excerpt_start + overshoot.saturating_sub(excerpt_header_height); + self.buffer_row_range.end = + excerpt_start + excerpt.text_summary.lines.row + 1 - excerpt_header_height; + } + } +} + impl<'a> Iterator for MultiBufferRows<'a> { type Item = Option; @@ -1867,16 +1881,14 @@ impl<'a> Iterator for MultiBufferRows<'a> { self.buffer_row_range.start += 1; return Some(row); } + self.excerpts.item()?; self.excerpts.next(&()); - if let Some(excerpt) = self.excerpts.item() { - self.header_height = excerpt.header_height as u32; - self.buffer_row_range.start = excerpt.range.start.to_point(&excerpt.buffer).row; - self.buffer_row_range.end = - self.buffer_row_range.start + excerpt.text_summary.lines.row + 1 - - self.header_height; - } else { - return None; - } + let excerpt = self.excerpts.item()?; + self.header_height = excerpt.header_height as u32; + self.buffer_row_range.start = excerpt.range.start.to_point(&excerpt.buffer).row; + self.buffer_row_range.end = + self.buffer_row_range.start + excerpt.text_summary.lines.row + 1 + - self.header_height; } } } @@ -2179,6 +2191,23 @@ mod tests { Some(3) ] ); + assert_eq!( + snapshot.buffer_rows(2).collect::>(), + &[ + Some(1), + Some(2), + None, + Some(3), + Some(4), + None, + None, + None, + Some(3) + ] + ); + assert_eq!(snapshot.buffer_rows(10).collect::>(), &[Some(3)]); + assert_eq!(snapshot.buffer_rows(11).collect::>(), &[]); + assert_eq!(snapshot.buffer_rows(12).collect::>(), &[]); { let snapshot = multibuffer.read(cx).read(cx); @@ -2283,6 +2312,16 @@ mod tests { ); } + #[gpui::test] + fn test_empty_excerpt_buffer(cx: &mut MutableAppContext) { + let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!(snapshot.text(), ""); + assert_eq!(snapshot.buffer_rows(0).collect::>(), &[Some(0)]); + assert_eq!(snapshot.buffer_rows(1).collect::>(), &[]); + } + #[gpui::test] fn test_singleton_multibuffer_anchors(cx: &mut MutableAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "abcd", cx));