enable Scalariform auto-formatting

This commit is contained in:
Johannes Rudolph 2015-11-17 17:32:02 +01:00
parent 0c3cf98a89
commit 0c4bc73a64
13 changed files with 164 additions and 169 deletions

View File

@ -24,3 +24,5 @@ sbt.CrossBuilding.latestCompatibleVersionMapper ~= {
case x => original(x)
}
}
ScalariformSupport.formatSettings

View File

@ -25,7 +25,7 @@ trait DependencyGraphKeys {
"Creates a graphml file containing the dependency-graph for a project")
val dependencyDotFile = SettingKey[File]("dependency-dot-file",
"The location the dot file should be generated at")
val dependencyDotNodeLabel = SettingKey[(String,String,String) => String]("dependency-dot-node-label",
val dependencyDotNodeLabel = SettingKey[(String, String, String) String]("dependency-dot-node-label",
"Returns a formated string of a dependency. Takes organisation, name and version as parameters")
val dependencyDotHeader = SettingKey[String]("dependency-dot-header",
"The header of the dot file. (e.g. to set your preferred node shapes)")
@ -41,14 +41,13 @@ trait DependencyGraphKeys {
"Returns a string containing an ascii tree representation of the dependency graph for a project")
val dependencyTree = TaskKey[Unit]("dependency-tree",
"Prints the ascii tree to the console")
val ivyReportFunction = TaskKey[String => File]("ivy-report-function",
val ivyReportFunction = TaskKey[String File]("ivy-report-function",
"A function which returns the file containing the ivy report from the ivy cache for a given configuration")
val ivyReport = TaskKey[File]("ivy-report",
"A task which returns the location of the ivy report file for a given configuration (default `compile`).")
val ignoreMissingUpdate = Keys.update in ivyReport
val filterScalaLibrary = SettingKey[Boolean]("filter-scala-library",
"Specifies if scala dependency should be filtered in dependency-* output"
)
"Specifies if scala dependency should be filtered in dependency-* output")
val licenseInfo = TaskKey[Unit]("dependency-license-info",
"Aggregates and shows information about the licenses of dependencies")

View File

@ -31,29 +31,28 @@ object DependencyGraphSettings {
import ModuleGraphProtocol._
def graphSettings = Seq(
ivyReportFunction <<= (sbtVersion, target, projectID, ivyModule, appConfiguration, streams) map { (sbtV, target, projectID, ivyModule, config, streams) =>
ivyReportFunction <<= (sbtVersion, target, projectID, ivyModule, appConfiguration, streams) map { (sbtV, target, projectID, ivyModule, config, streams)
sbtV match {
case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3) =>
(c: String) => file("%s/resolution-cache/reports/%s-%s-%s.xml".format(target, projectID.organization, crossName(ivyModule), c))
case Version(0, min, fix, _) if min == 12 && fix >= 1 && fix < 3 =>
ivyModule.withModule(streams.log) { (i, moduleDesc, _) =>
case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3)
(c: String) file("%s/resolution-cache/reports/%s-%s-%s.xml".format(target, projectID.organization, crossName(ivyModule), c))
case Version(0, min, fix, _) if min == 12 && fix >= 1 && fix < 3
ivyModule.withModule(streams.log) { (i, moduleDesc, _)
val id = ResolveOptions.getDefaultResolveId(moduleDesc)
(c: String) => file("%s/resolution-cache/reports/%s/%s-resolved.xml" format (target, id,c))
(c: String) file("%s/resolution-cache/reports/%s/%s-resolved.xml" format (target, id, c))
}
case _ =>
case _
val home = config.provider.scalaProvider.launcher.ivyHome
(c: String) => file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(ivyModule), c))
(c: String) file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(ivyModule), c))
}
},
updateConfiguration in ignoreMissingUpdate <<= updateConfiguration(config => new UpdateConfiguration(config.retrieve, true, config.logging)),
updateConfiguration in ignoreMissingUpdate <<= updateConfiguration(config new UpdateConfiguration(config.retrieve, true, config.logging)),
ignoreMissingUpdateT,
filterScalaLibrary in Global := true
) ++ Seq(Compile, Test, Runtime, Provided, Optional).flatMap(ivyReportForConfig)
filterScalaLibrary in Global := true) ++ Seq(Compile, Test, Runtime, Provided, Optional).flatMap(ivyReportForConfig)
def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq(
ivyReport <<= ivyReportFunction map (_(config.toString)) dependsOn(ignoreMissingUpdate),
ivyReport <<= ivyReportFunction map (_(config.toString)) dependsOn (ignoreMissingUpdate),
moduleGraph <<= ivyReport map (absoluteReportPath.andThen(frontend.IvyReport.fromReportFile)),
moduleGraph <<= (scalaVersion, moduleGraph, filterScalaLibrary) map { (scalaV, graph, filter) =>
moduleGraph <<= (scalaVersion, moduleGraph, filterScalaLibrary) map { (scalaV, graph, filter)
if (filter)
GraphTransformations.ignoreScalaLibrary(scalaV, graph)
else
@ -61,8 +60,8 @@ object DependencyGraphSettings {
},
moduleGraphStore <<= moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph,
asciiGraph <<= moduleGraph map rendering.AsciiGraph.asciiGraph,
dependencyGraph <<= InputTask(shouldForceParser) { force =>
(force, moduleGraph, streams) map { (force, graph, streams) =>
dependencyGraph <<= InputTask(shouldForceParser) { force
(force, moduleGraph, streams) map { (force, graph, streams)
if (force || graph.nodes.size < 15) {
streams.log.info(rendering.AsciiGraph.asciiGraph(graph))
streams.log.info("\n\n")
@ -91,35 +90,34 @@ object DependencyGraphSettings {
| edge [
| arrowtail="none"
| ]""".stripMargin,
dependencyDotNodeLabel := { (organisation: String, name: String, version: String) =>
dependencyDotNodeLabel := { (organisation: String, name: String, version: String)
"""<%s<BR/><B>%s</B><BR/>%s>""".format(organisation, name, version)
},
whatDependsOn <<= InputTask(artifactIdParser) { module =>
(module, streams, moduleGraph) map { (module, streams, graph) =>
whatDependsOn <<= InputTask(artifactIdParser) { module
(module, streams, moduleGraph) map { (module, streams, graph)
streams.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module)))
}
},
licenseInfo <<= (moduleGraph, streams) map showLicenseInfo
))
licenseInfo <<= (moduleGraph, streams) map showLicenseInfo))
def printAsciiGraphTask =
(streams, asciiGraph) map (_.log.info(_))
def dependencyGraphMLTask =
(moduleGraph, dependencyGraphMLFile, streams) map { (graph, resultFile, streams) =>
(moduleGraph, dependencyGraphMLFile, streams) map { (graph, resultFile, streams)
rendering.GraphML.saveAsGraphML(graph, resultFile.getAbsolutePath)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def dependencyDotTask =
(moduleGraph, dependencyDotHeader, dependencyDotNodeLabel, dependencyDotFile, streams).map {
(graph, dotHead, nodeLabel, outFile, streams) =>
(graph, dotHead, nodeLabel, outFile, streams)
val resultFile = rendering.DOT.saveAsDot(graph, dotHead, nodeLabel, outFile)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def absoluteReportPath = (file: File) => file.getAbsolutePath
def absoluteReportPath = (file: File) file.getAbsolutePath
def print(key: TaskKey[String]) =
(streams, key) map (_.log.info(_))
@ -127,38 +125,40 @@ object DependencyGraphSettings {
def showLicenseInfo(graph: ModuleGraph, streams: TaskStreams) {
val output =
graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map {
case (license, modules) =>
license.getOrElse("No license specified")+"\n"+
case (license, modules)
license.getOrElse("No license specified") + "\n" +
modules.map(_.id.idString formatted "\t %s").mkString("\n")
}.mkString("\n\n")
streams.log.info(output)
}
import Project._
val shouldForceParser: State => Parser[Boolean] = { (state: State) =>
val shouldForceParser: State Parser[Boolean] = { (state: State)
import sbt.complete.DefaultParsers._
(Space ~> token("--force")).?.map(_.isDefined)
}
val artifactIdParser: Initialize[State => Parser[ModuleId]] =
resolvedScoped { ctx => (state: State) =>
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)
val artifactIdParser: Initialize[State Parser[ModuleId]] =
resolvedScoped { ctx
(state: State)
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)
import sbt.complete.DefaultParsers._
def moduleFrom(modules: Seq[ModuleId]) =
modules.map { m =>
(token(m.name) ~ Space ~ token(m.version)).map(_ => m)
}.reduce(_ | _)
import sbt.complete.DefaultParsers._
def moduleFrom(modules: Seq[ModuleId]) =
modules.map { m
(token(m.name) ~ Space ~ token(m.version)).map(_ m)
}.reduce(_ | _)
graph.nodes.map(_.id).groupBy(_.organisation).map {
case (org, modules) =>
Space ~ token(org) ~ Space ~> moduleFrom(modules)
}.reduceOption(_ | _).getOrElse {
(Space ~> token(StringBasic, "organization") ~ Space ~ token(StringBasic, "module") ~ Space ~ token(StringBasic, "version") ).map { case ((((org, _), mod), _), version) =>
ModuleId(org, mod, version)
graph.nodes.map(_.id).groupBy(_.organisation).map {
case (org, modules)
Space ~ token(org) ~ Space ~> moduleFrom(modules)
}.reduceOption(_ | _).getOrElse {
(Space ~> token(StringBasic, "organization") ~ Space ~ token(StringBasic, "module") ~ Space ~ token(StringBasic, "version")).map {
case ((((org, _), mod), _), version)
ModuleId(org, mod, version)
}
}
}
}
// This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8
@ -167,17 +167,17 @@ object DependencyGraphSettings {
}
def crossName(ivyModule: IvySbt#Module) =
ivyModule.moduleSettings match {
case ic: InlineConfiguration => ic.module.name
case hm: HasModule if hm.getClass.getName == "sbt.InlineConfigurationWithExcludes" => hm.module.name
case _ =>
case ic: InlineConfiguration ic.module.name
case hm: HasModule if hm.getClass.getName == "sbt.InlineConfigurationWithExcludes" hm.module.name
case _
throw new IllegalStateException("sbt-dependency-graph plugin currently only supports InlineConfiguration of ivy settings (the default in sbt)")
}
val VersionPattern = """(\d+)\.(\d+)\.(\d+)(?:-(.*))?""".r
object Version {
def unapply(str: String): Option[(Int, Int, Int, Option[String])] = str match {
case VersionPattern(major, minor, fix, appendix) => Some((major.toInt, minor.toInt, fix.toInt, Option(appendix)))
case _ => None
case VersionPattern(major, minor, fix, appendix) Some((major.toInt, minor.toInt, fix.toInt, Option(appendix)))
case _ None
}
}
@ -195,17 +195,17 @@ object DependencyGraphSettings {
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
// the resolved Scala version and the scalaHome version: compatible (weakly- no qualifier checked)
// the resolved Scala version and the declared scalaVersion: assume the user intended scalaHome to override anything with scalaVersion
def subUnmanaged(subVersion: String, jars: Seq[File]) = (sv: String) =>
def subUnmanaged(subVersion: String, jars: Seq[File]) = (sv: String)
(partialVersion(sv), partialVersion(subVersion), partialVersion(scalaVersion.value)) match {
case (Some(res), Some(sh), _) if res == sh => jars
case (Some(res), _, Some(decl)) if res == decl => jars
case _ => Nil
case (Some(res), Some(sh), _) if res == sh jars
case (Some(res), _, Some(decl)) if res == decl jars
case _ Nil
}
val subScalaJars: String => Seq[File] = SbtAccess.unmanagedScalaInstanceOnly.value match {
case Some(si) => subUnmanaged(si.version, si.jars)
case None => sv => if(scalaProvider.version == sv) scalaProvider.jars else Nil
val subScalaJars: String Seq[File] = SbtAccess.unmanagedScalaInstanceOnly.value match {
case Some(si) subUnmanaged(si.version, si.jars)
case None sv if (scalaProvider.version == sv) scalaProvider.jars else Nil
}
val transform: UpdateReport => UpdateReport = r => Classpaths.substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
val transform: UpdateReport UpdateReport = r Classpaths.substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
val show = Reference.display(thisProjectRef.value)
Classpaths.cachedUpdate(s.cacheDirectory, show, ivyModule.value, (updateConfiguration in ignoreMissingUpdate).value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log)

View File

@ -25,15 +25,15 @@ object GraphTransformations {
Nil
else
deps.get(module) match {
case Some(deps) =>
deps.flatMap { to =>
case Some(deps)
deps.flatMap { to
(module, to.id) +: visit(to.id, visited + module)
}
case None => Nil
case None Nil
}
val edges = visit(root, Set.empty)
val nodes = edges.foldLeft(Set.empty[ModuleId])((set, edge) => set + edge._1 + edge._2).map(graph.module)
val nodes = edges.foldLeft(Set.empty[ModuleId])((set, edge) set + edge._1 + edge._2).map(graph.module)
ModuleGraph(nodes.toSeq, edges)
}
@ -52,7 +52,7 @@ object GraphTransformations {
}
val newNodes = graph.nodes.map(addScalaLibraryAnnotation).filterNot(isScalaLibrary)
val newEdges = graph.edges.filterNot(e => isScalaLibraryId(e._2))
val newEdges = graph.edges.filterNot(e isScalaLibraryId(e._2))
ModuleGraph(newNodes, newEdges)
}
}

View File

@ -26,7 +26,7 @@ object Main extends App {
def usage: String =
"Usage: <ivy-report-file> <output-file>"
val reportFile = args.lift(0).filter(f => new File(f).exists).getOrElse(die(usage))
val reportFile = args.lift(0).filter(f new File(f).exists).getOrElse(die(usage))
val outputFile = args.lift(1).getOrElse(die(usage))
val graph = frontend.IvyReport.fromReportFile(reportFile)
rendering.GraphML.saveAsGraphML(graph, outputFile)

View File

@ -18,7 +18,7 @@ package net.virtualvoid.sbt.graph.frontend
import net.virtualvoid.sbt.graph._
import scala.xml.{NodeSeq, Document, Node}
import scala.xml.{ NodeSeq, Document, Node }
import scala.xml.parsing.ConstructingParser
object IvyReport {
@ -28,16 +28,16 @@ object IvyReport {
def fromReportXML(doc: Document): ModuleGraph = {
def edgesForModule(id: ModuleId, revision: NodeSeq): Seq[Edge] =
for {
caller <- revision \ "caller"
caller revision \ "caller"
callerModule = moduleIdFromElement(caller, caller.attribute("callerrev").get.text)
} yield (moduleIdFromElement(caller, caller.attribute("callerrev").get.text), id)
val moduleEdges: Seq[(Module, Seq[Edge])] = for {
mod <- doc \ "dependencies" \ "module"
revision <- mod \ "revision"
rev = revision.attribute("name").get.text
moduleId = moduleIdFromElement(mod, rev)
module = Module(moduleId,
mod doc \ "dependencies" \ "module"
revision mod \ "revision"
rev = revision.attribute("name").get.text
moduleId = moduleIdFromElement(mod, rev)
module = Module(moduleId,
(revision \ "license").headOption.flatMap(_.attribute("name")).map(_.text),
evictedByVersion = (revision \ "evicted-by").headOption.flatMap(_.attribute("rev").map(_.text)),
error = revision.attribute("error").map(_.text))
@ -47,7 +47,7 @@ object IvyReport {
val info = (doc \ "info").head
def infoAttr(name: String): String =
info.attribute(name).getOrElse(throw new IllegalArgumentException("Missing attribute "+name)).text
info.attribute(name).getOrElse(throw new IllegalArgumentException("Missing attribute " + name)).text
val rootModule = Module(ModuleId(infoAttr("organisation"), infoAttr("module"), infoAttr("revision")))
ModuleGraph(rootModule +: nodes, edges.flatten)

View File

@ -16,12 +16,12 @@
package net.virtualvoid.sbt.graph
import scala.collection.mutable.{MultiMap, HashMap, Set}
import scala.collection.mutable.{ MultiMap, HashMap, Set }
case class ModuleId(organisation: String,
name: String,
version: String) {
def idString: String = organisation+":"+name+":"+version
def idString: String = organisation + ":" + name + ":" + version
}
case class Module(id: ModuleId,
license: Option[String] = None,
@ -34,7 +34,7 @@ case class Module(id: ModuleId,
case class ModuleGraph(nodes: Seq[Module], edges: Seq[Edge]) {
lazy val modules: Map[ModuleId, Module] =
nodes.map(n => (n.id, n)).toMap
nodes.map(n (n.id, n)).toMap
def module(id: ModuleId): Module = modules(id)
@ -42,11 +42,11 @@ case class ModuleGraph(nodes: Seq[Module], edges: Seq[Edge]) {
createMap(identity)
lazy val reverseDependencyMap: Map[ModuleId, Seq[Module]] =
createMap { case (a, b) => (b, a) }
createMap { case (a, b) (b, a) }
def createMap(bindingFor: ((ModuleId, ModuleId)) => (ModuleId, ModuleId)): Map[ModuleId, Seq[Module]] = {
def createMap(bindingFor: ((ModuleId, ModuleId)) (ModuleId, ModuleId)): Map[ModuleId, Seq[Module]] = {
val m = new HashMap[ModuleId, Set[Module]] with MultiMap[ModuleId, Module]
edges.foreach { entry =>
edges.foreach { entry
val (f, t) = bindingFor(entry)
m.addBinding(f, module(t))
}
@ -54,7 +54,7 @@ case class ModuleGraph(nodes: Seq[Module], edges: Seq[Edge]) {
}
}
import sbinary.{Format, DefaultProtocol}
import sbinary.{ Format, DefaultProtocol }
object ModuleGraphProtocol extends DefaultProtocol {
implicit def seqFormat[T: Format]: Format[Seq[T]] = wrap[Seq[T], List[T]](_.toList, _.toSeq)
implicit val ModuleIdFormat: Format[ModuleId] = asProduct3(ModuleId)(ModuleId.unapply(_).get)

View File

@ -28,7 +28,7 @@ object AsciiGraph {
module.id.name + module.extraInfo + "\n" +
module.id.organisation + "\n" +
module.id.version +
module.error.map("\nerror: "+_).getOrElse("") +
module.error.map("\nerror: " + _).getOrElse("") +
module.evictedByVersion.map(_ formatted "\nevicted by: %s").getOrElse("")
val vertices = moduleGraph.nodes.map(renderVertex).toList

View File

@ -25,15 +25,15 @@ object AsciiTree {
val deps = graph.dependencyMap
// there should only be one root node (the project itself)
val roots = graph.nodes.filter(n => !graph.edges.exists(_._2 == n.id)).sortBy(_.id.idString)
roots.map { root =>
AsciiTreeLayout.toAscii[Module](root, node => deps.getOrElse(node.id, Seq.empty[Module]), displayModule)
val roots = graph.nodes.filter(n !graph.edges.exists(_._2 == n.id)).sortBy(_.id.idString)
roots.map { root
AsciiTreeLayout.toAscii[Module](root, node deps.getOrElse(node.id, Seq.empty[Module]), displayModule)
}.mkString("\n")
}
def displayModule(module: Module): String =
red(module.id.idString +
module.extraInfo +
module.error.map(" (error: "+_+")").getOrElse("") +
module.error.map(" (error: " + _ + ")").getOrElse("") +
module.evictedByVersion.map(_ formatted " (evicted by: %s)").getOrElse(""), module.hadError)
}

View File

@ -23,19 +23,17 @@ import net.virtualvoid.sbt.graph.ModuleGraph
object DOT {
def saveAsDot(graph: ModuleGraph,
dotHead: String,
nodeFormation: (String, String, String) => String,
nodeFormation: (String, String, String) String,
outputFile: File): File = {
val nodes = {
for (n <- graph.nodes)
yield
""" "%s"[label=%s]""".format(n.id.idString,
nodeFormation(n.id.organisation, n.id.name, n.id.version))
for (n graph.nodes)
yield """ "%s"[label=%s]""".format(n.id.idString,
nodeFormation(n.id.organisation, n.id.name, n.id.version))
}.mkString("\n")
val edges = {
for ( e <- graph.edges)
yield
""" "%s" -> "%s"""".format(e._1.idString, e._2.idString)
for (e graph.edges)
yield """ "%s" -> "%s"""".format(e._1.idString, e._2.idString)
}.mkString("\n")
val dot = "%s\n%s\n%s\n}".format(dotHead, nodes, edges)

View File

@ -23,27 +23,23 @@ import scala.xml.XML
object GraphML {
def saveAsGraphML(graph: ModuleGraph, outputFile: String) {
val nodesXml =
for (n <- graph.nodes)
yield
<node id={n.id.idString}><data key="d0">
<y:ShapeNode>
<y:NodeLabel>{n.id.idString}</y:NodeLabel>
</y:ShapeNode>
</data></node>
for (n graph.nodes)
yield <node id={ n.id.idString }><data key="d0">
<y:ShapeNode>
<y:NodeLabel>{ n.id.idString }</y:NodeLabel>
</y:ShapeNode>
</data></node>
val edgesXml =
for (e <- graph.edges)
yield <edge source={e._1.idString} target={e._2.idString} />
for (e graph.edges)
yield <edge source={ e._1.idString } target={ e._2.idString }/>
val xml =
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:y="http://www.yworks.com/xml/graphml"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key for="node" id="d0" yfiles.type="nodegraphics"/>
<graph id="Graph" edgedefault="undirected">
{nodesXml}
{edgesXml}
{ nodesXml }
{ edgesXml }
</graph>
</graphml>

View File

@ -6,50 +6,50 @@
package net.virtualvoid.sbt.graph.util
object AsciiTreeLayout {
// [info] foo
// [info] +-bar
// [info] | +-baz
// [info] |
// [info] +-quux
def toAscii[A](top: A,
children: A => Seq[A],
display: A => String,
// [info] foo
// [info] +-bar
// [info] | +-baz
// [info] |
// [info] +-quux
def toAscii[A](top: A,
children: A Seq[A],
display: A String,
maxColumn: Int = defaultColumnSize): String = {
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 =
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 =
if (at < s.length)
s.slice(0, at) +
(s(at).toString match {
case " " => "|"
case x => x
}) +
s.slice(at + 1, s.length)
(s(at).toString match {
case " " "|"
case x x
}) +
s.slice(at + 1, s.length)
else s
def toAsciiLines(node: A, level: Int, parents: Set[A]): Vector[String] =
if (parents contains node) // cycle
Vector(limitLine((twoSpaces * level) + "#-" + display(node)))
else {
val line = limitLine((twoSpaces * level) + (if (level == 0) "" else "+-") + display(node))
val cs = Vector(children(node): _*)
val childLines = cs map {
toAsciiLines(_, level + 1, parents + node)
}
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
}
def toAsciiLines(node: A, level: Int, parents: Set[A]): Vector[String] =
if (parents contains node) // cycle
Vector(limitLine((twoSpaces * level) + "#-" + display(node)))
else {
val line = limitLine((twoSpaces * level) + (if (level == 0) "" else "+-") + display(node))
val cs = Vector(children(node): _*)
val childLines = cs map {
toAsciiLines(_, level + 1, parents + node)
}
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, Set.empty).mkString("\n")
}
toAsciiLines(top, 0, Set.empty).mkString("\n")
}
def defaultColumnSize: Int = {
val termWidth = sbt.SbtAccess.getTerminalWidth

View File

@ -8,12 +8,12 @@ class AsciiTreeLayoutSpecs extends Specification {
case class Leaf(i: Int) extends Tree
def children(t: Tree): Seq[Tree] = t match {
case Branch(left, right) => Seq(left, right)
case _: Leaf => Nil
case Branch(left, right) Seq(left, right)
case _: Leaf Nil
}
def display(t: Tree): String = t match {
case Branch(left, right) => "Branch"
case Leaf(value) => value.toString * value
case Branch(left, right) "Branch"
case Leaf(value) value.toString * value
}
"Graph" should {
@ -66,29 +66,29 @@ class AsciiTreeLayoutSpecs extends Specification {
}
"cut off cycles" in {
AsciiTreeLayout.toAscii[Int](1, Map(
1 -> Seq(2,3,4),
2 -> Seq(4,5),
1 -> Seq(2, 3, 4),
2 -> Seq(4, 5),
3 -> Seq(),
4 -> Seq(3),
5 -> Seq(1,4,6,7),
5 -> Seq(1, 4, 6, 7),
6 -> Seq(),
7 -> Seq()), _.toString).trim ===
"""1
| +-2
| | +-4
| | | +-3
| | |\u0020
| | +-5
| | #-1
| | +-4
| | | +-3
| | |\u0020
| | +-6
| | +-7
| |\u0020\u0020\u0020
| +-3
| +-4
| +-3""".stripMargin.trim
"""1
| +-2
| | +-4
| | | +-3
| | |\u0020
| | +-5
| | #-1
| | +-4
| | | +-3
| | |\u0020
| | +-6
| | +-7
| |\u0020\u0020\u0020
| +-3
| +-4
| +-3""".stripMargin.trim
}
}
}