Skip to content

Add basic support for colored printers #3526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,12 @@ class PlainPrinter(_ctx: Context) extends Printer {
}

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

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

def plain = this

protected def keywordStr(text: String): String = coloredStr(text, SyntaxHighlighting.KeywordColor)
protected def typeText(text: Text): Text = coloredText(text, SyntaxHighlighting.TypeColor)
protected def literalText(text: Text): Text = coloredText(text, SyntaxHighlighting.LiteralColor)
protected def stringText(text: Text): Text = coloredText(text, SyntaxHighlighting.StringColor)

private def coloredStr(text: String, color: String): String =
if (ctx.useColors) color + text + SyntaxHighlighting.NoColor else text
private def coloredText(text: Text, color: String): Text =
if (ctx.useColors) color ~ text ~ SyntaxHighlighting.NoColor else text
}

78 changes: 40 additions & 38 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import transform.SymUtils._
import scala.annotation.switch
import language.implicitConversions
import dotty.tools.dotc.util.SourcePosition

import Highlighting._

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

Expand Down Expand Up @@ -80,7 +80,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
override def toTextRef(tp: SingletonType): Text = controlled {
tp match {
case tp: ThisType =>
if (tp.cls.isAnonymousClass) return "this"
if (tp.cls.isAnonymousClass) return keywordStr("this")
if (tp.cls is ModuleClass) return fullNameString(tp.cls.sourceModule)
case _ =>
}
Expand Down Expand Up @@ -123,7 +123,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
atPrec(InfixPrec) { argText(args.head) }
else
toTextTuple(args.init)
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
(keywordStr("implicit ") provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
}

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

def enumText(tree: untpd.Tree) = tree match { // DD
case _: untpd.GenFrom | _: untpd.GenAlias => toText(tree)
case _ => "if " ~ toText(tree)
case _ => keywordStr("if ") ~ toText(tree)
}

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

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

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

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

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

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

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

prefix ~ (" extends" provided !ofNew) ~~ parentsText ~~ bodyText
prefix ~ (keywordStr(" extends") provided !ofNew) ~~ parentsText ~~ bodyText
}

