darcs-2.14.0: src/Darcs/UI/Commands/ShowDependencies.hs
module Darcs.UI.Commands.ShowDependencies
( showDeps )
where
import Control.Arrow ( (***) )
import Data.Maybe( fromMaybe )
import Data.GraphViz
import Data.GraphViz.Algorithms ( transitiveReduction )
import Data.GraphViz.Attributes.Complete
import Data.Graph.Inductive.Graph ( Graph(..), mkGraph, LNode, UEdge )
import Data.Graph.Inductive.PatriciaTree ( Gr )
import qualified Data.Text.Lazy as T
import Darcs.Repository ( readRepo, withRepositoryLocation, RepoJob(..) )
import Darcs.UI.Flags ( DarcsFlag(..), getRepourl
, useCache )
import Darcs.UI.Options ( oid, odesc, ocheck, onormalise, defaultFlags, (?) )
import qualified Darcs.UI.Options.All as O
import Darcs.UI.Commands ( DarcsCommand(..), nodefaults, findRepository, withStdOpts )
import Darcs.UI.Commands.Unrecord ( matchingHead )
import Darcs.UI.Completion ( noArgs )
import Darcs.Util.Text ( formatText )
import Darcs.Util.Path ( AbsolutePath )
import Darcs.Patch.Info ( piName )
import Darcs.Patch.PatchInfoAnd ( hopefully )
import Darcs.Patch.Named ( Named (..), patch2patchinfo )
import Darcs.Patch.Named.Wrapped ( removeInternalFL )
import Darcs.Patch.Match ( firstMatch, matchFirstPatchset )
import Darcs.Patch.Choices ( unLabel, LabelledPatch, label, getLabelInt )
import Darcs.Patch.Depends ( SPatchAndDeps, getDeps, findCommonWithThem )
import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), seal2, Sealed(..) )
import Darcs.Patch.Witnesses.Ordered ( (:>)(..), (:>)(..), foldlFL, mapFL_FL )
showDepsDescription :: String
showDepsDescription = "Generate the graph of dependencies."
showDepsHelp :: String
showDepsHelp = formatText 80
[ unwords [ "The `darcs show dependencies` command is used to create"
, "a graph of the dependencies between patches of the"
, "repository (by default up to last tag)."
]
, unwords [ "The resulting graph is described in Dot Language, a"
, "general example of use could be:"
]
, "darcs show dependencies | dot -Tpdf -o FILE.pdf"
]
showDeps :: DarcsCommand [DarcsFlag]
showDeps = DarcsCommand
{ commandProgramName = "darcs"
, commandName = "dependencies"
, commandHelp = showDepsHelp
, commandDescription = showDepsDescription
, commandExtraArgs = 0
, commandExtraArgHelp = []
, commandCommand = depsCmd
, commandPrereq = findRepository
, commandCompleteArgs = noArgs
, commandArgdefaults = nodefaults
, commandAdvancedOptions = []
, commandBasicOptions = odesc showDepsBasicOpts
, commandDefaults = defaultFlags showDepsOpts
, commandCheckOptions = ocheck showDepsOpts
, commandParseOptions = onormalise showDepsOpts
}
where
showDepsBasicOpts = O.matchSeveralOrLast
showDepsOpts = showDepsBasicOpts `withStdOpts` oid
type DepsGraph = Gr String ()
depsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()
depsCmd _ opts _ = do
let repodir = fromMaybe "." (getRepourl opts)
withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repo -> do
Sealed2 rFl <- readRepo repo >>= pruneRepo
let deps = getDeps
(removeInternalFL . mapFL_FL hopefully $ rFl)
rFl
dGraph = transitiveReduction $
graphToDot nodeLabeledParams $ makeGraph deps
putStrLn $ T.unpack $ printDotGraph dGraph
where
nodeLabeledParams :: GraphvizParams n String el () String
nodeLabeledParams =
defaultParams { globalAttributes =
[GraphAttrs {attrs = [RankDir FromLeft]}]
, fmtNode = \(_,l) ->
[ toLabel l
, ImageScale UniformScale
]
}
pruneRepo r = let matchFlags = O.matchSeveralOrLast ? opts in
if firstMatch matchFlags
then case getLastPatches matchFlags r of
Sealed2 ps -> return $ seal2 ps
else case matchingHead matchFlags r of
_ :> patches -> return $ seal2 patches
getLastPatches matchFlags ps =
case matchFirstPatchset matchFlags ps of
Sealed p1s -> case findCommonWithThem ps p1s of
_ :> ps' -> seal2 ps'
makeGraph :: [SPatchAndDeps p] -> DepsGraph
makeGraph = uncurry mkGraph . (id *** concat) . unzip . map mkNodeWithEdges
where
mkNodeWithEdges :: SPatchAndDeps p -> (LNode String, [UEdge])
mkNodeWithEdges (Sealed2 father, Sealed2 childs) = (mkLNode father,mkUEdges)
where
mkNode :: LabelledPatch (Named p) wX wY -> Int
mkNode = getLabelInt . label
mkUEdge :: [UEdge] -> LabelledPatch (Named p) wX wY -> [UEdge]
mkUEdge les child = (mkNode father, mkNode child,()) : les
mkLabel :: LabelledPatch (Named p) wX wY -> String
mkLabel = formatText 20 . (:[]) . piName . patch2patchinfo . unLabel
mkLNode :: LabelledPatch (Named p) wX wY -> LNode String
mkLNode p = (mkNode p, mkLabel p)
mkUEdges :: [UEdge]
mkUEdges = foldlFL mkUEdge [] childs