fix: guard stale showEndForLine index in sticky scroll rendering (fixes #325642)#325646
Open
vs-code-engineering[bot] wants to merge 1 commit into
Open
fix: guard stale showEndForLine index in sticky scroll rendering (fixes #325642)#325646vs-code-engineering[bot] wants to merge 1 commit into
vs-code-engineering[bot] wants to merge 1 commit into
Conversation
#325642) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
TypeError: Cannot read properties of undefined (reading 'isVisible')thrown fromViewModelLinesFromProjectedModel.modelPositionIsVisiblewhile the sticky scroll widget renders. The sticky scroll widget injects anundefinedvalue into itsnumber[]line-number array when the "show end line" index (showEndForLine) is stale relative to the current sticky-scroll state, and thatundefinedflows intonew Position(undefined, 1)and defeats the numeric bounds guard inmodelPositionIsVisible._findRenderingData(instickyScrollWidget.ts) does:state.showEndForLineis an index captured earlier from a rendered DOM node (on Shift+mouse-move,_onMouseMoveOrKeyDown→_showEndForLine = currentEndForLineIndex). It is held on the controller across model changes. When the model content shrinks before the next render,findScrollWidgetState()produces shorterstartLineNumbers/endLineNumbersarrays, but the staleshowEndForLineindex can be>= startLineNumbers.length. The assignment then writesstate.endLineNumbers[showEndForLine](which isundefined, and can also leave holes) intocandidateLineNumbers, so the declarednumber[]now containsundefined. The existingposition.lineNumber <= viewModel.getLineCount()guard only skips the height accumulation — the invalid entry still remains in the returnedlineNumbersarray and is later rendered.Fixes #325642
Recommended reviewer:
@aiday-marCulprit Commit
The
showEndForLinefeature was introduced in PR #236244 ("Immediately re-render sticky scroll on pressing Shift key to show end line") by@aiday-mar(merged 2024-12-16). That change added the unguardedcandidateLineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine]assignment. The crash surfaces through the newergetLineHeightForPositionpath, but the type-contract violation originates in that assignment.Code Flow
Affected Files
src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts—_findRenderingData(bypass site, line 160).Repro Steps
showEndForLinepoints at a high index.showEndForLineindex writesundefinedinto the rendered line list, and the next render throwsCannot read properties of undefined (reading 'isVisible').How the Fix Works
Chosen approach (
stickyScrollWidget.ts,_findRenderingData): guard the end-line substitution so it only runs whenshowEndForLineis a valid index into the current arrays:startLineNumbersandendLineNumbersare always built together with equal length infindScrollWidgetState(), soshowEndForLine < candidateLineNumbers.length(==startLineNumbers.length) guaranteesendLineNumbers[showEndForLine]is a definednumber. This fixes the bug at the data producer — the exact line that injectedundefinedinto thenumber[]— rather than guarding the crash site. After this change,_findRenderingDatacannot writeundefinedintocandidateLineNumbers, because the only assignment that could do so now runs only for in-range indices, so no invalid position reachesgetLineHeightForPosition/modelPositionIsVisible.Alternatives considered:
modelPositionIsVisible(crash site) — rejected: hides the producer bug and leaves the corruptnumber[]flowing to other consumers._showEndForLineon every model content change in the controller — rejected: broader, easy to miss an event path, and the widget still must reconcile the index against the freshly computed arrays it receives; the assignment site is the precise producer boundary.Recommended Owner
@aiday-mar— author of the sticky-scrollshowEndForLinefeature (PR #236244) and thegetLineHeightForPositionchange; owns this code path.