@@ -5,8 +5,10 @@ import java.nio.file.{Files, Paths}
5
5
6
6
import Settings ._
7
7
import core .Contexts ._
8
+ import printing .Highlighting
8
9
import Properties ._
9
10
11
+ import scala .util .chaining .given
10
12
import scala .PartialFunction .cond
11
13
12
14
trait CliCommand :
@@ -55,7 +57,7 @@ trait CliCommand:
55
57
protected def availableOptionsMsg (cond : Setting [? ] => Boolean )(using settings : ConcreteSettings )(using SettingsState ): String =
56
58
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
57
59
val maxNameWidth = 30
58
- val nameWidths = ss.map(_.name.length).partition (_ < maxNameWidth)._1
60
+ val nameWidths = ss.map(_.name.length).filter (_ < maxNameWidth)
59
61
val width = if nameWidths.nonEmpty then nameWidths.max else maxNameWidth
60
62
val terminalWidth = settings.pageWidth.value
61
63
val (nameWidth, descriptionWidth) = {
@@ -139,39 +141,10 @@ trait CliCommand:
139
141
createUsageMsg(" Possible private" , shouldExplain = true , isPrivate)
140
142
141
143
/** 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 =
146
145
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))))
175
148
176
149
/** Provide usage feedback on argument summary, assuming that all settings
177
150
* are already applied in context.
@@ -199,3 +172,55 @@ trait CliCommand:
199
172
200
173
extension [T ](setting : Setting [T ])
201
174
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
0 commit comments