From 4bd1a090d939534ceec725307d43e7b154832950 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 13 Mar 2026 02:09:23 +0900 Subject: [PATCH] editor: Fix bracket colorization with folds and large functions (#51108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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** 스크린샷 2026-03-10 오후 8 29 10 **After Fix** 스크린샷 2026-03-10 오후 8 32 27 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 --- crates/editor/src/bracket_colorization.rs | 54 +++++++++++++++++++++++ crates/editor/src/editor.rs | 24 +++++----- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/crates/editor/src/bracket_colorization.rs b/crates/editor/src/bracket_colorization.rs index 16fe29a7fa4aa066cf045a63c477fbb569d80334..657f1e1b23d91ca421da6a38fbeaa382a65863db 100644 --- a/crates/editor/src/bracket_colorization.rs +++ b/crates/editor/src/bracket_colorization.rs @@ -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"); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bec381506060435419e86727051cda53ab220316..707fb43cc3b573772ef24b7fe7eea69a2ad3c8ec 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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>, auto_scroll: bool, - _window: &mut Window, + window: &mut Window, cx: &mut Context, ) { 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>(