Skip to content

Commit b359465

Browse files
Add -h to display help
- --help and -h display usage and help - if an argument or an alias is the same as either help or h, disable help printing for that argument
1 parent 4e99de0 commit b359465

File tree

3 files changed

+129
-3
lines changed

3 files changed

+129
-3
lines changed

library/src/scala/main.scala

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ import annotation._
3636
* Note that main function overloading is not currently supported, i.e. you cannot define two main methods that have
3737
* the same name in the same project.
3838
*
39-
* A special argument is used to display help regarding a main function: `--help`. If used as argument, the program
39+
* Special arguments are used to display help regarding a main function: `--help` and `-h`. If used as argument, the program
4040
* will display some useful information about the main function. This help directly uses the ScalaDoc comment
4141
* associated with the function, more precisely its description and the description of the parameters documented with
42-
* `@param`.
42+
* `@param`. Note that if a parameter is named `help` or `h`, or if one of the parameters has as alias one of those names,
43+
* the help displaying will be disabled for that argument.
44+
* For example, for `@main def foo(help: Boolean)`, `scala foo -h` will display the help, but `scala foo --help` will fail,
45+
* as it will expect a Boolean value after `--help`.
4346
*
4447
* Parameters may be given annotations to add functionalities to the main function:
4548
* - `main.Alias` adds other names to a parameter. For example, if a parameter `node` has as aliases
@@ -69,6 +72,20 @@ final class main extends MainAnnotation:
6972
private val argMarker = "--"
7073
private val shortArgMarker = "-"
7174

75+
/**
76+
* The name of the special argument to display the method's help.
77+
* If one of the method's parameters is called the same, will be ignored.
78+
*/
79+
private val helpArg = "help"
80+
private var helpIsOverridden = false
81+
82+
/**
83+
* The short name of the special argument to display the method's help.
84+
* If one of the method's parameters uses the same short name, will be ignored.
85+
*/
86+
private val shortHelpArg = 'h'
87+
private var shortHelpIsOverridden = false
88+
7289
private val maxUsageLineLength = 120
7390

7491
/** A map from argument canonical name (the name of the parameter in the method definition) to parameter informations */
@@ -90,6 +107,9 @@ final class main extends MainAnnotation:
90107
names.map(_ -> canonicalName)
91108
).toMap
92109

110+
helpIsOverridden = namesToCanonicalName.exists((name, _) => name == helpArg)
111+
shortHelpIsOverridden = shortNamesToCanonicalName.exists((name, _) => name == shortHelpArg)
112+
93113
def getCanonicalArgName(arg: String): Option[String] =
94114
if arg.startsWith(argMarker) && arg.length > argMarker.length then
95115
namesToCanonicalName.get(arg.drop(argMarker.length))
@@ -305,7 +325,10 @@ final class main extends MainAnnotation:
305325
for (remainingArg <- positionalArgs) error(s"unused argument: $remainingArg")
306326
for (invalidArg <- invalidByNameArgs) error(s"unknown argument name: $invalidArg")
307327

308-
if args.contains(s"${argMarker}help") then
328+
val displayHelp =
329+
(!helpIsOverridden && args.contains(getNameWithMarker(helpArg))) || (!shortHelpIsOverridden && args.contains(getNameWithMarker(shortHelpArg)))
330+
331+
if displayHelp then
309332
usage()
310333
println()
311334
explain()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
##### --help
2+
Usage: helpOverride1 [--notHelp] <Int>
3+
4+
A method that should let --help and -h display help.
5+
Arguments:
6+
--notHelp - Int
7+
Error: invalid argument for help: --help
8+
Usage: helpOverride2 [--help] <Int>
9+
Usage: helpOverride3 [-h] <Int>
10+
11+
A method that should let --help display help, but not -h.
12+
Arguments:
13+
-h - Int
14+
Error: invalid argument for help: --help
15+
Error: missing argument for h
16+
Usage: helpOverride4 [--help] <Int> [-h] <Int>
17+
Error: invalid argument for notHelp: --help
18+
Usage: helpOverride5 [--notHelp | --help] <Int>
19+
Usage: helpOverride6 [--notHelp | -h] <Int>
20+
21+
A method that should let --help display help, but not -h.
22+
Arguments:
23+
--notHelp (-h) - Int
24+
Error: invalid argument for notHelp: --help
25+
Error: missing argument for notH
26+
Usage: helpOverride7 [--notHelp | --help] <Int> [--notH | -h] <Int>
27+
Error: invalid argument for notHelp: --help
28+
Usage: helpOverride8 [--notHelp | --help | -h] <Int>
29+
##### -h
30+
Usage: helpOverride1 [--notHelp] <Int>
31+
32+
A method that should let --help and -h display help.
33+
Arguments:
34+
--notHelp - Int
35+
Usage: helpOverride2 [--help] <Int>
36+
37+
A method that should let -h display help, but not --help.
38+
Arguments:
39+
--help - Int
40+
Error: invalid argument for h: -h
41+
Usage: helpOverride3 [-h] <Int>
42+
Error: invalid argument for help: -h
43+
Error: missing argument for h
44+
Usage: helpOverride4 [--help] <Int> [-h] <Int>
45+
Usage: helpOverride5 [--notHelp | --help] <Int>
46+
47+
A method that should let -h display help, but not --help.
48+
Arguments:
49+
--notHelp (--help) - Int
50+
Error: invalid argument for notHelp: -h
51+
Usage: helpOverride6 [--notHelp | -h] <Int>
52+
Error: invalid argument for notHelp: -h
53+
Error: missing argument for notH
54+
Usage: helpOverride7 [--notHelp | --help] <Int> [--notH | -h] <Int>
55+
Error: invalid argument for notHelp: -h
56+
Usage: helpOverride8 [--notHelp | --help | -h] <Int>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import scala.util.Try
2+
3+
object myProgram:
4+
5+
/** A method that should let --help and -h display help. */
6+
@main def helpOverride1(notHelp: Int) = ???
7+
8+
/** A method that should let -h display help, but not --help. */
9+
@main def helpOverride2(help: Int) = ???
10+
11+
/** A method that should let --help display help, but not -h. */
12+
@main def helpOverride3(h: Int) = ???
13+
14+
/** A method that should not let --help and -h display help. */
15+
@main def helpOverride4(help: Int, h: Int) = ???
16+
17+
18+
/** A method that should let -h display help, but not --help. */
19+
@main def helpOverride5(@main.Alias("help") notHelp: Int) = ???
20+
21+
/** A method that should let --help display help, but not -h. */
22+
@main def helpOverride6(@main.Alias("h") notHelp: Int) = ???
23+
24+
/** A method that should not let --help and -h display help. */
25+
@main def helpOverride7(@main.Alias("help") notHelp: Int, @main.Alias("h") notH: Int) = ???
26+
27+
/** A method that should not let --help and -h display help. */
28+
@main def helpOverride8(@main.Alias("help", "h") notHelp: Int) = ???
29+
30+
end myProgram
31+
32+
object Test:
33+
val allClazzes: Seq[Class[?]] =
34+
LazyList.from(1).map(i => Try(Class.forName("helpOverride" + i.toString))).takeWhile(_.isSuccess).map(_.get)
35+
36+
def callAllMains(args: Array[String]): Unit =
37+
for (clazz <- allClazzes) {
38+
val method = clazz.getMethod("main", classOf[Array[String]])
39+
method.invoke(null, args)
40+
}
41+
42+
def main(args: Array[String]): Unit =
43+
println("##### --help")
44+
callAllMains(Array("--help"))
45+
println("##### -h")
46+
callAllMains(Array("-h"))
47+
end Test

0 commit comments

Comments
 (0)