Revert "Optimize editor rendering when clipped by parent containers"#45011
Merged
JosephTLyons merged 1 commit intomainfrom Dec 16, 2025
Merged
Revert "Optimize editor rendering when clipped by parent containers"#45011JosephTLyons merged 1 commit intomainfrom
JosephTLyons merged 1 commit intomainfrom
Conversation
…44995)" This reverts commit 914b011. The optimization introduced a regression that causes the main thread to hang for 100+ seconds in certain scenarios. ## Analysis from spindump When a large AutoHeight editor is displayed inside a List (e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges: 1. `visible_bounds` from `window.content_mask().bounds` represents the window's content mask, not the intersection with the editor 2. When the editor is partially scrolled out of view, `clipped_top_in_lines` becomes extremely large 3. This causes `start_row` to be computed as an astronomically high value 4. `blocks_in_range(start_row..end_row)` then spends excessive time in `Cursor::search_forward` iterating through the block tree The spindump showed ~46% of samples (459/1001 over 10+ seconds) stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor iteration. ## Symptoms - Main thread unresponsive for 33-113 seconds - UI completely frozen - High CPU usage on main thread (10+ seconds of CPU time) - Force quit required to recover The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to correctly calculate the intersection of editor bounds with the visible viewport, and ensure row calculations stay within valid ranges.
JosephTLyons
pushed a commit
that referenced
this pull request
Dec 16, 2025
…45011) This reverts commit 914b011 (#44995). The optimization introduced a regression that causes the main thread to hang for **100+ seconds** in certain scenarios, requiring a force quit to recover. ## Analysis from spindump When a large `AutoHeight` editor is displayed inside a `List` (e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges: 1. `visible_bounds` from `window.content_mask().bounds` represents the window's content mask, not the intersection with the editor 2. When the editor is partially scrolled out of view, `clipped_top_in_lines` becomes extremely large 3. This causes `start_row` to be computed as an astronomically high value 4. `blocks_in_range(start_row..end_row)` then spends excessive time in `Cursor::search_forward` iterating through the block tree The spindump showed **~46% of samples** (459/1001 over 10+ seconds) stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor iteration. ### Heaviest stack trace ``` EditorElement::prepaint └─ blocks_in_range + 236 └─ Cursor::search_forward (459 samples) ``` ## Symptoms - Main thread unresponsive for 33-113 seconds before sampling even began - UI completely frozen - High CPU usage on main thread (10+ seconds of CPU time in the sample) - Force quit required to recover ## Path forward The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to: 1. Correctly calculate the **intersection** of editor bounds with the visible viewport 2. Ensure row calculations stay within valid ranges (clamped to `max_row`) 3. Handle edge cases where the editor is completely outside the visible bounds Release Notes: - Fixed a hang that could occur when viewing large diffs in the Agent Panel
as-cii
added a commit
that referenced
this pull request
Dec 17, 2025
The editor optimization from #45077 uses window.content_mask().bounds to determine visible rows. In tests, the content mask defaults to the window's viewport size (1920x1043 from TestDisplay), not the draw size passed to cx.draw(). Fix by calling cx.simulate_resize() before drawing to ensure the window's viewport matches the intended draw size (3000x3000). This fix was originally part of #44995 but was lost when that PR was reverted in #45011.
as-cii
added a commit
that referenced
this pull request
Dec 17, 2025
Fixes the hang introduced in #44995 (which was reverted in #45011) and re-enables the optimization. ## Background PR #44995 introduced an optimization to skip rendering lines that are clipped by parent containers (e.g., when a large AutoHeight editor is inside a scrollable List). This significantly improved performance for large diffs in the Agent Panel. However, #45011 reverted this change because it caused the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover. ## Root Cause The original analysis in #45011 suggested that visible_bounds wasn’t being intersected properly, but that was incorrect—the intersection via with_content_mask works correctly. The actual bug: when an editor is positioned above the visible viewport (e.g., scrolled past in a List), the clipping calculation produces a start_row that exceeds max_row: 1. Editor’s bounds.origin.y becomes very negative (e.g., -10000px) 2. After intersection, visible_bounds.origin.y is at the viewport top (e.g., 0) 3. clipped_top_in_lines = (0 - (-10000)) / line_height = huge number 4. start_row = huge number, but end_row is clamped to max_row 5. This creates an invalid range where start_row > end_row This caused two different failures depending on build mode: - Debug mode: Panic from subtraction overflow in Range<DisplayRow>::len() - Release mode: Integer wraparound causing blocks_in_range to enter an infinite loop (the 100+ second hang) ## Fix Simply clamp start_row to max_row, ensuring the row range is always valid: ```rs let start_row = cmp::min( DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32), max_row, ); ``` ## Testing Added a regression test that draws an editor at y=-10000 to simulate an editor that’s been scrolled past in a List. This would panic in debug mode (and hang in release mode) before the fix. Release Notes: - Improved agent panel performance when rendering large diffs.
HactarCE
pushed a commit
that referenced
this pull request
Dec 17, 2025
Fixes the hang introduced in #44995 (which was reverted in #45011) and re-enables the optimization. ## Background PR #44995 introduced an optimization to skip rendering lines that are clipped by parent containers (e.g., when a large AutoHeight editor is inside a scrollable List). This significantly improved performance for large diffs in the Agent Panel. However, #45011 reverted this change because it caused the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover. ## Root Cause The original analysis in #45011 suggested that visible_bounds wasn’t being intersected properly, but that was incorrect—the intersection via with_content_mask works correctly. The actual bug: when an editor is positioned above the visible viewport (e.g., scrolled past in a List), the clipping calculation produces a start_row that exceeds max_row: 1. Editor’s bounds.origin.y becomes very negative (e.g., -10000px) 2. After intersection, visible_bounds.origin.y is at the viewport top (e.g., 0) 3. clipped_top_in_lines = (0 - (-10000)) / line_height = huge number 4. start_row = huge number, but end_row is clamped to max_row 5. This creates an invalid range where start_row > end_row This caused two different failures depending on build mode: - Debug mode: Panic from subtraction overflow in Range<DisplayRow>::len() - Release mode: Integer wraparound causing blocks_in_range to enter an infinite loop (the 100+ second hang) ## Fix Simply clamp start_row to max_row, ensuring the row range is always valid: ```rs let start_row = cmp::min( DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32), max_row, ); ``` ## Testing Added a regression test that draws an editor at y=-10000 to simulate an editor that’s been scrolled past in a List. This would panic in debug mode (and hang in release mode) before the fix. Release Notes: - Improved agent panel performance when rendering large diffs.
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Jan 20, 2026
…ed-industries#45011) This reverts commit 914b011 (zed-industries#44995). The optimization introduced a regression that causes the main thread to hang for **100+ seconds** in certain scenarios, requiring a force quit to recover. ## Analysis from spindump When a large `AutoHeight` editor is displayed inside a `List` (e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges: 1. `visible_bounds` from `window.content_mask().bounds` represents the window's content mask, not the intersection with the editor 2. When the editor is partially scrolled out of view, `clipped_top_in_lines` becomes extremely large 3. This causes `start_row` to be computed as an astronomically high value 4. `blocks_in_range(start_row..end_row)` then spends excessive time in `Cursor::search_forward` iterating through the block tree The spindump showed **~46% of samples** (459/1001 over 10+ seconds) stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor iteration. ### Heaviest stack trace ``` EditorElement::prepaint └─ blocks_in_range + 236 └─ Cursor::search_forward (459 samples) ``` ## Symptoms - Main thread unresponsive for 33-113 seconds before sampling even began - UI completely frozen - High CPU usage on main thread (10+ seconds of CPU time in the sample) - Force quit required to recover ## Path forward The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to: 1. Correctly calculate the **intersection** of editor bounds with the visible viewport 2. Ensure row calculations stay within valid ranges (clamped to `max_row`) 3. Handle edge cases where the editor is completely outside the visible bounds Release Notes: - Fixed a hang that could occur when viewing large diffs in the Agent Panel
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Jan 20, 2026
Fixes the hang introduced in zed-industries#44995 (which was reverted in zed-industries#45011) and re-enables the optimization. ## Background PR zed-industries#44995 introduced an optimization to skip rendering lines that are clipped by parent containers (e.g., when a large AutoHeight editor is inside a scrollable List). This significantly improved performance for large diffs in the Agent Panel. However, zed-industries#45011 reverted this change because it caused the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover. ## Root Cause The original analysis in zed-industries#45011 suggested that visible_bounds wasn’t being intersected properly, but that was incorrect—the intersection via with_content_mask works correctly. The actual bug: when an editor is positioned above the visible viewport (e.g., scrolled past in a List), the clipping calculation produces a start_row that exceeds max_row: 1. Editor’s bounds.origin.y becomes very negative (e.g., -10000px) 2. After intersection, visible_bounds.origin.y is at the viewport top (e.g., 0) 3. clipped_top_in_lines = (0 - (-10000)) / line_height = huge number 4. start_row = huge number, but end_row is clamped to max_row 5. This creates an invalid range where start_row > end_row This caused two different failures depending on build mode: - Debug mode: Panic from subtraction overflow in Range<DisplayRow>::len() - Release mode: Integer wraparound causing blocks_in_range to enter an infinite loop (the 100+ second hang) ## Fix Simply clamp start_row to max_row, ensuring the row range is always valid: ```rs let start_row = cmp::min( DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32), max_row, ); ``` ## Testing Added a regression test that draws an editor at y=-10000 to simulate an editor that’s been scrolled past in a List. This would panic in debug mode (and hang in release mode) before the fix. Release Notes: - Improved agent panel performance when rendering large diffs.
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Jan 20, 2026
…ed-industries#45011) This reverts commit 914b011 (zed-industries#44995). The optimization introduced a regression that causes the main thread to hang for **100+ seconds** in certain scenarios, requiring a force quit to recover. ## Analysis from spindump When a large `AutoHeight` editor is displayed inside a `List` (e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges: 1. `visible_bounds` from `window.content_mask().bounds` represents the window's content mask, not the intersection with the editor 2. When the editor is partially scrolled out of view, `clipped_top_in_lines` becomes extremely large 3. This causes `start_row` to be computed as an astronomically high value 4. `blocks_in_range(start_row..end_row)` then spends excessive time in `Cursor::search_forward` iterating through the block tree The spindump showed **~46% of samples** (459/1001 over 10+ seconds) stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor iteration. ### Heaviest stack trace ``` EditorElement::prepaint └─ blocks_in_range + 236 └─ Cursor::search_forward (459 samples) ``` ## Symptoms - Main thread unresponsive for 33-113 seconds before sampling even began - UI completely frozen - High CPU usage on main thread (10+ seconds of CPU time in the sample) - Force quit required to recover ## Path forward The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to: 1. Correctly calculate the **intersection** of editor bounds with the visible viewport 2. Ensure row calculations stay within valid ranges (clamped to `max_row`) 3. Handle edge cases where the editor is completely outside the visible bounds Release Notes: - Fixed a hang that could occur when viewing large diffs in the Agent Panel
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Jan 20, 2026
Fixes the hang introduced in zed-industries#44995 (which was reverted in zed-industries#45011) and re-enables the optimization. ## Background PR zed-industries#44995 introduced an optimization to skip rendering lines that are clipped by parent containers (e.g., when a large AutoHeight editor is inside a scrollable List). This significantly improved performance for large diffs in the Agent Panel. However, zed-industries#45011 reverted this change because it caused the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover. ## Root Cause The original analysis in zed-industries#45011 suggested that visible_bounds wasn’t being intersected properly, but that was incorrect—the intersection via with_content_mask works correctly. The actual bug: when an editor is positioned above the visible viewport (e.g., scrolled past in a List), the clipping calculation produces a start_row that exceeds max_row: 1. Editor’s bounds.origin.y becomes very negative (e.g., -10000px) 2. After intersection, visible_bounds.origin.y is at the viewport top (e.g., 0) 3. clipped_top_in_lines = (0 - (-10000)) / line_height = huge number 4. start_row = huge number, but end_row is clamped to max_row 5. This creates an invalid range where start_row > end_row This caused two different failures depending on build mode: - Debug mode: Panic from subtraction overflow in Range<DisplayRow>::len() - Release mode: Integer wraparound causing blocks_in_range to enter an infinite loop (the 100+ second hang) ## Fix Simply clamp start_row to max_row, ensuring the row range is always valid: ```rs let start_row = cmp::min( DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32), max_row, ); ``` ## Testing Added a regression test that draws an editor at y=-10000 to simulate an editor that’s been scrolled past in a List. This would panic in debug mode (and hang in release mode) before the fix. Release Notes: - Improved agent panel performance when rendering large diffs.
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Feb 15, 2026
…ed-industries#45011) This reverts commit 914b011 (zed-industries#44995). The optimization introduced a regression that causes the main thread to hang for **100+ seconds** in certain scenarios, requiring a force quit to recover. ## Analysis from spindump When a large `AutoHeight` editor is displayed inside a `List` (e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges: 1. `visible_bounds` from `window.content_mask().bounds` represents the window's content mask, not the intersection with the editor 2. When the editor is partially scrolled out of view, `clipped_top_in_lines` becomes extremely large 3. This causes `start_row` to be computed as an astronomically high value 4. `blocks_in_range(start_row..end_row)` then spends excessive time in `Cursor::search_forward` iterating through the block tree The spindump showed **~46% of samples** (459/1001 over 10+ seconds) stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor iteration. ### Heaviest stack trace ``` EditorElement::prepaint └─ blocks_in_range + 236 └─ Cursor::search_forward (459 samples) ``` ## Symptoms - Main thread unresponsive for 33-113 seconds before sampling even began - UI completely frozen - High CPU usage on main thread (10+ seconds of CPU time in the sample) - Force quit required to recover ## Path forward The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to: 1. Correctly calculate the **intersection** of editor bounds with the visible viewport 2. Ensure row calculations stay within valid ranges (clamped to `max_row`) 3. Handle edge cases where the editor is completely outside the visible bounds Release Notes: - Fixed a hang that could occur when viewing large diffs in the Agent Panel
LivioGama
pushed a commit
to LivioGama/zed
that referenced
this pull request
Feb 15, 2026
Fixes the hang introduced in zed-industries#44995 (which was reverted in zed-industries#45011) and re-enables the optimization. ## Background PR zed-industries#44995 introduced an optimization to skip rendering lines that are clipped by parent containers (e.g., when a large AutoHeight editor is inside a scrollable List). This significantly improved performance for large diffs in the Agent Panel. However, zed-industries#45011 reverted this change because it caused the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover. ## Root Cause The original analysis in zed-industries#45011 suggested that visible_bounds wasn’t being intersected properly, but that was incorrect—the intersection via with_content_mask works correctly. The actual bug: when an editor is positioned above the visible viewport (e.g., scrolled past in a List), the clipping calculation produces a start_row that exceeds max_row: 1. Editor’s bounds.origin.y becomes very negative (e.g., -10000px) 2. After intersection, visible_bounds.origin.y is at the viewport top (e.g., 0) 3. clipped_top_in_lines = (0 - (-10000)) / line_height = huge number 4. start_row = huge number, but end_row is clamped to max_row 5. This creates an invalid range where start_row > end_row This caused two different failures depending on build mode: - Debug mode: Panic from subtraction overflow in Range<DisplayRow>::len() - Release mode: Integer wraparound causing blocks_in_range to enter an infinite loop (the 100+ second hang) ## Fix Simply clamp start_row to max_row, ensuring the row range is always valid: ```rs let start_row = cmp::min( DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32), max_row, ); ``` ## Testing Added a regression test that draws an editor at y=-10000 to simulate an editor that’s been scrolled past in a List. This would panic in debug mode (and hang in release mode) before the fix. Release Notes: - Improved agent panel performance when rendering large diffs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This reverts commit 914b011 (#44995).
The optimization introduced a regression that causes the main thread to hang for 100+ seconds in certain scenarios, requiring a force quit to recover.
Analysis from spindump
When a large
AutoHeighteditor is displayed inside aList(e.g., Agent Panel thread view), the clipping calculation can produce invalid row ranges:visible_boundsfromwindow.content_mask().boundsrepresents the window's content mask, not the intersection with the editorclipped_top_in_linesbecomes extremely largestart_rowto be computed as an astronomically high valueblocks_in_range(start_row..end_row)then spends excessive time inCursor::search_forwarditerating through the block treeThe spindump showed ~46% of samples (459/1001 over 10+ seconds) stuck in
BlockSnapshot::blocks_in_range(), specifically in cursor iteration.Heaviest stack trace
Symptoms
Path forward
The original optimization goal (reducing line layout work for clipped editors) is valid, but the implementation needs to:
max_row)Release Notes: