Browse Source
The per-form hint labels rendered beside a multi-form definition's synopsis lines in the usage/arg_error table (hard-coded ' i -form N ...' previously) are now a template substituted per form: %formindex% is replaced with the form's 0-based ordinal, %formname% with its name, and an empty template suppresses the labels entirely (the cell keeps just the Synopsis: label, as single-form renders always have). Both acceptance arms landed. Definition side: new '@cmd -formhint <template>' key - rides the existing arbitrary-key cmd_info merge (no resolver change), round-trips through resolved_def, not display -deferred; documented in the define -help directive-options. Caller side: new -formhint option on arg_error (manual parser + PUNKARGS block) pulled into usage via its existing resolved_def opts pull; supplied-empty (suppress) is distinct from not-supplied (the option declares no -default, so an unsupplied usage call passes nothing through). Precedence: caller > definition > built-in default, whose template substitutes to the exact prior bytes. Motivation (G-143 origin): script-level (script) ids - make.tcl's - are unreachable from any repl, so the repl-oriented 'i' hint was not actionable there. Scope decisions pinned: single-form renders never carry hint labels; the -return string renderer has no hint column and is unchanged. Byte-identity for unconfigured definitions proven by pre/post capture diff of 'punk::args::usage ::punk::args::parse' (identical bytes, through the usage pass-through path). Tests: new formhint.test (9 tests - default labels, directive/caller/suppression matrix, parse -error path, single-form + string-renderer scope); full punk/args subtree 302/0-fail on BOTH tclsh90 9.0.3 and the punk86 kit (8.6); punk/ns subtree 115/115 (cmdhelp consumer). make.tcl help untouched until promotion (maketclhelp.test still pins the bootsupport hint). Goals: G-149 activated at user direction then flipped achieved 2026-08-05 + archived (index record newest-first, detail file to goals/archive with Progress evidence and 1 follow-on: make.tcl adoption after a bootsupport promotion => open). Activation-freshness survey recorded the G-167 relationship (same renderer surface - composition note both sides); reference sweep updated G-151/G-167 mentions; goals_lint clean, no orphaned xref pairs. punkshell 0.55.0 + CHANGELOG. Bootsupport/vfscommon snapshots unchanged until promotion + bake. Claude-Session: https://claude.ai/code/session_01QgaxV27VZkmEec7oNbEVFc Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
11 changed files with 336 additions and 35 deletions
@ -1,18 +0,0 @@
|
||||
# G-149 punk::args: configurable multi-form synopsis hint labels |
||||
|
||||
Status: proposed |
||||
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/ |
||||
Goal: The per-form hint labels rendered beside a multi-form definition's Synopsis lines (hard-coded ' i -form N ...' today) are configurable - a definition or rendering caller can supply its own hint text (parameterized by form index/name) or suppress the column - so consumers whose users cannot type 'i' (script-level definitions like make.tcl's, whose (script) ids are unreachable from any repl) show an actionable hint, while unconfigured definitions keep the current repl-oriented default. |
||||
Acceptance: a punk::args mechanism (definition directive and/or usage/arg_error caller option) replaces the ' i -form N ...' hint labels with caller-supplied text and can suppress the column entirely; unconfigured definitions render byte-identical to today (existing synopsis/usage/arg_error pins pass unchanged); the mechanism is pinned in the args testsuite. make.tcl adopting it (e.g a 'make.tcl help tool <action>' phrasing) is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance. |
||||
|
||||
## Context |
||||
|
||||
Identified during G-143 (achieved 2026-08-01 - goals/archive/G-143-maketcl-multiform-help.md): multi-form usage tables render ' i -form N ...' beside each form's synopsis line, hard-coded in the renderer (bootsupport args-0.13.0.tm ~5475; same code in the dev module). The wording is repl-centric and doubly inapplicable in make.tcl output - 'i' is not a make.tcl surface, and (script)-scoped ids exist only inside a running make.tcl process, so no repl can address them. make.tcl meanwhile has its own per-form navigation the hint could name ('make.tcl help <subcommand> <action>' - G-143's dry-run help). |
||||
|
||||
## Notes |
||||
|
||||
- Related: G-050 (proposed) - synopsis argument-validity marking; same renderer surface, independent mechanism. |
||||
- Related: G-056 (proposed) - display-time word wrapping for help content; sibling render-surface work. |
||||
- Related: G-151 (proposed) - annotated success render; sibling punk::args render-surface goal drafted the same session. |
||||
- Origin: G-144 (achieved 2026-08-01 - goals/archive/G-144-punkargs-synopsis-name-fallback.md) shipped the @cmd -name auto-synopsis lines these hints sit beside. |
||||
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides those named above the family entries are parser/spec mechanisms unrelated to the hint column. Consumer side: make.tcl's argdoc (src/AGENTS.md make.tcl bullet) after a bootsupport promotion. |
||||
@ -0,0 +1,69 @@
|
||||
# G-149 punk::args: configurable multi-form synopsis hint labels |
||||
|
||||
Status: achieved 2026-08-05 |
||||
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/ |
||||
Goal: The per-form hint labels rendered beside a multi-form definition's Synopsis lines (hard-coded ' i -form N ...' today) are configurable - a definition or rendering caller can supply its own hint text (parameterized by form index/name) or suppress the column - so consumers whose users cannot type 'i' (script-level definitions like make.tcl's, whose (script) ids are unreachable from any repl) show an actionable hint, while unconfigured definitions keep the current repl-oriented default. |
||||
Acceptance: a punk::args mechanism (definition directive and/or usage/arg_error caller option) replaces the ' i -form N ...' hint labels with caller-supplied text and can suppress the column entirely; unconfigured definitions render byte-identical to today (existing synopsis/usage/arg_error pins pass unchanged); the mechanism is pinned in the args testsuite. make.tcl adopting it (e.g a 'make.tcl help tool <action>' phrasing) is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance. |
||||
|
||||
## Context |
||||
|
||||
Identified during G-143 (achieved 2026-08-01 - goals/archive/G-143-maketcl-multiform-help.md): multi-form usage tables render ' i -form N ...' beside each form's synopsis line, hard-coded in the renderer (bootsupport args-0.13.0.tm ~5475; same code in the dev module). The wording is repl-centric and doubly inapplicable in make.tcl output - 'i' is not a make.tcl surface, and (script)-scoped ids exist only inside a running make.tcl process, so no repl can address them. make.tcl meanwhile has its own per-form navigation the hint could name ('make.tcl help <subcommand> <action>' - G-143's dry-run help). |
||||
|
||||
## Notes |
||||
|
||||
- Related: G-050 (proposed) - synopsis argument-validity marking; same renderer surface, independent mechanism. |
||||
- Related: G-056 (proposed) - display-time word wrapping for help content; sibling render-surface work. |
||||
- Related: G-151 (proposed) - annotated success render; sibling punk::args render-surface goal drafted the same session. |
||||
- Related: G-167 (proposed) - version-delta availability; drafted 2026-08-05, touches the same multi-form synopsis renderer surface (annotated form lines beside the same hint column). Found by the activation-freshness overlap survey 2026-08-05. Composition note at achievement: this goal's template governs the LEFT label column of the Synopsis header cell; G-167's annotations mark the synopsis form lines themselves (right column) - the mechanisms compose without collision, and per G-167's Notes the shared marking vocabulary is owned by whichever of its sibling surfaces lands next. |
||||
- Origin: G-144 (achieved 2026-08-01 - goals/archive/G-144-punkargs-synopsis-name-fallback.md) shipped the @cmd -name auto-synopsis lines these hints sit beside. |
||||
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides those named above the family entries are parser/spec mechanisms unrelated to the hint column. Consumer side: make.tcl's argdoc (src/AGENTS.md make.tcl bullet) after a bootsupport promotion. |
||||
|
||||
## Progress |
||||
|
||||
Achieved 2026-08-05 in one session (activated and implemented same day; punk::args 0.17.0, punkshell 0.55.0). |
||||
|
||||
Mechanism landed (both arms of the "definition directive and/or caller option" acceptance): |
||||
|
||||
- Definition directive: `@cmd -formhint <template>`. Rides the existing @cmd |
||||
arbitrary-key merge into cmd_info - no resolver change; round-trips through |
||||
punk::args::resolved_def (whole-dict emission) automatically; not part of the |
||||
G-046 display-deferral set (only -help defers), so the renderer reads it |
||||
directly. Documented in the define -help @cmd directive-options. |
||||
- Caller option: `-formhint <template>` on arg_error (manual dict-for parser + |
||||
PUNKARGS block); usage pulls it from arg_error's definition via its existing |
||||
resolved_def opts pull and passes it through. Supplied-empty is distinct from |
||||
not-supplied (formhint_received flag; the option declares no -default so an |
||||
unsupplied usage call passes nothing through - proven by the byte-identity |
||||
capture below running THROUGH the usage path). |
||||
- Template semantics: rendered once per form; %formindex% -> 0-based ordinal, |
||||
%formname% -> form name (string map; the repo's %...% placeholder |
||||
convention). Empty template (from either source) suppresses the hint lines - |
||||
the cell keeps just the Synopsis: label, as single-form renders always have. |
||||
Precedence: caller > definition > built-in default |
||||
" i -form %formindex% <U+2026>" (substitutes to the exact prior bytes). |
||||
- Scope decisions (deliberate, pinned): single-form definitions never render |
||||
hint labels (unchanged - the column is multi-form only); the -return string |
||||
renderer has no hint column and is unchanged. |
||||
|
||||
Verification evidence: |
||||
|
||||
- Byte-identity: pre/post-change captures of `punk::args::usage |
||||
::punk::args::parse` (multi-form, unconfigured) via `bin/punk91 src script` |
||||
byte-identical apart from a boot-noise threadid line outside the render. |
||||
- New suite src/tests/modules/punk/args/testsuites/args/formhint.test |
||||
(9 tests): default labels, single-form scope, directive template + |
||||
cmd_info round-trip, directive suppression, caller override, caller |
||||
supplied-empty suppression, arg_error direct option, parse-failure path |
||||
using the directive, string-renderer scope. |
||||
- Full modules/punk/args subtree: 302 tests, 0 failed (3 pre-existing skips) |
||||
on BOTH tclsh90 (Tcl 9.0.3) and the punk86 kit (Tcl 8.6) - existing |
||||
synopsis/usage/arg_error pins pass unchanged per acceptance. |
||||
- Adjacent consumer: modules/punk/ns subtree 115/115 on tclsh90 (cmdhelp |
||||
renders through the same machinery). |
||||
- make.tcl's own help (bootsupport punk::args) is untouched until promotion; |
||||
src/tests/shell/testsuites/punkexe/maketclhelp.test continues to pin the |
||||
current ' i -form 0 ' hint there. |
||||
|
||||
## Follow-ons |
||||
|
||||
Follow-on: make.tcl adopts the mechanism for its (script)::punkboot definitions (e.g a 'make.tcl help <subcommand> <action>' hint phrasing via @cmd -formhint) after a bootsupport promotion carrying punk::args 0.17.0- (maketclhelp.test formhints pin updates with it) => open |
||||
@ -0,0 +1,184 @@
|
||||
package require tcltest |
||||
|
||||
package require punk::args |
||||
package require punk::ansi |
||||
|
||||
#G-149: configurable multi-form synopsis hint labels - added 2026-08-05. |
||||
#The usage/arg_error table renders per-form hint labels beside a multi-form |
||||
#definition's synopsis lines (the left column of the Synopsis: header cell). |
||||
#Previously hard-coded ' i -form N ...' (repl-oriented), the labels are now a |
||||
#template substituted per form: %formindex% -> the form's 0-based ordinal, |
||||
#%formname% -> its name. Precedence: caller -formhint option (arg_error/usage) |
||||
#> definition '@cmd -formhint' > built-in default (byte-identical to the old |
||||
#hard-coded labels). An empty template - from either source - suppresses the |
||||
#hint lines entirely (supplied-empty at the caller is distinct from |
||||
#not-supplied). Single-form definitions never render hint labels, and the |
||||
#-return string renderer has no hint column - both pinned here as scope. |
||||
# |
||||
#Assertions are on ANSI-stripped output only (no SGR-dependent pins), so the |
||||
#suite is independent of terminal colour state. |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
variable common { |
||||
set result "" |
||||
} |
||||
|
||||
#U+2026 horizontal ellipsis - trailing char of the built-in default label |
||||
#(built with format so this source file stays pure ASCII) |
||||
variable ellipsis [format %c 0x2026] |
||||
|
||||
#multi-form fixture with no -formhint configuration (default labels) |
||||
punk::args::define { |
||||
@id -id ::testspace::fh_plain |
||||
@cmd -name testspace::fh_plain -summary "formhint default fixture" -help "formhint default fixture" |
||||
@form -form alpha |
||||
@values -min 1 -max 1 |
||||
v1 -type string |
||||
@form -form beta |
||||
@values -min 2 -max 2 |
||||
a -type string |
||||
b -type string |
||||
} |
||||
|
||||
#multi-form fixture carrying a definition-supplied hint template |
||||
punk::args::define { |
||||
@id -id ::testspace::fh_hinted |
||||
@cmd -name testspace::fh_hinted -summary "formhint directive fixture" -help "formhint directive fixture" -formhint "myprog help %formname% (form %formindex%)" |
||||
@form -form alpha |
||||
@values -min 1 -max 1 |
||||
v1 -type string |
||||
@form -form beta |
||||
@values -min 2 -max 2 |
||||
a -type string |
||||
b -type string |
||||
} |
||||
|
||||
#multi-form fixture suppressing the hint labels at the definition |
||||
punk::args::define { |
||||
@id -id ::testspace::fh_suppressed |
||||
@cmd -name testspace::fh_suppressed -summary "formhint suppress fixture" -help "formhint suppress fixture" -formhint "" |
||||
@form -form one |
||||
@values -min 1 -max 1 |
||||
x -type string |
||||
@form -form two |
||||
@values -min 2 -max 2 |
||||
y -type string |
||||
z -type string |
||||
} |
||||
|
||||
#single-form fixture carrying a hint template (must render no hint labels) |
||||
punk::args::define { |
||||
@id -id ::testspace::fh_single |
||||
@cmd -name testspace::fh_single -summary "formhint single-form fixture" -help "formhint single-form fixture" -formhint "should not appear %formname%" |
||||
@values -min 1 -max 1 |
||||
only -type string |
||||
} |
||||
|
||||
#added 2026-08-05 (agent, G-149) |
||||
test formhint_default_render {unconfigured multi-form definition renders the built-in ' i -form N ...' hint labels, one per form}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_plain]] |
||||
lappend result [string match "* i -form 0 $::testspace::ellipsis*" $out] |
||||
lappend result [string match "* i -form 1 $::testspace::ellipsis*" $out] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1] |
||||
|
||||
test formhint_single_form_no_hints {single-form definitions render no hint labels even when @cmd -formhint is set}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_single]] |
||||
lappend result [expr {![string match "*should not appear*" $out]}] |
||||
lappend result [expr {![string match "* i -form *" $out]}] |
||||
lappend result [string match "*Synopsis:*" $out] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1] |
||||
|
||||
test formhint_cmd_directive_template {@cmd -formhint template substitutes %formindex% and %formname% per form and replaces the built-in labels}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_hinted]] |
||||
lappend result [string match "*myprog help alpha (form 0)*" $out] |
||||
lappend result [string match "*myprog help beta (form 1)*" $out] |
||||
lappend result [expr {![string match "* i -form *" $out]}] |
||||
#the key is stored in cmd_info (round-trips via get_spec/resolved_def) |
||||
lappend result [dict get [punk::args::get_spec ::testspace::fh_hinted] cmd_info -formhint] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1 "myprog help %formname% (form %formindex%)"] |
||||
|
||||
test formhint_cmd_directive_suppress {@cmd -formhint {} suppresses the hint labels while the per-form synopsis lines still render}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_suppressed]] |
||||
lappend result [expr {![string match "* i -form *" $out]}] |
||||
lappend result [string match "*Synopsis:*" $out] |
||||
#both forms' synopsis lines remain |
||||
lappend result [string match "*fh_suppressed x*" $out] |
||||
lappend result [string match "*fh_suppressed y z*" $out] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1 1] |
||||
|
||||
test formhint_caller_option_overrides_directive {usage -formhint overrides a definition-supplied template}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage -formhint "caller says %formindex%" ::testspace::fh_hinted]] |
||||
lappend result [string match "*caller says 0*" $out] |
||||
lappend result [string match "*caller says 1*" $out] |
||||
lappend result [expr {![string match "*myprog help*" $out]}] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1] |
||||
|
||||
test formhint_caller_empty_suppresses {usage -formhint {} suppresses the hint labels even when the definition supplies a template (supplied-empty is distinct from not-supplied)}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage -formhint "" ::testspace::fh_hinted]] |
||||
lappend result [expr {![string match "*myprog help*" $out]}] |
||||
lappend result [expr {![string match "* i -form *" $out]}] |
||||
lappend result [string match "*Synopsis:*" $out] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1] |
||||
|
||||
test formhint_arg_error_direct_option {arg_error accepts -formhint directly (the option usage passes through)}\ |
||||
-setup $common -body { |
||||
set spec [punk::args::get_spec ::testspace::fh_plain] |
||||
set out [punk::ansi::ansistrip [punk::args::arg_error "" $spec -aserror 0 -formhint "direct %formname%"]] |
||||
lappend result [string match "*direct alpha*" $out] |
||||
lappend result [string match "*direct beta*" $out] |
||||
lappend result [expr {![string match "* i -form *" $out]}] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1 1] |
||||
|
||||
test formhint_parse_error_uses_directive {a failed parse's raised usage table renders the definition's @cmd -formhint labels (internal arg_error path)}\ |
||||
-setup $common -body { |
||||
if {[catch {punk::args::parse {bogus x y z toomany} withid ::testspace::fh_hinted} errmsg]} { |
||||
set stripped [punk::ansi::ansistrip $errmsg] |
||||
lappend result [string match "*myprog help alpha (form 0)*" $stripped] |
||||
lappend result [expr {![string match "* i -form *" $stripped]}] |
||||
} else { |
||||
lappend result UNEXPECTED-parse-accepted |
||||
} |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1] |
||||
|
||||
test formhint_string_renderer_unchanged {the -return string renderer has no hint column - a definition template does not appear there}\ |
||||
-setup $common -body { |
||||
set out [punk::ansi::ansistrip [punk::args::usage -return string ::testspace::fh_hinted]] |
||||
lappend result [expr {![string match "*myprog help*" $out]}] |
||||
lappend result [string match "*Synopsis:*" $out] |
||||
}\ |
||||
-cleanup { |
||||
}\ |
||||
-result [list 1 1] |
||||
} |
||||
tcltest::cleanupTests ;#needed to produce test summary line. |
||||
Loading…
Reference in new issue