Skip to content

Commit 53c1fe5

Browse files
committed
Vphases uses Highlight, option formatting
1 parent 862b5d9 commit 53c1fe5

File tree

3 files changed

+60
-37
lines changed

3 files changed

+60
-37
lines changed

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

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import java.nio.file.{Files, Paths}
55

66
import Settings._
77
import core.Contexts._
8+
import printing.Highlighting
89
import Properties._
910

11+
import scala.util.chaining.given
1012
import scala.PartialFunction.cond
1113

1214
trait CliCommand:
@@ -55,7 +57,7 @@ trait CliCommand:
5557
protected def availableOptionsMsg(cond: Setting[?] => Boolean)(using settings: ConcreteSettings)(using SettingsState): String =
5658
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
5759
val maxNameWidth = 30
58-
val nameWidths = ss.map(_.name.length).partition(_ < maxNameWidth)._1
60+
val nameWidths = ss.map(_.name.length).filter(_ < maxNameWidth)
5961
val width = if nameWidths.nonEmpty then nameWidths.max else maxNameWidth
6062
val terminalWidth = settings.pageWidth.value
6163
val (nameWidth, descriptionWidth) = {
@@ -139,39 +141,10 @@ trait CliCommand:
139141
createUsageMsg("Possible private", shouldExplain = true, isPrivate)
140142

141143
/** Used for the formatted output of -Xshow-phases */
142-
protected def phasesMessage(using ctx: Context): String =
143-
144-
import scala.io.AnsiColor.*
145-
val colors = Array(GREEN, YELLOW, /*BLUE,*/ MAGENTA, CYAN, RED)
144+
protected def phasesMessage(using Context): String =
146145
val phases = new Compiler().phases
147-
val nameLimit = 25
148-
val maxCol = ctx.settings.pageWidth.value
149-
val maxName = phases.flatten.map(_.phaseName.length).max
150-
val width = maxName.min(nameLimit)
151-
val maxDesc = maxCol - (width + 6)
152-
val colorSlot = if ctx.useColors then GREEN.length.toString else "0"
153-
val fmt = s"%.${colorSlot}s%${width}.${width}s%.${colorSlot}s %.${maxDesc}s%n"
154-
def plain(name: String, description: String) = fmt.format("", name, "", description)
155-
156-
val sb = new StringBuilder
157-
sb ++= plain("phase name", "description")
158-
sb ++= plain("----------", "-----------")
159-
160-
def color(index: Int): String = colors(index % colors.length)
161-
def emit(index: Int)(phase: core.Phases.Phase): Unit = sb ++= fmt.format(color(index), phase.phaseName, RESET, phase.description)
162-
def group(index: Int)(body: Int => Unit): Unit =
163-
if !ctx.useColors then sb ++= plain(s"{", "")
164-
body(index)
165-
if !ctx.useColors then sb ++= plain(s"}", "")
166-
167-
phases.zipWithIndex.foreach { (phase, index) =>
168-
phase match
169-
case List(single) => emit(index)(single)
170-
case Nil =>
171-
case mega => group(index)(i => mega.foreach(emit(i)))
172-
}
173-
sb.mkString
174-
146+
val formatter = Columnator("phase name", "description", maxField = 25, separation = 2)
147+
formatter(phases.map(mega => mega.map(p => (p.phaseName, p.description))))
175148

176149
/** Provide usage feedback on argument summary, assuming that all settings
177150
* are already applied in context.
@@ -199,3 +172,55 @@ trait CliCommand:
199172

200173
extension [T](setting: Setting[T])
201174
protected def value(using ss: SettingsState): T = setting.valueIn(ss)
175+
176+
extension (s: String)
177+
def padLeft(width: Int): String = StringBuilder().tap(_.append(" " * (width - s.length)).append(s)).toString
178+
179+
// Formatting for -help and -Vphases in two columns, handling long field1 and wrapping long field2
180+
class Columnator(heading1: String, heading2: String, maxField: Int, separation: Int = 1):
181+
def apply(texts: List[List[(String, String)]])(using Context): String = StringBuilder().tap(columnate(_, texts)).toString
182+
183+
private def columnate(sb: StringBuilder, texts: List[List[(String, String)]])(using Context): Unit =
184+
import scala.util.Properties.{lineSeparator => EOL}
185+
import Highlighting.*
186+
val colors = Seq(Green(_), Yellow(_), Magenta(_), Cyan(_), Red(_))
187+
val nocolor = texts.length == 1
188+
def color(index: Int): String => Highlight = if nocolor then NoColor(_) else colors(index % colors.length)
189+
val maxCol = ctx.settings.pageWidth.value
190+
val field1 = maxField.min(texts.flatten.map(_._1.length).filter(_ < maxField).max) // widest field under maxField
191+
val field2 = if field1 + separation + maxField < maxCol then maxCol - field1 - separation else 0 // skinny window -> terminal wrap
192+
val separator = " " * separation
193+
def formatField1(text: String): String = if text.length <= field1 then text.padLeft(field1) else EOL + "".padLeft(field1)
194+
def formatField2(text: String): String =
195+
if field2 == 0 || text.length <= field2 then text
196+
else
197+
text.lastIndexOf(" ", field2) match
198+
case -1 => text
199+
case i =>
200+
val (prefix, rest) = text.splitAt(i)
201+
s"${prefix}${EOL}${formatField1("")}${separator}${formatField2(rest.trim)}"
202+
def format(first: String, second: String, index: Int, colorPicker: Int => String => Highlight) =
203+
sb.append(colorPicker(index)(formatField1(first)).show)
204+
.append(separator)
205+
.append(formatField2(second))
206+
.append(EOL): Unit
207+
def fancy(first: String, second: String, index: Int) = format(first, second, index, color)
208+
def plain(first: String, second: String) = format(first, second, 0, _ => NoColor(_))
209+
210+
if heading1.nonEmpty then
211+
plain(heading1, heading2)
212+
plain("-" * heading1.length, "-" * heading2.length)
213+
214+
def emit(index: Int)(textPair: (String, String)): Unit = fancy(textPair._1, textPair._2, index)
215+
def group(index: Int)(body: Int => Unit): Unit =
216+
if !ctx.useColors then plain(s"{", "")
217+
body(index)
218+
if !ctx.useColors then plain(s"}", "")
219+
220+
texts.zipWithIndex.foreach { (text, index) =>
221+
text match
222+
case List(single) => emit(index)(single)
223+
case Nil =>
224+
case mega => group(index)(i => mega.foreach(emit(i)))
225+
}
226+
end Columnator

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ private sealed trait VerboseSettings:
134134
self: SettingGroup =>
135135
val Vhelp: Setting[Boolean] = BooleanSetting("-V", "Print a synopsis of verbose options.")
136136
val Xprint: Setting[List[String]] = PhasesSetting("-Vprint", "Print out program after", aliases = List("-Xprint"))
137+
val XshowPhases: Setting[Boolean] = BooleanSetting("-Vphases", "List compiler phases.", aliases = List("-Xshow-phases"))
137138

138139
/** -W "Warnings" settings
139140
*/
@@ -215,7 +216,6 @@ private sealed trait XSettings:
215216
val XprintInline: Setting[Boolean] = BooleanSetting("-Xprint-inline", "Show where inlined code comes from.")
216217
val XprintSuspension: Setting[Boolean] = BooleanSetting("-Xprint-suspension", "Show when code is suspended until macros are compiled.")
217218
val Xprompt: Setting[Boolean] = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).")
218-
val XshowPhases: Setting[Boolean] = BooleanSetting("-Xshow-phases", "Print all compiler phases.")
219219
val XreplDisableDisplay: Setting[Boolean] = BooleanSetting("-Xrepl-disable-display", "Do not display definitions in REPL.")
220220
val XverifySignatures: Setting[Boolean] = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.")
221221
val XignoreScala2Macros: Setting[Boolean] = BooleanSetting("-Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.")

compiler/src/dotty/tools/dotc/printing/Highlighting.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ object Highlighting {
1010
abstract class Highlight(private val highlight: String) {
1111
def text: String
1212

13-
def show(using Context): String =
14-
if (ctx.settings.color.value == "never") text
15-
else highlight + text + Console.RESET
13+
def show(using Context): String = if ctx.useColors then highlight + text + Console.RESET else text
1614

1715
override def toString: String =
1816
highlight + text + Console.RESET

0 commit comments

Comments
 (0)