Fix handling of empty blocks in BlockMap::chunks (#25031)

Max Brunsfeld created

Closes https://github.com/zed-industries/zed/issues/23391

Release Notes:

- Fixed a bug that sometimes caused incorrect syntax highlighting when
deploying the inline assistant.

Change summary

crates/editor/src/display_map.rs           | 32 ++++++++++++++++-------
crates/editor/src/display_map/block_map.rs | 15 ++++++----
2 files changed, 31 insertions(+), 16 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -2070,18 +2070,30 @@ pub mod tests {
             )
         });
 
-        // Insert a block in the middle of a multi-line string literal
+        // Insert two blocks in the middle of a multi-line string literal.
+        // The second block has zero height.
         map.update(cx, |map, cx| {
             map.insert_blocks(
-                [BlockProperties {
-                    placement: BlockPlacement::Below(
-                        buffer_snapshot.anchor_before(Point::new(1, 0)),
-                    ),
-                    height: 1,
-                    style: BlockStyle::Sticky,
-                    render: Arc::new(|_| div().into_any()),
-                    priority: 0,
-                }],
+                [
+                    BlockProperties {
+                        placement: BlockPlacement::Below(
+                            buffer_snapshot.anchor_before(Point::new(1, 0)),
+                        ),
+                        height: 1,
+                        style: BlockStyle::Sticky,
+                        render: Arc::new(|_| div().into_any()),
+                        priority: 0,
+                    },
+                    BlockProperties {
+                        placement: BlockPlacement::Below(
+                            buffer_snapshot.anchor_before(Point::new(2, 0)),
+                        ),
+                        height: 0,
+                        style: BlockStyle::Sticky,
+                        render: Arc::new(|_| div().into_any()),
+                        priority: 0,
+                    },
+                ],
                 cx,
             )
         });

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

@@ -1721,6 +1721,7 @@ impl BlockSnapshot {
 impl<'a> BlockChunks<'a> {
     /// Go to the next transform
     fn advance(&mut self) {
+        self.input_chunk = Chunk::default();
         self.transforms.next(&());
         while let Some(transform) = self.transforms.item() {
             if transform
@@ -1748,7 +1749,6 @@ impl<'a> BlockChunks<'a> {
                 );
                 self.input_chunks.seek(start_input_row..end_input_row);
             }
-            self.input_chunk = Chunk::default();
         }
     }
 }
@@ -1812,9 +1812,6 @@ impl<'a> Iterator for BlockChunks<'a> {
 
         let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
         self.input_chunk.text = suffix;
-        if self.output_row == transform_end {
-            self.advance();
-        }
 
         if self.masked {
             // Not great for multibyte text because to keep cursor math correct we
@@ -1824,10 +1821,16 @@ impl<'a> Iterator for BlockChunks<'a> {
             prefix = &BULLETS[..bullet_len];
         }
 
-        Some(Chunk {
+        let chunk = Chunk {
             text: prefix,
             ..self.input_chunk.clone()
-        })
+        };
+
+        if self.output_row == transform_end {
+            self.advance();
+        }
+
+        Some(chunk)
     }
 }