diagnostics: Always expand at least `multibuffer_context_lines` per diagnostic (#56172)

Lukas Wirth created

Otherwise we can sometimes end up with single line excerpts which looks
very off



Release Notes:

- Improved the minimum size of diagnostics pane excerpts

Change summary

Cargo.lock                            |  1 +
crates/diagnostics/Cargo.toml         |  1 +
crates/diagnostics/src/diagnostics.rs | 27 +++++++++++++++------------
3 files changed, 17 insertions(+), 12 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4941,6 +4941,7 @@ dependencies = [
  "component",
  "ctor",
  "editor",
+ "futures-lite 1.13.0",
  "gpui",
  "indoc",
  "itertools 0.14.0",

crates/diagnostics/Cargo.toml 🔗

@@ -19,6 +19,7 @@ collections.workspace = true
 component.workspace = true
 ctor.workspace = true
 editor.workspace = true
+futures-lite.workspace = true
 gpui.workspace = true
 indoc.workspace = true
 itertools.workspace = true

crates/diagnostics/src/diagnostics.rs 🔗

@@ -981,24 +981,26 @@ async fn context_range_for_entry(
     snapshot: BufferSnapshot,
     cx: &mut AsyncApp,
 ) -> Range<text::Anchor> {
-    let range = if let Some(rows) = heuristic_syntactic_expand(
+    let expanded_range = heuristic_syntactic_expand(
         range.clone(),
         DIAGNOSTIC_EXPANSION_ROW_LIMIT,
         snapshot.clone(),
         cx,
     )
-    .await
-    .filter(|rows| rows.start() != rows.end())
-    {
-        Range {
-            start: Point::new(*rows.start(), 0),
-            end: snapshot.clip_point(Point::new(*rows.end(), u32::MAX), Bias::Left),
-        }
+    .await;
+    let row_range = expanded_range.unwrap_or_else(|| range.start.row..=range.end.row);
+    let row_count = row_range.end().saturating_sub(*row_range.start()) + 1;
+    let target_row_count = context.saturating_mul(2).saturating_add(1);
+    let row_range = if let Some(rows_to_add) = target_row_count.checked_sub(row_count) {
+        let rows_before = rows_to_add.div_ceil(2);
+        let rows_after = rows_to_add / 2;
+        row_range.start().saturating_sub(rows_before)..=row_range.end().saturating_add(rows_after)
     } else {
-        Range {
-            start: Point::new(range.start.row.saturating_sub(context), 0),
-            end: snapshot.clip_point(Point::new(range.end.row + context, u32::MAX), Bias::Left),
-        }
+        row_range
+    };
+    let range = Range {
+        start: Point::new(*row_range.start(), 0),
+        end: snapshot.clip_point(Point::new(*row_range.end(), u32::MAX), Bias::Left),
     };
     snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end)
 }
@@ -1133,6 +1135,7 @@ async fn heuristic_syntactic_expand(
             return None;
         };
         node = parent;
+        futures_lite::future::yield_now().await;
     }
 }