@@ -60,7 +60,7 @@ var ErrEnvironmentBindFailure = errors.New("cmder: failed to update flag from en
6060//
6161// If the command also implements [FlagInitializer], InitializeFlags() will be invoked to register additional
6262// command-line flags. Each command/subcommand is given a unique [flag.FlagSet]. Help flags ('-h', '--help') are
63- // configured automatically and must not be set by the application .
63+ // configured automatically if not defined and will instruct Execute to render command usage .
6464//
6565// Execute parses getopt-style (GNU/POSIX) command-line arguments with the help of package [getopt]. To use the standard
6666// [flag] syntax instead, see [WithNativeFlags]. Flags and arguments cannot be interspersed by default. You can change
@@ -70,12 +70,13 @@ var ErrEnvironmentBindFailure = errors.New("cmder: failed to update flag from en
7070//
7171// # Usage and Help Texts
7272//
73- // Whenever the user provides the '-h' or '--help' flag at the command line, [Execute] will display command usage and
74- // exit. The format of the help text can be adjusted with [WithUsageTemplate]. By default, usage information will
75- // be written to stderr, but this can be adjusted by setting [WithUsageOutput].
73+ // Whenever the user provides the '-h' or '--help' flag at the command line and the command doesn't register custom help
74+ // flags, Execute will display command usage and return [ErrShowUsage]. The format of the help text can be adjusted with
75+ // [WithUsageTemplate]. By default, usage information will be written to stderr, but this can be adjusted by setting
76+ // [WithUsageOutput].
7677//
77- // If a command's [ Run] routine returns [ErrShowUsage] (or an error wrapping [ErrShowUsage]), [ Execute] will render
78- // help text and exit with status 2 .
78+ // If a command's Run routine returns [ErrShowUsage] (or an error wrapping [ErrShowUsage]), Execute will render
79+ // help text and return the error .
7980func Execute (ctx context.Context , cmd Command , op ... ExecuteOption ) error {
8081 // do some checks
8182 if cmd == nil {
@@ -98,11 +99,6 @@ func Execute(ctx context.Context, cmd Command, op ...ExecuteOption) error {
9899 return err
99100 }
100101
101- // if help was requested, display and exit
102- if cmd , ok := helpRequested (stack ); ok {
103- return usage (* cmd , ops )
104- }
105-
106102 return execute (ctx , stack , ops )
107103}
108104
@@ -157,13 +153,16 @@ type command struct {
157153func (c command ) onInit (ctx context.Context , ops * ExecuteOptions ) error {
158154 var err error
159155
156+ if c .showHelp {
157+ return errors .Join (ErrShowUsage , usage (c , ops ))
158+ }
159+
160160 if cmd , ok := c .Command .(Initializer ); ok {
161161 err = cmd .Initialize (ctx , c .args )
162162 }
163163
164164 if errors .Is (err , ErrShowUsage ) {
165- _ = usage (c , ops )
166- os .Exit (2 )
165+ return errors .Join (err , usage (c , ops ))
167166 }
168167
169168 return err
@@ -173,8 +172,7 @@ func (c command) onInit(ctx context.Context, ops *ExecuteOptions) error {
173172func (c command ) run (ctx context.Context , ops * ExecuteOptions ) error {
174173 err := c .Run (ctx , c .args )
175174 if errors .Is (err , ErrShowUsage ) {
176- _ = usage (c , ops )
177- os .Exit (2 )
175+ return errors .Join (err , usage (c , ops ))
178176 }
179177
180178 return err
@@ -189,8 +187,7 @@ func (c command) onDestroy(ctx context.Context, ops *ExecuteOptions) error {
189187 }
190188
191189 if errors .Is (err , ErrShowUsage ) {
192- _ = usage (c , ops )
193- os .Exit (2 )
190+ return errors .Join (err , usage (c , ops ))
194191 }
195192
196193 return err
@@ -212,14 +209,16 @@ func buildCallStack(cmd Command, ops *ExecuteOptions) ([]command, error) {
212209 fs : flag .NewFlagSet (cmd .Name (), flag .ContinueOnError ),
213210 }
214211
215- // add help flags
216- this .fs .BoolVar (& this .showHelp , "h" , false , "show command help and usage information" )
217- this .fs .BoolVar (& this .showHelp , "help" , false , "show command help and usage information" )
218-
219212 if c , ok := cmd .(FlagInitializer ); ok {
220213 c .InitializeFlags (this .fs )
221214 }
222215
216+ // add help flags
217+ if this .fs .Lookup ("h" ) == nil && this .fs .Lookup ("help" ) == nil {
218+ this .fs .BoolVar (& this .showHelp , "h" , false , "show command help and usage information" )
219+ this .fs .BoolVar (& this .showHelp , "help" , false , "show command help and usage information" )
220+ }
221+
223222 // bind environment variables
224223 if ops .bindEnv {
225224 if err := bindEnvironmentFlags (stack , this , ops ); err != nil {
@@ -330,19 +329,3 @@ func formatEnvvar(flagpath []string) string {
330329
331330 return strings .Join (flagpath , "_" )
332331}
333-
334- // helpRequested traverses the command stack and returns whether help text was requested with '-h' or '--help' flags,
335- // returning the leaf command from stack and true.
336- func helpRequested (stack []command ) (* command , bool ) {
337- if len (stack ) == 0 {
338- return nil , false
339- }
340-
341- for _ , cmd := range stack {
342- if cmd .showHelp {
343- return & stack [len (stack )- 1 ], true
344- }
345- }
346-
347- return nil , false
348- }
0 commit comments