mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
Add roadmap feature to js-dep-visualizer.
The js-dep-visualizer tool now attempts to find a set of edges to remove from a call graph that would reduce it to having only trivial mutual dependencies, and it produces a roadmap of the changes that need to happen. If the tool can't reduce the graph all the way, it still produces a DOT file that can be visualized. This fix also has some significant code cleanup.
This commit is contained in:
@@ -3,11 +3,14 @@ from __future__ import print_function
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from typing import DefaultDict, List, Set, Tuple
|
||||
from typing import Callable, DefaultDict, Iterator, List, Set, Tuple
|
||||
|
||||
Edge = Tuple[str, str]
|
||||
EdgeSet = Set[Edge]
|
||||
|
||||
class Graph(object):
|
||||
def __init__(self, *tuples):
|
||||
# type: (Tuple[str, str]) -> None
|
||||
def __init__(self, tuples):
|
||||
# type: (EdgeSet) -> None
|
||||
self.children = defaultdict(list) # type: DefaultDict[str, List[str]]
|
||||
self.parents = defaultdict(list) # type: DefaultDict[str, List[str]]
|
||||
self.nodes = set() # type: Set[str]
|
||||
@@ -18,6 +21,28 @@ class Graph(object):
|
||||
self.nodes.add(parent)
|
||||
self.nodes.add(child)
|
||||
|
||||
def copy(self):
|
||||
# type: () -> Graph
|
||||
return Graph(self.edges())
|
||||
|
||||
def num_edges(self):
|
||||
# type: () -> int
|
||||
return len(self.edges())
|
||||
|
||||
def minus_edge(self, edge):
|
||||
# type: (Edge) -> Graph
|
||||
edges = self.edges().copy()
|
||||
edges.remove(edge)
|
||||
return Graph(edges)
|
||||
|
||||
def edges(self):
|
||||
# type: () -> EdgeSet
|
||||
s = set()
|
||||
for parent in self.nodes:
|
||||
for child in self.children[parent]:
|
||||
s.add((parent, child))
|
||||
return s
|
||||
|
||||
def remove_exterior_nodes(self):
|
||||
# type: () -> None
|
||||
still_work_to_do = True
|
||||
@@ -61,6 +86,30 @@ class Graph(object):
|
||||
for tup in tups:
|
||||
print(tup)
|
||||
|
||||
def best_edge_to_remove(orig_graph, is_exempt):
|
||||
# type: (Graph, Callable[[Edge], bool]) -> Edge
|
||||
# expects an already reduced graph as input
|
||||
|
||||
orig_edges = orig_graph.edges()
|
||||
|
||||
def get_choices():
|
||||
# type: () -> Iterator[Tuple[int, Edge]]
|
||||
for edge in orig_edges:
|
||||
if is_exempt(edge):
|
||||
continue
|
||||
graph = orig_graph.minus_edge(edge)
|
||||
graph.remove_exterior_nodes()
|
||||
size = graph.num_edges()
|
||||
yield (size, edge)
|
||||
|
||||
choices = list(get_choices())
|
||||
if not choices:
|
||||
return None
|
||||
min_size, best_edge = min(choices)
|
||||
if min_size >= orig_graph.num_edges():
|
||||
raise Exception('no edges work here')
|
||||
return best_edge
|
||||
|
||||
def make_dot_file(graph):
|
||||
# type: (Graph) -> str
|
||||
buffer = 'digraph G {\n'
|
||||
@@ -73,7 +122,7 @@ def make_dot_file(graph):
|
||||
|
||||
def test():
|
||||
# type: () -> None
|
||||
graph = Graph(
|
||||
graph = Graph(set([
|
||||
('x', 'a'),
|
||||
('a', 'b'),
|
||||
('b', 'c'),
|
||||
@@ -82,7 +131,7 @@ def test():
|
||||
('d', 'e'),
|
||||
('e', 'f'),
|
||||
('e', 'g'),
|
||||
)
|
||||
]))
|
||||
graph.remove_exterior_nodes()
|
||||
|
||||
s = make_dot_file(graph)
|
||||
|
||||
Reference in New Issue
Block a user