nptools-0.6.0: git-prompt.hs
{-# LANGUAGE RecordWildCards #-}
import System.Console.ANSI
import System.Environment
import System.Exit
import System.IO.Error (catchIOError)
import HSH
import Control.Monad
import Control.Applicative
import Data.Char
import Data.Maybe
import Debug.Trace (trace)
-- TODO: show stash information
-- tell Zsh to not count the contents for the length
noLength :: String -> String
noLength = ("%{"++) . (++"%}")
color :: Color -> String
color c
= setSGRCode [ SetConsoleIntensity BoldIntensity
, SetColor Foreground Dull c]
-- I didn't found how to generate sgr0 from Sys.Console.ANSI
sgr0 :: String
sgr0 = "\SI"
data Colors = Colors
{ blue, magenta, green, red, noColor :: String }
noColors :: Colors
noColors =
Colors{blue = ""
,magenta = ""
,green = ""
,red = ""
,noColor = ""
}
mapColors :: (String -> String) -> Colors -> Colors
mapColors f Colors{..} =
Colors{blue = f blue
,magenta = f magenta
,green = f green
,red = f red
,noColor = f noColor
}
colors :: Colors
colors = Colors{blue = color Blue
,magenta = color Magenta
,green = color Green
,red = color Red
,noColor = setSGRCode [] ++ sgr0}
statsInfo :: Colors -> IO String
statsInfo Colors{..} = do
gitstat <- fmap lines . run $ "git status 2> /dev/null"
-|- egrep "(# On branch|# Untracked|# Changes|# Your branch|# and have)"
let f msg = listToMaybe . map digits . egrep msg $ gitstat
g msg = fmap words . listToMaybe . egrep msg $ gitstat
ahead = f "# Your branch is ahead of "
behind = f "# Your branch is behind "
diverged = parseDiverged =<< g "# and have"
branch = fromMaybe (red ++ "ERR") (parseOnBranch =<< g "# On branch")
return . concat $
["±"
,blue,":"
,branch
,green
,['!' | "# Changes to be committed:" `elem` gitstat]
,['?' | "# Untracked files:" `elem` gitstat
|| "# Changes not staged for commit:" `elem` gitstat]
,maybe "" ((blue++":"++green++"-")++) behind
,maybe "" ((blue++":"++green++"+")++) ahead
,maybe "" (\(x,y) -> blue++":"++red++"+"++x++"-"++y) diverged
,noColor]
where
digits = reverse . takeWhile isDigit . dropWhile (not . isDigit) . reverse
parseDiverged ["#", "and", "have", x, "and", y, "different", "commit(s)", "each,", "respectively."] = Just (x, y)
parseDiverged _ = trace "git-prompt: git status message seems to have changed (diverged branches)" Nothing
parseOnBranch ["#", "On", "branch", x] = Just (magenta ++ x)
parseOnBranch _ = Nothing
isBareRepo :: IO Bool
isBareRepo = readbool =<< run "git config core.bare"
where readbool "false\n" = return False
readbool "true\n" = return True
readbool s = fail ("Unexpected result: " ++ show s)
cond :: a -> a -> Bool -> a
cond x y b = if b then x else y
main :: IO ()
main = do
args <- getArgs
ref <- head . lines <$> run "git symbolic-ref HEAD 2> /dev/null"
when (null ref) exitFailure
let myColors
| "--no-color" `elem` args = noColors
| "--zsh-no-length" `elem` args = mapColors noLength colors
| otherwise = colors
Colors{..} = myColors
stats <- catchIOError (statsInfo myColors) . const $
((blue++":")++) . cond (green ++ "bare") (red ++ "ERR")
<$> isBareRepo
putStrLn stats