{- | Module : $Header$ Copyright : (c) Simon Bergot License : BSD3 Maintainer : simon.bergot@gmail.com Stability : unstable Portability : portable Module containing helpers to print information about a parser. -} module System.Console.ArgParser.Format ( -- * Print information about the parser showCmdLineAppUsage , showCmdLineVersion -- * Help formatting , 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 -- | Specification of the help layout data CmdLineFormat = CmdLineFormat { maxKeyWidth :: Int , keyIndentWidth :: Int , maxDescrWidth :: Int } -- | Default specification for the help layout defaultFormat :: CmdLineFormat defaultFormat = CmdLineFormat 30 1 35 -- | Prints the application name and version showCmdLineVersion :: CmdLnInterface a -> String showCmdLineVersion app = appName ++ appVersion where appName = getAppName app appVersion = maybe "" (" " ++) $ getAppVersion app -- | Prints a long usage such as -- -- @ -- foo bar [bay] -- @ 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 -- one space is appended to each line so we drop one char _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