Skip to content

Commit

Permalink
Merge pull request #35 from olafurpg/debug
Browse files Browse the repository at this point in the history
Add heatmap over state visits.
  • Loading branch information
olafurpg committed Jan 25, 2016
2 parents 3e40106 + ec7bccf commit 1d267ff
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 15 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-classic" % "1.1.3",
"org.scalameta" %% "scalameta" % "0.1.0-SNAPSHOT",
"com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" % "test",
"com.lihaoyi" %% "scalatags" % "0.5.4" % "test",
"org.scalatest" %% "scalatest" % "2.2.1" % "test"
)

Expand Down
30 changes: 21 additions & 9 deletions src/main/scala/org/scalafmt/Debug.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,52 @@ package org.scalafmt

import scala.collection.mutable
import scala.meta.Tree
import scala.meta.tokens.Token

object Debug extends ScalaFmtLogger {
val treeExplored = mutable.Map.empty[Tree, Int]
val tokenExplored = mutable.Map.empty[FormatToken, Int]
val tokenExplored = mutable.Map.empty[Token, Int]
val formatTokenExplored = mutable.Map.empty[FormatToken, Int]
var explored = 0
var state = State.start
var toks = Array.empty[FormatToken]

def clear(): Unit = {
treeExplored.clear()
tokenExplored.clear()
}

def visit(token: FormatToken): Unit = {
def visit(token: Token): Unit = {
val visits = tokenExplored.getOrElse(token, 0) + 1
tokenExplored += token -> visits
}

def reportTokens = {
tokenExplored.toSeq.sortBy(_._1.right.end).foreach {
def visit(token: FormatToken): Unit = {
visit(token.left)
visit(token.right)
val visits = formatTokenExplored.getOrElse(token, 0) + 1
formatTokenExplored += token -> visits
}

def visit(tree: Tree): Unit = {
val visits = treeExplored.getOrElse(tree, 0) + 1
treeExplored += tree -> visits
}

def reportTokens() = {
formatTokenExplored.toSeq.sortBy(_._1.right.end).foreach {
case (code, count) =>
logger.debug(
f"""$count%-5s $code""".stripMargin)
}
}

def reportTrees = {
def reportTrees() = {
Debug.treeExplored.toSeq.sortBy(_._2).foreach { case (code, count) =>
logger.debug(
f"""$count%-5s
|$code""".stripMargin)
}
}

def visit(tree: Tree): Unit = {
val visits = treeExplored.getOrElse(tree, 0) + 1
treeExplored += tree -> visits
}
}
2 changes: 2 additions & 0 deletions src/main/scala/org/scalafmt/ScalaFmt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class ScalaFmt(val style: ScalaStyle) extends ScalaFmtLogger {
logger.warn("UNABLE TO FORMAT")
}
Debug.explored += explored
Debug.state = state
Debug.toks = toks
state.reconstructPath(toks, style)
}

Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/org/scalafmt/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ case class State(cost: Int,
* Returns formatted output from FormatTokens and Splits.
*/
def reconstructPath(toks: Array[FormatToken],
style: ScalaStyle): String = {
style: ScalaStyle,
f: FormatToken => String = _.left.code): String = {
// require(splits.length - start.splits.length == toks.length,
// s"${toks.toVector} $splits")
val sb = new StringBuilder()
Expand All @@ -42,7 +43,7 @@ case class State(cost: Int,
case (tok, split) =>
// logger.debug(s"${log(tok.left)} $split ${state.indents}")
state = state.next(style, split, tok)
sb.append(tok.left.code)
sb.append(f(tok))
val ws = split.modification match {
case Space =>
sb.append(" ")
Expand Down
10 changes: 10 additions & 0 deletions src/test/resources/TypeArgument.test
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ object a {
function[function[function[a, b],
function[c, d]]]
}
<<< SKIP Break on higher level of nesting 3x.
object a {
function[function[function[function[a, b], function[c, d]]]]
}
>>>
object a {
function[function[
function[function[a, b],
function[c, d]]]]
}
<<< SKIP Type args and args different policy.
object Object {
val x = function[a1234567, b1234567](a)
Expand Down
5 changes: 5 additions & 0 deletions src/test/scala/org/scalafmt/FilesUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ object FilesUtil {
def readFile(filename: String): String = new String(
java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(filename)))

def writeFile(filename: String, content: String): Unit = {
val path = java.nio.file.Paths.get(filename)
java.nio.file.Files.write(path, content.getBytes)
}

}
42 changes: 38 additions & 4 deletions src/test/scala/org/scalafmt/FormatTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ import org.scalatest.FunSuite
import org.scalatest.concurrent.Timeouts
import org.scalatest.time.SpanSugar._

import scala.collection.mutable

case class DiffTest(spec: String, name: String, original: String, expected: String)

case class Result(test: DiffTest,
obtained: String,
obtainedHtml: String,
redness: Int,
time: Long)

trait FormatTest
extends FunSuite with Timeouts with ScalaFmtLogger with BeforeAndAfterAll {

Expand All @@ -21,6 +29,14 @@ trait FormatTest
val fmt = new ScalaFmt(style)

lazy val onlyOne = tests.exists(_.name.startsWith("ONLY"))
val reports = mutable.ArrayBuilder.make[Result]

def red(token: FormatToken): Int = {
val max = 10
val i = Math.min(max, Debug.formatTokenExplored(token))
val k = (i.toDouble / max.toDouble * 256).toInt
Math.min(256, 270 - k)
}

tests.sortWith {
case (left, right) =>
Expand All @@ -31,22 +47,40 @@ trait FormatTest
!t.name.startsWith("SKIP") &&
(!onlyOne || t.name.startsWith("ONLY"))
}.foreach {
case DiffTest(spec, name, original, expected) =>
case t@DiffTest(spec, name, original, expected) =>
val testName = s"$spec: $name"
test(f"$testName%-50s|") {
failAfter(10 seconds) {
Debug.clear()
val before = Debug.explored
val result = fmt.format(original)
val start = System.currentTimeMillis()
val obtained = fmt.format(original)
logger.debug(f"${Debug.explored - before}%-4s $testName")
var maxTok = 0
val obtainedHtml =
Debug.state.reconstructPath(Debug.toks, style, { tok =>
import scalatags.Text.all._
val color = red(tok)
maxTok = Math.max(Debug.formatTokenExplored(tok), maxTok)
span(background := s"rgb(256, $color, $color)", tok.left.code).render
})
reports += Result(t,
obtained,
obtainedHtml,
maxTok,
System.currentTimeMillis() - start)

if (name.startsWith("ONLY"))
Debug.reportTokens
assert(result diff expected)
Debug.reportTokens()
assert(obtained diff expected)
}
}
}

override def afterAll(configMap: ConfigMap): Unit = {
logger.debug(s"Total explored: ${Debug.explored}")
val report = Report.generate(reports.result())
val filename = "target/index.html"
FilesUtil.writeFile(filename, report)
}
}
29 changes: 29 additions & 0 deletions src/test/scala/org/scalafmt/Report.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.scalafmt

import scalatags.Text.all._

object Report {
def generate(results: Seq[Result]): String = {
html(
head(
),
body(
div(
h1(id := "title", "Heatmap"),
for (result <- results.sortBy(-_.redness)
if result.test.name != "Warmup") yield {
div(
h2(result.test.name),
pre(
code(
raw(result.obtainedHtml)
)
)
)
}
)
)
).render
}

}

0 comments on commit 1d267ff

Please sign in to comment.