Skip to content

Commit 2fba289

Browse files
Merge pull request #3526 from dotty-staging/color-printer
Add basic support for colored printers
2 parents 463b996 + ac6315b commit 2fba289

File tree

6 files changed

+103
-79
lines changed

6 files changed

+103
-79
lines changed

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

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

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

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

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

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

Lines changed: 40 additions & 38 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
}
@@ -124,7 +124,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
124124
atPrec(InfixPrec) { argText(args.head) }
125125
else
126126
toTextTuple(args.init)
127-
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
127+
(keywordStr("implicit ") provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
128128
}
129129

130130
def toTextDependentFunction(appType: MethodType): Text = {
@@ -244,18 +244,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
244244

245245
def enumText(tree: untpd.Tree) = tree match { // DD
246246
case _: untpd.GenFrom | _: untpd.GenAlias => toText(tree)
247-
case _ => "if " ~ toText(tree)
247+
case _ => keywordStr("if ") ~ toText(tree)
248248
}
249249

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

253253
def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD
254254
case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt)
255255
case untpd.Function(_, tpt) => " <% " ~ toText(tpt)
256256
}
257257

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

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

@@ -322,9 +322,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
322322
if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this"
323323
withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) }
324324
}
325-
val parentsText = Text(parents map constrText, " with ")
325+
val parentsText = Text(parents map constrText, keywordStr(" with "))
326326
val selfText = {
327-
val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString
327+
val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString
328328
(selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close
329329
} provided !self.isEmpty
330330

@@ -341,7 +341,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
341341

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

344-
prefix ~ (" extends" provided !ofNew) ~~ parentsText ~~ bodyText
344+
prefix ~ (keywordStr(" extends") provided !ofNew) ~~ parentsText ~~ bodyText
345345
}
346346

