Skip to content

Commit 74bd066

Browse files
committed
Add basic support for colored printers
1 parent 1d79ae2 commit 74bd066

File tree

5 files changed

+63
-47
lines changed

5 files changed

+63
-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 & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import transform.SymUtils._
1717
import scala.annotation.switch
1818
import language.implicitConversions
1919
import dotty.tools.dotc.util.SourcePosition
20-
20+
import Highlighting._
2121

2222
class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
2323

@@ -80,7 +80,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
8080
override def toTextRef(tp: SingletonType): Text = controlled {
8181
tp match {
8282
case tp: ThisType =>
83-
if (tp.cls.isAnonymousClass) return "this"
83+
if (tp.cls.isAnonymousClass) return keywordStr("this")
8484
if (tp.cls is ModuleClass) return fullNameString(tp.cls.sourceModule)
8585
case _ =>
8686
}
@@ -123,7 +123,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
123123
atPrec(InfixPrec) { argText(args.head) }
124124
else
125125
toTextTuple(args.init)
126-
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
126+
(keywordStr("implicit ") provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
127127
}
128128

129129
def isInfixType(tp: Type): Boolean = tp match {
@@ -236,18 +236,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
236236

237237
def enumText(tree: untpd.Tree) = tree match { // DD
238238
case _: untpd.GenFrom | _: untpd.GenAlias => toText(tree)
239-
case _ => "if " ~ toText(tree)
239+
case _ => keywordStr("if ") ~ toText(tree)
240240
}
241241

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

245245
def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD
246246
case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt)
247247
case untpd.Function(_, tpt) => " <% " ~ toText(tpt)
248248
}
249249

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

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

@@ -314,9 +314,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
314314
if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this"
315315
withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) }
316316
}
317-
val parentsText = Text(parents map constrText, " with ")
317+
val parentsText = Text(parents map constrText, keywordStr(" with "))
318318
val selfText = {
319-
val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString
319+
val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString
320320
(selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close
321321
} provided !self.isEmpty
322322

@@ -333,7 +333,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
333333

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

336-
prefix ~ (" extends" provided !ofNew) ~~ parentsText ~~ bodyText
336+
prefix ~ (keywordStr(" extends") provided !ofNew) ~~ parentsText ~~ bodyText
337337
}
338338

