/* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah, Eugene Yokota */ package sbt import java.net.URI import java.io.File import Def.{compiled,flattenLocals,ScopedKey} 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 related = cMap.keys.filter(k => k.key == key && k.scope != scope) val depends = cMap.get(scoped) match { case Some(c) => c.dependencies.toSet; case None => Set.empty } // val reverse = reverseDependencies(cMap, scoped) SettingGraph(display(scoped), definedIn, Project.scopedKeyData(structure, scope, key), key.description, basedir, depends map { (x: ScopedKey[_]) => apply(structure, basedir, x, 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 { // [info] foo // [info] +-bar // [info] | +-baz // [info] | // [info] +-quux def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = { val maxColumn = JLine.usingTerminal(_.getTerminalWidth) - 8 val twoSpaces = " " + " " // prevent accidentally being converted into a tab 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") } }