From b3b7459e3e9220e8a0a698fbb2d8aeff66e27379 Mon Sep 17 00:00:00 2001 From: "Nguyen Anh Tu (Elior)" <122720156+xcb3d@users.noreply.github.com> Date: Thu, 14 May 2026 22:31:46 +0700 Subject: [PATCH] Fix GPUI crash when using cached view with Input (#50665) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `draw()` method was calling `.pop()` on `next_frame.input_handlers` to extract the active input handler for the platform window. This reduced the Vec length, making cached `paint_range` indices stale. On the next frame, when a cached view called `reuse_paint()`, it would index into `rendered_frame.input_handlers` with out-of-bounds indices, causing a panic: `range start index 1 out of range for slice of length 0`. **Fix:** Use `.last_mut().and_then(|h| h.take())` instead of `.pop()` to extract the handler without changing the Vec length. The slot becomes `None`, which is already handled by `reuse_paint`'s `.take()` logic. Closes #50456 ### Testing - All 83 existing GPUI unit tests pass - Manually verified with [longbridge/gpui-component](https://github.com/longbridge/gpui-component) `cargo run --example dock`: - Switch to Input panel → type text → no crash ✅ - Before fix: crash immediately on first keystroke --- 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 - [x] 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) *(N/A — no UI changes)* Release Notes: - Fixed a crash in GPUI when typing into an Input widget inside a cached view ([#50456](https://github.com/zed-industries/zed/issues/50456)) Co-authored-by: Mikayla Maki --- crates/gpui/src/window.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 659a34dec9..1590fe1795 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2494,8 +2494,21 @@ impl Window { self.requested_autoscroll = None; // Restore the previously-used input handler. + // Place it back into a None slot (left by a previous .take()) so that + // cached paint_range indices in reuse_paint find the handler at the + // expected position. if let Some(input_handler) = self.platform_window.take_input_handler() { - self.rendered_frame.input_handlers.push(Some(input_handler)); + if let Some(slot) = self + .rendered_frame + .input_handlers + .iter_mut() + .rev() + .find(|h| h.is_none()) + { + *slot = Some(input_handler); + } else { + self.rendered_frame.input_handlers.push(Some(input_handler)); + } } if !cx.mode.skip_drawing() { self.draw_roots(cx); @@ -2504,9 +2517,18 @@ impl Window { self.next_frame.window_active = self.active.get(); // Register requested input handler with the platform window. - if let Some(input_handler) = self.next_frame.input_handlers.pop() { - self.platform_window - .set_input_handler(input_handler.unwrap()); + // Use .take() instead of .pop() to preserve Vec length, so that cached + // paint_range indices remain valid for reuse_paint on the next frame. + // Search backwards to find the last Some entry, since reuse_paint may + // have copied None slots from the previous frame. (Fixes #50456) + if let Some(input_handler) = self + .next_frame + .input_handlers + .iter_mut() + .rev() + .find_map(|h| h.take()) + { + self.platform_window.set_input_handler(input_handler); } self.layout_engine.as_mut().unwrap().clear();