Skip to content

[codex] Persist terminal highlights across scrolling - #2348

Open
mam15mon wants to merge 12 commits into
binaricat:mainfrom
mam15mon:codex/persist-terminal-highlights
Open

[codex] Persist terminal highlights across scrolling#2348
mam15mon wants to merge 12 commits into
binaricat:mainfrom
mam15mon:codex/persist-terminal-highlights

Conversation

@mam15mon

Copy link
Copy Markdown
Contributor

Summary

  • render newly exposed keyword highlights synchronously before xterm paints a user scroll, including distant scrollbar jumps
  • retain nearby matched-line decorations across scrollback navigation instead of deleting them at viewport boundaries
  • keep persistent marker lookup constant-time across uniform scrollback trims and rebuild only for resize or structural redraws
  • bound retained markers to 20 viewports and at most 1,200 matched lines so broad rules cannot create unbounded xterm trim listeners

Root cause

The current user-scroll path intentionally waits 120 ms, scans only the settled viewport, and then deletes decorations outside that viewport. This makes unhighlighted text visible before the delayed decoration refresh. Restoring the old full-window synchronous rebuild would bring back scroll jank because it rescanned roughly five viewports on every scroll event.

Behavior

User scrolling now reuses the contiguous indexed range and scans only newly exposed rows. A distant jump scans one viewport synchronously in xterm's onScroll callback, before xterm queues its own refresh. Output-driven scrolling, large-output quiet windows, recent-input protection, long-line safeguards, and alternate-buffer behavior remain unchanged.

Validation

  • npm run lint (0 errors; 4 pre-existing warnings outside this change)
  • node --test --import tsx components/terminal/keywordHighlight.test.ts components/terminal/keywordHighlightRegex.test.ts components/terminal/runtime/terminalOutputPressure.test.ts (31 passed)
  • git diff upstream/main...HEAD --check

Regression coverage includes preindexed scrolling, distant jumps, continuous jumps, scrolling during write bursts, marker disposal and reset, uniform scrollback shifts, constant-time marker lookup, bounded broad-rule retention, ANSI in-place redraws, large output, recent input, and oversized wrapped lines.

@mam15mon

Copy link
Copy Markdown
Contributor Author

@codex review

cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
Replace Enter scroll-skip band-aid with mam15mon/#2348 persistence:
keep decorations across scroll, sync only newly exposed rows, bound
retention (~20 viewports / max 1200). Keep Enter write-path protections
and cursor-line atomic swap. Continuous Enter may scroll; prune-on-scroll
is gone.

Co-authored-by: mam15mon <mam15mon@users.noreply.github.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

感谢 @mam15mon 的方案。

我们在 #2805(修 #2784)里手工移植了本 PR 的核心模型:滚动时保留附近关键词高亮、只扫新露出行、并对持久 decoration 做视口/行数上限,而不是用 Enter 特判去跳过 scroll prune。连续回车会真实滚动,因此「滚动保留高亮」比 Enter 特判更合适。

因 main 上 keywordHighlight.ts 已大幅演进,未能直接 rebase 本分支;#2805 的实现与测试吸收了这里的思路,并保留了当前行高亮的 atomic swap。

若维护者认同,可将本 PR 标为 superseded by #2805 后关闭。

cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
* fix(terminal): stop keyword highlight flicker on Enter

Idle Enter was misclassified as user scroll so overscan decorations were
pruned before writeParsed cancel (Ubuntu RTT). Also swap cursor-line
decorations atomically to avoid clear-before-create flash (fixes #2784).

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): catch up highlights after Enter protection window

While enterInputPending blocked scroll-prune, viewport scrolls were also
ignored with no dirty mark. Mark dirty during the window and trigger a
write refresh when Enter protection clears.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): persist keyword highlights across scroll (#2784)

Replace Enter scroll-skip band-aid with mam15mon/#2348 persistence:
keep decorations across scroll, sync only newly exposed rows, bound
retention (~20 viewports / max 1200). Keep Enter write-path protections
and cursor-line atomic swap. Continuous Enter may scroll; prune-on-scroll
is gone.

Co-authored-by: mam15mon <mam15mon@users.noreply.github.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): bound persistent keyword prune to scroll path

Keep Enter/write deferred pruning intact by pruning the retention cap
only after scroll viewport indexing, not on every decoration apply.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): rescan dirty overlap on persistent scroll refresh

Overlapping scroll only scanned newly exposed rows then cleared the whole
viewport dirty set, which could drop in-place redraw work when scroll
outranked a pending write refresh. Rescan write-dirtied overlap lines and
only clear dirty for ranges that were actually processed.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
Co-authored-by: mam15mon <mam15mon@users.noreply.github.com>
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