SourceGraph 0.5.0.0 → 0.5.1.0
raw patch · 5 files changed
+119/−40 lines, 5 files
Files
- Analyse/Everything.hs +7/−4
- Analyse/Utils.hs +11/−3
- ChangeLog +19/−0
- Main.hs +51/−30
- SourceGraph.cabal +31/−3
Analyse/Everything.hs view
@@ -43,8 +43,8 @@ import System.Random(RandomGen) -- | Performs analysis of the entire codebase.-analyseEverything :: (RandomGen g) => g -> [ModName] -> ParsedModules- -> DocElement+analyseEverything :: (RandomGen g) => g -> [ModName] -> ParsedModules+ -> DocElement analyseEverything g exps hm = Section sec elems where cd = codeToGraph exps hm@@ -87,7 +87,7 @@ clustersOf :: (RandomGen g) => g -> HSData -> Maybe DocElement clustersOf g cd = Just $ Section sec [ text, gc, textAfter- , blank, cwMsg, cw, blank, rngMsg, rng]+ , blank, cwMsg, cw] -- , blank, rngMsg, rng] where blank = Paragraph [BlankSpace] sec = Text "Visualisation of overall function calls"@@ -95,15 +95,18 @@ text = Paragraph [Text "Here is the current module grouping of functions:"] lbl = "Current module groupings"- textAfter = Paragraph [Text "Here are two proposed module groupings:"]+ textAfter = Paragraph [Text "Here is a proposed alternate module grouping:"]+ -- [Text "Here are two proposed module groupings:"] cwMsg = Paragraph [Emphasis $ Text "Using the Chinese Whispers algorithm:"] cw = GraphImage ("codeCW", Text cwLbl, drawClusters cwLbl (chineseWhispers g) cd) cwLbl = "Chinese Whispers module suggestions"+{- rngMsg = Paragraph [Emphasis $ Text "Using the Relative Neighbourhood algorithm:"] rng = GraphImage ("codeRNG", Text rngLbl, drawClusters lbl relNbrhd cd) -- Being naughty to avoid having to define drawClusters' relNbrhd = relativeNeighbourhood $ directedData cd rngLbl = "Relative Neighbourhood module suggestions"+-} componentAnal :: HSData -> Maybe DocElement componentAnal cd
Analyse/Utils.hs view
@@ -37,7 +37,7 @@ import Data.GraphViz import Data.List(groupBy, sortBy)-import Data.Maybe(isJust)+-- import Data.Maybe(isJust) import Data.Function(on) import qualified Data.Set as S import Data.Set(Set)@@ -240,7 +240,11 @@ clustAttributes' = return . GraphAttrs . clustAttributes modClustAttrs :: ModName -> [GlobalAttributes]-modClustAttrs m = [GraphAttrs [Label . StrLabel $ nameOfModule m]]+modClustAttrs m = [GraphAttrs [ Label . StrLabel $ nameOfModule m+ , Style [SItem Filled []]+ , FillColor $ ColorName "wheat1"+ ]+ ] -- ----------------------------------------------------------------------------- @@ -249,11 +253,15 @@ drawClusters gid cf dg = setID (Str gid) $ graphvizClusters dg' gAttrs- (const [])+ (const cAttr) nAttr callAttributes' where gAttrs = [] -- [GraphAttrs [Label $ StrLabel t]]+ cAttr = [GraphAttrs [ Style [SItem Filled []]+ , FillColor $ ColorName "wheat1"+ ]+ ] dg' = updateGraph (compactSame . cf . collapseStructures') dg rs = getRoots dg ls = getLeaves dg
+ ChangeLog view
@@ -0,0 +1,19 @@+Changes since 0.5.1.0+=====================++* Add support for passing a single Haskell source file rather than a+ Cabal file as the argument, as requested by Curt Sampson.++* Shade backgrounds of modules when clustering Entities.++* RelativeNeighbourhood doesn't provide good alternate modules and+ takes up too much runtime, and so has been removed.++Changes since 0.5.0.0+=====================++* Re-write of parsing.++* Usage of new graphviz and Graphalyze features.++* More abstraction into common routines into Analyse.Utils
Main.hs view
@@ -31,6 +31,7 @@ module Main where import Parsing+import Parsing.Types(nameOfModule) import Analyse import Data.Graph.Analysis@@ -44,15 +45,16 @@ import Data.Char(toLower) import Data.List(nub)-import Data.Maybe(isJust, fromJust, listToMaybe, catMaybes)+import Data.Maybe(isJust, fromJust, catMaybes)+import qualified Data.Map as M import System.IO(hPutStrLn, stderr) import System.Directory( getCurrentDirectory , doesDirectoryExist+ , doesFileExist , getDirectoryContents) import System.FilePath( dropFileName , dropExtension , takeExtension- , extSeparator , isPathSeparator , (</>) , (<.>))@@ -68,22 +70,16 @@ main :: IO () main = do input <- getArgs- let mcbl = getCabalFile input- case mcbl of- Nothing- -> putErrLn "Please pass in a .cabal file"- Just cbl- -> do pcbl <- parseCabal cbl- case pcbl of- Nothing- -> putErrLn $ unwords [cbl,"is unparseable"]- Just (nm,exps)- -> do let dir = dropFileName cbl- dir' <- if null dir- then getCurrentDirectory- else return dir- hms <- parseFilesFrom dir'- analyseCode dir nm exps hms+ mInfo <- getPkgInfo input+ case mInfo of+ Nothing -> putErrLn "No parseable package information found."+ Just (f,(nm,exps))+ -> do let dir = dropFileName f+ dir' <- if null dir+ then getCurrentDirectory+ else return dir+ hms <- parseFilesFrom dir'+ analyseCode dir nm exps hms programmeName :: String programmeName = "SourceGraph"@@ -96,6 +92,24 @@ -- ----------------------------------------------------------------------------- +getPkgInfo :: [String] -> IO (Maybe (FilePath, (String, [ModName])))+getPkgInfo [] = do putErrLn "Please provide either a Cabal file \+ \or a Haskell source file as an argument."+ return Nothing+getPkgInfo [f]+ | isCabalFile f = withF parseCabal+ | isHaskellFile f = withF parseMain+ where+ withF func = do ex <- doesFileExist f+ if ex+ then addF $ func f+ else do putErrLn "The provided file does not exist."+ return Nothing+ addF = fmap (fmap ((,) f))+getPkgInfo _ = do putErrLn "Please provide a single Cabal \+ \or Haskell source file as an argument."+ return Nothing+ parseCabal :: FilePath -> IO (Maybe (String, [ModName])) parseCabal fp = do gpd <- try $ readPackageDescription silent fp case gpd of@@ -121,11 +135,21 @@ | otherwise = error "No exposed modules" exps' = map fpToModule exps -getCabalFile :: [FilePath] -> Maybe FilePath-getCabalFile = listToMaybe . filter isCabalFile- where- isCabalFile f = takeExtension f == extSeparator : "cabal"+isCabalFile :: FilePath -> Bool+isCabalFile = hasExt "cabal" +parseMain :: FilePath -> IO (Maybe (String, [ModName]))+parseMain fp = do pms <- parseHaskellFiles [fp]+ let mn = fst $ M.findMin pms+ return $ Just (nameOfModule mn, [mn])++-- | Determine if this is the path of a Haskell file.+isHaskellFile :: FilePath -> Bool+isHaskellFile fp = any (flip hasExt fp) haskellExtensions++hasExt :: String -> FilePath -> Bool+hasExt ext = (==) ext . drop 1 . takeExtension+ fpToModule :: FilePath -> ModName fpToModule = createModule . map pSep where@@ -137,10 +161,11 @@ -- | Recursively parse all files from this directory parseFilesFrom :: FilePath -> IO ParsedModules-parseFilesFrom fp = do files <- getHaskellFilesFrom fp- cnts <- readFiles files- return $ parseHaskell cnts+parseFilesFrom fp = parseHaskellFiles =<< getHaskellFilesFrom fp +parseHaskellFiles :: [FilePath] -> IO ParsedModules+parseHaskellFiles = liftM parseHaskell . readFiles+ -- ----------------------------------------------------------------------------- -- Reading in the files.@@ -168,12 +193,8 @@ recursiveFiles <- concatMapM getHaskellFilesFrom dirs return (hFiles ++ recursiveFiles) --- | Determine if this is the path of a Haskell file.-isHaskellFile :: FilePath -> Bool-isHaskellFile f = takeExtension f `elem` haskellExtensions- haskellExtensions :: [FilePath]-haskellExtensions = map (extSeparator :) ["hs","lhs"]+haskellExtensions = ["hs","lhs"] -- | Read in all the files that it can. readFiles :: [FilePath] -> IO [FileContents]
SourceGraph.cabal view
@@ -1,8 +1,35 @@ Name: SourceGraph-Version: 0.5.0.0+Version: 0.5.1.0 Synopsis: Use graph-theory to analyse your code-Description: SourceGraph uses the Graphalyze library to analyse- Cabalized Haskell code.+Description: {+Statically analyse Haskell source code using graph-theoretic+techniques.+.+To use SourceGraph, call it as either:+.+> SourceGraph path/to/Foo.cabal+.+Or, if your project doesn't use Cabal, then there is limited support+for using an overall module from your program\/library:+.+> SourceGraph path/to/Foo.hs+.+Note that the Cabal method is preferred, as it is better able to+determine the project name and exported modules (when passing a+Haskell file to SourceGraph, it uses that module's name as the overall+name of project and assumes that it is the only exported module; as+such, it works better for programs than libraries).+.+Whichever way your run SourceGraph, it then creates a @SourceGraph@+subdirectory in the same directory as the file that was passed to it,+and within that subdirectory creates the analysis report in+@Foo.html@.+.+SourceGraph is still experimental in terms of its ability to parse and+properly understand Haskell source code and in the types of analyses+it performs.+}+ Category: Development License: GPL License-file: COPYRIGHT@@ -12,6 +39,7 @@ Cabal-Version: >= 1.6 Build-Type: Simple Extra-Source-Files: TODO+ ChangeLog KnownProblems.txt ParsingProblems.txt