Implement basic version of BlockMap::buffer_rows

Max Brunsfeld and Nathan Sobo created

Passed 1 simple test

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/editor/src/display_map/block_map.rs | 66 ++++++++++++++++++++++++
1 file changed, 66 insertions(+)

Detailed changes

crates/editor/src/display_map/block_map.rs 🔗

@@ -93,6 +93,13 @@ struct BlockChunks<'a> {
     offset: usize,
 }
 
+struct BufferRows<'a> {
+    transforms: sum_tree::Cursor<'a, Transform, (BlockPoint, WrapPoint)>,
+    input_buffer_rows: wrap_map::BufferRows<'a>,
+    input_buffer_row: (u32, bool),
+    output_row: u32,
+}
+
 impl BlockMap {
     pub fn new(buffer: ModelHandle<Buffer>, wrap_snapshot: WrapSnapshot) -> Self {
         Self {
@@ -445,6 +452,25 @@ impl BlockSnapshot {
         }
     }
 
+    pub fn buffer_rows(&mut self, start_row: u32) -> BufferRows {
+        let mut transforms = self.transforms.cursor::<(BlockPoint, WrapPoint)>();
+        transforms.seek(&BlockPoint::new(start_row, 0), Bias::Left, &());
+        let mut input_row = transforms.start().1.row();
+        if let Some(transform) = transforms.item() {
+            if transform.is_isomorphic() {
+                input_row += start_row - transforms.start().0.row;
+            }
+        }
+        let mut input_buffer_rows = self.wrap_snapshot.buffer_rows(input_row);
+        let input_buffer_row = input_buffer_rows.next().unwrap();
+        BufferRows {
+            transforms,
+            input_buffer_row,
+            input_buffer_rows,
+            output_row: start_row,
+        }
+    }
+
     pub fn max_point(&self) -> BlockPoint {
         BlockPoint(self.transforms.summary().output)
     }
@@ -661,6 +687,33 @@ impl<'a> Iterator for BlockChunks<'a> {
     }
 }
 
+impl<'a> Iterator for BufferRows<'a> {
+    type Item = (u32, bool);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let transform = self.transforms.item()?;
+        let block_disposition = transform.block.as_ref().map(|b| b.disposition);
+        let (buffer_row, is_wrapped) = self.input_buffer_row;
+
+        self.output_row += 1;
+        if BlockPoint::new(self.output_row, 0) >= self.transforms.end(&()).0 {
+            self.transforms.next(&());
+            if let Some(transform) = self.transforms.item() {
+                let next_block_disposition = transform.block.as_ref().map(|b| b.disposition);
+                if block_disposition != Some(BlockDisposition::Above)
+                    && next_block_disposition != Some(BlockDisposition::Below)
+                {
+                    self.input_buffer_row = self.input_buffer_rows.next().unwrap();
+                }
+            }
+        } else if block_disposition.is_none() {
+            self.input_buffer_row = self.input_buffer_rows.next().unwrap();
+        }
+
+        Some((buffer_row, !is_wrapped && block_disposition.is_none()))
+    }
+}
+
 impl sum_tree::Item for Transform {
     type Summary = TransformSummary;
 
@@ -817,6 +870,19 @@ mod tests {
             BlockPoint::new(5, 3)
         );
 
+        assert_eq!(
+            snapshot.buffer_rows(0).collect::<Vec<_>>(),
+            &[
+                (0, true),
+                (1, false),
+                (1, false),
+                (1, true),
+                (2, true),
+                (3, true),
+                (3, false),
+            ]
+        );
+
         // Insert a line break, separating two block decorations into separate
         // lines.
         buffer.update(cx, |buffer, cx| {