editor: Fix crash from stale state after prepaint depth exhaustion (#49664)

Closes #49662

## Summary

Fixes an index-out-of-bounds crash in `EditorElement::prepaint` that
occurs when block decorations oscillate in size, exhausting the
recursive prepaint budget (`MAX_PREPAINT_DEPTH = 5`).

## Root cause

When block decorations resize during `render_blocks`, prepaint recurses
to rebuild layout state. Previously, both `resize_blocks()` and
`update_renderer_widths()` mutated the display map **before** checking
`can_prepaint()`. At max depth:

1. `resize_blocks()` mutated the block map, changing the display row
mapping
2. `can_prepaint()` returned false, skipping the recursive rebuild
3. All local state (`snapshot`, `start_row`, `end_row`, `line_layouts`,
`row_infos`) remained stale
4. Downstream code — particularly `layout_inline_diagnostics` which
takes a **fresh** `editor.snapshot()` — would see the new row mapping
but index into the stale `line_layouts`
5. `row.minus(start_row)` produced an index exceeding
`line_layouts.len()` → panic

The crash is most likely to occur when many block decorations appear or
resize simultaneously (e.g., unfolding a folder containing many git
repos, causing a burst of diff hunk blocks), or when `place_near` blocks
oscillate between inline (height=0) and block (height>=1) mode, burning
through all 5 recursion levels without converging.

## Fix

Guard both mutation sites with `can_prepaint()` so the display map is
NOT mutated when we cannot recurse to rebuild state:

- **Block resize**: move `resize_blocks()` inside the `can_prepaint()`
guard. At max depth, defer the resize to the next frame via
`cx.notify()`. The resize will be re-detected because the stored height
still mismatches the rendered height.


https://github.com/jean-humann/zed/blob/021978ecf939723a6ba4ab9843572b6bcefe7cb7/crates/editor/src/element.rs#L10207-L10239

- **Renderer widths**: short-circuit `update_renderer_widths()` with
`can_prepaint()` so fold widths are not updated at max depth.


https://github.com/jean-humann/zed/blob/021978ecf939723a6ba4ab9843572b6bcefe7cb7/crates/editor/src/element.rs#L10097-L10115

In both cases the next frame starts with a fresh recursion budget
(`EditorRequestLayoutState::default()` resets `prepaint_depth` to 0) and
applies the deferred changes normally. The worst case is one frame of
slightly wrong-sized blocks — vastly preferable to a crash.

## Test plan

- [x] `cargo check -p editor` passes
- [ ] Manual testing with many open files, inline diagnostics, hover
popovers, and git diff hunks

Release Notes:

- Fixed a crash (index out of bounds) during editor rendering when block
decorations repeatedly resize, exhausting the recursive prepaint budget.

šŸ¤– Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
19c8363a8eJean Humann committed on 3/19/2026, 7:42:28 AMĀ· committed by GitHubparent927cc30
1 file changedLine totals unavailable