module System.Console.ArgParser.Format (
showCmdLineAppUsage
, showCmdLineVersion
, CmdLineFormat (..)
, defaultFormat
) where
import Control.Applicative
import Data.Char (isSpace)
import Data.List (intercalate, unfoldr)
import qualified Data.Map as M
import Data.Maybe
import System.Console.ArgParser.BaseType
data CmdLineFormat = CmdLineFormat
{ maxKeyWidth :: Int
, keyIndentWidth :: Int
, maxDescrWidth :: Int
}
defaultFormat :: CmdLineFormat
defaultFormat = CmdLineFormat 30 1 35
showCmdLineVersion :: CmdLnInterface a -> String
showCmdLineVersion app = appName ++ appVersion where
appName = getAppName app
appVersion = maybe "" (" " ++) $ getAppVersion app
showCmdLineAppUsage :: CmdLineFormat -> CmdLnInterface a -> String
showCmdLineAppUsage fmt app = (++ "\n\n") . trim $ intercalate "\n"
[ showCmdLineVersion app
, appUsage
, appDescr
, appParams
, appEpilog
]
where
_reflow = reflow $ maxDescrWidth fmt
appDescr = fromMaybe "" ((++ "\n") . _reflow 0 <$> getAppDescr app)
appEpilog = fromMaybe "" (_reflow 0 <$> getAppEpilog app)
paramdescrs = userDescr ++ specialDescr
userDescr = getParserParams $ cmdArgParser app
specialDescr = concatMap (getParserParams . fst) $ specialFlags app
appParams = formatParamDescrs fmt paramdescrs
appUsage = "usage : " ++ getAppName app ++ " " ++ usage
usage = unwords $ filter (not . null) $ map argUsage paramdescrs
groupByKey :: Ord k => (a -> k) -> [a] -> [(k, [a])]
groupByKey getkey xs = M.toList $ M.fromListWith (flip (++))
$ map (\x -> (getkey x, [x])) xs
formatParamDescrs :: CmdLineFormat -> [ParamDescr] -> String
formatParamDescrs fmt paramdescrs = unlines $ map showCategory categories where
categories :: [(String, [ParamDescr])]
categories = groupByKey argCategory paramdescrs
showCategory :: (String, [ParamDescr]) -> String
showCategory (cat, descrs) =
cat ++ ":\n" ++ formattedargs where
formattedargs = unlines $ map (showargformat fmt) descrs
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
showargformat :: CmdLineFormat -> ParamDescr -> String
showargformat fmt descr =
keyindent ++ trim (formattedkey ++ sep ++ descrtext) where
keyindent = replicate (keyIndentWidth fmt) ' '
formattedkey = getArgFormat descr
_maxkeywidth = maxKeyWidth fmt
padding = _maxkeywidth length formattedkey
sep = if padding > 0
then replicate padding ' '
else "\n" ++ keyindent ++ replicate _maxkeywidth ' '
indent = maxKeyWidth fmt + keyIndentWidth fmt
descrtext = reflow (maxDescrWidth fmt) indent $ argDescr descr
reflow :: Int -> Int -> String -> String
reflow width indent text = intercalate ('\n' : replicate indent ' ') _lines where
_lines = map (drop 1) $ unfoldr takeOneLine $ words text
takeOneLine :: [String] -> Maybe (String, [String])
takeOneLine = loop 0 ""
loop currWidth accum rest = case rest of
[] -> case accum of
[] -> Nothing
_ -> Just (accum, rest)
word:_words -> let
newWidth = currWidth + 1 + length word
in if newWidth > width
then Just (accum, rest)
else loop newWidth (accum ++ ' ':word) _words