@@ -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,105 @@ 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- }
214+ private class Help (info : Info ):
215+
235216
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)
217+
218+ def printUsage (): Unit =
219+ def argsUsage : Seq [String ] =
220+ for (infos <- info.parameters)
221+ yield {
222+ val canonicalName = getNameWithMarker(infos.name)
223+ val shortNames = getShortNames(infos).map(getNameWithMarker)
224+ val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
225+ val namesPrint = (canonicalName +: alternativeNames ++: shortNames).mkString(" [" , " | " , " ]" )
226+ val shortTypeName = infos.typeName.split('.' ).last
227+ if infos.isVarargs then s " [< $shortTypeName> [< $shortTypeName> [...]]] "
228+ else if infos.hasDefault then s " [ $namesPrint < $shortTypeName>] "
229+ else s " $namesPrint < $shortTypeName> "
244230 }
245231
246- recurse(argsUsage, " " , Vector ()).toList
247- }
232+ def wrapArgumentUsages (argsUsage : Seq [String ], maxLength : Int ): Seq [String ] = {
233+ def recurse (args : Seq [String ], currentLine : String , acc : Vector [String ]): Seq [String ] =
234+ (args, currentLine) match {
235+ case (Nil , " " ) => acc
236+ case (Nil , l) => (acc :+ l)
237+ case (arg +: t, " " ) => recurse(t, arg, acc)
238+ case (arg +: t, l) if l.length + 1 + arg.length <= maxLength => recurse(t, s " $l $arg" , acc)
239+ case (arg +: t, l) => recurse(t, arg, acc :+ l)
240+ }
248241
249- val usageBeginning = s " Usage: ${info.name} "
250- val argsOffset = usageBeginning.length
251- val usages = wrapArgumentUsages(argsUsage, maxUsageLineLength - argsOffset)
242+ recurse(argsUsage, " " , Vector ()).toList
243+ }
252244
253- println(usageBeginning + usages.mkString(" \n " + " " * argsOffset))
254- end usage
245+ val printUsageBeginning = s " Usage: ${info.name} "
246+ val argsOffset = printUsageBeginning.length
247+ val printUsages = wrapArgumentUsages(argsUsage, maxUsageLineLength - argsOffset)
255248
256- private def explain () : Unit =
257- inline def shiftLines ( s : Seq [ String ], shift : Int ) : String = s.map( " " * shift + _).mkString( " \n " )
249+ println(printUsageBeginning + printUsages.mkString( " \n " + " " * argsOffset))
250+ end printUsage
258251
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- }
252+ def printExplain (): Unit =
253+ def shiftLines (s : Seq [String ], shift : Int ): String = s.map(" " * shift + _).mkString(" \n " )
268254
269- recurse(line, Vector ()).toList
270- }
255+ def wrapLongLine (line : String , maxLength : Int ): List [String ] = {
256+ def recurse (s : String , acc : Vector [String ]): Seq [String ] =
257+ val lastSpace = s.trim.nn.lastIndexOf(' ' , maxLength)
258+ if ((s.length <= maxLength) || (lastSpace < 0 ))
259+ acc :+ s
260+ else {
261+ val (shortLine, rest) = s.splitAt(lastSpace)
262+ recurse(rest.trim.nn, acc :+ shortLine)
263+ }
271264
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- }
265+ recurse(line, Vector ()).toList
266+ }
299267
300- println(argDoc)
301- }
302- end explain
268+ if (info.documentation.nonEmpty)
269+ println(wrapLongLine(info.documentation, maxUsageLineLength).mkString(" \n " ))
270+ if (info.parameters.nonEmpty) {
271+ val argNameShift = 2
272+ val argDocShift = argNameShift + 2
273+
274+ println(" Arguments:" )
275+ for infos <- info.parameters do
276+ val canonicalName = getNameWithMarker(infos.name)
277+ val shortNames = getShortNames(infos).map(getNameWithMarker)
278+ val alternativeNames = getAlternativeNames(infos).map(getNameWithMarker)
279+ val otherNames = (alternativeNames ++: shortNames) match {
280+ case Seq () => " "
281+ case names => names.mkString(" (" , " , " , " ) " )
282+ }
283+ val argDoc = StringBuilder (" " * argNameShift)
284+ argDoc.append(s " $canonicalName $otherNames- ${infos.typeName.split('.' ).last}" )
285+ if infos.isVarargs then argDoc.append(" (vararg)" )
286+ else if infos.hasDefault then argDoc.append(" (optional)" )
287+
288+ if (infos.documentation.nonEmpty) {
289+ val shiftedDoc =
290+ infos.documentation.split(" \n " ).nn
291+ .map(line => shiftLines(wrapLongLine(line.nn, maxUsageLineLength - argDocShift), argDocShift))
292+ .mkString(" \n " )
293+ argDoc.append(" \n " ).append(shiftedDoc)
294+ }
295+
296+ println(argDoc)
297+ }
298+ end printExplain
299+
300+ private object Help :
301+ def hasHelpArg (canonicalNames : CanonicalNames , args : Seq [String ]): Boolean =
302+ val helpIsOverridden = canonicalNames.getName(helpArg).isDefined
303+ val shortHelpIsOverridden = canonicalNames.getShortName(shortHelpArg).isDefined
304+ (! helpIsOverridden && args.contains(getNameWithMarker(helpArg))) ||
305+ (! shortHelpIsOverridden && args.contains(getNameWithMarker(shortHelpArg)))
303306
304307 def argGetter [T ](param : Parameter , arg : String , defaultArgument : Option [() => T ])(using p : FromString [T ]): () => T = {
305308 if arg.nonEmpty then parse[T ](param, arg)
@@ -330,7 +333,7 @@ final class newMain extends MainAnnotation[FromString, Any]:
330333 def run (execProgram : () => Any ): Unit = {
331334 if parseErrors.nonEmpty then
332335 for msg <- parseErrors do println(s " Error: $msg" )
333- usage ()
336+ help.printUsage ()
334337 else
335338 execProgram()
336339 }
0 commit comments