editor: Fix bracket colorization with folds and large functions (#51108)

Closes #47846

`visible_excerpts` computed the visible buffer range by adding display
line count directly to the buffer start row:

```rust
// Before
multi_buffer_visible_start + Point::new(visible_line_count, 0)
```
This ignores folds entirely. When a 700-line function is folded into one
display line, content after the fold is visible on screen but falls
outside the computed buffer range, so its brackets are never colorized.

The fix converts through display coordinates so the fold/wrap layers are
respected:

```rust
// After
let display_end = DisplayPoint::new(display_start.row + visible_line_count, 0);
let multi_buffer_visible_end = display_end.to_point(&display_snapshot);
```

### Results

**Before Fix**
<img width="852" height="778" alt="스크린샷 2026-03-10 오후 8 29 10"
src="https://github.com/user-attachments/assets/a0d2d81f-a8b2-4cf4-b1f3-cf5f8288a696"
/>

**After Fix**
<img width="1031" height="794" alt="스크린샷 2026-03-10 오후 8 32 27"
src="https://github.com/user-attachments/assets/2b0496b1-8302-4248-b73a-c31f5d0b0c4b"
/>

Before you mark this PR as ready for review, make sure that you have:
- [X] Added a solid test coverage and/or screenshots from doing manual
testing
- [ ] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- Fixed bracket colorization not working for content after folded
regions and for functions with large bodies.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
Lee ByeongJunandKirill Bulatov authored and GitHub committed 2026-03-12 17:09:23 +00:00
1 parent 7611c0e4df
commit addd79958a
2 files changed
+67 -11

No files matched your search

+54
View File
@@ -1455,6 +1455,60 @@ mod foo «1{
);
}
#[gpui::test]
// reproduction of #47846
async fn test_bracket_colorization_with_folds(cx: &mut gpui::TestAppContext) {
init_test(cx, |language_settings| {
language_settings.defaults.colorize_brackets = Some(true);
});
let mut cx = EditorLspTestContext::new(
Arc::into_inner(rust_lang()).unwrap(),
lsp::ServerCapabilities::default(),
cx,
)
.await;
// Generate a large function body. When folded, this collapses
// to a single display line, making small_function visible on screen.
let mut big_body = String::new();
for i in 0..700 {
big_body.push_str(&format!(" let var_{i:04} = ({i});\n"));
}
let source = format!(
"ˇfn big_function() {{\n{big_body}}}\n\nfn small_function() {{\n let x = (1, (2, 3));\n}}\n"
);
cx.set_state(&source);
cx.executor().advance_clock(Duration::from_millis(100));
cx.executor().run_until_parked();
cx.update_editor(|editor, window, cx| {
editor.fold_ranges(
vec![Point::new(0, 0)..Point::new(701, 1)],
false,
window,
cx,
);
});
cx.executor().advance_clock(Duration::from_millis(100));
cx.executor().run_until_parked();
assert_eq!(
indoc! {r#"
⋯1»
fn small_function«1()1» «1{
let x = «2(1, «3(2, 3)3»)2»;
}1»
1 hsla(207.80, 16.20%, 69.19%, 1.00)
2 hsla(29.00, 54.00%, 65.88%, 1.00)
3 hsla(286.00, 51.00%, 75.25%, 1.00)
"#,},
bracket_colors_markup(&mut cx),
);
}
fn separate_with_comment_lines(head: &str, tail: &str, comment_lines: usize) -> String {
let mut result = head.to_string();
result.push_str("\n");
+13 -11
View File
@@ -2621,16 +2621,7 @@ impl Editor {
.await;
editor
.update_in(cx, |editor, window, cx| {
editor.register_visible_buffers(cx);
editor.colorize_brackets(false, cx);
editor.refresh_inlay_hints(
InlayHintRefreshReason::NewLinesShown,
cx,
);
if !editor.buffer().read(cx).is_singleton() {
editor.update_lsp_data(None, window, cx);
editor.refresh_runnables(window, cx);
}
editor.update_data_on_scroll(window, cx)
})
.ok();
});
@@ -20055,7 +20046,7 @@ impl Editor {
&mut self,
creases: Vec<Crease<T>>,
auto_scroll: bool,
_window: &mut Window,
window: &mut Window,
cx: &mut Context<Self>,
) {
if creases.is_empty() {
@@ -20071,6 +20062,7 @@ impl Editor {
cx.notify();
self.scrollbar_marker_state.dirty = true;
self.update_data_on_scroll(window, cx);
self.folds_did_change(cx);
}
@@ -25367,6 +25359,16 @@ impl Editor {
fn disable_runnables(&mut self) {
self.enable_runnables = false;
}
fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
self.register_visible_buffers(cx);
self.colorize_brackets(false, cx);
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
if !self.buffer().read(cx).is_singleton() {
self.update_lsp_data(None, window, cx);
self.refresh_runnables(window, cx);
}
}
}
fn edit_for_markdown_paste<'a>(