Repository navigation
feat: make help width configurable instead of hardcoded 80 - #758
Closed
maxandersen wants to merge 3 commits into
Closed
maxandersen wants to merge 3 commits into
maxandersen wants to merge 3 commits into
Conversation
Help text was always wrapped to the hardcoded HELP_WIDTH (80), regardless of terminal size or user preference. Add resolveHelpWidth(): an explicit override wins, in order the `aesh.help.width` system property then the `COLUMNS` environment variable (as many CLIs honour), otherwise the 80 default. A resolved value is clamped to a 40-column minimum so a tiny or bogus override cannot garble wrapping, and non-numeric values are ignored. printHelp() and the command-list description wrap now use the resolved width. HELP_WIDTH stays as the documented default.
Contributor
Author
|
this first version is not enough ...examiing. |
Build on the configurable help width: besides the aesh.help.width system property and COLUMNS, resolve the live terminal width when help is actually printed interactively. Why a lazily-queried hook rather than reading the terminal eagerly: printHelp() also runs where there is no terminal — documentation generation and programmatic/LazyHelp rendering — so querying a terminal unconditionally would either fail or force a terminal connection that the caller never wanted. The width is therefore resolved through an optional IntSupplier installed only by a caller that owns a terminal (AeshCommandInvocation.getHelpInfo), consulted here and only here (when usage prints), and fully guarded: a missing or misbehaving terminal (or a non-positive size) falls back to the override, then to the 80 default. Any resolved value is clamped to a 40-column minimum. Resolution order in ProcessedCommand.helpWidth(): aesh.help.width -> COLUMNS -> live terminal (if a supplier is set) -> HELP_WIDTH. The static resolveHelpWidth() keeps the terminal-free override-or-default behaviour for callers with no render context. The supplier is propagated to child commands so subcommand list entries wrap to the same width.
The terminal-width supplier relied on the shell's own terminal, which is blind when the command's stdout is captured/redirected (e.g. a launcher that runs the app via command substitution): the shell sees a non-terminal stdout and reports the default width, even though a real terminal is right there on the controlling tty. Add ProcessedCommand.detectTerminalWidth(): on POSIX it asks the controlling terminal directly via `stty size </dev/tty`, correct regardless of stdin/stdout redirection. It is guarded and time-boxed, so no tty, no stty, or any failure yields -1; Windows returns -1. It is only invoked on demand, when usage actually prints, via the width supplier. Both getHelpInfo paths (AeshCommandInvocation and DefaultCommandInvocation, the latter used by the one-shot AeshRuntimeRunner) now resolve the width by probing the controlling terminal first and falling back to the shell size.
Contributor
Author
|
ok - this isn't pretty ... @stalep suggestions on making it better / possible to utilize terminal api without circular dependency ? :) |
Member
|
I've added support for terminal size in aesh-readline:terminal-detect. Doing a new release of that now. |
stalep
added a commit
that referenced
this pull request
Nov 10, 2026
helpWidth: aesh.help.width sysprop, then COLUMNS, then the controlling terminal size, then 80; clamped to a 40-column minimum. Consumes the new terminal-detect query instead of spawning a probe, so every render path (interactive, one-shot, docgen) resolves the same way with no per-path plumbing.
Member
|
@maxandersen I've fixed this by using aesh-readline upstream so closing it. The feature is in aesh though! |
This was referenced Nov 10, 2026
Contributor
Author
|
close but no cigar :) see #763 |
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.
What
Help text was always wrapped to the hardcoded
ProcessedCommand.HELP_WIDTH(80), regardless of terminal size or user preference. This makes the width resolvable — including from the real terminal — while staying safe where there is no terminal.Resolution order
ProcessedCommand.helpWidth()resolves, at render time:aesh.help.widthsystem propertyCOLUMNSenvironment variableHELP_WIDTHdefault (80)Any value is clamped to a 40-column minimum; non-numeric overrides are ignored.
Querying the terminal: lazy, opt-in, and from the controlling tty
Two design points, both driven by real constraints:
1. The query must be lazy and opt-in — only when usage actually prints.
printHelp()also runs where there is no terminal (documentation generation, programmatic/LazyHelprendering). Reading the terminal unconditionally would fail there, or — with a lazy shell — force a terminal connection the caller never wanted, as a side effect of building a string. So the width comes from an optionalIntSupplierthat a caller with a terminal installs (getHelpInfo), consulted here and only here, and fully guarded (any failure / non-positive size falls through).2. It must read the controlling terminal, not the shell's stdout.
A common launcher pattern runs the app via command substitution —
out=$(app …)— which makes the app's stdout a pipe. The shell's own terminal then looks absent and reports the default width, even though the user is sitting at a real terminal. Interactive UIs avoid this because they talk to the tty directly; help rendering did not.ProcessedCommand.detectTerminalWidth()fixes that: on POSIX it asks the controlling terminal directly withstty size </dev/tty, which is correct regardless of stdin/stdout redirection. It is guarded and time-boxed (no tty / nostty/ any error →-1); Windows returns-1.getHelpInforesolves width by probing the controlling terminal first, then the shell's reported size, then the default — in bothAeshCommandInvocationandDefaultCommandInvocation(the latter is what the one-shotAeshRuntimeRunneruses).The supplier is propagated to child commands so subcommand-list entries wrap to the same width.
Tests
HelpWidthTestcovers: default;aesh.help.widthoverride; min clamp; non-numeric ignored; terminal-width supplier used when no override; override beats supplier; supplier exception / non-positive falls back; tiny size clamped;detectTerminalWidth()is safe (no throw,-1-or-positive) with no terminal; and that a long description wraps onto more lines at a narrower width. All existing help/formatter tests pass (152 in the help + formatter suites).