perf(tui): cache file-viewer line split and header gradient - #95
Merged
Conversation
The header's gradient-coloured image reference was recomputed on every View() call — per-rune float colour interpolation plus a lipgloss.Style allocation per character — even though both inputs (the image reference and the theme gradient stops) are fixed for the session. bubbletea renders after every Update, so this ran on every keypress, scroll, and 10 Hz spinner tick for a result that never changes. Compute it once in NewModel as renderedImageRef and have renderHeader read the cached string. Output is unchanged; only the redundant per-frame work is removed.
The file viewer re-ran splitFileLines on every keystroke and every frame: it copied the whole file body into a string, ran two ReplaceAll passes, then Split. On a large file that is megabytes of throwaway allocation per redraw across six call sites (render, scroll clamp, cursor-column bound, search indexing). Split once when the file opens, cache the result on the model as viewLines, and read the cache from every hot path. A new viewLineCount method preserves the single-trailing-newline-counts-as-one contract that fileViewLineCount defined. Rendered output and line counts are unchanged.
viewReady always computed the flatten/filter/sort tree result to pass to renderStatusBar, but renderStatusBar returns early via renderViewerStatusBar when the viewer is open and never reads it. Guard the displayTree() call on viewState so the pipeline does not run for a value that is discarded. Added a regression test that renders View() with the viewer open and asserts the viewer status bar (not the tree/efficiency bar) is shown. Rendered output is unchanged.
viewLineCount reads the cached m.viewLines that scroll clamping and the status-bar line counter rely on, but only the direct fileViewLineCount reference had test coverage — the cached runtime path could drift from the reference (e.g. losing the binary guard or the trailing-newline terminator rule) without any test catching it. Add a table test that asserts viewLineCount matches fileViewLineCount for every input, including the binary case the reference table omitted, plus a nil-content case. Test-only change; no production behaviour affected.
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
Three rendering-path optimisations for the interactive TUI. All remove redundant per-frame work; none change any user-visible output.
File viewer: split content into lines once per open
The file viewer previously re-split the entire file body into lines on every keystroke and every frame — copying the whole file into a string and running two normalisation passes plus a split, across six separate call sites (render, scroll clamp, cursor-column bound, and search indexing). For a large file this allocated megabytes of throwaway strings on each redraw.
The split now happens once when a file is opened; the cached lines are reused by every hot path. Scrolling and incremental search through large files (minified JSON, logs, long single-line files) no longer generate that per-redraw allocation churn.
Skip the file-tree pipeline while the viewer is open
When the file viewer is open the status bar shows viewer hints, not the tree status bar — yet the frame still computed the full flatten/filter/sort tree result and discarded it. That computation is now skipped while the viewer is open.
Header: render the gradient image reference once
The gradient-coloured image reference in the header was recomputed on every frame, doing per-character colour interpolation on a value that is fixed for the whole session. It is now computed once at startup and reused.
Behaviour
No change to rendered output, line counts, key handling, JSON, CLI flags, or error messages. These are internal performance changes only.
Testing
go buildandgo vetclean. The TUI test suite was updated to keep the viewer's line cache in sync with its content in tests that set viewer state directly (via a shared helper), and a regression test renders the viewer with a file open to confirm the viewer status bar is shown. Full test suite runs in CI.