Skip to content

Commit a52c9d6

Browse files
committed
Options help uses columnator
1 parent 73218a0 commit a52c9d6

File tree

1 file changed

+23
-57
lines changed

1 file changed

+23
-57
lines changed

compiler/src/dotty/tools/dotc/config/CliCommand.scala

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -51,59 +51,31 @@ trait CliCommand:
5151
end distill
5252

5353
/** Creates a help message for a subset of options based on cond */
54-
protected def availableOptionsMsg(cond: Setting[?] => Boolean)(using settings: ConcreteSettings)(using SettingsState): String =
55-
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
56-
val maxNameWidth = 30
57-
val nameWidths = ss.map(_.name.length).filter(_ < maxNameWidth)
58-
val width = if nameWidths.nonEmpty then nameWidths.max else maxNameWidth
59-
val terminalWidth = settings.pageWidth.value
60-
val (nameWidth, descriptionWidth) = {
61-
val w1 =
62-
if width < maxNameWidth then width
63-
else maxNameWidth
64-
val w2 =
65-
if terminalWidth < w1 + maxNameWidth then 0
66-
else terminalWidth - w1 - 1
67-
(w1, w2)
68-
}
69-
def formatName(name: String) =
70-
if name.length <= nameWidth then ("%-" + nameWidth + "s") format name
71-
else (name + "\n%-" + nameWidth + "s") format ""
72-
def formatDescription(text: String): String =
73-
if descriptionWidth == 0 then text
74-
else if text.length < descriptionWidth then text
75-
else {
76-
val inx = text.substring(0, descriptionWidth).lastIndexOf(" ")
77-
if inx < 0 then text
78-
else
79-
val str = text.substring(0, inx)
80-
s"${str}\n${formatName("")} ${formatDescription(text.substring(inx + 1))}"
81-
}
82-
def formatSetting(name: String, value: String) =
83-
if (value.nonEmpty)
84-
// the format here is helping to make empty padding and put the additional information exactly under the description.
85-
s"\n${formatName("")} $name: $value."
86-
else
87-
""
88-
def helpStr(s: Setting[?]) =
54+
protected def availableOptionsMsg(p: Setting[?] => Boolean)(using settings: ConcreteSettings)(using SettingsState): String =
55+
// result is (Option Name, descrption\ndefault: value\nchoices: x, y, z
56+
def help(s: Setting[?]): (String, String) =
57+
// For now, skip the default values that do not make sense for the end user, such as 'false' for the version command.
8958
def defaultValue = s.default match
9059
case _: Int | _: String => s.default.toString
91-
case _ =>
92-
// For now, skip the default values that do not make sense for the end user.
93-
// For example 'false' for the version command.
94-
""
95-
s"${formatName(s.name)} ${formatDescription(shortHelp(s))}${formatSetting("Default", defaultValue)}${formatSetting("Choices", s.legalChoices)}"
96-
ss.map(helpStr).mkString("", "\n", s"\n${formatName("@<file>")} ${formatDescription("A text file containing compiler arguments (options and source files).")}\n")
60+
case _ => ""
61+
val info = List(shortHelp(s), if defaultValue.nonEmpty then s"Default $defaultValue" else "", if s.legalChoices.nonEmpty then s"Choices ${s.legalChoices}" else "")
62+
(s.name, info.filter(_.nonEmpty).mkString("\n"))
63+
end help
64+
65+
val ss = settings.allSettings.filter(p).toList.sortBy(_.name)
66+
val formatter = Columnator("", "", maxField = 30)
67+
val fresh = ContextBase().initialCtx.fresh.setSettings(summon[SettingsState])
68+
formatter(List(ss.map(help) :+ ("@<file>", "A text file containing compiler arguments (options and source files).")))(using fresh)
9769
end availableOptionsMsg
9870

9971
protected def shortUsage: String = s"Usage: $cmdName <options> <source files>"
10072

10173
protected def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting[?] => Boolean)(using settings: ConcreteSettings)(using SettingsState): String =
10274
val prefix = List(
10375
Some(shortUsage),
104-
Some(explainAdvanced) filter (_ => shouldExplain),
76+
Some(explainAdvanced).filter(_ => shouldExplain),
10577
Some(label + " options include:")
106-
).flatten mkString "\n"
78+
).flatten.mkString("\n")
10779

10880
prefix + "\n" + availableOptionsMsg(cond)
10981

@@ -140,7 +112,7 @@ trait CliCommand:
140112
/** Used for the formatted output of -Xshow-phases */
141113
protected def phasesMessage(using Context): String =
142114
val phases = new Compiler().phases
143-
val formatter = Columnator("phase name", "description", maxField = 25, separation = 2)
115+
val formatter = Columnator("phase name", "description", maxField = 25)
144116
formatter(phases.map(mega => mega.map(p => (p.phaseName, p.description))))
145117

146118
/** Provide usage feedback on argument summary, assuming that all settings
@@ -174,7 +146,7 @@ trait CliCommand:
174146
def padLeft(width: Int): String = StringBuilder().tap(_.append(" " * (width - s.length)).append(s)).toString
175147

176148
// Formatting for -help and -Vphases in two columns, handling long field1 and wrapping long field2
177-
class Columnator(heading1: String, heading2: String, maxField: Int, separation: Int = 1):
149+
class Columnator(heading1: String, heading2: String, maxField: Int, separation: Int = 2):
178150
def apply(texts: List[List[(String, String)]])(using Context): String = StringBuilder().tap(columnate(_, texts)).toString
179151

180152
private def columnate(sb: StringBuilder, texts: List[List[(String, String)]])(using Context): Unit =
@@ -187,21 +159,15 @@ trait CliCommand:
187159
val field1 = maxField.min(texts.flatten.map(_._1.length).filter(_ < maxField).max) // widest field under maxField
188160
val field2 = if field1 + separation + maxField < maxCol then maxCol - field1 - separation else 0 // skinny window -> terminal wrap
189161
val separator = " " * separation
190-
def formatField1(text: String): String = if text.length <= field1 then text.padLeft(field1) else EOL + "".padLeft(field1)
162+
def formatField1(text: String): String = if text.length <= field1 then text.padLeft(field1) else text + EOL + "".padLeft(field1)
191163
def formatField2(text: String): String =
192-
def loopOverField2(fld: String): String =
193-
if field2 == 0 || fld.length <= field2 then fld
164+
def loopOverField2(fld: String): List[String] =
165+
if field2 == 0 || fld.length <= field2 then List(fld)
194166
else
195167
fld.lastIndexOf(" ", field2) match
196-
case -1 => fld
197-
case i =>
198-
val (prefix, rest) = fld.splitAt(i)
199-
s"${prefix}${EOL}${formatField1("")}${separator}${loopOverField2(rest.trim)}"
200-
def loopOverFields2(rest: List[String]): String =
201-
rest match
202-
case h :: t => loopOverField2(h.trim) + loopOverFields2(t)
203-
case Nil => ""
204-
loopOverFields2(text.split("\n").toList)
168+
case -1 => List(fld)
169+
case i => val (prefix, rest) = fld.splitAt(i) ; prefix :: loopOverField2(rest.trim)
170+
text.split("\n").toList.flatMap(loopOverField2).filter(_.nonEmpty).mkString(EOL + "".padLeft(field1) + separator)
205171
end formatField2
206172
def format(first: String, second: String, index: Int, colorPicker: Int => String => Highlight) =
207173
sb.append(colorPicker(index)(formatField1(first)).show)

0 commit comments

Comments
 (0)