package sbt
import java.net.URI
import java.io.File
import Project.{ScopedKey, flattenLocals, compiled}
import Load.BuildStructure
import Predef.{any2stringadd => _, _}
object SettingGraph
{
def apply(structure: BuildStructure, basedir: File, scoped: ScopedKey[_], generation: Int)
(implicit display: Show[ScopedKey[_]]): SettingGraph =
{
val key = scoped.key
val scope = scoped.scope
val definedIn = structure.data.definingScope(scope, key) map { sc => display(ScopedKey(sc, key)) }
val cMap = flattenLocals(compiled(structure.settings, false)(structure.delegates, structure.scopeLocal, display))
val depends = cMap.get(scoped) match { case Some(c) => c.dependencies.toSet; case None => Set.empty }
SettingGraph(display(scoped), definedIn,
Project.scopedKeyData(structure, scope, key),
key.description, basedir,
depends map { apply(structure, basedir, _, generation + 1) })
}
}
case class SettingGraph(name: String,
definedIn: Option[String],
data: Option[ScopedKeyData[_]],
description: Option[String],
basedir: File,
depends: Set[SettingGraph])
{
def dataString: String =
data map { d =>
d.settingValue map {
case f: File => IO.relativize(basedir, f) getOrElse {f.toString}
case x => x.toString
} getOrElse {d.typeName}
} getOrElse {""}
def dependsAscii: String = Graph.toAscii(this,
(x: SettingGraph) => x.depends.toSeq,
(x: SettingGraph) => "%s = %s" format(x.definedIn getOrElse {""}, x.dataString))
}
object Graph
{
def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = {
val maxColumn = JLine.usingTerminal(_.getTerminalWidth) - 8
val twoSpaces = " " + " "
def limitLine(s: String): String =
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
else s
def insertBar(s: String, at: Int): String =
s.slice(0, at) +
(s(at).toString match {
case " " => "|"
case x => x
}) +
s.slice(at + 1, s.length)
def toAsciiLines(node: A, level: Int): Vector[String] = {
val line = limitLine((twoSpaces * level) + (if (level == 0) "" else "+-") + display(node))
val cs = Vector(children(node): _*)
val childLines = cs map {toAsciiLines(_, level + 1)}
val withBar = childLines.zipWithIndex flatMap {
case (lines, pos) if pos < (cs.size - 1) => lines map {insertBar(_, 2 * (level + 1))}
case (lines, pos) =>
if (lines.last.trim != "") lines ++ Vector(twoSpaces * (level + 1))
else lines
}
line +: withBar
}
toAsciiLines(top, 0).mkString("\n")
}
}