public void Render( ITree tree, IRemote usedRemote, IBranchesKnowledge branchesKnowledge, ITreeRenderingOptions options) { IRemoteWebUrlProvider remoteUrlProvider = _remoteWebUrlProviderFactory.CreateUrlProvider(usedRemote.Url, options.ForceTreatAsGitHub); WriteHeader(); IBranch[] branchesWithNodes = tree.Branches.Where(b => tree.EnumerateNodes(b).Any()).ToArray(); IBranch[] currentBranchesSorted = OrderByFirstCommitDate(branchesWithNodes, tree).ToArray(); IPairList <INode, INode> unwrittenMerges = new PairList <INode, INode>(); IPairList <INode, INode> writtenMerges = new PairList <INode, INode>(); // Main branches. foreach (IBranch b in currentBranchesSorted) { _gvWriter.EmptyLine(); _gvWriter.Comment($"Branch {b.Label}."); using (_gvWriter.StartSubGraph()) { _gvWriter.SetNodeAttributes(AttrSet.Empty.Group(b.Label)); Color drawColor = branchesKnowledge.GetSuggestedDrawingColorFor(b); bool isLesser = branchesKnowledge.IsAWorkItemBranch(b); IAttrSet nodeStyle = _style.GetBranchNodeStyle(drawColor, isLesser); IAttrSet edgeStyle = _style.GetBranchEdgeStyle(drawColor, isLesser); _gvWriter.SetNodeAttributes(nodeStyle); _gvWriter.SetEdgeAttributes(edgeStyle); INode[] nodes = tree.EnumerateNodes(b).ToArray(); for (int i = 0; i < nodes.Length; i++) { INode currentNode = nodes[i]; WriteNode(currentNode, remoteUrlProvider); if (i == 0) { // Starting node. INode parent = currentNode.Parents.FirstOrDefault(); bool hasParent = parent != null; if (!hasParent) { _gvWriter.Comment("Starting line."); string id = string.Format($"{currentNode.Commit.Treeish}_start"); // Write starting empty node. using (_gvWriter.StartSubGraph()) { _gvWriter.RawAttributes(AttrSet.Empty.Rank(RankType.Source)); _gvWriter.Node(id, AttrSet.Empty.Width(0).Height(0).PenWidth(0)); } _gvWriter.Edge(id, currentNode.Commit, _style.EdgeBranchStartVirtual); _gvWriter.EmptyLine(); } else { // It is some other branch, and we draw that edge. WriteEdge(parent, currentNode, remoteUrlProvider); writtenMerges.Add(parent, currentNode); } } bool isLast = i == nodes.Length - 1; INode nextNode = isLast ? null : nodes[i + 1]; if (!isLast) { // Edge to the next node. WriteEdge(currentNode, nextNode, remoteUrlProvider); } else { WriteBranchPointer(b, tree, isLesser, remoteUrlProvider); } INode[] otherChildren = currentNode.Children.Except(nextNode).ToArray(); foreach (INode child in otherChildren) { unwrittenMerges.Add(currentNode, child); } } } } IBranch[] branchesWithoutNodes = tree.Branches.Except(branchesWithNodes).ToArray(); foreach (IBranch b in branchesWithoutNodes) { bool isLesser = branchesKnowledge.IsAWorkItemBranch(b); WriteBranchPointer(b, tree, isLesser, remoteUrlProvider); } // Tags. ITag[] tags = tree.Tags.ToArray(); foreach (ITag t in tags) { _gvWriter.EmptyLine(); INode n = tree.GetNode(t.Tip); string id = MakeNodeIdForPointerLabel(n, t); using (_gvWriter.StartSubGraph()) { _gvWriter.Comment($"Tag {t.Label}."); _gvWriter.RawAttributes(AttrSet.Empty.Rank(RankType.Same)); string url = remoteUrlProvider?.GetTagLink(t); _gvWriter.Node(id, _style.LabelTag.Label(t.Label).Url(url)); _gvWriter.Edge(n.Commit, id, _style.EdgeTagLabel); } } INode[] allLeftOvers = tree.Nodes.Where(n => tree.GetContainingBranch(n) == null).ToArray(); if (allLeftOvers.Length > 0) { using (_gvWriter.StartSubGraph()) { _gvWriter.EmptyLine(); _gvWriter.Comment("Leftover nodes."); _gvWriter.SetNodeAttributes(_style.NodeOrphaned); foreach (INode currentNode in allLeftOvers) { WriteNode(currentNode, remoteUrlProvider); // Remember children. foreach (INode child in currentNode.Children) { unwrittenMerges.Add(currentNode, child); } } } } PairList <INode, INode> edgesToWrite = unwrittenMerges.Except(writtenMerges).ToPairList(); if (edgesToWrite.Count > 0) { using (_gvWriter.StartSubGraph()) { _gvWriter.EmptyLine(); _gvWriter.Comment("Other edges."); _gvWriter.SetEdgeAttributes(_style.EdgeOther); foreach (Tuple <INode, INode> edge in edgesToWrite) { _gvWriter.Edge(edge.Item1.Commit, edge.Item2.Commit); } } } WriteFooter(); }
public static void Edge(this IGraphVizWriter writer, ITreeish a, ITreeish b, IAttrSet attrSet = null) { writer.Edge(a.Treeish, b.Treeish, attrSet); }