Skip to content

fix: Disable hl-line-mode in chat buffer to prevent scroll oscillation#100

Merged
dnouri merged 1 commit intomasterfrom
fix/hl-line-scroll-oscillation
Feb 6, 2026
Merged

fix: Disable hl-line-mode in chat buffer to prevent scroll oscillation#100
dnouri merged 1 commit intomasterfrom
fix/hl-line-scroll-oscillation

Conversation

@dnouri
Copy link
Owner

@dnouri dnouri commented Feb 6, 2026

Problem

In GUI Emacs, scrolling through the chat buffer with global-hl-line-mode active causes the window to jump ~27 lines and oscillate between two positions. Three factors must combine to trigger it:

  • hl-line-mode (overlay mutation on post-command-hook)
  • Invisible text (markdown-hide-markup)
  • Variable line heights (heading faces)

Background

hl-line-mode calls move-overlay in post-command-hook, which sets prevent_redisplay_optimizations_p in the C display engine. This forces a full window-start recalculation via try_to_scroll — but with invisible text and variable pixel heights, the engine picks a different position each frame, creating an oscillation loop. Terminal Emacs is unaffected because all lines have identical pixel height.

This is a known bug class going back to 2004. Emacs 31 fixes it for global-hl-line-mode by moving to pre-redisplay-functions (commit 40a22ced147), but local hl-line-mode still uses the buggy path on all versions.

Fix

Disable both local and global hl-line-mode in pi-coding-agent-chat-mode using the documented opt-out. Users lose line highlighting in the chat buffer — acceptable for a buffer that was unusable with it enabled.

Changes

  • pi-coding-agent.el: Disable hl-line in chat-mode body
  • test/pi-coding-agent-test.el: Regression test following existing chat-mode property test pattern

424/424 tests pass.

The combination of hl-line-mode's post-command-hook overlay mutation,
invisible text (markdown-hide-markup), and variable line heights
(heading faces) triggers a redisplay feedback loop in GUI Emacs —
the window oscillates ±25 lines when scrolling past headings.

This is a known class of bugs in hl-line.el going back to 2004.
Emacs 31 fixes it for global-hl-line-mode by moving to
pre-redisplay-functions (commit 40a22ced147), but local hl-line-mode
still uses post-command-hook on all versions.

Disable both local and global hl-line-mode in chat-mode using the
documented opt-out mechanism (hl-line.el line 50). Add regression
test following the existing chat-mode property test pattern.
@dnouri dnouri merged commit dea416a into master Feb 6, 2026
7 checks passed
@dnouri dnouri deleted the fix/hl-line-scroll-oscillation branch February 6, 2026 21:34
dnouri added a commit that referenced this pull request Feb 25, 2026
#100)

The combination of hl-line-mode's post-command-hook overlay mutation,
invisible text (markdown-hide-markup), and variable line heights
(heading faces) triggers a redisplay feedback loop in GUI Emacs —
the window oscillates ±25 lines when scrolling past headings.

This is a known class of bugs in hl-line.el going back to 2004.
Emacs 31 fixes it for global-hl-line-mode by moving to
pre-redisplay-functions (commit 40a22ced147), but local hl-line-mode
still uses post-command-hook on all versions.

Disable both local and global hl-line-mode in chat-mode using the
documented opt-out mechanism (hl-line.el line 50). Add regression
test following the existing chat-mode property test pattern.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant