@@ -77,7 +77,7 @@ final class newMain extends MainAnnotation[FromString, Any]:
7777
7878 private inline val maxUsageLineLength = 120
7979
80- private var info : Info = _ // TODO remove this var
80+ private var help : Help = _
8181
8282 private def getAliases (param : Parameter ): Seq [String ] =
8383 param.annotations.collect{ case a : Alias => a }.flatMap(_.aliases)
@@ -107,27 +107,19 @@ final class newMain extends MainAnnotation[FromString, Any]:
107107 getAliases(param).filter(name => ! nameIsValid(name) && ! shortNameIsValid(name))
108108
109109 def command (info : Info , args : Seq [String ]): Option [Seq [String ]] =
110- this .info = info
110+ help = new Help (info)
111+ val canonicalNames = CanonicalNames (info)
111112
112113 val errors = new mutable.ArrayBuffer [String ]
113114
114115 def error (msg : String ): Unit = {
115116 errors += msg
116117 }
117118
118- val canonicalNames = CanonicalNames (info)
119-
120- val helpIsOverridden = canonicalNames.getName(helpArg).isDefined
121- val shortHelpIsOverridden = canonicalNames.getShortName(shortHelpArg).isDefined
122-
123- val displayHelp =
124- (! helpIsOverridden && args.contains(getNameWithMarker(helpArg))) ||
125- (! shortHelpIsOverridden && args.contains(getNameWithMarker(shortHelpArg)))
126-
127- if displayHelp then
128- usage()
119+ if Help .hasHelpArg(canonicalNames, args) then
120+ help.printUsage()
129121 println()
130- explain ()
122+ help.printExplain ()
131123 None
132124 else
133125 val (positionalArgs, byNameArgs, invalidByNameArgs) = {
@@ -212,94 +204,103 @@ final class newMain extends MainAnnotation[FromString, Any]:
212204
213205 if errors.nonEmpty then
214206 for msg <- errors do println(s " Error: $msg" )
215- usage ()
207+ help.printUsage ()
216208 None
217209 else
218210 Some (argStrings.flatten)
219211 end if
220212 end command
221213
222- private def usage (): Unit =
223- def argsUsage : Seq [String ] =
224- for (infos <- info.parameters)
225- yield {
226- val canonicalName = getNameWithMarker(infos.name)
227- val shortNames = getShortNames(infos).map(getNameWithMarker)
228- val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
229- val namesPrint = (canonicalName +: alternativeNames ++: shortNames).mkString(" [" , " | " , " ]" )
230- val shortTypeName = infos.typeName.split('.' ).last
231- if infos.isVarargs then s " [< $shortTypeName> [< $shortTypeName> [...]]] "
232- else if infos.hasDefault then s " [ $namesPrint < $shortTypeName>] "
233- else s " $namesPrint < $shortTypeName> "
234- }
235-
236- def wrapArgumentUsages (argsUsage : Seq [String ], maxLength : Int ): Seq [String ] = {
237- def recurse (args : Seq [String ], currentLine : String , acc : Vector [String ]): Seq [String ] =
238- (args, currentLine) match {
239- case (Nil , " " ) => acc
240- case (Nil , l) => (acc :+ l)
241- case (arg +: t, " " ) => recurse(t, arg, acc)
242- case (arg +: t, l) if l.length + 1 + arg.length <= maxLength => recurse(t, s " $l $arg" , acc)
243- case (arg +: t, l) => recurse(t, arg, acc :+ l)
214+ private class Help (info : Info ):
215+
216+ def printUsage (): Unit =
217+ def argsUsage : Seq [String ] =
218+ for (infos <- info.parameters)
219+ yield {
220+ val canonicalName = getNameWithMarker(infos.name)
221+ val shortNames = getShortNames(infos).map(getNameWithMarker)
222+ val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
223+ val namesPrint = (canonicalName +: alternativeNames ++: shortNames).mkString(" [" , " | " , " ]" )
224+ val shortTypeName = infos.typeName.split('.' ).last
225+ if infos.isVarargs then s " [< $shortTypeName> [< $shortTypeName> [...]]] "
226+ else if infos.hasDefault then s " [ $namesPrint < $shortTypeName>] "
227+ else s " $namesPrint < $shortTypeName> "
244228 }
245229
246- recurse(argsUsage, " " , Vector ()).toList
247- }
230+ def wrapArgumentUsages (argsUsage : Seq [String ], maxLength : Int ): Seq [String ] = {
231+ def recurse (args : Seq [String ], currentLine : String , acc : Vector [String ]): Seq [String ] =
232+ (args, currentLine) match {
233+ case (Nil , " " ) => acc
234+ case (Nil , l) => (acc :+ l)
235+ case (arg +: t, " " ) => recurse(t, arg, acc)
236+ case (arg +: t, l) if l.length + 1 + arg.length <= maxLength => recurse(t, s " $l $arg" , acc)
237+ case (arg +: t, l) => recurse(t, arg, acc :+ l)
238+ }
248239
249- val usageBeginning = s " Usage: ${info.name} "
250- val argsOffset = usageBeginning.length
251- val usages = wrapArgumentUsages(argsUsage, maxUsageLineLength - argsOffset)
240+ recurse(argsUsage, " " , Vector ()).toList
241+ }
252242
253- println(usageBeginning + usages.mkString(" \n " + " " * argsOffset))
254- end usage
243+ val printUsageBeginning = s " Usage: ${info.name} "
244+ val argsOffset = printUsageBeginning.length
245+ val printUsages = wrapArgumentUsages(argsUsage, maxUsageLineLength - argsOffset)
255246
256- private def explain () : Unit =
257- inline def shiftLines ( s : Seq [ String ], shift : Int ) : String = s.map( " " * shift + _).mkString( " \n " )
247+ println(printUsageBeginning + printUsages.mkString( " \n " + " " * argsOffset))
248+ end printUsage
258249
259- def wrapLongLine (line : String , maxLength : Int ): List [String ] = {
260- def recurse (s : String , acc : Vector [String ]): Seq [String ] =
261- val lastSpace = s.trim.nn.lastIndexOf(' ' , maxLength)
262- if ((s.length <= maxLength) || (lastSpace < 0 ))
263- acc :+ s
264- else {
265- val (shortLine, rest) = s.splitAt(lastSpace)
266- recurse(rest.trim.nn, acc :+ shortLine)
267- }
250+ def printExplain (): Unit =
251+ def shiftLines (s : Seq [String ], shift : Int ): String = s.map(" " * shift + _).mkString(" \n " )
268252
269- recurse(line, Vector ()).toList
270- }
253+ def wrapLongLine (line : String , maxLength : Int ): List [String ] = {
254+ def recurse (s : String , acc : Vector [String ]): Seq [String ] =
255+ val lastSpace = s.trim.nn.lastIndexOf(' ' , maxLength)
256+ if ((s.length <= maxLength) || (lastSpace < 0 ))
257+ acc :+ s
258+ else {
259+ val (shortLine, rest) = s.splitAt(lastSpace)
260+ recurse(rest.trim.nn, acc :+ shortLine)
261+ }
271262
272- if (info.documentation.nonEmpty)
273- println(wrapLongLine(info.documentation, maxUsageLineLength).mkString(" \n " ))
274- if (info.parameters.nonEmpty) {
275- val argNameShift = 2
276- val argDocShift = argNameShift + 2
277-
278- println(" Arguments:" )
279- for infos <- info.parameters do
280- val canonicalName = getNameWithMarker(infos.name)
281- val shortNames = getShortNames(infos).map(getNameWithMarker)
282- val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
283- val otherNames = (alternativeNames ++: shortNames) match {
284- case Seq () => " "
285- case names => names.mkString(" (" , " , " , " ) " )
286- }
287- val argDoc = StringBuilder (" " * argNameShift)
288- argDoc.append(s " $canonicalName $otherNames- ${infos.typeName.split('.' ).last}" )
289- if infos.isVarargs then argDoc.append(" (vararg)" )
290- else if infos.hasDefault then argDoc.append(" (optional)" )
291-
292- if (infos.documentation.nonEmpty) {
293- val shiftedDoc =
294- infos.documentation.split(" \n " ).nn
295- .map(line => shiftLines(wrapLongLine(line.nn, maxUsageLineLength - argDocShift), argDocShift))
296- .mkString(" \n " )
297- argDoc.append(" \n " ).append(shiftedDoc)
298- }
263+ recurse(line, Vector ()).toList
264+ }
299265
300- println(argDoc)
301- }
302- end explain
266+ if (info.documentation.nonEmpty)
267+ println(wrapLongLine(info.documentation, maxUsageLineLength).mkString(" \n " ))
268+ if (info.parameters.nonEmpty) {
269+ val argNameShift = 2
270+ val argDocShift = argNameShift + 2
271+
272+ println(" Arguments:" )
273+ for infos <- info.parameters do
274+ val canonicalName = getNameWithMarker(infos.name)
275+ val shortNames = getShortNames(infos).map(getNameWithMarker)
276+ val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
277+ val otherNames = (alternativeNames ++: shortNames) match {
278+ case Seq () => " "
279+ case names => names.mkString(" (" , " , " , " ) " )
280+ }
281+ val argDoc = StringBuilder (" " * argNameShift)
282+ argDoc.append(s " $canonicalName $otherNames- ${infos.typeName.split('.' ).last}" )
283+ if infos.isVarargs then argDoc.append(" (vararg)" )
284+ else if infos.hasDefault then argDoc.append(" (optional)" )
285+
286+ if (infos.documentation.nonEmpty) {
287+ val shiftedDoc =
288+ infos.documentation.split(" \n " ).nn
289+ .map(line => shiftLines(wrapLongLine(line.nn, maxUsageLineLength - argDocShift), argDocShift))
290+ .mkString(" \n " )
291+ argDoc.append(" \n " ).append(shiftedDoc)
292+ }
293+
294+ println(argDoc)
295+ }
296+ end printExplain
297+
298+ private object Help :
299+ def hasHelpArg (canonicalNames : CanonicalNames , args : Seq [String ]): Boolean =
300+ val helpIsOverridden = canonicalNames.getName(helpArg).isDefined
301+ val shortHelpIsOverridden = canonicalNames.getShortName(shortHelpArg).isDefined
302+ (! helpIsOverridden && args.contains(getNameWithMarker(helpArg))) ||
303+ (! shortHelpIsOverridden && args.contains(getNameWithMarker(shortHelpArg)))
303304
304305 def argGetter [T ](param : Parameter , arg : String , defaultArgument : Option [() => T ])(using p : FromString [T ]): () => T = {
305306 if arg.nonEmpty then parse[T ](param, arg)
@@ -330,7 +331,7 @@ final class newMain extends MainAnnotation[FromString, Any]:
330331 def run (execProgram : () => Any ): Unit = {
331332 if parseErrors.nonEmpty then
332333 for msg <- parseErrors do println(s " Error: $msg" )
333- usage ()
334+ help.printUsage ()
334335 else
335336 execProgram()
336337 }
0 commit comments