@@ -3,7 +3,9 @@ package config
3
3
4
4
import Settings ._
5
5
import core .Contexts ._
6
+ import printing .Highlighting
6
7
8
+ import scala .util .chaining .given
7
9
import scala .PartialFunction .cond
8
10
9
11
trait CliCommand :
@@ -52,7 +54,7 @@ trait CliCommand:
52
54
protected def availableOptionsMsg (cond : Setting [? ] => Boolean )(using settings : ConcreteSettings )(using SettingsState ): String =
53
55
val ss = (settings.allSettings filter cond).toList sortBy (_.name)
54
56
val maxNameWidth = 30
55
- val nameWidths = ss.map(_.name.length).partition (_ < maxNameWidth)._1
57
+ val nameWidths = ss.map(_.name.length).filter (_ < maxNameWidth)
56
58
val width = if nameWidths.nonEmpty then nameWidths.max else maxNameWidth
57
59
val terminalWidth = settings.pageWidth.value
58
60
val (nameWidth, descriptionWidth) = {
@@ -136,39 +138,10 @@ trait CliCommand:
136
138
createUsageMsg(" Possible private" , shouldExplain = true , isPrivate)
137
139
138
140
/** Used for the formatted output of -Xshow-phases */
139
- protected def phasesMessage (using ctx : Context ): String =
140
-
141
- import scala .io .AnsiColor .*
142
- val colors = Array (GREEN , YELLOW , /* BLUE,*/ MAGENTA , CYAN , RED )
141
+ protected def phasesMessage (using Context ): String =
143
142
val phases = new Compiler ().phases
144
- val nameLimit = 25
145
- val maxCol = ctx.settings.pageWidth.value
146
- val maxName = phases.flatten.map(_.phaseName.length).max
147
- val width = maxName.min(nameLimit)
148
- val maxDesc = maxCol - (width + 6 )
149
- val colorSlot = if ctx.useColors then GREEN .length.toString else " 0"
150
- val fmt = s " %. ${colorSlot}s% ${width}. ${width}s%. ${colorSlot}s %. ${maxDesc}s%n "
151
- def plain (name : String , description : String ) = fmt.format(" " , name, " " , description)
152
-
153
- val sb = new StringBuilder
154
- sb ++= plain(" phase name" , " description" )
155
- sb ++= plain(" ----------" , " -----------" )
156
-
157
- def color (index : Int ): String = colors(index % colors.length)
158
- def emit (index : Int )(phase : core.Phases .Phase ): Unit = sb ++= fmt.format(color(index), phase.phaseName, RESET , phase.description)
159
- def group (index : Int )(body : Int => Unit ): Unit =
160
- if ! ctx.useColors then sb ++= plain(s " { " , " " )
161
- body(index)
162
- if ! ctx.useColors then sb ++= plain(s " } " , " " )
163
-
164
- phases.zipWithIndex.foreach { (phase, index) =>
165
- phase match
166
- case List (single) => emit(index)(single)
167
- case Nil =>
168
- case mega => group(index)(i => mega.foreach(emit(i)))
169
- }
170
- sb.mkString
171
-
143
+ val formatter = Columnator (" phase name" , " description" , maxField = 25 , separation = 2 )
144
+ formatter(phases.map(mega => mega.map(p => (p.phaseName, p.description))))
172
145
173
146
/** Provide usage feedback on argument summary, assuming that all settings
174
147
* are already applied in context.
@@ -196,3 +169,55 @@ trait CliCommand:
196
169
197
170
extension [T ](setting : Setting [T ])
198
171
protected def value (using ss : SettingsState ): T = setting.valueIn(ss)
172
+
173
+ extension (s : String )
174
+ def padLeft (width : Int ): String = StringBuilder ().tap(_.append(" " * (width - s.length)).append(s)).toString
175
+
176
+ // 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 ):
178
+ def apply (texts : List [List [(String , String )]])(using Context ): String = StringBuilder ().tap(columnate(_, texts)).toString
179
+
180
+ private def columnate (sb : StringBuilder , texts : List [List [(String , String )]])(using Context ): Unit =
181
+ import scala .util .Properties .{lineSeparator => EOL }
182
+ import Highlighting .*
183
+ val colors = Seq (Green (_), Yellow (_), Magenta (_), Cyan (_), Red (_))
184
+ val nocolor = texts.length == 1
185
+ def color (index : Int ): String => Highlight = if nocolor then NoColor (_) else colors(index % colors.length)
186
+ val maxCol = ctx.settings.pageWidth.value
187
+ val field1 = maxField.min(texts.flatten.map(_._1.length).filter(_ < maxField).max) // widest field under maxField
188
+ val field2 = if field1 + separation + maxField < maxCol then maxCol - field1 - separation else 0 // skinny window -> terminal wrap
189
+ val separator = " " * separation
190
+ def formatField1 (text : String ): String = if text.length <= field1 then text.padLeft(field1) else EOL + " " .padLeft(field1)
191
+ def formatField2 (text : String ): String =
192
+ if field2 == 0 || text.length <= field2 then text
193
+ else
194
+ text.lastIndexOf(" " , field2) match
195
+ case - 1 => text
196
+ case i =>
197
+ val (prefix, rest) = text.splitAt(i)
198
+ s " ${prefix}${EOL }${formatField1(" " )}${separator}${formatField2(rest.trim)}"
199
+ def format (first : String , second : String , index : Int , colorPicker : Int => String => Highlight ) =
200
+ sb.append(colorPicker(index)(formatField1(first)).show)
201
+ .append(separator)
202
+ .append(formatField2(second))
203
+ .append(EOL ): Unit
204
+ def fancy (first : String , second : String , index : Int ) = format(first, second, index, color)
205
+ def plain (first : String , second : String ) = format(first, second, 0 , _ => NoColor (_))
206
+
207
+ if heading1.nonEmpty then
208
+ plain(heading1, heading2)
209
+ plain(" -" * heading1.length, " -" * heading2.length)
210
+
211
+ def emit (index : Int )(textPair : (String , String )): Unit = fancy(textPair._1, textPair._2, index)
212
+ def group (index : Int )(body : Int => Unit ): Unit =
213
+ if ! ctx.useColors then plain(s " { " , " " )
214
+ body(index)
215
+ if ! ctx.useColors then plain(s " } " , " " )
216
+
217
+ texts.zipWithIndex.foreach { (text, index) =>
218
+ text match
219
+ case List (single) => emit(index)(single)
220
+ case Nil =>
221
+ case mega => group(index)(i => mega.foreach(emit(i)))
222
+ }
223
+ end Columnator
0 commit comments