git-freq 0.0.2 → 0.0.3
raw patch · 6 files changed
+101/−137 lines, 6 filesdep +io-streamssetup-changed
Dependencies added: io-streams
Files
- Setup.hs +0/−2
- git-freq.cabal +45/−44
- src/Git/Freq.hs +44/−48
- src/Git/Freq/Source.hs +0/−19
- src/Git/Freq/Version.hs +0/−6
- src/Main.hs +12/−18
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
git-freq.cabal view
@@ -1,47 +1,48 @@-Name: git-freq-Version: 0.0.2-Author: Fujimura Daisuke<me@fujimuradaisuke.com>-Maintainer: Fujimura Daisuke<me@fujimuradaisuke.com>-License: BSD3-License-File: LICENSE-Synopsis: A Git subcommand to show total addition, deletion per file-Description: A Git subcommand to show total addition, deletion per file-Cabal-Version: >= 1.10-Homepage: https://github.com/fujimura/git-freq-Build-Type: Simple+name: git-freq+version: 0.0.3+cabal-version: >=1.10+build-type: Simple+license: BSD3+license-file: LICENSE+maintainer: Fujimura Daisuke<me@fujimuradaisuke.com>+homepage: https://github.com/fujimura/git-freq+synopsis: A Git subcommand to show total addition, deletion per file+description:+ A Git subcommand to show total addition, deletion per file+author: Fujimura Daisuke<me@fujimuradaisuke.com> -Executable git-freq- Main-Is: Main.hs- Default-Language: Haskell2010- GHC-Options: -Wall- -fno-warn-name-shadowing- Hs-Source-Dirs: src- Build-Depends: base >= 4 && < 5- , bytestring- , containers- , optparse-applicative- , process- , text- Other-modules: Git.Freq- , Git.Freq.Source- , Git.Freq.Version+source-repository head+ type: git+ location: git@github.com:fujimura/git-freq.git -test-suite spec- Main-Is: Spec.hs- Default-Language: Haskell2010- GHC-Options: -Wall- -fno-warn-name-shadowing- Hs-Source-Dirs: src- , test- Type: exitcode-stdio-1.0- Build-Depends: base- , bytestring- , containers- , hspec- , optparse-applicative- , process- , text+executable git-freq+ main-is: Main.hs+ build-depends:+ base >=4 && <5,+ bytestring -any,+ containers -any,+ io-streams -any,+ optparse-applicative -any,+ process -any,+ text -any+ default-language: Haskell2010+ hs-source-dirs: src+ other-modules:+ Git.Freq+ ghc-options: -Wall -fno-warn-name-shadowing -Source-Repository head- Type: git- Location: git@github.com:fujimura/git-freq.git+test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ build-depends:+ base -any,+ bytestring -any,+ containers -any,+ io-streams -any,+ hspec -any,+ optparse-applicative -any,+ process -any,+ text -any+ default-language: Haskell2010+ hs-source-dirs: src test+ ghc-options: -Wall -fno-warn-name-shadowing
src/Git/Freq.hs view
@@ -1,46 +1,46 @@ {-# LANGUAGE OverloadedStrings #-} module Git.Freq where -import Control.Arrow ((***))-import Data.List (sortBy)-import Data.Map.Strict (Map)-import qualified Data.Map.Strict as Map-import Data.Monoid-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.IO as T-import System.Process (runInteractiveProcess)-import Text.Read (readMaybe)+import Control.Arrow ((***))+import Data.ByteString (ByteString)+import Data.List (sortBy)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T+import Text.Read (readMaybe) -import Git.Freq.Source (Source)-import qualified Git.Freq.Source as Source+import System.IO.Streams (InputStream)+import qualified System.IO.Streams as Streams type FileName = Text type NumStat = (Int, Int) type Change = (FileName, NumStat) type Result = Map FileName NumStat -parseLine :: Text -> Maybe Change-parseLine = go . T.splitOn (T.pack "\t")- where go :: [Text] -> Maybe Change- go (_:_:"":_) = Nothing- go (added:deleted:filename:_) =- case (readIntMaybe added, readIntMaybe deleted) of- (Just a, Just d) -> Just (filename, (a, d))- _ -> Nothing- go _ = Nothing- readIntMaybe x = readMaybe (T.unpack x) :: Maybe Int+freq :: [FilePath] -> IO ()+freq paths = do+ r <- getNumStatStream paths >>= freq'+ mapM_ render (sortResult $ Map.toList r) -walk :: Source a => a -> [Change] -> IO [Change]-walk h changes = do- eof <- Source.isEOF h- if eof then return changes- else do- l <- Source.getLine h- case parseLine l of- Just c -> walk h (c:changes)- Nothing -> walk h changes+getNumStatStream :: [FilePath] -> IO (InputStream ByteString)+getNumStatStream paths = do+ let args = ["log"+ , "--numstat"+ , "--pretty=\"%0\""+ ] ++ paths + (_,is,_,_) <- Streams.runInteractiveProcess "git" args Nothing Nothing+ return is++freq' :: InputStream ByteString -> IO Result+freq' is = Streams.lines is >>=+ Streams.map (parseLine . T.decodeUtf8) >>=+ Streams.mapMaybe id >>=+ Streams.fold incrementChange Map.empty+ sumChanges :: [Change] -> Result sumChanges = foldl incrementChange Map.empty @@ -49,24 +49,20 @@ where f numstat' = Just $ maybe numstat ((a+) *** (d+)) numstat' -render :: Change -> IO ()-render (fileName,(added,deleted)) =- T.putStrLn . T.pack . mconcat $ [T.unpack fileName, ",", show added, ",", show deleted]- sortResult :: [Change] -> [Change] sortResult = let f (_,(xa,xd)) (_,(ya,yd)) = (ya+yd) `compare` (xa+xd) in sortBy f -freq :: [FilePath] -> IO ()-freq paths = do- let args = ["log"- , "--numstat"- , "--pretty=\"%0\""- ] ++ paths-- (_,out,_,_) <- runInteractiveProcess "git" args Nothing Nothing- freq' out >>= mapM_ render+parseLine :: Text -> Maybe Change+parseLine = go . T.splitOn (T.pack "\t")+ where go :: [Text] -> Maybe Change+ go (_:_:"":_) = Nothing+ go (added:deleted:filename:_) =+ case (readIntMaybe added, readIntMaybe deleted) of+ (Just a, Just d) -> Just (filename, (a, d))+ _ -> Nothing+ go _ = Nothing+ readIntMaybe x = readMaybe (T.unpack x) :: Maybe Int -freq' :: Source a => a -> IO [Change]-freq' source = do- changes <- walk source []- return $ sortResult . Map.toList $ sumChanges changes+render :: Change -> IO ()+render (fileName,(added,deleted)) =+ T.putStrLn . T.pack . mconcat $ [T.unpack fileName, ",", show added, ",", show deleted]
− src/Git/Freq/Source.hs
@@ -1,19 +0,0 @@-module Git.Freq.Source- ( Source- , getLine- , isEOF- ) where--import Data.Text (Text)-import qualified Data.Text.IO as T-import Prelude hiding (getLine)-import System.IO (Handle)-import qualified System.IO as IO--class Source s where- isEOF :: s -> IO Bool- getLine :: s -> IO Text--instance Source Handle where- isEOF = IO.hIsEOF- getLine = T.hGetLine
− src/Git/Freq/Version.hs
@@ -1,6 +0,0 @@-module Git.Freq.Version- ( version- ) where--version :: String-version = "0.0.2"
src/Main.hs view
@@ -1,32 +1,26 @@ module Main where +import Data.Version (showVersion) import Git.Freq-import qualified Git.Freq.Version as Version import Options.Applicative-+import qualified Paths_git_freq (version) -data Config = Config- { version :: Bool- , paths :: [FilePath]- }+filePaths :: Parser [FilePath]+filePaths = many (argument str (metavar "PATH..." <> help "Target paths")) -config :: Parser Config-config = Config- <$> switch- (long "version" <> short 'v' <> help "Show version")- <*> many- (argument str (metavar "PATH..." <> help "Target paths"))+version :: Parser (a -> a)+version = infoOption (showVersion Paths_git_freq.version)+ ( short 'v'+ <> long "version"+ <> help "Print version information" ) main :: IO ()-main = do- (Config showVersion paths) <- execParser opts- if showVersion then putStrLn Version.version- else freq paths+main = execParser opts >>= freq where- opts = info (helper <*> config)+ opts = info (helper <*> version <*> filePaths) ( fullDesc <> progDesc (unlines [ "Total addition, deletion per file will be shown as a csv in following format:" , "`file name, addition, deletion`" ]) - <> header "git-freq - Show frequently changed code in the repository")+ <> header "git-freq - Show frequently changed code in a repository")