From f9a2724a8b85f4dd15b5af1659d3ac088c2188db Mon Sep 17 00:00:00 2001 From: Vinicius da Motta Date: Tue, 7 Oct 2025 13:38:35 -0300 Subject: [PATCH] Remove empty line when collapsing diagnostics (#39459) Closes #39028 Fixed empty lines appearing when collapsing files with diagnostic messages in the diagnostics panel. Added a flag to track when processing a `FoldedBuffer` and skip `Near/Below` blocks (diagnostic messages) that immediately follow it. This prevents diagnostics from rendering as empty lines when their file is collapsed. Before: before After: after Release Notes: - Fixed empty lines when collapsing files with diagnostics in the diagnostics panel --- crates/editor/src/display_map/block_map.rs | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index c8e5a25c5273694adc49c201055b020f78af473e..c954e1ba1b487c1c33187e895b7897f8ed67f94e 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -689,6 +689,7 @@ impl BlockMap { // For each of these blocks, insert a new isomorphic transform preceding the block, // and then insert the block itself. + let mut just_processed_folded_buffer = false; for (block_placement, block) in blocks_in_edit.drain(..) { let mut summary = TransformSummary { input_rows: 0, @@ -701,8 +702,12 @@ impl BlockMap { match block_placement { BlockPlacement::Above(position) => { rows_before_block = position.0 - new_transforms.summary().input_rows; + just_processed_folded_buffer = false; } BlockPlacement::Near(position) | BlockPlacement::Below(position) => { + if just_processed_folded_buffer { + continue; + } if position.0 + 1 < new_transforms.summary().input_rows { continue; } @@ -711,6 +716,7 @@ impl BlockMap { BlockPlacement::Replace(range) => { rows_before_block = range.start().0 - new_transforms.summary().input_rows; summary.input_rows = range.end().0 - range.start().0 + 1; + just_processed_folded_buffer = matches!(block, Block::FoldedBuffer { .. }); } } @@ -3566,6 +3572,96 @@ mod tests { assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno"); } + #[gpui::test] + fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) { + cx.update(init_test); + + let text = "line 1\nline 2\nline 3"; + let buffer = cx.update(|cx| { + MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx) + }); + let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx)); + let buffer_ids = buffer_snapshot + .excerpts() + .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id()) + .dedup() + .collect::>(); + assert_eq!(buffer_ids.len(), 1); + let buffer_id = buffer_ids[0]; + + let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); + let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); + let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); + let (_, wrap_snapshot) = + cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); + let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1); + + let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default()); + writer.insert(vec![BlockProperties { + style: BlockStyle::Fixed, + placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))), + height: Some(1), + render: Arc::new(|_| div().into_any()), + priority: 0, + }]); + + let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default()); + assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3"); + + let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default()); + buffer.read_with(cx, |buffer, cx| { + writer.fold_buffers([buffer_id], buffer, cx); + }); + + let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default()); + assert_eq!(blocks_snapshot.text(), ""); + } + + #[gpui::test] + fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) { + cx.update(init_test); + + let text = "line 1\nline 2\nline 3\nline 4"; + let buffer = cx.update(|cx| { + MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx) + }); + let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx)); + let buffer_ids = buffer_snapshot + .excerpts() + .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id()) + .dedup() + .collect::>(); + assert_eq!(buffer_ids.len(), 1); + let buffer_id = buffer_ids[0]; + + let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); + let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); + let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); + let (_, wrap_snapshot) = + cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); + let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1); + + let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default()); + writer.insert(vec![BlockProperties { + style: BlockStyle::Fixed, + placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))), + height: Some(1), + render: Arc::new(|_| div().into_any()), + priority: 0, + }]); + + let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default()); + assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n"); + + let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default()); + buffer.read_with(cx, |buffer, cx| { + writer.fold_buffers([buffer_id], buffer, cx); + }); + + let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default()); + assert_eq!(blocks_snapshot.text(), ""); + } + fn init_test(cx: &mut gpui::App) { let settings = SettingsStore::test(cx); cx.set_global(settings);