Skip to content

Commit

Permalink
Shrink munit diff by extracting most of the printer to main munit
Browse files Browse the repository at this point in the history
  • Loading branch information
majk-p committed Apr 23, 2024
1 parent 129a98c commit e728d92
Show file tree
Hide file tree
Showing 17 changed files with 215 additions and 276 deletions.
@@ -1,6 +1,6 @@
package munit.internal

import munit.diff.Clue
import munit.Clue
import munit.Location
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
Expand Down
@@ -1,6 +1,6 @@
package munit.internal

import munit.diff.Clue
import munit.Clue
import munit.Location
import scala.quoted._
import scala.language.experimental.macros
Expand Down
6 changes: 3 additions & 3 deletions munit/shared/src/main/scala/munit/Assertions.scala
@@ -1,10 +1,10 @@
package munit

import munit.internal.console.{Lines, StackTraces}
import munit.diff.console.Printers
import munit.internal.console.Printers
import munit.diff.Printer
import munit.diff.Clue
import munit.diff.Clues
import munit.Clue
import munit.Clues
import munit.diff.EmptyPrinter

import scala.reflect.ClassTag
Expand Down
20 changes: 10 additions & 10 deletions munit/shared/src/main/scala/munit/Clue.scala
Expand Up @@ -2,16 +2,16 @@ package munit

import munit.internal.MacroCompat

// class Clue[+T](
// val source: String,
// val value: T,
// val valueType: String
// ) extends Serializable {
// override def toString(): String = s"Clue($source, $value)"
// }
class Clue[+T](
val source: String,
val value: T,
val valueType: String
) extends Serializable {
override def toString(): String = s"Clue($source, $value)"
}
object Clue extends MacroCompat.ClueMacro {
@deprecated("use fromValue instead", "1.0.0")
def empty[T](value: T): munit.diff.Clue[T] = fromValue(value)
def fromValue[T](value: T): munit.diff.Clue[T] =
new munit.diff.Clue("", value, "")
def empty[T](value: T): Clue[T] = fromValue(value)
def fromValue[T](value: T): Clue[T] =
new Clue("", value, "")
}
14 changes: 7 additions & 7 deletions munit/shared/src/main/scala/munit/Clues.scala
Expand Up @@ -2,10 +2,10 @@ package munit

import munit.internal.console.Printers

// class Clues(val values: List[Clue[_]]) {
// override def toString(): String = Printers.print(this)
// }
// object Clues {
// def empty: Clues = new Clues(List())
// def fromValue[T](value: T): Clues = new Clues(List(Clue.fromValue(value)))
// }
class Clues(val values: List[Clue[_]]) {
override def toString(): String = Printers.print(this)
}
object Clues {
def empty: Clues = new Clues(List())
def fromValue[T](value: T): Clues = new Clues(List(Clue.fromValue(value)))
}
2 changes: 1 addition & 1 deletion munit/shared/src/main/scala/munit/MUnitRunner.scala
Expand Up @@ -80,7 +80,7 @@ class MUnitRunner(val cls: Class[_ <: Suite], newInstance: () => Suite)
descriptions.getOrElseUpdate(
test, {
val escapedName = Printers.escapeNonVisible(test.name)
val testName = munit.diff.internal.Compat.LazyList
val testName = munit.internal.Compat.LazyList
.from(0)
.map {
case 0 => escapedName
Expand Down
@@ -1,6 +1,6 @@
package munit.internal

import munit.diff.Clue
import munit.Clue
import munit.Location
import scala.reflect.macros.blackbox.Context
import scala.reflect.macros.TypecheckException
Expand Down
Expand Up @@ -6,7 +6,7 @@ import munit.Location
import scala.collection.JavaConverters._
import scala.collection.mutable
import scala.util.control.NonFatal
import munit.diff.Clues
import munit.Clues

class Lines extends Serializable {
private val filecache = mutable.Map.empty[Path, Array[String]]
Expand Down
188 changes: 183 additions & 5 deletions munit/shared/src/main/scala/munit/internal/console/Printers.scala
Expand Up @@ -5,17 +5,195 @@ import munit.Location
import munit.diff.Printer
import munit.diff.EmptyPrinter
import munit.diff.console.{Printers => DiffPrinters}
import munit.Clues
import munit.Printable
import munit.internal.Compat

object Printers {

import DiffPrinters._

def log(any: Any, printer: Printer = EmptyPrinter)(implicit
loc: Location
): Unit = {
println(loc.toString)
println(DiffPrinters.print(any, printer))
println(print(any, printer))
}

/** Pretty-prints the value in a format that's optimized for producing diffs */
def print(any: Any, printer: Printer = EmptyPrinter): String = {
var height = printer.height
val out = new StringBuilder()
val indentStep = 2
def loop(a: Any, indent: Int): Unit = {
height -= 1
if (height < 0) {
out.append("...")
return
}
val nextIndent = indent + indentStep
val isDone = printer.print(a, out, indent)
if (!isDone) {
a match {
case null => out.append("null")
case x: Printable => x.print(out, indent)
case x: Char =>
out.append('\'')
if (x == '\'') out.append("\\'")
else printChar(x, out)
out.append('\'')
case x: Byte => out.append(x.toString())
case x: Short => out.append(x.toString())
case x: Int => out.append(x.toString())
case x: Long => out.append(x.toString())
case x: Float => out.append(x.toString())
case x: Double => out.append(x.toString())
case x: String => printString(x, out, printer)
case x: Clues =>
printApply(
"Clues",
x.values.iterator,
out,
indent,
nextIndent,
open = " {",
close = "}",
comma = ""
) { clue =>
if (clue.source.nonEmpty) {
out.append(clue.source)
}
if (clue.valueType.nonEmpty) {
out.append(": ").append(clue.valueType)
}
out.append(" = ")
loop(clue.value, nextIndent)
}
case None =>
out.append("None")
case Nil =>
out.append("Nil")
case x: Map[_, _] =>
printApply(
Compat.collectionClassName(x),
x.iterator,
out,
indent,
nextIndent
) { case (key, value) =>
loop(key, nextIndent)
out.append(" -> ")
loop(value, nextIndent)
}
case x: Iterable[_] =>
printApply(
Compat.collectionClassName(x),
x.iterator,
out,
indent,
nextIndent
) { value => loop(value, nextIndent) }
case x: Array[_] =>
printApply(
"Array",
x.iterator,
out,
indent,
nextIndent
) { value => loop(value, nextIndent) }
case it: Iterator[_] =>
if (it.isEmpty) out.append("empty iterator")
else out.append("non-empty iterator")
case p: Product =>
val elementNames = Compat.productElementNames(p)
val infiniteElementNames = Iterator.continually {
if (elementNames.hasNext) elementNames.next()
else ""
}
printApply(
p.productPrefix,
p.productIterator.zip(infiniteElementNames),
out,
indent,
nextIndent
) { case (value, key) =>
if (key.nonEmpty) {
out.append(key).append(" = ")
}
loop(value, nextIndent)
}
case _ =>
out.append(a.toString())
}
}
}
loop(any, indent = 0)
munit.diff.console.AnsiColors.filterAnsi(out.toString())
}

private def printApply[T](
prefix: String,
it: Iterator[T],
out: StringBuilder,
indent: Int,
nextIndent: Int,
open: String = "(",
close: String = ")",
comma: String = ","
)(fn: T => Unit): Unit = {
out.append(prefix)
out.append(open)
if (it.hasNext) {
printNewline(out, nextIndent)
while (it.hasNext) {
val value = it.next()
fn(value)
if (it.hasNext) {
out.append(comma)
printNewline(out, nextIndent)
} else {
printNewline(out, indent)
}
}
}
out.append(close)
}

private def printNewline(out: StringBuilder, indent: Int): Unit = {
out.append("\n")
var i = 0
while (i < indent) {
out.append(' ')
i += 1
}
}

/**
* Pretty-prints this string with non-visible characters escaped.
*
* The exact definition of "non-visible" is fuzzy and is subject to change.
* The original motivation for this method was to fix
* https://github.com/scalameta/munit/issues/258 related to escaping \r in
* test names.
*
* The spirit of this method is to preserve "visible" characters like emojis
* and double quotes and escape "non-visible" characters like newlines and
* ANSI escape codes. A non-goal of this method is to make the output
* copy-pasteable back into source code unlike the `printChar` method, which
* escapes for example double-quote characters.
*/
def escapeNonVisible(string: String): String = {
val out = new StringBuilder()
var i = 0
while (i < string.length()) {
val ch = string.charAt(i)
ch match {
case '"' | '\'' => out.append(ch)
case _ => printChar(ch, out, isEscapeUnicode = false)
}
i += 1
}
out.toString()
}
def print(any: Any, printer: Printer = EmptyPrinter): String =
DiffPrinters.print(any, printer)

def escapeNonVisible(string: String): String =
DiffPrinters.escapeNonVisible(string)
}

This file was deleted.

This file was deleted.

This file was deleted.

13 changes: 0 additions & 13 deletions scala-diff/shared/src/main/scala/munit/diff/Clue.scala

This file was deleted.

11 changes: 0 additions & 11 deletions scala-diff/shared/src/main/scala/munit/diff/Clues.scala

This file was deleted.

8 changes: 0 additions & 8 deletions scala-diff/shared/src/main/scala/munit/diff/Printable.scala

This file was deleted.

0 comments on commit e728d92

Please sign in to comment.