Skip to content

Fix wrong main view content in rare edge case situations - #6089

Merged
stefanhaller merged 7 commits into
masterfrom
keep-the-order-of-main-view-renders
Oct 5, 2026
Merged

stefanhaller merged 7 commits into
masterfrom
keep-the-order-of-main-view-renders

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

See the individual commit messages for what exactly is fixed here.

stefanhaller and others added 7 commits October 5, 2026 13:55
An integration test sets its caption in the options view from the
test's own goroutine, while the UI thread may be drawing that view. The
race detector reports this now and then, and fails whichever test
happens to be running.

Set the caption on the UI thread instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If two tab switches are handled before the next layout, and the first
tab shows a diff in the main view while the second one shows a message,
the main view ends up showing the diff.

A diff's task is only created after the layout, since the layout
settles the width that the diff is laid out to. A message's task is
created right away. So the diff's task is created after the message's,
and replaces it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a diff and then a message are asked for in the main view before the
next layout, the main view shows the diff. One way this happens is
switching tabs twice in rapid succession. Another is discarding the
changes of the only changed file. Closing the menu asks for the file's
diff, and if the files are refreshed before the layout, "No changed
files" is asked for next. The main view then stays empty, because by
the time the diff runs, the file has no changes. This made the
hide_selection_when_changes_vanish test fail now and then.

A diff's task is only created after the layout, since the layout settles
the width that the diff is laid out to. A diff renderer's task has been
created there since 8b8343b, and the task for git's own diff since
0afb94e. A view shows the task that was created last, so the diff's
task replaced the message's.

Reserve the diff's place among the view's tasks when the diff is asked
for, and give its task that place when it is created after the layout.
If another task has been asked for in the meantime, don't create the
diff's task at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a message replaces a command's output while the command is still
being read, the view keeps counting as loading until another command
has been read to the end. Until then, the layout doesn't clamp the
view's scroll position to its content, and IsSingleHunkForWholeFile
returns false.

If an earlier command reaches the end of its input after a later one
has been asked for, the view stops counting as loading, although the
later command hasn't started yet. The layout can then clamp the scroll
position to the earlier command's output before the later command has
been read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The loading state is set when a command task is asked for, and cleared
when a command task reaches the end of its input, whichever task that
is. A task that is stopped before then doesn't clear it, so a message
that replaces the task leaves the view loading. And a task that was
asked for earlier clears the state that a later one has set.

Count the view as loading only while the task asked for last is a
command task that is still reading its input. When a task reaches the
end of its input, let it end the loading only if it is that task.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the lower pane of the main view is asked to show a diff and is then
emptied before the next layout, it ends up holding the diff. Selecting
a file with staged changes and moving back to one without any in rapid
succession does this. The pane is hidden then, but when it is shown
again, it shows the other file's diff until its next render replaces
it.

Emptying a pane clears the view right away, but leaves the view's tasks
alone. So the diff's task, which is only created after the layout,
fills the pane again. A task that is still reading a diff into the pane
isn't stopped either, so it can write into the emptied view.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Emptying a pane of the main view clears it right away, but leaves the
view's tasks alone. If a diff was asked for before, and its task is
only created after the layout, that task fills the pane again. A diff's
task that is still reading can also write into the emptied view.

Give the pane an empty render as well. The render takes its place among
the view's tasks, so the earlier diff's task isn't created, and it stops
a task that is still reading. Once that task has stopped, the render
empties the view again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@stefanhaller stefanhaller added the bug Something isn't working label Oct 5, 2026
@stefanhaller
stefanhaller merged commit fc85e93 into master Oct 5, 2026
14 checks passed
@stefanhaller
stefanhaller deleted the keep-the-order-of-main-view-renders branch October 5, 2026 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant