Skip to content

Commit d23ae39

Browse files
committed
Add basic support for colored printers
1 parent 1d24b19 commit d23ae39

File tree

5 files changed

+64
-47
lines changed

5 files changed

+64
-47
lines changed

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,12 @@ class PlainPrinter(_ctx: Context) extends Printer {
456456
}
457457

458458
def toText(const: Constant): Text = const.tag match {
459-
case StringTag => "\"" + escapedString(const.value.toString) + "\""
459+
case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"")
460460
case ClazzTag => "classOf[" ~ toText(const.typeValue.classSymbol) ~ "]"
461-
case CharTag => s"'${escapedChar(const.charValue)}'"
462-
case LongTag => const.longValue.toString + "L"
463-
case EnumTag => const.symbolValue.name.toString
464-
case _ => String.valueOf(const.value)
461+
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
462+
case LongTag => literalText(const.longValue.toString + "L")
463+
case EnumTag => literalText(const.symbolValue.name.toString)
464+
case _ => literalText(String.valueOf(const.value))
465465
}
466466

467467
def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now
@@ -534,5 +534,15 @@ class PlainPrinter(_ctx: Context) extends Printer {
534534
def summarized[T](op: => T): T = summarized(summarizeDepth)(op)
535535

536536
def plain = this
537+
538+
protected def keywordStr(text: String): String = coloredStr(text, SyntaxHighlighting.KeywordColor)
539+
protected def typeText(text: Text): Text = coloredText(text, SyntaxHighlighting.TypeColor)
540+
protected def literalText(text: Text): Text = coloredText(text, SyntaxHighlighting.LiteralColor)
541+
protected def stringText(text: Text): Text = coloredText(text, SyntaxHighlighting.StringColor)
542+
543+
private def coloredStr(text: String, color: String): String =
544+
if (ctx.useColors) color + text + SyntaxHighlighting.NoColor else text
545+
private def coloredText(text: Text, color: String): Text =
546+
if (ctx.useColors) color ~ text ~ SyntaxHighlighting.NoColor else text
537547
}
538548

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

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import config.Config
1616
import transform.SymUtils._
1717
import scala.annotation.switch
1818
import language.implicitConversions
19+
import Highlighting._
1920

2021
class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
2122

@@ -77,7 +78,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
7778
override def toTextRef(tp: SingletonType): Text = controlled {
7879
tp match {
7980
case tp: ThisType =>
80-
if (tp.cls.isAnonymousClass) return "this"
81+
if (tp.cls.isAnonymousClass) return keywordStr("this")
8182
if (tp.cls is ModuleClass) return fullNameString(tp.cls.sourceModule)
8283
case _ =>
8384
}
@@ -120,7 +121,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
120121
atPrec(InfixPrec) { argText(args.head) }
121122
else
122123
toTextTuple(args.init)
123-
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
124+
(keywordStr("implicit ") provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
124125
}
125126

126127
def isInfixType(tp: Type): Boolean = tp match {
@@ -233,18 +234,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
233234

234235
def enumText(tree: untpd.Tree) = tree match { // DD
235236
case _: untpd.GenFrom | _: untpd.GenAlias => toText(tree)
236-
case _ => "if " ~ toText(tree)
237+
case _ => keywordStr("if ") ~ toText(tree)
237238
}
238239

239240
def forText(enums: List[untpd.Tree], expr: untpd.Tree, sep: String): Text = // DD
240-
changePrec(GlobalPrec) { "for " ~ Text(enums map enumText, "; ") ~ sep ~ toText(expr) }
241+
changePrec(GlobalPrec) { keywordStr("for ") ~ Text(enums map enumText, "; ") ~ sep ~ toText(expr) }
241242

242243
def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD
243244
case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt)
244245
case untpd.Function(_, tpt) => " <% " ~ toText(tpt)
245246
}
246247

247-
def constrText(tree: untpd.Tree): Text = toTextLocal(tree).stripPrefix("new ") // DD
248+
def constrText(tree: untpd.Tree): Text = toTextLocal(tree).stripPrefix(keywordStr("new ")) // DD
248249

249250
def annotText(tree: untpd.Tree): Text = "@" ~ constrText(tree) // DD
250251

@@ -303,9 +304,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
303304
if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this"
304305
withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) }
305306
}
306-
val parentsText = Text(parents map constrText, " with ")
307+
val parentsText = Text(parents map constrText, keywordStr(" with "))
307308
val selfText = {
308-
val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString
309+
val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString
309310
(selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close
310311
} provided !self.isEmpty
311312

@@ -322,7 +323,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
322323

323324
val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}"
324325

325-
prefix ~ (" extends" provided !ofNew) ~~ parentsText ~~ bodyText
326+
prefix ~ (keywordStr(" extends") provided !ofNew) ~~ parentsText ~~ bodyText
326327
}
327328

328329
def toTextPackageId(pid: Tree): Text =
@@ -335,29 +336,31 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
335336
case id: Trees.SearchFailureIdent[_] =>
336337
tree.typeOpt match {
337338
case reason: Implicits.SearchFailureType =>
338-
toText(id.name) ~ "implicitly[" ~ toText(reason.expectedType) ~ "]"
339+
toText(id.name) ~ "implicitly" ~ toText(reason.expectedType) ~ "]"
339340
case _ =>
340341
toText(id.name)
341342
}
342343
case Ident(name) =>
343-
tree.typeOpt match {
344+
val txt = tree.typeOpt match {
344345
case tp: NamedType if name != nme.WILDCARD =>
345346
val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix
346347
toTextPrefix(pre) ~ selectionString(tp)
347348
case _ =>
348349
toText(name)
349350
}
351+
if (name.isType) typeText(txt)
352+
else txt
350353
case tree @ Select(qual, name) =>
351-
if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name)
354+
if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
352355
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
353356
case tree: This =>
354-
optDotPrefix(tree) ~ "this" ~ idText(tree)
357+
optDotPrefix(tree) ~ keywordStr("this") ~ idText(tree)
355358
case Super(qual: This, mix) =>
356-
optDotPrefix(qual) ~ "super" ~ optText(mix)("[" ~ _ ~ "]")
359+
optDotPrefix(qual) ~ keywordStr("super") ~ optText(mix)("[" ~ _ ~ "]")
357360
case Apply(fun, args) =>
358361
if (fun.hasType && fun.symbol == defn.throwMethod)
359362
changePrec (GlobalPrec) {
360-
"throw " ~ toText(args.head)
363+
keywordStr("throw ") ~ toText(args.head)
361364
}
362365
else
363366
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
@@ -369,7 +372,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
369372
case _ => toText(c)
370373
}
371374
case New(tpt) =>
372-
"new " ~ {
375+
keywordStr("new ") ~ {
373376
tpt match {
374377
case tpt: Template => toTextTemplate(tpt, ofNew = true)
375378
case _ =>
@@ -389,25 +392,25 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
389392
blockText(stats :+ expr)
390393
case If(cond, thenp, elsep) =>
391394
changePrec(GlobalPrec) {
392-
"if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _)
395+
keywordStr("if ") ~ toText(cond) ~ (keywordStr(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
393396
}
394397
case Closure(env, ref, target) =>
395398
"closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~
396399
toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")"
397400
case Match(sel, cases) =>
398401
if (sel.isEmpty) blockText(cases)
399-
else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) }
402+
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
400403
case CaseDef(pat, guard, body) =>
401-
"case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
404+
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
402405
case Return(expr, from) =>
403-
changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) }
406+
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
404407
case Try(expr, cases, finalizer) =>
405408
changePrec(GlobalPrec) {
406-
"try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
409+
keywordStr("try ") ~ toText(expr) ~ optText(cases)(keywordStr(" catch ") ~ _) ~ optText(finalizer)(keywordStr(" finally ") ~ _)
407410
}
408411
case Throw(expr) =>
409412
changePrec(GlobalPrec) {
410-
"throw " ~ toText(expr)
413+
keywordStr("throw ") ~ toText(expr)
411414
}
412415
case SeqLiteral(elems, elemtpt) =>
413416
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
@@ -417,9 +420,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
417420
case tpt: untpd.DerivedTypeTree =>
418421
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
419422
case TypeTree() =>
420-
toText(tree.typeOpt)
423+
typeText(toText(tree.typeOpt))
421424
case SingletonTypeTree(ref) =>
422-
toTextLocal(ref) ~ ".type"
425+
toTextLocal(ref) ~ "." ~ keywordStr("type")
423426
case AndTypeTree(l, r) =>
424427
changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) }
425428
case OrTypeTree(l, r) =>
@@ -450,13 +453,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
450453
("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty)
451454
case tree @ ValDef(name, tpt, _) =>
452455
dclTextOr {
453-
modText(tree.mods, if (tree.mods is Mutable) "var" else "val") ~~
456+
modText(tree.mods, keywordStr(if (tree.mods is Mutable) "var" else "val")) ~~
454457
nameIdText(tree) ~ optAscription(tpt) ~
455458
withEnclosingDef(tree) { optText(tree.rhs)(" = " ~ _) }
456459
}
457460
case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
458461
dclTextOr {
459-
val prefix = modText(tree.mods, "def") ~~ nameIdText(tree)
462+
val prefix = modText(tree.mods, keywordStr("def")) ~~ nameIdText(tree)
460463
withEnclosingDef(tree) {
461464
addVparamssText(prefix ~ tparamsText(tparams), vparamss) ~ optAscription(tpt) ~
462465
optText(tree.rhs)(" = " ~ _)
@@ -465,16 +468,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
465468
case tree @ TypeDef(name, rhs) =>
466469
def typeDefText(tparamsText: => Text, rhsText: => Text) =
467470
dclTextOr {
468-
modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~
471+
modText(tree.mods, keywordStr("type")) ~~ (varianceText(tree.mods) ~ typeText(nameIdText(tree))) ~
469472
withEnclosingDef(tree) {
470473
if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees
471474
else tparamsText ~ rhsText
472475
}
473476
}
474477
def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match {
475478
case impl: Template =>
476-
modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~
477-
nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
479+
modText(tree.mods, keywordStr(if ((tree).mods is Trait) "trait" else "class")) ~~
480+
typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
478481
(if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "")
479482
case rhs: TypeBoundsTree =>
480483
typeDefText(tparamsTxt, toText(rhs))
@@ -493,15 +496,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
493496
case id :: Nil => toText(id)
494497
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
495498
}
496-
"import " ~ toTextLocal(expr) ~ "." ~ selectorsText
499+
keywordStr("import ") ~ toTextLocal(expr) ~ "." ~ selectorsText
497500
case PackageDef(pid, stats) =>
498501
val statsText = stats match {
499502
case (pdef: PackageDef) :: Nil => toText(pdef)
500503
case _ => toTextGlobal(stats, "\n")
501504
}
502505
val bodyText =
503506
if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}"
504-
"package " ~ toTextPackageId(pid) ~ bodyText
507+
keywordStr("package ") ~ toTextPackageId(pid) ~ bodyText
505508
case tree: Template =>
506509
toTextTemplate(tree)
507510
case Annotated(arg, annot) =>
@@ -512,7 +515,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
512515
toText(t)
513516
case tree @ ModuleDef(name, impl) =>
514517
withEnclosingDef(tree) {
515-
modText(tree.mods, "object") ~~ nameIdText(tree) ~ toTextTemplate(impl)
518+
modText(tree.mods, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl)
516519
}
517520
case SymbolLit(str) =>
518521
"'" + str
@@ -528,7 +531,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
528531
def argToText(arg: Tree) = arg match {
529532
case arg @ ValDef(name, tpt, _) =>
530533
val implicitText =
531-
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " }
534+
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; keywordStr("implicit ") }
532535
else ""
533536
implicitText ~ toText(name) ~ optAscription(tpt)
534537
case _ =>
@@ -553,13 +556,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
553556
case Tuple(ts) =>
554557
"(" ~ toTextGlobal(ts, ", ") ~ ")"
555558
case WhileDo(cond, body) =>
556-
changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) }
559+
changePrec(GlobalPrec) { keywordStr("while ") ~ toText(cond) ~ keywordStr(" do ") ~ toText(body) }
557560
case DoWhile(cond, body) =>
558-
changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) }
561+
changePrec(GlobalPrec) { keywordStr("do ") ~ toText(body) ~ keywordStr(" while ") ~ toText(cond) }
559562
case ForYield(enums, expr) =>
560-
forText(enums, expr, " yield ")
563+
forText(enums, expr, keywordStr(" yield "))
561564
case ForDo(enums, expr) =>
562-
forText(enums, expr, " do ")
565+
forText(enums, expr, keywordStr(" do "))
563566
case GenFrom(pat, expr) =>
564567
toText(pat) ~ " <- " ~ toText(expr)
565568
case GenAlias(pat, expr) =>
@@ -569,11 +572,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
569572
t ~ cxBoundToText(cxb)
570573
}
571574
case PatDef(mods, pats, tpt, rhs) =>
572-
modText(mods, "val") ~~ toText(pats, ", ") ~ optAscription(tpt) ~
575+
modText(mods, keywordStr("val")) ~~ toText(pats, ", ") ~ optAscription(tpt) ~
573576
optText(rhs)(" = " ~ _)
574577
case ParsedTry(expr, handler, finalizer) =>
575578
changePrec(GlobalPrec) {
576-
"try " ~ toText(expr) ~ " catch {" ~ toText(handler) ~ "}" ~ optText(finalizer)(" finally " ~ _)
579+
keywordStr("try ") ~ toText(expr) ~ " " ~ keywordStr("catch") ~ " {" ~ toText(handler) ~ "}" ~ optText(finalizer)(keywordStr(" finally ") ~ _)
577580
}
578581
case Thicket(trees) =>
579582
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ object SyntaxHighlighting {
1717
val KeywordColor = Console.YELLOW
1818
val ValDefColor = Console.CYAN
1919
val LiteralColor = Console.RED
20+
val StringColor = Console.GREEN
2021
val TypeColor = Console.MAGENTA
2122
val AnnotationColor = Console.MAGENTA
2223

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dotty.tools.dotc
22
package printing
3-
import core.Contexts.Context
3+
44
import language.implicitConversions
55

66
object Texts {
@@ -26,7 +26,7 @@ object Texts {
2626

2727
def remaining(width: Int): Int = this match {
2828
case Str(s) =>
29-
width - s.length
29+
width - lengthWithoutAnsi(s)
3030
case Fluid(Nil) =>
3131
width
3232
case Fluid(last :: prevs) =>
@@ -60,11 +60,14 @@ object Texts {
6060
else if (that.isEmpty) this
6161
else if (that.isVertical) appendIndented(that)(width)
6262
else if (this.isVertical) Fluid(that.layout(width) :: this.relems)
63-
else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that)
63+
else if (that.remaining(width - lengthWithoutAnsi(lastLine)) >= 0) appendToLastLine(that)
6464
else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_))
6565
else appendIndented(that)(width)
6666
}
6767

68+
private def lengthWithoutAnsi(str: String): Int =
69+
str.replaceAll("\u001b\\[\\d+m", "").length
70+
6871
def layout(width: Int): Text = this match {
6972
case Str(_) =>
7073
this

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ trait ErrorMessagesTest extends DottyTest {
1414
private def newContext = {
1515
val rep = new StoreReporter(null)
1616
with UniqueMessagePositions with HideNonSensicalMessages
17-
initialCtx.setReporter(rep)
17+
initialCtx.setReporter(rep).setSetting(ctx.settings.color, "never")
1818
}
1919

2020
class Report(messages: List[Message], ictx: Context) {

0 commit comments

Comments
 (0)