mirror of https://github.com/sbt/sbt.git
"inspect tree <key>" command
This is a slightly cleaned up version of sbt-inspectr. When "inspect tree <key>" is called, SettingGraph case class is created recursively along the dependencies, calling the similar code as "inspect" command's Project.details. Graph object then renders it as an ascii tree.
This commit is contained in:
parent
7c0952a266
commit
e85833b2de
|
|
@ -142,6 +142,13 @@ object Command
|
|||
( (c & opOrIDSpaced(name)) ~ c.+) map { case (f, rem) => (f +: rem).mkString }
|
||||
}
|
||||
|
||||
sealed trait InspectOption
|
||||
object InspectOption
|
||||
{
|
||||
final case class Details(actual: Boolean) extends InspectOption
|
||||
case object DependencyTree extends InspectOption
|
||||
}
|
||||
|
||||
trait Help
|
||||
{
|
||||
def detail: Map[String, String]
|
||||
|
|
|
|||
|
|
@ -84,14 +84,17 @@ LastCommand + """ <key>
|
|||
See also '""" + LastGrepCommand + "'."
|
||||
|
||||
val InspectCommand = "inspect"
|
||||
val inspectBrief = (InspectCommand + " <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectBrief = (InspectCommand + " [tree] <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectDetailed =
|
||||
InspectCommand + """ <key>
|
||||
InspectCommand + """ [tree] <key>
|
||||
|
||||
For a plain setting, the value bound to the key argument is displayed using its toString method.
|
||||
Otherwise, the type of task ("Task" or "Input task") is displayed.
|
||||
|
||||
"Dependencies" shows the settings that this setting depends on.
|
||||
If 'tree' is specified, the bound value as well as the settings that this setting depends on
|
||||
(and their bound values) are displayed as a tree structure.
|
||||
|
||||
"Reverse dependencies" shows the settings that depend on this setting.
|
||||
|
||||
When a key is resolved to a value, it may not actually be defined in the requested scope.
|
||||
|
|
|
|||
|
|
@ -382,10 +382,16 @@ object BuiltinCommands
|
|||
val newSession = session.appendSettings( append map (a => (a, arg)))
|
||||
reapply(newSession, structure, s)
|
||||
}
|
||||
def inspect = Command(InspectCommand, inspectBrief, inspectDetailed)(inspectParser) { case (s,(actual,sk)) =>
|
||||
val detailString = Project.details(Project.structure(s), actual, sk.scope, sk.key)( Project.showContextKey(s) )
|
||||
logger(s).info(detailString)
|
||||
s
|
||||
def inspect = Command(InspectCommand, inspectBrief, inspectDetailed)(inspectParser) {
|
||||
case (s, (InspectOption.DependencyTree, sk)) =>
|
||||
val basedir = new File(Project.session(s).current.build)
|
||||
val treeString = Project.settingGraph(Project.structure(s), basedir, sk)( Project.showContextKey(s) ).dependsAscii
|
||||
logger(s).info(treeString)
|
||||
s
|
||||
case (s, (InspectOption.Details(actual), sk)) =>
|
||||
val detailString = Project.details(Project.structure(s), actual, sk.scope, sk.key)( Project.showContextKey(s) )
|
||||
logger(s).info(detailString)
|
||||
s
|
||||
}
|
||||
def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern,Some(sk))) =>
|
||||
|
|
@ -401,7 +407,13 @@ object BuiltinCommands
|
|||
val ext = Project.extract(s)
|
||||
(ext.structure, Select(ext.currentRef), ext.showKey)
|
||||
}
|
||||
def inspectParser = (s: State) => token((Space ~> ("actual" ^^^ true)) ?? false) ~ spacedKeyParser(s)
|
||||
def inspectParser = (s: State) => spacedInspectOptionParser(s) ~ spacedKeyParser(s)
|
||||
val spacedInspectOptionParser: (State => Parser[InspectOption]) = (s: State) => {
|
||||
import InspectOption._
|
||||
val actual = "actual" ^^^ Details(true)
|
||||
val tree = "tree" ^^^ DependencyTree
|
||||
token(Space ~> (tree | actual)) ?? Details(false)
|
||||
}
|
||||
val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
|
||||
val optSpacedKeyParser = (s: State) => spacedKeyParser(s).?
|
||||
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ optSpacedKeyParser(s))
|
||||
|
|
|
|||
|
|
@ -291,6 +291,8 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
printScopes("Delegates", delegates(structure, scope, key)) +
|
||||
printScopes("Related", related)
|
||||
}
|
||||
def settingGraph(structure: BuildStructure, basedir: File, scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): SettingGraph =
|
||||
SettingGraph(structure, basedir, scoped, 0)
|
||||
def graphSettings(structure: BuildStructure, basedir: File)(implicit display: Show[ScopedKey[_]])
|
||||
{
|
||||
def graph(actual: Boolean, name: String) = graphSettings(structure, actual, name, new File(basedir, name + ".dot"))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah, Eugene Yokota
|
||||
*/
|
||||
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
|
||||
|
||||
lazy val clazz = key.manifest.erasure
|
||||
lazy val firstType = key.manifest.typeArguments.head
|
||||
val (typeName: String, value: Option[_]) =
|
||||
structure.data.get(scope, key) match {
|
||||
case None => ("", None)
|
||||
case Some(v) =>
|
||||
if(clazz == classOf[Task[_]]) ("Task[" + firstType.toString + "]", None)
|
||||
else if(clazz == classOf[InputTask[_]]) ("InputTask[" + firstType.toString + "]", None)
|
||||
else (key.manifest.toString, Some(v))
|
||||
}
|
||||
|
||||
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, typeName, value, key.description, basedir,
|
||||
depends map { apply(structure, basedir, _, generation + 1) })
|
||||
}
|
||||
}
|
||||
|
||||
case class SettingGraph(name: String,
|
||||
definedIn: Option[String],
|
||||
typeName: String,
|
||||
value: Option[Any],
|
||||
description: Option[String],
|
||||
basedir: File,
|
||||
depends: Set[SettingGraph])
|
||||
{
|
||||
def valueString: String =
|
||||
value map {
|
||||
case f: File => IO.relativize(basedir, f) getOrElse {f.toString}
|
||||
case x => x.toString
|
||||
} getOrElse {typeName}
|
||||
|
||||
def dependsAscii: String = Graph.toAscii(this,
|
||||
(x: SettingGraph) => x.depends.toSeq,
|
||||
(x: SettingGraph) => "%s = %s" format(x.definedIn getOrElse {""}, x.valueString))
|
||||
}
|
||||
|
||||
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.Terminal.getTerminal.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")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue