From ef0b2d6b833c74e92f1099c37ac31fef0a71ba65 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Thu, 1 Feb 2018 15:31:55 +0100 Subject: [PATCH 1/2] group class/trait and companion object together --- .../com/lightbend/tools/sculpt/model/ClassMode.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/lightbend/tools/sculpt/model/ClassMode.scala b/src/main/scala/com/lightbend/tools/sculpt/model/ClassMode.scala index a1fd40d..4118d2b 100644 --- a/src/main/scala/com/lightbend/tools/sculpt/model/ClassMode.scala +++ b/src/main/scala/com/lightbend/tools/sculpt/model/ClassMode.scala @@ -33,11 +33,13 @@ object ClassMode { path.elems.span(isPackage) match { case (packages, next +: _) => next.kind match { + case EntityKind.Trait => + Some(Path(packages :+ next.copy(kind = EntityKind.Class))) + // collapse Module/ModuleClass distinction + case EntityKind.Module | EntityKind.ModuleClass => + Some(Path(packages :+ next.copy(kind = EntityKind.Class))) case k if isClassKind(k) => Some(Path(packages :+ next)) - // collapse Module/ModuleClass distinction - case EntityKind.Module => - Some(Path(packages :+ next.copy(kind = EntityKind.ModuleClass))) // ignore strange dependencies on bare terms; // see https://github.com/lightbend/scala-sculpt/issues/28 case EntityKind.Term if packages.isEmpty => From 6062b3ce10618313970bf259d31b15512133b381 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Thu, 1 Feb 2018 15:32:18 +0100 Subject: [PATCH 2/2] add simple path finding algorithm and dot generation --- .../lightbend/tools/sculpt/model/Cycles.scala | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/main/scala/com/lightbend/tools/sculpt/model/Cycles.scala b/src/main/scala/com/lightbend/tools/sculpt/model/Cycles.scala index 3da73fe..c026fdf 100644 --- a/src/main/scala/com/lightbend/tools/sculpt/model/Cycles.scala +++ b/src/main/scala/com/lightbend/tools/sculpt/model/Cycles.scala @@ -57,4 +57,66 @@ object Cycles { .mkString} .mkString + def path(from: Node, to: Node): Option[Seq[Node]] = { + type Result = (Set[Node], Option[Seq[Node]]) + def findPath(current: Node, currentPath: List[Node], visitedNodes: Set[Node]): Result = + if (current == to) Set.empty -> Some((current :: currentPath).reverse) + else { + val newVisited = visitedNodes + current + val targets = current.edgesOut.map(_.to) + val newPath = current :: currentPath + targets.foldLeft((newVisited, None): Result) { (res, next) => + res match { + case res@(_, Some(_)) => res + case res@(newVisited, None) => + if (!newVisited(next)) + findPath(next, newPath, newVisited) + else + res + } + } + } + + findPath(from, Nil, Set.empty)._2 + } + def path(nodes: Nodes, from: String, to: String): Option[Seq[Node]] = { + def get(name: String): Node = + nodes.find(_.path.nameString.endsWith(name)).getOrElse(throw new NoSuchElementException(name)) + + path(get(from), get(to)) + } + + def pp(nodes: Nodes, from: String, to: String): Unit = { + println("Forward") + path(nodes, from ,to).foreach(_.foreach(n => println(n.path.simpleString))) + println("Backward") + path(nodes, to,from).foreach(_.foreach(n => println(n.path.simpleString))) + } + + def dot(graph: Graph): String = { + def nodeFilter(node: Node): Boolean = + node.path.simpleString.contains(":akka.http") + /*!node.path.simpleString.contains(":scala") && + !node.path.simpleString.contains(":java") && + !node.path.simpleString.contains(":akka.stream") && + !node.path.simpleString.contains(":akka.util") && + !node.path.simpleString.contains(":akka.parboiled2") && + !node.path.simpleString.contains(":akka.actor")*/ + + def edgeFilter(edge: Edge): Boolean = + nodeFilter(edge.from) && nodeFilter(edge.to) + + def formatNode(node: Node): String = s""""${node.path.simpleString}"""" + def formatEdge(edge: Edge): String = s""""${edge.from.path.simpleString}"->"${edge.to.path.simpleString}"""" + + s"""|digraph "dependency-graph" { + | graph[rankdir="LR"] + | edge [ + | arrowtail="none" + | ] + | ${graph.nodes.iterator.filter(nodeFilter).map(formatNode).mkString("\n ")} + | ${graph.edges.iterator.filter(edgeFilter).map(formatEdge).mkString("\n ")} + |} + |""".stripMargin + } }