Add interactive pager for list commands with a row template#5015
Open
simonfaltum wants to merge 3 commits intomainfrom
Open
Add interactive pager for list commands with a row template#5015simonfaltum wants to merge 3 commits intomainfrom
simonfaltum wants to merge 3 commits intomainfrom
Conversation
Approval status: pending
|
15b0327 to
8003087
Compare
When stdin, stdout, and stderr are all TTYs and the command has a row template (jobs list, clusters list, apps list, pipelines list, etc.), the CLI streams 50 rows at a time and prompts on stderr: [space] more [enter] all [q|esc] quit SPACE fetches the next page. ENTER drains the rest (interruptible by q/esc/Ctrl+C between pages). q/esc/Ctrl+C quit immediately. Piped output and --output json keep the existing non-paged behavior. Rendering reuses the existing template + headerTemplate annotations (same colors, same alignment as today). Column widths are locked from the first page so they stay stable across batches. Co-authored-by: Isaac
ab60e08 to
cbd549c
Compare
Cuts ~100 lines without behavior changes: - Trim over-long doc blocks on SupportsPager, startRawStdinKeyReader, and renderIteratorPagedTemplateCore. - Drop comments that restate the code. - Extract the flushPage closure into a templatePager struct. - Collapse q/Q/esc/Ctrl+C exit tests into a table-driven test. - Drop the brittle hard-coded-offset column test; single-page equivalence and the header-once test already cover the behavior. Co-authored-by: Isaac
Two principles from docs/go-code-structure: - Table-driven tests: collapse four pagination behavior tests into one. - Test the pure logic directly: add unit tests for visualWidth, computeWidths, and padRow instead of exercising them only via the integration path. Failures now point directly at the broken helper. Co-authored-by: Isaac
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.
Why
List commands with a row template (
jobs list,clusters list,apps list,pipelines list,workspace list, etc.) drain the full iterator and render every row at once. In workspaces with hundreds of resources, the output scrolls past before you can read it. An interactive terminal should get a chance to step through the output.This PR is an alternative to #4729 (the Bubble Tea TUI). It only solves pagination, nothing else. Smaller diff, no new public API, no override file changes, no external deps beyond
golang.org/x/term.Changes
Before:
databricks <resource> listdrained the full iterator through the existing template + tabwriter pipeline before showing anything.Now: when stdin, stdout, and stderr are all TTYs and the command has a row template, the CLI streams 50 rows at a time and prompts on stderr:
SPACEfetches the next page.ENTERdrains the rest (still interruptible byq/esc/Ctrl+Cbetween pages).q/esc/Ctrl+Cstop immediately. Piped output and--output jsonkeep the existing non-paged behavior.Rendering reuses the existing
Annotations["template"]andAnnotations["headerTemplate"]: colors, alignment, and row format come from the same code path as today's non-pagedjobs list. No newTableConfig, no newColumnDef, no changes to any override files.New files under
libs/cmdio/:capabilities.go:SupportsPager()(stdin + stdout + stderr all TTYs, not Git Bash).pager.go: raw-mode stdin setup with a key-reader goroutine,pagerNextKey/pagerShouldQuit, acrlfWriterto compensate for the terminal's clearedOPOSTflag while raw mode is active, and the prompt/key constants.paged_template.go: the template pager. Executes the header + row templates into an intermediate buffer per batch, splits by tab, computes visual column widths (stripping ANSI SGR so colors don't inflate), locks those widths from the first page, and pads every subsequent page to the same widths. Single-page output is visually indistinguishable from tabwriter; columns stay aligned across pages for longer lists.render.go:RenderIteratorroutes to the template pager when the capability check passes and a row template is set.No
cmd/changes. No new public API beyondCapabilities.SupportsPager.Subtle rendering bugs caught along the way (regression tests included):
term.MakeRawclears the TTY'sOPOSTflag, which disables\nto\r\ntranslation. Newlines become bare LF and output staircases down the terminal. ThecrlfWriterputs the\rback.*template.Templateinstances. Sharing one receiver causes the secondParseto overwrite the first, which madeapps listrender the header in place of every data row.History: this consolidates #5016 (shared pager infrastructure) and drops an earlier JSON-output pager. JSON output is mostly consumed by scripts, so paging it adds complexity without a clear win.
Test plan
go test ./libs/cmdio/...passes (new coverage includescrlfWriter, the key helpers, and every pager control path: page size, SPACE, ENTER, quit keys, Ctrl+C-mid-drain,--limitintegration, empty iterator, header + rows regression, cross-batch column stability, byte-for-byte equivalence to the non-paged path for single-page lists).make checkspasses.make lintpasses (0 issues).apps list,jobs list,clusters list,workspace list /. First page renders immediately, SPACE fetches next, ENTER drains,Ctrl+C/esc/qquit (and interrupt a drain).--output json: output unchanged frommain.