Better Printers
Built-in tree/type printers in macros have their limitations:
- Scala 2's
showCodeandshowRaw: have no syntax highlighting and no line breaks and indentations, that makes reading code easier - Scala 3's
Printer.TreeAnsiCodeandPrinter.TypeReprAnsiCodeare fixing that, butPrinter.TreeStructurehas no support for syntax highlighting
This module addresses these shortcomings by providing improved printers with syntax highlighting and proper formatting for both Scala 2 and Scala 3.
Scala 2 vs Scala 3 Differences
The implementations for Scala 2 and Scala 3 are platform-specific and use different underlying APIs:
- Scala 2: Uses
showCodePrettyandshowRawPrettymethods that extend Scala 2'sCodePrinterandRawTreePrinter - Scala 3: Uses
FormattedTreeStructureandFormattedTreeStructureAnsiprinters that extend Scala 3'sPrinter[Tree]
While both provide similar functionality (syntax highlighting and formatting), they do not aim for identical output due to fundamental differences in how Scala 2 and Scala 3 represent ASTs. The output format will differ between platforms, but both provide improved readability over the built-in printers.
Basic Utilities integrations
If you are using MacroCommons and its:
Expr.prettyPrint(expr)/expr.prettyPrintExpr.plainPrint(expr)/expr.plainPrintExpr.prettyAST(expr)/expr.prettyASTExpr.plainAST(expr)/expr.plainASTType.prettyPrint[A]/Type[A].prettyPrintType.plainPrint[A]/Type[A].plainPrint
then you are using Better Printers already. You don't need to additionally add them to your project.
Installation
Installation is only necessary if you want to use Better Printers without the core Hearth library.
Standalone usage
When to Use What
Scala 2:
- showCodePretty: Use when you want to print code as it would appear in source (human-readable Scala code)
- showRawPretty: Use when you want to see the raw AST structure (useful for debugging macro implementations)
- SyntaxHighlight.ANSI: Use for terminal/console output with colors
- SyntaxHighlight.plain: Use when you need plain text without ANSI codes (e.g., for IDE hints or logs)
Scala 3:
- FormattedTreeStructureAnsi: Use for terminal/console output with colors and formatting
- FormattedTreeStructure: Use when you need plain text without ANSI codes (e.g., for IDE hints or logs)
Both provide indentation and formatting improvements over the built-in printers.
Scala 2
// file: Printers.scala - part of Scala 2 example
//> using scala 2.13.16
//> using dep com.kubuszok::hearth-better-printers::0.3.0
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
import hearth.treeprinter.{ShowCodePrettyScala2, SyntaxHighlight}
// Mix-in ShowCodePrettyScala2 using `c` as the name of the `blackbox.Context` value.
class Printers(val c: blackbox.Context) extends ShowCodePrettyScala2 {
import c.universe._, c.internal._
def previewASTImpl[A: c.WeakTypeTag](expr: c.Expr[A]): c.Expr[String] = c.Expr[String](
// Use SyntaxHighlight.plain for plain text
q"${showRawPretty(expr.tree, SyntaxHighlight.ANSI)}"
)
def previewCodeImpl[A: c.WeakTypeTag](expr: c.Expr[A]): c.Expr[String] = c.Expr[String](
// Use SyntaxHighlight.plain for plain text
q"${showCodePretty(expr.tree, SyntaxHighlight.ANSI)}"
)
}
object Printers {
def previewAST[A](expr: A): String = macro Printers.previewASTImpl[A]
def previewCode[A](expr: A): String = macro Printers.previewCodeImpl[A]
}
Scala 3
// file: Printers.scala - part of Scala 3 example
//> using scala 3.3.7
//> using dep com.kubuszok::hearth-better-printers::0.3.0
import scala.quoted.*
import hearth.treeprinter.ShowCodePrettyScala3
// Mix-in ShowCodePrettyScala3 using `quotes` as the name of the `Quotes` value.
class Printers(using val quotes: Quotes) extends ShowCodePrettyScala3 {
import quotes.*, quotes.reflect.*
def previewAST[A: Type](expr: Expr[A]): Expr[String] = Expr(
// Use FormattedTreeStructure for plain text
expr.asTerm.show(using FormattedTreeStructureAnsi)
)
def previewCode[A: Type](expr: Expr[A]): Expr[String] = Expr(
// For code-like output, use Printer.TreeAnsiCode
expr.asTerm.show(using Printer.TreeAnsiCode)
)
}
object Printers {
inline def previewAST[A](inline expr: A): String = ${ previewASTImpl('{ expr }) }
def previewASTImpl[A: Type](expr: Expr[A])
(using q: Quotes): Expr[String] =
(new Printers).previewAST(expr)
inline def previewCode[A](inline expr: A): String = ${ previewCodeImpl('{ expr }) }
def previewCodeImpl[A: Type](expr: Expr[A])
(using q: Quotes): Expr[String] =
(new Printers).previewCode(expr)
}
// file: Printers.test.scala - part of Scala 3 example
//> using test.dep org.scalameta::munit::1.2.4
final class PrintersSpec extends munit.FunSuite {
test("Print") {
println(Printers.previewAST(2 + 2)) // Shows formatted AST structure
println(Printers.previewCode(2 + 2)) // Shows formatted code
}
}
Runtime-Aware Type Printing
Better Printers also provides runtime-aware type printing (since 0.3.0) through RuntimeAwareTypePrinterScala2 and RuntimeAwareTypePrinterScala3 traits. These are mixed into MacroCommons automatically.
Runtime-aware printing decomposes applied types (e.g., Option[A]) into their type constructor and arguments. For each type argument, a callback can provide a runtime Expr[String] to substitute in place of the compile-time name. Non-overridden parts fall back to the standard printer (plain, pretty, or short).
This is used internally by Type.runtimePlainPrint, Type.runtimePrettyPrint, and Type.runtimeShortPrint — see Basic Utilities for the API. The TypeName[A] type class is built on top of these methods — see Type Name Utilities.
Comparison with Built-in Printers
| Feature | Scala 2 Built-in | Better Printers (Scala 2) | Scala 3 Built-in | Better Printers (Scala 3) |
|---|---|---|---|---|
| Code rendering | ||||
| ANSI colors | ❌ | ✅ | ✅ (TreeAnsiCode) |
✅ |
| Indentation | ❌ | ✅ | ✅ | ✅ |
| Tree rendering | ||||
| ANSI colors | ❌ | ✅ | ❌ | ✅ |
| Indentation | ❌ | ✅ | ❌ | ✅ |
Tip
If you're using MacroCommons from Hearth's Basic Utilities, you already have access to Better Printers through methods like expr.prettyPrint and expr.prettyAST. You don't need to use Better Printers directly unless you're building a standalone macro library.