Skip to content

Commit 9531534

Browse files
authored
improved readability of help messages (#10304)
This PR aims to improve the readability of help messages. Concretely it addresses 2 weaknesses of the current behavior: 1. In the presence of a very long option name, e.g. `-scalajs-genStaticForwardersForNonTopLevelObjects`, all descriptions of the selected option subset are currently displayed with a large empty space on their left. **NB.** Alternative (and cheap) solution: define a maximum length for option names. 2. If the text length of the description is greater than the terminal width minus the largest option width (which not exceeds 30 chars), the rear part of the text flows to the next line, starting at **column 0**. **NB.** `javac` mixes two ways to display descriptions in help messages (see e.g. `javac -X`): descriptions are either programmatically split into several lines or their rear part simply flows to the next line, starting at **column 0**.
1 parent 84f85c2 commit 9531534

File tree

3 files changed

+54
-14
lines changed

3 files changed

+54
-14
lines changed

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

+35-13
Original file line numberDiff line numberDiff line change
@@ -66,26 +66,48 @@ trait CliCommand:
6666

6767
/** Creates a help message for a subset of options based on cond */
6868
protected def availableOptionsMsg(cond: Setting[?] => Boolean)(using settings: ConcreteSettings)(using SettingsState): String =
69-
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
70-
val width = (ss map (_.name.length)).max
71-
def format(s: String) = ("%-" + width + "s") format s
69+
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
70+
val maxNameWidth = 30
71+
val nameWidths = ss.map(_.name.length).partition(_ < maxNameWidth)._1
72+
val width = if nameWidths.nonEmpty then nameWidths.max else maxNameWidth
73+
val terminalWidth = settings.pageWidth.value
74+
val (nameWidth, descriptionWidth) = {
75+
val w1 =
76+
if width < maxNameWidth then width
77+
else maxNameWidth
78+
val w2 =
79+
if terminalWidth < w1 + maxNameWidth then 0
80+
else terminalWidth - w1 - 1
81+
(w1, w2)
82+
}
83+
def formatName(name: String) =
84+
if name.length <= nameWidth then ("%-" + nameWidth + "s") format name
85+
else (name + "\n%-" + nameWidth + "s") format ""
86+
def formatDescription(text: String): String =
87+
if descriptionWidth == 0 then text
88+
else if text.length < descriptionWidth then text
89+
else {
90+
val inx = text.substring(0, descriptionWidth).lastIndexOf(" ")
91+
if inx < 0 then text
92+
else
93+
val str = text.substring(0, inx)
94+
s"${str}\n${formatName("")} ${formatDescription(text.substring(inx + 1))}"
95+
}
96+
def formatSetting(name: String, value: String) =
97+
if (value.nonEmpty)
98+
// the format here is helping to make empty padding and put the additional information exactly under the description.
99+
s"\n${formatName("")} $name: $value."
100+
else
101+
""
72102
def helpStr(s: Setting[?]) =
73103
def defaultValue = s.default match
74104
case _: Int | _: String => s.default.toString
75105
case _ =>
76106
// For now, skip the default values that do not make sense for the end user.
77107
// For example 'false' for the version command.
78108
""
79-
80-
def formatSetting(name: String, value: String) =
81-
if (value.nonEmpty)
82-
// the format here is helping to make empty padding and put the additional information exactly under the description.
83-
s"\n${format("")} $name: $value."
84-
else
85-
""
86-
s"${format(s.name)} ${s.description}${formatSetting("Default", defaultValue)}${formatSetting("Choices", s.legalChoices)}"
87-
88-
ss.map(helpStr).mkString("", "\n", s"\n${format("@<file>")} A text file containing compiler arguments (options and source files).\n")
109+
s"${formatName(s.name)} ${formatDescription(s.description)}${formatSetting("Default", defaultValue)}${formatSetting("Choices", s.legalChoices)}"
110+
ss.map(helpStr).mkString("", "\n", s"\n${formatName("@<file>")} ${formatDescription("A text file containing compiler arguments (options and source files).")}\n")
89111

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

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ import Settings.Setting
1111
trait CommonScalaSettings { self: Settings.SettingGroup =>
1212
protected def defaultClasspath: String = sys.env.getOrElse("CLASSPATH", ".")
1313

14+
protected def defaultPageWidth: Int = {
15+
val defaultWidth = 80
16+
val columnsVar = System.getenv("COLUMNS")
17+
if columnsVar != null then columnsVar.toInt
18+
else if Properties.isWin then
19+
val ansiconVar = System.getenv("ANSICON") // eg. "142x32766 (142x26)"
20+
if ansiconVar != null && ansiconVar.matches("[0-9]+x.*") then
21+
ansiconVar.substring(0, ansiconVar.indexOf("x")).toInt
22+
else defaultWidth
23+
else defaultWidth
24+
}
25+
1426
/** Path related settings */
1527
val bootclasspath: Setting[String] = PathSetting("-bootclasspath", "Override location of bootstrap class files.", Defaults.scalaBootClassPath, aliases = List("--boot-class-path"))
1628
val extdirs: Setting[String] = PathSetting("-extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs, aliases = List("--extension-directories"))
@@ -26,7 +38,7 @@ trait CommonScalaSettings { self: Settings.SettingGroup =>
2638
val verbose: Setting[Boolean] = BooleanSetting("-verbose", "Output messages about what the compiler is doing.", aliases = List("--verbose"))
2739
val version: Setting[Boolean] = BooleanSetting("-version", "Print product version and exit.", aliases = List("--version"))
2840
val help: Setting[Boolean] = BooleanSetting("-help", "Print a synopsis of standard options.", aliases = List("--help"))
29-
val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", 80, aliases = List("--page-width"))
41+
val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", defaultPageWidth, aliases = List("--page-width"))
3042
val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.", aliases = List("--no-warnings"))
3143

3244
/** Other settings */

dist/bin/common

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ function onExit() {
2727
trap onExit INT TERM EXIT
2828

2929
unset cygwin mingw msys darwin conemu
30+
31+
# COLUMNS is used together with command line option '-pageWidth'.
32+
if command -v tput >/dev/null 2>&1; then
33+
export COLUMNS="$(tput -Tdumb cols)"
34+
fi
35+
3036
case "`uname`" in
3137
CYGWIN*) cygwin=true
3238
;;

0 commit comments

Comments
 (0)