339339
def toTextPackageId(pid: Tree): Text =
@@ -346,29 +346,31 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
346346
case id: Trees.SearchFailureIdent[_] =>
347347
tree.typeOpt match {
348348
case reason: Implicits.SearchFailureType =>
349-
toText(id.name) ~ "implicitly[" ~ toText(reason.expectedType) ~ "]"
349+
toText(id.name) ~ "implicitly" ~ toText(reason.expectedType) ~ "]"
350350
case _ =>
351351
toText(id.name)
352352
}
353353
case Ident(name) =>
354-
tree.typeOpt match {
354+
val txt = tree.typeOpt match {
355355
case tp: NamedType if name != nme.WILDCARD =>
356356
val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix
357357
toTextPrefix(pre) ~ withPos(selectionString(tp), tree.pos)
358358
case _ =>
359359
toText(name)
360360
}
361+
if (name.isType) typeText(txt)
362+
else txt
361363
case tree @ Select(qual, name) =>
362-
if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name)
364+
if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
363365
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
364366
case tree: This =>
365-
optDotPrefix(tree) ~ "this" ~ idText(tree)
367+
optDotPrefix(tree) ~ keywordStr("this") ~ idText(tree)
366368
case Super(qual: This, mix) =>
367-
optDotPrefix(qual) ~ "super" ~ optText(mix)("[" ~ _ ~ "]")
369+
optDotPrefix(qual) ~ keywordStr("super") ~ optText(mix)("[" ~ _ ~ "]")
368370
case Apply(fun, args) =>
369371
if (fun.hasType && fun.symbol == defn.throwMethod)
370372
changePrec (GlobalPrec) {
371-
"throw " ~ toText(args.head)
373+
keywordStr("throw ") ~ toText(args.head)
372374
}
373375
else
374376
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
@@ -380,7 +382,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
380382
case _ => withPos(toText(c), tree.pos)
381383
}
382384
case New(tpt) =>
383-
"new " ~ {
385+
keywordStr("new ") ~ {
384386
tpt match {
385387
case tpt: Template => toTextTemplate(tpt, ofNew = true)
386388
case _ =>
@@ -400,25 +402,25 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
400402
blockText(stats :+ expr)
401403
case If(cond, thenp, elsep) =>
402404
changePrec(GlobalPrec) {
403-
"if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _)
405+
keywordStr("if ") ~ toText(cond) ~ (keywordStr(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
404406
}
405407
case Closure(env, ref, target) =>
406408
"closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~
407409
toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")"
408410
case Match(sel, cases) =>
409411
if (sel.isEmpty) blockText(cases)
410-
else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) }
412+
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
411413
case CaseDef(pat, guard, body) =>
412-
"case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
414+
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
413415
case Return(expr, from) =>
414-
changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) }
416+
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
415417
case Try(expr, cases, finalizer) =>
416418
changePrec(GlobalPrec) {
417-
"try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
419+
keywordStr("try ") ~ toText(expr) ~ optText(cases)(keywordStr(" catch ") ~ _) ~ optText(finalizer)(keywordStr(" finally ") ~ _)
418420
}
419421
case Throw(expr) =>
420422
changePrec(GlobalPrec) {
421-
"throw " ~ toText(expr)
423+
keywordStr("throw ") ~ toText(expr)
422424
}
423425
case SeqLiteral(elems, elemtpt) =>
424426
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
@@ -428,9 +430,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
428430
case tpt: untpd.DerivedTypeTree =>
429431
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
430432
case TypeTree() =>
431-
toText(tree.typeOpt)
433+
typeText(toText(tree.typeOpt))
432434
case SingletonTypeTree(ref) =>
433-
toTextLocal(ref) ~ ".type"
435+
toTextLocal(ref) ~ "." ~ keywordStr("type")
434436
case AndTypeTree(l, r) =>
435437
changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) }
436438
case OrTypeTree(l, r) =>
@@ -461,13 +463,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
461463
("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty)
462464
case tree @ ValDef(name, tpt, _) =>
463465
dclTextOr {
464-
modText(tree.mods, if (tree.mods is Mutable) "var" else "val") ~~
466+
modText(tree.mods, keywordStr(if (tree.mods is Mutable) "var" else "val")) ~~
465467
nameIdText(tree) ~ optAscription(tpt) ~
466468
withEnclosingDef(tree) { optText(tree.rhs)(" = " ~ _) }
467469
}
468470
case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
469471
dclTextOr {
470-
val prefix = modText(tree.mods, "def") ~~ nameIdText(tree)
472+
val prefix = modText(tree.mods, keywordStr("def")) ~~ nameIdText(tree)
471473
withEnclosingDef(tree) {
472474
addVparamssText(prefix ~ tparamsText(tparams), vparamss) ~ optAscription(tpt) ~
473475
optText(tree.rhs)(" = " ~ _)
@@ -476,16 +478,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
476478
case tree @ TypeDef(name, rhs) =>
477479
def typeDefText(tparamsText: => Text, rhsText: => Text) =
478480
dclTextOr {
479-
modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~
481+
modText(tree.mods, keywordStr("type")) ~~ (varianceText(tree.mods) ~ typeText(nameIdText(tree))) ~
480482
withEnclosingDef(tree) {
481483
if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees
482484
else tparamsText ~ rhsText
483485
}
484486
}
485487
def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match {
486488
case impl: Template =>
487-
modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~
488-
nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
489+
modText(tree.mods, keywordStr(if ((tree).mods is Trait) "trait" else "class")) ~~
490+
typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
489491
(if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "")
490492
case rhs: TypeBoundsTree =>
491493
typeDefText(tparamsTxt, toText(rhs))
@@ -504,15 +506,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
504506
case id :: Nil => toText(id)
505507
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
506508
}
507-
"import " ~ toTextLocal(expr) ~ "." ~ selectorsText
509+
keywordStr("import ") ~ toTextLocal(expr) ~ "." ~ selectorsText
508510
case PackageDef(pid, stats) =>
509511
val statsText = stats match {
510512
case (pdef: PackageDef) :: Nil => toText(pdef)
511513
case _ => toTextGlobal(stats, "\n")
512514
}
513515
val bodyText =
514516
if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}"
515-
"package " ~ toTextPackageId(pid) ~ bodyText
517+
keywordStr("package ") ~ toTextPackageId(pid) ~ bodyText
516518
case tree: Template =>
517519
toTextTemplate(tree)
518520
case Annotated(arg, annot) =>
@@ -523,7 +525,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
523525
toText(t)
524526
case tree @ ModuleDef(name, impl) =>
525527
withEnclosingDef(tree) {
526-
modText(tree.mods, "object") ~~ nameIdText(tree) ~ toTextTemplate(impl)
528+
modText(tree.mods, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl)
527529
}
528530
case SymbolLit(str) =>
529531
"'" + str
@@ -539,7 +541,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
539541
def argToText(arg: Tree) = arg match {
540542
case arg @ ValDef(name, tpt, _) =>
541543
val implicitText =
542-
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " }
544+
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; keywordStr("implicit ") }
543545
else ""
544546
implicitText ~ toText(name) ~ optAscription(tpt)
545547
case _ =>
@@ -564,13 +566,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
564566
case Tuple(ts) =>
565567
"(" ~ toTextGlobal(ts, ", ") ~ ")"
566568
case WhileDo(cond, body) =>
567-
changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) }
569+
changePrec(GlobalPrec) { keywordStr("while ") ~ toText(cond) ~ keywordStr(" do ") ~ toText(body) }
568570
case DoWhile(cond, body) =>
569-
changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) }
571+
changePrec(GlobalPrec) { keywordStr("do ") ~ toText(body) ~ keywordStr(" while ") ~ toText(cond) }
570572
case ForYield(enums, expr) =>
571-
forText(enums, expr, " yield ")
573+
forText(enums, expr, keywordStr(" yield "))
572574
case ForDo(enums, expr) =>
573-
forText(enums, expr, " do ")
575+
forText(enums, expr, keywordStr(" do "))
574576
case GenFrom(pat, expr) =>
575577
toText(pat) ~ " <- " ~ toText(expr)
576578
case GenAlias(pat, expr) =>
@@ -580,11 +582,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
580582
t ~ cxBoundToText(cxb)
581583
}
582584
case PatDef(mods, pats, tpt, rhs) =>
583-
modText(mods, "val") ~~ toText(pats, ", ") ~ optAscription(tpt) ~
585+
modText(mods, keywordStr("val")) ~~ toText(pats, ", ") ~ optAscription(tpt) ~
584586
optText(rhs)(" = " ~ _)
585587
case ParsedTry(expr, handler, finalizer) =>
586588
changePrec(GlobalPrec) {
587-
"try " ~ toText(expr) ~ " catch {" ~ toText(handler) ~ "}" ~ optText(finalizer)(" finally " ~ _)
589+
keywordStr("try ") ~ toText(expr) ~ " " ~ keywordStr("catch") ~ " {" ~ toText(handler) ~ "}" ~ optText(finalizer)(keywordStr(" finally ") ~ _)
588590
}
589591
case Thicket(trees) =>
590592
"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: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ object Texts {
2525

2626
def remaining(width: Int): Int = this match {
2727
case Str(s, _) =>
28-
width - s.length
28+
width - lengthWithoutAnsi(s)
2929
case Fluid(Nil) =>
3030
width
3131
case Fluid(last :: prevs) =>
@@ -59,11 +59,14 @@ object Texts {
5959
else if (that.isEmpty) this
6060
else if (that.isVertical) appendIndented(that)(width)
6161
else if (this.isVertical) Fluid(that.layout(width) :: this.relems)
62-
else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that)
62+
else if (that.remaining(width - lengthWithoutAnsi(lastLine)) >= 0) appendToLastLine(that)
6363
else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_))
6464
else appendIndented(that)(width)
6565
}
6666

67+
private def lengthWithoutAnsi(str: String): Int =
68+
str.replaceAll("\u001b\\[\\d+m", "").length
69+
6770
def layout(width: Int): Text = this match {
6871
case Str(s, _) =>
6972
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)