Don't insert unnecessary space below the end of an inline transformation (#15865)

Antonio Scandurra and Jason created

We achieved this by allowing block decorations to have a height of `0`
and superimposing the border on top of the line, as opposed to carving
out space below it.

Release Notes:

- N/A

---------

Co-authored-by: Jason <jason@zed.dev>

Change summary

crates/assistant/src/inline_assistant.rs   |  2 
crates/editor/src/display_map/block_map.rs | 55 ++++++++++++++++++++---
crates/editor/src/element.rs               |  9 ++-
3 files changed, 55 insertions(+), 11 deletions(-)

Detailed changes

crates/assistant/src/inline_assistant.rs 🔗

@@ -334,7 +334,7 @@ impl InlineAssistant {
             BlockProperties {
                 style: BlockStyle::Sticky,
                 position: range.end,
-                height: 1,
+                height: 0,
                 render: Box::new(|cx| {
                     v_flex()
                         .h_full()

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

@@ -972,11 +972,21 @@ impl BlockSnapshot {
 
     pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
         let mut cursor = self.transforms.cursor::<BlockRow>();
-        cursor.seek(&BlockRow(rows.start), Bias::Right, &());
+        cursor.seek(&BlockRow(rows.start), Bias::Left, &());
+        while cursor.start().0 < rows.start && cursor.end(&()).0 <= rows.start {
+            cursor.next(&());
+        }
+
         std::iter::from_fn(move || {
             while let Some(transform) = cursor.item() {
                 let start_row = cursor.start().0;
-                if start_row >= rows.end {
+                if start_row > rows.end
+                    || (start_row == rows.end
+                        && transform
+                            .block
+                            .as_ref()
+                            .map_or(false, |block| block.height() > 0))
+                {
                     break;
                 }
                 if let Some(block) = &transform.block {
@@ -1188,6 +1198,23 @@ impl Transform {
     }
 }
 
+impl<'a> BlockChunks<'a> {
+    fn advance(&mut self) {
+        self.transforms.next(&());
+        while let Some(transform) = self.transforms.item() {
+            if transform
+                .block
+                .as_ref()
+                .map_or(false, |block| block.height() == 0)
+            {
+                self.transforms.next(&());
+            } else {
+                break;
+            }
+        }
+    }
+}
+
 impl<'a> Iterator for BlockChunks<'a> {
     type Item = Chunk<'a>;
 
@@ -1200,7 +1227,7 @@ impl<'a> Iterator for BlockChunks<'a> {
         if transform.block.is_some() {
             let block_start = self.transforms.start().0 .0;
             let mut block_end = self.transforms.end(&()).0 .0;
-            self.transforms.next(&());
+            self.advance();
             if self.transforms.item().is_none() {
                 block_end -= 1;
             }
@@ -1222,7 +1249,7 @@ impl<'a> Iterator for BlockChunks<'a> {
             } else {
                 self.output_row += 1;
                 if self.output_row < self.max_output_row {
-                    self.transforms.next(&());
+                    self.advance();
                     return Some(Chunk {
                         text: "\n",
                         ..Default::default()
@@ -1240,7 +1267,7 @@ 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.transforms.next(&());
+            self.advance();
         }
 
         if self.masked {
@@ -1272,6 +1299,18 @@ impl<'a> Iterator for BlockBufferRows<'a> {
             self.transforms.next(&());
         }
 
+        while let Some(transform) = self.transforms.item() {
+            if transform
+                .block
+                .as_ref()
+                .map_or(false, |block| block.height() == 0)
+            {
+                self.transforms.next(&());
+            } else {
+                break;
+            }
+        }
+
         let transform = self.transforms.item()?;
         if transform.block.is_some() {
             Some(None)
@@ -1872,7 +1911,7 @@ mod tests {
                             } else {
                                 BlockDisposition::Below
                             };
-                            let height = rng.gen_range(1..5);
+                            let height = rng.gen_range(0..5);
                             log::info!(
                                 "inserting block {:?} {:?} with height {}",
                                 disposition,
@@ -2083,7 +2122,9 @@ mod tests {
                     .blocks_in_range(0..(expected_row_count as u32))
                     .map(|(row, block)| (row, block.clone().into()))
                     .collect::<Vec<_>>(),
-                expected_block_positions
+                expected_block_positions,
+                "invalid blocks_in_range({:?})",
+                0..expected_row_count
             );
 
             for (_, expected_block) in

crates/editor/src/element.rs 🔗

@@ -2379,9 +2379,12 @@ impl EditorElement {
         };
 
         if let BlockId::Custom(custom_block_id) = block_id {
-            let element_height_in_lines = ((final_size.height / line_height).ceil() as u32).max(1);
-            if element_height_in_lines != block.height() {
-                resized_blocks.insert(custom_block_id, element_height_in_lines);
+            if block.height() > 0 {
+                let element_height_in_lines =
+                    ((final_size.height / line_height).ceil() as u32).max(1);
+                if element_height_in_lines != block.height() {
+                    resized_blocks.insert(custom_block_id, element_height_in_lines);
+                }
             }
         }