347347
def toTextPackageId(pid: Tree): Text =
@@ -359,24 +359,26 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
359359
toText(id.name)
360360
}
361361
case Ident(name) =>
362-
tree.typeOpt match {
362+
val txt = tree.typeOpt match {
363363
case tp: NamedType if name != nme.WILDCARD =>
364364
val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix
365365
toTextPrefix(pre) ~ withPos(selectionString(tp), tree.pos)
366366
case _ =>
367367
toText(name)
368368
}
369+
if (name.isType) typeText(txt)
370+
else txt
369371
case tree @ Select(qual, name) =>
370-
if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name)
372+
if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
371373
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
372374
case tree: This =>
373-
optDotPrefix(tree) ~ "this" ~ idText(tree)
375+
optDotPrefix(tree) ~ keywordStr("this") ~ idText(tree)
374376
case Super(qual: This, mix) =>
375-
optDotPrefix(qual) ~ "super" ~ optText(mix)("[" ~ _ ~ "]")
377+
optDotPrefix(qual) ~ keywordStr("super") ~ optText(mix)("[" ~ _ ~ "]")
376378
case Apply(fun, args) =>
377379
if (fun.hasType && fun.symbol == defn.throwMethod)
378380
changePrec (GlobalPrec) {
379-
"throw " ~ toText(args.head)
381+
keywordStr("throw ") ~ toText(args.head)
380382
}
381383
else
382384
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
@@ -388,7 +390,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
388390
case _ => withPos(toText(c), tree.pos)
389391
}
390392
case New(tpt) =>
391-
"new " ~ {
393+
keywordStr("new ") ~ {
392394
tpt match {
393395
case tpt: Template => toTextTemplate(tpt, ofNew = true)
394396
case _ =>
@@ -408,25 +410,25 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
408410
blockText(stats :+ expr)
409411
case If(cond, thenp, elsep) =>
410412
changePrec(GlobalPrec) {
411-
"if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _)
413+
keywordStr("if ") ~ toText(cond) ~ (keywordStr(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
412414
}
413415
case Closure(env, ref, target) =>
414416
"closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~
415417
toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")"
416418
case Match(sel, cases) =>
417419
if (sel.isEmpty) blockText(cases)
418-
else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) }
420+
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
419421
case CaseDef(pat, guard, body) =>
420-
"case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
422+
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
421423
case Return(expr, from) =>
422-
changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) }
424+
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
423425
case Try(expr, cases, finalizer) =>
424426
changePrec(GlobalPrec) {
425-
"try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
427+
keywordStr("try ") ~ toText(expr) ~ optText(cases)(keywordStr(" catch ") ~ _) ~ optText(finalizer)(keywordStr(" finally ") ~ _)
426428
}
427429
case Throw(expr) =>
428430
changePrec(GlobalPrec) {
429-
"throw " ~ toText(expr)
431+
keywordStr("throw ") ~ toText(expr)
430432
}
431433
case SeqLiteral(elems, elemtpt) =>
432434
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
@@ -436,9 +438,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
436438
case tpt: untpd.DerivedTypeTree =>
437439
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
438440
case TypeTree() =>
439-
toText(tree.typeOpt)
441+
typeText(toText(tree.typeOpt))
440442
case SingletonTypeTree(ref) =>
441-
toTextLocal(ref) ~ ".type"
443+
toTextLocal(ref) ~ "." ~ keywordStr("type")
442444
case AndTypeTree(l, r) =>
443445
changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) }
444446
case OrTypeTree(l, r) =>
@@ -469,13 +471,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
469471
("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty)
470472
case tree @ ValDef(name, tpt, _) =>
471473
dclTextOr {
472-
modText(tree.mods, if (tree.mods is Mutable) "var" else "val") ~~
474+
modText(tree.mods, keywordStr(if (tree.mods is Mutable) "var" else "val")) ~~
473475
nameIdText(tree) ~ optAscription(tpt) ~
474476
withEnclosingDef(tree) { optText(tree.rhs)(" = " ~ _) }
475477
}
476478
case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
477479
dclTextOr {
478-
val prefix = modText(tree.mods, "def") ~~ nameIdText(tree)
480+
val prefix = modText(tree.mods, keywordStr("def")) ~~ nameIdText(tree)
479481
withEnclosingDef(tree) {
480482
addVparamssText(prefix ~ tparamsText(tparams), vparamss) ~ optAscription(tpt) ~
481483
optText(tree.rhs)(" = " ~ _)
@@ -484,16 +486,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
484486
case tree @ TypeDef(name, rhs) =>
485487
def typeDefText(tparamsText: => Text, rhsText: => Text) =
486488
dclTextOr {
487-
modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~
489+
modText(tree.mods, keywordStr("type")) ~~ (varianceText(tree.mods) ~ typeText(nameIdText(tree))) ~
488490
withEnclosingDef(tree) {
489491
if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees
490492
else tparamsText ~ rhsText
491493
}
492494
}
493495
def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match {
494496
case impl: Template =>
495-
modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~
496-
nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
497+
modText(tree.mods, keywordStr(if ((tree).mods is Trait) "trait" else "class")) ~~
498+
typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
497499
(if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "")
498500
case rhs: TypeBoundsTree =>
499501
typeDefText(tparamsTxt, toText(rhs))
@@ -512,15 +514,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
512514
case id :: Nil => toText(id)
513515
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
514516
}
515-
"import " ~ toTextLocal(expr) ~ "." ~ selectorsText
517+
keywordStr("import ") ~ toTextLocal(expr) ~ "." ~ selectorsText
516518
case PackageDef(pid, stats) =>
517519
val statsText = stats match {
518520
case (pdef: PackageDef) :: Nil => toText(pdef)
519521
case _ => toTextGlobal(stats, "\n")
520522
}
521523
val bodyText =
522524
if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}"
523-
"package " ~ toTextPackageId(pid) ~ bodyText
525+
keywordStr("package ") ~ toTextPackageId(pid) ~ bodyText
524526
case tree: Template =>
525527
toTextTemplate(tree)
526528
case Annotated(arg, annot) =>
@@ -531,7 +533,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
531533
toText(t)
532534
case tree @ ModuleDef(name, impl) =>
533535
withEnclosingDef(tree) {
534-
modText(tree.mods, "object") ~~ nameIdText(tree) ~ toTextTemplate(impl)
536+
modText(tree.mods, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl)
535537
}
536538
case SymbolLit(str) =>
537539
"'" + str
@@ -547,7 +549,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
547549
def argToText(arg: Tree) = arg match {
548550
case arg @ ValDef(name, tpt, _) =>
549551
val implicitText =
550-
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " }
552+
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; keywordStr("implicit ") }
551553
else ""
552554
implicitText ~ toText(name) ~ optAscription(tpt)
553555
case _ =>
@@ -572,13 +574,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
572574
case Tuple(ts) =>
573575
"(" ~ toTextGlobal(ts, ", ") ~ ")"
574576
case WhileDo(cond, body) =>
575-
changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) }
577+
changePrec(GlobalPrec) { keywordStr("while ") ~ toText(cond) ~ keywordStr(" do ") ~ toText(body) }
576578
case DoWhile(cond, body) =>
577-
changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) }
579+
changePrec(GlobalPrec) { keywordStr("do ") ~ toText(body) ~ keywordStr(" while ") ~ toText(cond) }
578580
case ForYield(enums, expr) =>
579-
forText(enums, expr, " yield ")
581+
forText(enums, expr, keywordStr(" yield "))
580582
case ForDo(enums, expr) =>
581-
forText(enums, expr, " do ")
583+
forText(enums, expr, keywordStr(" do "))
582584
case GenFrom(pat, expr) =>
583585
toText(pat) ~ " <- " ~ toText(expr)
584586
case GenAlias(pat, expr) =>
@@ -588,11 +590,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
588590
t ~ cxBoundToText(cxb)
589591
}
590592
case PatDef(mods, pats, tpt, rhs) =>
591-
modText(mods, "val") ~~ toText(pats, ", ") ~ optAscription(tpt) ~
593+
modText(mods, keywordStr("val")) ~~ toText(pats, ", ") ~ optAscription(tpt) ~
592594
optText(rhs)(" = " ~ _)
593595
case ParsedTry(expr, handler, finalizer) =>
594596
changePrec(GlobalPrec) {
595-
"try " ~ toText(expr) ~ " catch {" ~ toText(handler) ~ "}" ~ optText(finalizer)(" finally " ~ _)
597+
keywordStr("try ") ~ toText(expr) ~ " " ~ keywordStr("catch") ~ " {" ~ toText(handler) ~ "}" ~ optText(finalizer)(keywordStr(" finally ") ~ _)
596598
}
597599
case Thicket(trees) =>
598600
"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

0 commit comments

Comments
 (0)