def toTextPackageId(pid: Tree): Text =
Expand All @@ -351,24 +351,26 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
toText(id.name)
}
case Ident(name) =>
tree.typeOpt match {
val txt = tree.typeOpt match {
case tp: NamedType if name != nme.WILDCARD =>
val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix
toTextPrefix(pre) ~ withPos(selectionString(tp), tree.pos)
case _ =>
toText(name)
}
if (name.isType) typeText(txt)
else txt
case tree @ Select(qual, name) =>
if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name)
if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
case tree: This =>
optDotPrefix(tree) ~ "this" ~ idText(tree)
optDotPrefix(tree) ~ keywordStr("this") ~ idText(tree)
case Super(qual: This, mix) =>
optDotPrefix(qual) ~ "super" ~ optText(mix)("[" ~ _ ~ "]")
optDotPrefix(qual) ~ keywordStr("super") ~ optText(mix)("[" ~ _ ~ "]")
case Apply(fun, args) =>
if (fun.hasType && fun.symbol == defn.throwMethod)
changePrec (GlobalPrec) {
"throw " ~ toText(args.head)
keywordStr("throw ") ~ toText(args.head)
}
else
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
Expand All @@ -380,7 +382,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case _ => withPos(toText(c), tree.pos)
}
case New(tpt) =>
"new " ~ {
keywordStr("new ") ~ {
tpt match {
case tpt: Template => toTextTemplate(tpt, ofNew = true)
case _ =>
Expand All @@ -400,25 +402,25 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
blockText(stats :+ expr)
case If(cond, thenp, elsep) =>
changePrec(GlobalPrec) {
"if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _)
keywordStr("if ") ~ toText(cond) ~ (keywordStr(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _)
}
case Closure(env, ref, target) =>
"closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~
toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")"
case Match(sel, cases) =>
if (sel.isEmpty) blockText(cases)
else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) }
else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) }
case CaseDef(pat, guard, body) =>
"case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
case Return(expr, from) =>
changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) }
changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) }
case Try(expr, cases, finalizer) =>
changePrec(GlobalPrec) {
"try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
keywordStr("try ") ~ toText(expr) ~ optText(cases)(keywordStr(" catch ") ~ _) ~ optText(finalizer)(keywordStr(" finally ") ~ _)
}
case Throw(expr) =>
changePrec(GlobalPrec) {
"throw " ~ toText(expr)
keywordStr("throw ") ~ toText(expr)
}
case SeqLiteral(elems, elemtpt) =>
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
Expand All @@ -428,9 +430,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tpt: untpd.DerivedTypeTree =>
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
case TypeTree() =>
toText(tree.typeOpt)
typeText(toText(tree.typeOpt))
case SingletonTypeTree(ref) =>
toTextLocal(ref) ~ ".type"
toTextLocal(ref) ~ "." ~ keywordStr("type")
case AndTypeTree(l, r) =>
changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) }
case OrTypeTree(l, r) =>
Expand Down Expand Up @@ -461,13 +463,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty)
case tree @ ValDef(name, tpt, _) =>
dclTextOr {
modText(tree.mods, if (tree.mods is Mutable) "var" else "val") ~~
modText(tree.mods, keywordStr(if (tree.mods is Mutable) "var" else "val")) ~~
nameIdText(tree) ~ optAscription(tpt) ~
withEnclosingDef(tree) { optText(tree.rhs)(" = " ~ _) }
}
case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
dclTextOr {
val prefix = modText(tree.mods, "def") ~~ nameIdText(tree)
val prefix = modText(tree.mods, keywordStr("def")) ~~ nameIdText(tree)
withEnclosingDef(tree) {
addVparamssText(prefix ~ tparamsText(tparams), vparamss) ~ optAscription(tpt) ~
optText(tree.rhs)(" = " ~ _)
Expand All @@ -476,16 +478,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tree @ TypeDef(name, rhs) =>
def typeDefText(tparamsText: => Text, rhsText: => Text) =
dclTextOr {
modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~
modText(tree.mods, keywordStr("type")) ~~ (varianceText(tree.mods) ~ typeText(nameIdText(tree))) ~
withEnclosingDef(tree) {
if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees
else tparamsText ~ rhsText
}
}
def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match {
case impl: Template =>
modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~
nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
modText(tree.mods, keywordStr(if ((tree).mods is Trait) "trait" else "class")) ~~
typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~
(if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "")
case rhs: TypeBoundsTree =>
typeDefText(tparamsTxt, toText(rhs))
Expand All @@ -504,15 +506,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case id :: Nil => toText(id)
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
}
"import " ~ toTextLocal(expr) ~ "." ~ selectorsText
keywordStr("import ") ~ toTextLocal(expr) ~ "." ~ selectorsText
case PackageDef(pid, stats) =>
val statsText = stats match {
case (pdef: PackageDef) :: Nil => toText(pdef)
case _ => toTextGlobal(stats, "\n")
}
val bodyText =
if (currentPrecedence == TopLevelPrec) "\n" ~ statsText else " {" ~ statsText ~ "}"
"package " ~ toTextPackageId(pid) ~ bodyText
keywordStr("package ") ~ toTextPackageId(pid) ~ bodyText
case tree: Template =>
toTextTemplate(tree)
case Annotated(arg, annot) =>
Expand All @@ -523,7 +525,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
toText(t)
case tree @ ModuleDef(name, impl) =>
withEnclosingDef(tree) {
modText(tree.mods, "object") ~~ nameIdText(tree) ~ toTextTemplate(impl)
modText(tree.mods, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl)
}
case SymbolLit(str) =>
"'" + str
Expand All @@ -539,7 +541,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
def argToText(arg: Tree) = arg match {
case arg @ ValDef(name, tpt, _) =>
val implicitText =
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; "implicit " }
if ((arg.mods is Implicit) && !implicitSeen) { implicitSeen = true; keywordStr("implicit ") }
else ""
implicitText ~ toText(name) ~ optAscription(tpt)
case _ =>
Expand All @@ -564,13 +566,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case Tuple(ts) =>
"(" ~ toTextGlobal(ts, ", ") ~ ")"
case WhileDo(cond, body) =>
changePrec(GlobalPrec) { "while " ~ toText(cond) ~ " do " ~ toText(body) }
changePrec(GlobalPrec) { keywordStr("while ") ~ toText(cond) ~ keywordStr(" do ") ~ toText(body) }
case DoWhile(cond, body) =>
changePrec(GlobalPrec) { "do " ~ toText(body) ~ " while " ~ toText(cond) }
changePrec(GlobalPrec) { keywordStr("do ") ~ toText(body) ~ keywordStr(" while ") ~ toText(cond) }
case ForYield(enums, expr) =>
forText(enums, expr, " yield ")
forText(enums, expr, keywordStr(" yield "))
case ForDo(enums, expr) =>
forText(enums, expr, " do ")
forText(enums, expr, keywordStr(" do "))
case GenFrom(pat, expr) =>
toText(pat) ~ " <- " ~ toText(expr)
case GenAlias(pat, expr) =>
Expand All @@ -580,11 +582,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
t ~ cxBoundToText(cxb)
}
case PatDef(mods, pats, tpt, rhs) =>
modText(mods, "val") ~~ toText(pats, ", ") ~ optAscription(tpt) ~
modText(mods, keywordStr("val")) ~~ toText(pats, ", ") ~ optAscription(tpt) ~
optText(rhs)(" = " ~ _)
case ParsedTry(expr, handler, finalizer) =>
changePrec(GlobalPrec) {
"try " ~ toText(expr) ~ " catch {" ~ toText(handler) ~ "}" ~ optText(finalizer)(" finally " ~ _)
keywordStr("try ") ~ toText(expr) ~ " " ~ keywordStr("catch") ~ " {" ~ toText(handler) ~ "}" ~ optText(finalizer)(keywordStr(" finally ") ~ _)
}
case Thicket(trees) =>
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object SyntaxHighlighting {
val KeywordColor = Console.YELLOW
val ValDefColor = Console.CYAN
val LiteralColor = Console.RED
val StringColor = Console.GREEN
val TypeColor = Console.MAGENTA
val AnnotationColor = Console.MAGENTA

Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/Texts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ object Texts {

def remaining(width: Int): Int = this match {
case Str(s, _) =>
width - s.length
width - lengthWithoutAnsi(s)
case Fluid(Nil) =>
width
case Fluid(last :: prevs) =>
Expand Down Expand Up @@ -59,11 +59,14 @@ object Texts {
else if (that.isEmpty) this
else if (that.isVertical) appendIndented(that)(width)
else if (this.isVertical) Fluid(that.layout(width) :: this.relems)
else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that)
else if (that.remaining(width - lengthWithoutAnsi(lastLine)) >= 0) appendToLastLine(that)
else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_))
else appendIndented(that)(width)
}

private def lengthWithoutAnsi(str: String): Int =
str.replaceAll("\u001b\\[\\d+m", "").length

def layout(width: Int): Text = this match {
case Str(s, _) =>
this
Expand Down
Loading