optparse-applicative 0.7.0.2 → 0.8.0
raw patch · 16 files changed
+709/−421 lines, 16 filesdep +QuickCheckdep +ansi-wl-pprintdep +test-framework-quickcheck2
Dependencies added: QuickCheck, ansi-wl-pprint, test-framework-quickcheck2
Files
- Options/Applicative/BashCompletion.hs +18/−15
- Options/Applicative/Builder.hs +50/−16
- Options/Applicative/Builder/Internal.hs +20/−1
- Options/Applicative/Common.hs +21/−23
- Options/Applicative/Extra.hs +66/−72
- Options/Applicative/Help.hs +4/−112
- Options/Applicative/Help/Chunk.hs +136/−0
- Options/Applicative/Help/Core.hs +153/−0
- Options/Applicative/Help/Pretty.hs +10/−0
- Options/Applicative/Types.hs +37/−20
- Options/Applicative/Utils.hs +0/−32
- README.md +13/−33
- optparse-applicative.cabal +10/−5
- tests/Examples/Cabal.hs +2/−1
- tests/Examples/Commands.hs +1/−1
- tests/Tests.hs +168/−90
Options/Applicative/BashCompletion.hs view
@@ -6,23 +6,21 @@ import Data.Foldable (asum) import Data.List (isPrefixOf) import Data.Maybe (fromMaybe, listToMaybe)-import System.Exit (ExitCode(..)) import Options.Applicative.Builder import Options.Applicative.Common import Options.Applicative.Internal import Options.Applicative.Types -bashCompletionParser :: Parser a -> ParserPrefs -> Parser ParserFailure-bashCompletionParser parser pprefs = complParser+bashCompletionParser :: ParserInfo a -> ParserPrefs -> Parser CompletionResult+bashCompletionParser pinfo pprefs = complParser where- failure opts = ParserFailure- { errMessage = \progn -> unlines <$> opts progn- , errExitCode = ExitSuccess }+ failure opts = CompletionResult+ { execCompletion = \progn -> unlines <$> opts progn } complParser = asum [ failure <$>- ( bashCompletionQuery parser pprefs+ ( bashCompletionQuery pinfo pprefs <$> (many . strOption) (long "bash-completion-word" `mappend` internal) <*> option (long "bash-completion-index" `mappend` internal) )@@ -30,8 +28,8 @@ (bashCompletionScript <$> strOption (long "bash-completion-script" `mappend` internal)) ] -bashCompletionQuery :: Parser a -> ParserPrefs -> [String] -> Int -> String -> IO [String]-bashCompletionQuery parser pprefs ws i _ = case runCompletion compl pprefs of+bashCompletionQuery :: ParserInfo a -> ParserPrefs -> [String] -> Int -> String -> IO [String]+bashCompletionQuery pinfo pprefs ws i _ = case runCompletion compl pprefs of Just (Left (SomeParser p)) -> list_options p Just (Right c) -> run_completer c _ -> return []@@ -42,30 +40,35 @@ . mapParser (\_ -> opt_completions) opt_completions opt = case optMain opt of- OptReader ns _ _ -> show_names ns- FlagReader ns _ -> show_names ns+ OptReader ns _ _ -> return $ show_names ns+ FlagReader ns _ -> return $ show_names ns ArgReader rdr -> run_completer (crCompleter rdr)- CmdReader ns _ -> filter_names ns+ CmdReader ns _ -> return $ filter_names ns + show_name :: OptName -> String show_name (OptShort c) = '-':[c] show_name (OptLong name) = "--" ++ name + show_names :: [OptName] -> [String] show_names = filter_names . map show_name- filter_names = return . filter is_completion + filter_names :: [String] -> [String]+ filter_names = filter is_completion+ run_completer :: Completer -> IO [String] run_completer c = runCompleter c (fromMaybe "" (listToMaybe ws'')) (ws', ws'') = splitAt i ws + is_completion :: String -> Bool is_completion = case ws'' of w:_ -> isPrefixOf w _ -> const True compl = do- setParser Nothing parser- runParserFully parser (drop 1 ws')+ setParser Nothing (infoParser pinfo)+ runParserInfo pinfo (drop 1 ws') bashCompletionScript :: String -> String -> IO [String] bashCompletionScript prog progn = return
Options/Applicative/Builder.hs view
@@ -34,6 +34,7 @@ short, long, help,+ helpDoc, value, showDefaultWith, showDefault,@@ -69,9 +70,13 @@ fullDesc, briefDesc, header,- progDesc,+ headerDoc, footer,+ footerDoc,+ progDesc,+ progDescDoc, failureCode,+ noIntersperse, info, -- * Builder for 'ParserPrefs'@@ -80,7 +85,7 @@ disambiguate, showHelpOnError, noBacktrack,- noIntersperse,+ columns, prefs, -- * Types@@ -103,6 +108,8 @@ import Options.Applicative.Builder.Internal import Options.Applicative.Common import Options.Applicative.Types+import Options.Applicative.Help.Pretty+import Options.Applicative.Help.Chunk -- readers -- @@ -131,7 +138,7 @@ long = fieldMod . name . OptLong -- | Specify a default value for an option.-value :: a -> Mod f a+value :: HasValue f => a -> Mod f a value x = Mod id (DefaultProp (Just x) Nothing) id -- | Specify a function to show the default value for an option.@@ -144,8 +151,13 @@ -- | Specify the help text for an option. help :: String -> Mod f a-help s = optionMod $ \p -> p { propHelp = s }+help s = optionMod $ \p -> p { propHelp = paragraph s } +-- | Specify the help text for an option as a 'Text.PrettyPrint.ANSI.Leijen.Doc'+-- value.+helpDoc :: Maybe Doc -> Mod f a+helpDoc doc = optionMod $ \p -> p { propHelp = Chunk doc }+ -- | Specify the 'Option' reader. reader :: (String -> ReadM a) -> Mod OptionFields a reader f = fieldMod $ \p -> p { optReader = f }@@ -159,7 +171,7 @@ noArgError e = fieldMod $ \p -> p { optNoArgError = e } -- | Specify the metavariable.-metavar :: String -> Mod f a+metavar :: HasMetavar f => String -> Mod f a metavar var = optionMod $ \p -> p { propMetaVar = var } -- | Hide this option from the brief description.@@ -197,7 +209,7 @@ subparser :: Mod CommandFields a -> Parser a subparser m = mkParser d g rdr where- Mod _ d g = m `mappend` metavar "COMMAND"+ Mod _ d g = metavar "COMMAND" `mappend` m rdr = uncurry CmdReader (mkCommand m) -- | Builder for an argument parser.@@ -209,10 +221,12 @@ -- | Builder for an argument list parser. All arguments are collected and -- returned as a list.+{-# DEPRECATED arguments "Use many and argument instead" #-} arguments :: (String -> Maybe a) -> Mod ArgumentFields a -> Parser [a] arguments r m = many (argument r m) -- | Like `arguments`, but require at least one argument.+{-# DEPRECATED arguments1 "Use some and argument instead" #-} arguments1 :: (String -> Maybe a) -> Mod ArgumentFields a -> Parser [a] arguments1 r m = some (argument r m) @@ -304,20 +318,39 @@ -- | Specify a header for this parser. header :: String -> InfoMod a-header s = InfoMod $ \i -> i { infoHeader = s }+header s = InfoMod $ \i -> i { infoHeader = paragraph s } +-- | Specify a header for this parser as a 'Text.PrettyPrint.ANSI.Leijen.Doc'+-- value.+headerDoc :: Maybe Doc -> InfoMod a+headerDoc doc = InfoMod $ \i -> i { infoHeader = Chunk doc }+ -- | Specify a footer for this parser. footer :: String -> InfoMod a-footer s = InfoMod $ \i -> i { infoFooter = s }+footer s = InfoMod $ \i -> i { infoFooter = paragraph s } +-- | Specify a footer for this parser as a 'Text.PrettyPrint.ANSI.Leijen.Doc'+-- value.+footerDoc :: Maybe Doc -> InfoMod a+footerDoc doc = InfoMod $ \i -> i { infoFooter = Chunk doc }+ -- | Specify a short program description. progDesc :: String -> InfoMod a-progDesc s = InfoMod $ \i -> i { infoProgDesc = s }+progDesc s = InfoMod $ \i -> i { infoProgDesc = paragraph s } +-- | Specify a short program description as a 'Text.PrettyPrint.ANSI.Leijen.Doc'+-- value.+progDescDoc :: Maybe Doc -> InfoMod a+progDescDoc doc = InfoMod $ \i -> i { infoProgDesc = Chunk doc }+ -- | Specify an exit code if a parse error occurs. failureCode :: Int -> InfoMod a failureCode n = InfoMod $ \i -> i { infoFailureCode = n } +-- | Disable parsing of regular options after arguments+noIntersperse :: InfoMod a+noIntersperse = InfoMod $ \p -> p { infoIntersperse = False }+ -- | Create a 'ParserInfo' given a 'Parser' and a modifier. info :: Parser a -> InfoMod a -> ParserInfo a info parser m = applyInfoMod m base@@ -325,10 +358,11 @@ base = ParserInfo { infoParser = parser , infoFullDesc = True- , infoProgDesc = ""- , infoHeader = ""- , infoFooter = ""- , infoFailureCode = 1 }+ , infoProgDesc = mempty+ , infoHeader = mempty+ , infoFooter = mempty+ , infoFailureCode = 1+ , infoIntersperse = True } newtype PrefsMod = PrefsMod { applyPrefsMod :: ParserPrefs -> ParserPrefs }@@ -349,8 +383,8 @@ noBacktrack :: PrefsMod noBacktrack = PrefsMod $ \p -> p { prefBacktrack = False } -noIntersperse :: PrefsMod-noIntersperse = PrefsMod $ \p -> p { prefIntersperse = False }+columns :: Int -> PrefsMod+columns cols = PrefsMod $ \p -> p { prefColumns = cols } prefs :: PrefsMod -> ParserPrefs prefs m = applyPrefsMod m base@@ -360,7 +394,7 @@ , prefDisambiguate = False , prefShowHelpOnError = False , prefBacktrack = True- , prefIntersperse = True }+ , prefColumns = 80 } -- convenience shortcuts
Options/Applicative/Builder/Internal.hs view
@@ -3,6 +3,8 @@ Mod(..), HasName(..), HasCompleter(..),+ HasValue,+ HasMetavar, OptionFields(..), FlagFields(..), CommandFields(..),@@ -62,6 +64,23 @@ instance HasCompleter ArgumentFields where modCompleter f p = p { argCompleter = f (argCompleter p) } +class HasValue f where+ -- this is just so that it is not necessary to specify the kind of f+ hasValueDummy :: f a -> ()+instance HasValue OptionFields where+ hasValueDummy _ = ()+instance HasValue ArgumentFields where+ hasValueDummy _ = ()++class HasMetavar f where+ hasMetavarDummy :: f a -> ()+instance HasMetavar OptionFields where+ hasMetavarDummy _ = ()+instance HasMetavar ArgumentFields where+ hasMetavarDummy _ = ()+instance HasMetavar CommandFields where+ hasMetavarDummy _ = ()+ -- mod -- data DefaultProp a = DefaultProp@@ -118,7 +137,7 @@ baseProps = OptProperties { propMetaVar = "" , propVisibility = Visible- , propHelp = ""+ , propHelp = mempty , propShowDefault = Nothing } mkCommand :: Mod CommandFields a -> ([String], String -> Maybe (ParserInfo a))
Options/Applicative/Common.hs view
@@ -32,11 +32,16 @@ -- -- A basic 'ParserInfo' with default values for fields can be created using -- the 'info' function.+ --+ -- A 'ParserPrefs' contains general preferences for all command-line+ -- options, and can be built with the 'prefs' function. ParserInfo(..),+ ParserPrefs(..), -- * Running parsers- runParser,+ runParserInfo, runParserFully,+ runParser, evalParser, -- * Low-level utilities@@ -83,8 +88,6 @@ mappend m@(Match _) _ = m mappend _ m = m -type Args = [String]- argMatches :: MonadP m => OptReader a -> String -> Maybe (StateT Args m a) argMatches opt arg = case opt of@@ -96,12 +99,11 @@ setContext (Just arg) subp prefs <- getPrefs let runSubparser- | prefBacktrack prefs = \p a -> do- policy <- getPolicy- runParser policy p a- | otherwise = \p a- -> (,) <$> runParserFully p a <*> pure []- runSubparser (infoParser subp) args+ | prefBacktrack prefs = \i a -> do+ runParser (getPolicy i) (infoParser i) a+ | otherwise = \i a+ -> (,) <$> runParserInfo i a <*> pure []+ runSubparser subp args _ -> Nothing optMatches :: MonadP m => Bool -> OptReader a -> OptWord -> Maybe (StateT Args m a)@@ -185,11 +187,6 @@ Just matcher -> lift matcher Nothing -> mzero -data ArgPolicy- = SkipOpts- | AllowOpts- deriving Eq- stepParser :: MonadP m => ParserPrefs -> ArgPolicy -> String -> Parser a -> NondetT (StateT Args m) (Parser a) stepParser pprefs SkipOpts arg p = case parseWord arg of@@ -226,16 +223,17 @@ ('-':_) -> "Invalid option `" ++ arg ++ "'" _ -> "Invalid argument `" ++ arg ++ "'" -getPolicy :: MonadP m => m ArgPolicy-getPolicy = do- prefs <- getPrefs- return $ if prefIntersperse prefs- then SkipOpts- else AllowOpts+getPolicy :: ParserInfo a -> ArgPolicy+getPolicy i = if infoIntersperse i+ then SkipOpts+ else AllowOpts -runParserFully :: MonadP m => Parser a -> Args -> m a-runParserFully p args = do- policy <- getPolicy+runParserInfo :: MonadP m => ParserInfo a -> Args -> m a+runParserInfo i args+ = runParserFully (getPolicy i) (infoParser i) args++runParserFully :: MonadP m => ArgPolicy -> Parser a -> Args -> m a+runParserFully policy p args = do (r, args') <- runParser policy p args guard $ null args' return r
Options/Applicative/Extra.hs view
@@ -10,15 +10,17 @@ customExecParser, customExecParserMaybe, execParserPure,- usage, ParserFailure(..),+ ParserResult(..),+ ParserPrefs(..),+ CompletionResult(..), ) where -import Control.Applicative ((<$>), (<|>), (<**>))-import Data.Monoid (mconcat)+import Control.Applicative (pure, (<$>), (<|>), (<**>))+import Data.Monoid (mempty, mconcat) import System.Environment (getArgs, getProgName) import System.Exit (exitWith, ExitCode(..))-import System.IO (hPutStr, stderr)+import System.IO (hPutStrLn, stderr) import Options.Applicative.BashCompletion import Options.Applicative.Builder hiding (briefDesc)@@ -27,7 +29,6 @@ import Options.Applicative.Help import Options.Applicative.Internal import Options.Applicative.Types-import Options.Applicative.Utils -- | A hidden \"helper\" option which always fails. helper :: Parser (a -> a)@@ -57,15 +58,19 @@ customExecParser pprefs pinfo = do args <- getArgs case execParserPure pprefs pinfo args of- Right a -> return a- Left failure -> do+ Success a -> return a+ Failure failure -> do progn <- getProgName- let c = errExitCode failure- msg <- errMessage failure progn- case c of- ExitSuccess -> putStr msg- _ -> hPutStr stderr msg- exitWith c+ let (msg, exit) = execFailure failure progn+ case exit of+ ExitSuccess -> putStrLn msg+ _ -> hPutStrLn stderr msg+ exitWith exit+ CompletionInvoked compl -> do+ progn <- getProgName+ msg <- execCompletion compl progn+ putStr msg+ exitWith ExitSuccess -- | Run a program description in pure code. --@@ -81,73 +86,39 @@ -- -- See 'execParserMaybe' for details. customExecParserMaybe :: ParserPrefs -> ParserInfo a -> [String] -> Maybe a-customExecParserMaybe pprefs pinfo- = either (const Nothing) Just- . execParserPure pprefs pinfo--data Result a = Result a- | Extra ParserFailure+customExecParserMaybe pprefs pinfo args = case execParserPure pprefs pinfo args of+ Success r -> Just r+ _ -> Nothing -- | The most general way to run a program description in pure code. execParserPure :: ParserPrefs -- ^ Global preferences for this parser -> ParserInfo a -- ^ Description of the program to run -> [String] -- ^ Program arguments- -> Either ParserFailure a+ -> ParserResult a execParserPure pprefs pinfo args = case runP p pprefs of- (Right r, _) -> case r of- Result a -> Right a- Extra failure -> Left failure- (Left msg, ctx) -> Left $- parserFailure pprefs pinfo msg ctx+ (Right (Right r), _) -> Success r+ (Right (Left c), _) -> CompletionInvoked c+ (Left err, ctx) -> Failure $ parserFailure pprefs pinfo err ctx where- parser = infoParser pinfo- parser' = (Extra <$> bashCompletionParser parser pprefs)- <|> (Result <$> parser)- p = runParserFully parser' args+ pinfo' = pinfo+ { infoParser = (Left <$> bashCompletionParser pinfo pprefs)+ <|> (Right <$> infoParser pinfo) }+ p = runParserInfo pinfo' args parserFailure :: ParserPrefs -> ParserInfo a -> ParseError -> Context -> ParserFailure-parserFailure pprefs pinfo msg ctx = ParserFailure- { errMessage = \progn- -> with_context ctx pinfo $ \names ->- return- . show_help- . add_error- . add_usage names progn- , errExitCode = exit_code }+parserFailure pprefs pinfo msg ctx = ParserFailure $ \progn ->+ let h = with_context ctx pinfo $ \names pinfo' -> mconcat+ [ base_help pinfo'+ , usage_help progn names pinfo'+ , error_help ]+ in (render_help h, exit_code) where- add_usage names progn i = case msg of- InfoMsg _ -> i- _ -> i- { infoHeader = vcat- ( header_line i ++- [ usage pprefs (infoParser i) ename ] ) }- where- ename = unwords (progn : names)- add_error i = i- { infoHeader = vcat (error_msg ++ [infoHeader i]) }- error_msg = case msg of- ShowHelpText -> []- ErrorMsg m -> [m]- InfoMsg m -> [m] exit_code = case msg of- InfoMsg _ -> ExitSuccess- _ -> ExitFailure (infoFailureCode pinfo)- show_full_help = case msg of- ShowHelpText -> True- _ -> prefShowHelpOnError pprefs- show_help i- | show_full_help- = parserHelpText pprefs i- | otherwise- = unlines $ filter (not . null) [ infoHeader i ]- header_line i- | show_full_help- = [ infoHeader i ]- | otherwise- = []+ ErrorMsg _ -> ExitFailure (infoFailureCode pinfo)+ _ -> ExitSuccess with_context :: Context -> ParserInfo a@@ -156,9 +127,32 @@ with_context NullContext i f = f [] i with_context (Context n i) _ f = f n i --- | Generate option summary.-usage :: ParserPrefs -> Parser a -> String -> String-usage pprefs p progn = foldr (<+>) ""- [ "Usage:"- , progn- , briefDesc pprefs p ]+ render_help :: ParserHelp -> String+ render_help = (`displayS` "")+ . renderPretty 1.0 (prefColumns pprefs)+ . helpText++ show_full_help = case msg of+ ShowHelpText -> True+ _ -> prefShowHelpOnError pprefs++ usage_help progn names i = case msg of+ InfoMsg _ -> mempty+ _ -> usageHelp $ vcatChunks+ [ pure . parserUsage pprefs (infoParser i) . unwords $ progn : names+ , fmap (indent 2) . infoProgDesc $ i ]++ error_help = headerHelp $ case msg of+ ShowHelpText -> mempty+ ErrorMsg m -> stringChunk m+ InfoMsg m -> stringChunk m++ base_help :: ParserInfo a -> ParserHelp+ base_help i+ | show_full_help+ = mconcat [h, f, parserHelp pprefs (infoParser i)]+ | otherwise+ = h+ where+ h = headerHelp (infoHeader i)+ f = footerHelp (infoFooter i)
Options/Applicative/Help.hs view
@@ -1,115 +1,7 @@ module Options.Applicative.Help (- cmdDesc,- briefDesc,- fullDesc,- parserHelpText,+ module X ) where -import Data.List (intercalate, sort)-import Data.Maybe (maybeToList, catMaybes)--import Options.Applicative.Common-import Options.Applicative.Types-import Options.Applicative.Utils---- | Style for rendering an option.-data OptDescStyle = OptDescStyle- { descSep :: String- , descHidden :: Bool- , descSurround :: Bool }---- | Generate description for a single option.-optDesc :: ParserPrefs -> OptDescStyle -> OptHelpInfo -> Option a -> String-optDesc pprefs style info opt =- let ns = optionNames $ optMain opt- mv = optMetaVar opt- descs = map showOption (sort ns)- desc' = intercalate (descSep style) descs <+> mv- show_opt- | optVisibility opt == Hidden- = descHidden style- | otherwise- = optVisibility opt == Visible- suffix- | hinfoMulti info- = prefMultiSuffix pprefs- | otherwise- = ""- render text- | not show_opt- = ""- | null text || not (descSurround style)- = text ++ suffix- | hinfoDefault info- = "[" ++ text ++ "]" ++ suffix- | null (drop 1 descs)- = text ++ suffix- | otherwise- = "(" ++ text ++ ")" ++ suffix- in render desc'---- | Generate descriptions for commands.-cmdDesc :: Parser a -> [String]-cmdDesc = concat . mapParser desc- where- desc _ opt =- case optMain opt of- CmdReader cmds p ->- tabulate [(cmd, d)- | cmd <- reverse cmds- , d <- maybeToList . fmap infoProgDesc $ p cmd ]- _ -> []---- | Generate a brief help text for a parser.-briefDesc :: ParserPrefs -> Parser a -> String-briefDesc pprefs = fold_tree . treeMapParser (optDesc pprefs style)- where- style = OptDescStyle- { descSep = "|"- , descHidden = False- , descSurround = True }-- fold_tree (Leaf x) = x- fold_tree (MultNode xs) = unwords (fold_trees xs)- fold_tree (AltNode xs) = alt_node (fold_trees xs)-- alt_node [n] = n- alt_node ns = "(" ++ intercalate " | " ns ++ ")"-- fold_trees = filter (not . null) . map fold_tree---- | Generate a full help text for a parser.-fullDesc :: ParserPrefs -> Parser a -> [String]-fullDesc pprefs = tabulate . catMaybes . mapParser doc- where- doc info opt- | null n = Nothing- | null h = Nothing- | otherwise = Just (n, h ++ hdef)- where n = optDesc pprefs style info opt- h = optHelp opt- hdef = maybe "" show_def (optShowDefault opt)- show_def s = " (default: " ++ s ++ ")"- style = OptDescStyle- { descSep = ","- , descHidden = True- , descSurround = False }---- | Generate the help text for a program.-parserHelpText :: ParserPrefs -> ParserInfo a -> String-parserHelpText pprefs pinfo = unlines- $ nn [infoHeader pinfo]- ++ [ " " ++ line | line <- nn [infoProgDesc pinfo] ]- ++ [ line | let opts = fullDesc pprefs p- , not (null opts)- , line <- ["", "Available options:"] ++ opts- , infoFullDesc pinfo ]- ++ [ line | let cmds = cmdDesc p- , not (null cmds)- , line <- ["", "Available commands:"] ++ cmds- , infoFullDesc pinfo ]- ++ [ line | footer <- nn [infoFooter pinfo]- , line <- ["", footer] ]- where- nn = filter (not . null)- p = infoParser pinfo+import Options.Applicative.Help.Pretty as X+import Options.Applicative.Help.Chunk as X+import Options.Applicative.Help.Core as X
+ Options/Applicative/Help/Chunk.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE CPP #-}+module Options.Applicative.Help.Chunk+ ( mappendWith+ , Chunk(..)+ , chunked+ , listToChunk+ , (<<+>>)+ , (<</>>)+ , vcatChunks+ , vsepChunks+ , isEmpty+ , stringChunk+ , paragraph+ , extractChunk+ , tabulate+ ) where++import Control.Applicative+import Control.Monad+import Data.Maybe+import Data.Monoid++import Options.Applicative.Help.Pretty++mappendWith :: Monoid a => a -> a -> a -> a+mappendWith s x y = mconcat [x, s, y]++-- | The free monoid on a semigroup 'a'.+newtype Chunk a = Chunk+ { unChunk :: Maybe a }+ deriving (Eq, Show)++instance Functor Chunk where+ fmap f = Chunk . fmap f . unChunk++instance Applicative Chunk where+ pure = Chunk . pure+ Chunk f <*> Chunk x = Chunk (f <*> x)++instance Monad Chunk where+ return = pure+ m >>= f = Chunk $ unChunk m >>= unChunk . f++instance MonadPlus Chunk where+ mzero = Chunk mzero+ mplus m1 m2 = Chunk $ mplus (unChunk m1) (unChunk m2)++-- | Given a semigroup structure on 'a', return a monoid structure on 'Chunk a'.+--+-- Note that this is /not/ the same as 'liftA2'.+chunked :: (a -> a -> a)+ -> Chunk a -> Chunk a -> Chunk a+chunked _ (Chunk Nothing) y = y+chunked _ x (Chunk Nothing) = x+chunked f (Chunk (Just x)) (Chunk (Just y)) = Chunk (Just (f x y))++-- | Concatenate a list into a Chunk. 'listToChunk' satisfies:+--+-- > isEmpty . listToChunk = null+-- > listToChunk = mconcat . fmap pure+listToChunk :: Monoid a => [a] -> Chunk a+listToChunk [] = mempty+listToChunk xs = pure (mconcat xs)++instance Monoid a => Monoid (Chunk a) where+ mempty = Chunk Nothing+ mappend = chunked mappend++-- | Part of a constrained comonad instance.+--+-- This is the counit of the adjunction between 'Chunk' and the forgetful+-- functor from monoids to semigroups. It satisfies:+--+-- > extractChunk . pure = id+-- > extractChunk . fmap pure = id+extractChunk :: Monoid a => Chunk a -> a+extractChunk = fromMaybe mempty . unChunk+-- we could also define:+-- duplicate :: Monoid a => Chunk a -> Chunk (Chunk a)+-- duplicate = fmap pure++-- | Concatenate two 'Chunk's with a space in between. If one is empty, this+-- just returns the other one.+--+-- Unlike '<+>' for 'Doc', this operation has a unit element, namely the empty+-- 'Chunk'.+(<<+>>) :: Chunk Doc -> Chunk Doc -> Chunk Doc+(<<+>>) = chunked (<+>)++-- | Concatenate two 'Chunk's with a softline in between. This is exactly like+-- '<<+>>', but uses a softline instead of a space.+(<</>>) :: Chunk Doc -> Chunk Doc -> Chunk Doc+(<</>>) = chunked (</>)++-- | Concatenate 'Chunk's vertically.+vcatChunks :: [Chunk Doc] -> Chunk Doc+vcatChunks = foldr (chunked (.$.)) mempty++-- | Concatenate 'Chunk's vertically separated by empty lines.+vsepChunks :: [Chunk Doc] -> Chunk Doc+vsepChunks = foldr (chunked (\x y -> x .$. mempty .$. y)) mempty++-- | Whether a 'Chunk' is empty. Note that something like 'pure mempty' is not+-- considered an empty chunk, even though the underlying 'Doc' is empty.+isEmpty :: Chunk a -> Bool+isEmpty = isNothing . unChunk++-- | Convert a 'String' into a 'Chunk'. This satisfies:+--+-- > isEmpty . stringChunk = null+-- > extractChunk . stringChunk = string+stringChunk :: String -> Chunk Doc+stringChunk "" = mempty+stringChunk s = pure (string s)++-- | Convert a paragraph into a 'Chunk'. The resulting chunk is composed by the+-- words of the original paragraph separated by softlines, so it will be+-- automatically word-wrapped when rendering the underlying document.+--+-- This satisfies:+--+-- > isEmpty . paragraph = null . words+paragraph :: String -> Chunk Doc+paragraph = foldr (chunked (</>)) mempty+ . map stringChunk+ . words++tabulate' :: Int -> [(Doc, Doc)] -> Chunk Doc+tabulate' _ [] = mempty+tabulate' size table = pure $ vcat+ [ indent 2 (fillBreak size key <+> value)+ | (key, value) <- table ]++-- | Display pairs of strings in a table.+tabulate :: [(Doc, Doc)] -> Chunk Doc+tabulate = tabulate' 24
+ Options/Applicative/Help/Core.hs view
@@ -0,0 +1,153 @@+module Options.Applicative.Help.Core (+ cmdDesc,+ briefDesc,+ fullDesc,+ ParserHelp(..),+ helpText,+ headerHelp,+ usageHelp,+ bodyHelp,+ footerHelp,+ parserHelp,+ parserUsage,+ ) where++import Control.Monad (guard)+import Data.List (intersperse, sort)+import Data.Maybe (maybeToList, catMaybes)+import Data.Monoid (Monoid, mempty, mappend, mconcat)++import Options.Applicative.Common+import Options.Applicative.Types+import Options.Applicative.Help.Pretty+import Options.Applicative.Help.Chunk++-- | Style for rendering an option.+data OptDescStyle = OptDescStyle+ { descSep :: Doc+ , descHidden :: Bool+ , descSurround :: Bool }++-- | Generate description for a single option.+optDesc :: ParserPrefs -> OptDescStyle -> OptHelpInfo -> Option a -> Chunk Doc+optDesc pprefs style info opt =+ let ns = optionNames $ optMain opt+ mv = stringChunk $ optMetaVar opt+ descs = map (string . showOption) (sort ns)+ desc' = listToChunk (intersperse (descSep style) descs) <<+>> mv+ show_opt+ | optVisibility opt == Hidden+ = descHidden style+ | otherwise+ = optVisibility opt == Visible+ suffix+ | hinfoMulti info+ = stringChunk . prefMultiSuffix $ pprefs+ | otherwise+ = mempty+ render chunk+ | not show_opt+ = mempty+ | isEmpty chunk || not (descSurround style)+ = mappend chunk suffix+ | hinfoDefault info+ = mappend (fmap brackets chunk) suffix+ | null (drop 1 descs)+ = mappend chunk suffix+ | otherwise+ = mappend (fmap parens chunk) suffix+ in render desc'++-- | Generate descriptions for commands.+cmdDesc :: Parser a -> Chunk Doc+cmdDesc = mconcat . mapParser desc+ where+ desc _ opt =+ case optMain opt of+ CmdReader cmds p ->+ tabulate [(string cmd, align (extractChunk d))+ | cmd <- reverse cmds+ , d <- maybeToList . fmap infoProgDesc $ p cmd ]+ _ -> mempty++-- | Generate a brief help text for a parser.+briefDesc :: ParserPrefs -> Parser a -> Chunk Doc+briefDesc pprefs = fold_tree . treeMapParser (optDesc pprefs style)+ where+ style = OptDescStyle+ { descSep = string "|"+ , descHidden = False+ , descSurround = True }++ fold_tree (Leaf x) = x+ fold_tree (MultNode xs) = foldr (<</>>) mempty . map fold_tree $ xs+ fold_tree (AltNode xs) = alt_node+ . filter (not . isEmpty)+ . map fold_tree $ xs++ alt_node :: [Chunk Doc] -> Chunk Doc+ alt_node [n] = n+ alt_node ns = fmap parens+ . foldr (chunked (\x y -> x </> char '|' </> y)) mempty+ $ ns++-- | Generate a full help text for a parser.+fullDesc :: ParserPrefs -> Parser a -> Chunk Doc+fullDesc pprefs = tabulate . catMaybes . mapParser doc+ where+ doc info opt = do+ guard . not . isEmpty $ n+ guard . not . isEmpty $ h+ return (extractChunk n, align . extractChunk $ h <<+>> hdef)+ where+ n = optDesc pprefs style info opt+ h = optHelp $ opt+ hdef = Chunk . fmap show_def . optShowDefault $ opt+ show_def s = parens (string "default:" <+> string s)+ style = OptDescStyle+ { descSep = string ","+ , descHidden = True+ , descSurround = False }++data ParserHelp = ParserHelp+ { helpHeader :: Chunk Doc+ , helpUsage :: Chunk Doc+ , helpBody :: Chunk Doc+ , helpFooter :: Chunk Doc }++instance Monoid ParserHelp where+ mempty = ParserHelp mempty mempty mempty mempty+ mappend (ParserHelp h1 u1 b1 f1) (ParserHelp h2 u2 b2 f2)+ = ParserHelp (mappend h1 h2) (mappend u1 u2)+ (mappend b1 b2) (mappend f1 f2)++headerHelp :: Chunk Doc -> ParserHelp+headerHelp chunk = ParserHelp chunk mempty mempty mempty++usageHelp :: Chunk Doc -> ParserHelp+usageHelp chunk = ParserHelp mempty chunk mempty mempty++bodyHelp :: Chunk Doc -> ParserHelp+bodyHelp chunk = ParserHelp mempty mempty chunk mempty++footerHelp :: Chunk Doc -> ParserHelp+footerHelp chunk = ParserHelp mempty mempty mempty chunk++helpText :: ParserHelp -> Doc+helpText (ParserHelp h u b f) = extractChunk . vsepChunks $ [h, u, b, f]++-- | Generate the help text for a program.+parserHelp :: ParserPrefs -> Parser a -> ParserHelp+parserHelp pprefs p = bodyHelp . vsepChunks $+ [ with_title "Available options:" (fullDesc pprefs p)+ , with_title "Available commands:" (cmdDesc p) ]+ where+ with_title :: String -> Chunk Doc -> Chunk Doc+ with_title title = fmap (string title .$.)++-- | Generate option summary.+parserUsage :: ParserPrefs -> Parser a -> String -> Doc+parserUsage pprefs p progn = hsep $+ [ string "Usage:"+ , string progn+ , align (extractChunk (briefDesc pprefs p)) ]
+ Options/Applicative/Help/Pretty.hs view
@@ -0,0 +1,10 @@+module Options.Applicative.Help.Pretty+ ( module Text.PrettyPrint.ANSI.Leijen+ , (.$.)+ ) where++import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>), columns)+import qualified Text.PrettyPrint.ANSI.Leijen as PP++(.$.) :: Doc -> Doc -> Doc+(.$.) = (PP.<$>)
Options/Applicative/Types.hs view
@@ -17,7 +17,11 @@ ParserM(..), Completer(..), mkCompleter,+ CompletionResult(..), ParserFailure(..),+ ParserResult(..),+ Args,+ ArgPolicy(..), OptHelpInfo(..), OptTree(..), @@ -39,6 +43,9 @@ import Data.Monoid (Monoid(..)) import System.Exit (ExitCode(..)) +import Options.Applicative.Help.Pretty+import Options.Applicative.Help.Chunk+ data ParseError = ErrorMsg String | InfoMsg String@@ -50,12 +57,15 @@ -- | A full description for a runnable 'Parser' for a program. data ParserInfo a = ParserInfo- { infoParser :: Parser a -- ^ the option parser for the program- , infoFullDesc :: Bool -- ^ whether the help text should contain full documentation- , infoProgDesc :: String -- ^ brief parser description- , infoHeader :: String -- ^ header of the full parser description- , infoFooter :: String -- ^ footer of the full parser description- , infoFailureCode :: Int -- ^ exit code for a parser failure+ { infoParser :: Parser a -- ^ the option parser for the program+ , infoFullDesc :: Bool -- ^ whether the help text should contain full+ -- documentation+ , infoProgDesc :: Chunk Doc -- ^ brief parser description+ , infoHeader :: Chunk Doc -- ^ header of the full parser description+ , infoFooter :: Chunk Doc -- ^ footer of the full parser description+ , infoFailureCode :: Int -- ^ exit code for a parser failure+ , infoIntersperse :: Bool -- ^ allow regular options and flags to occur+ -- after arguments (default: True) } instance Functor ParserInfo where@@ -70,8 +80,8 @@ -- (default: False) , prefBacktrack :: Bool -- ^ backtrack to parent parser when a -- subcommand fails (default: True)- , prefIntersperse :: Bool -- ^ allow regular options and flags to occur- -- after arguments (default: True)+ , prefColumns :: Int -- ^ number of columns in the terminal, used to+ -- format the help page (default: 80) } data OptName = OptShort !Char@@ -88,7 +98,7 @@ -- | Specification for an individual parser option. data OptProperties = OptProperties { propVisibility :: OptVisibility -- ^ whether this flag is shown is the brief description- , propHelp :: String -- ^ help text for this option+ , propHelp :: Chunk Doc -- ^ help text for this option , propMetaVar :: String -- ^ metavariable for this option , propShowDefault :: Maybe String -- ^ what to show in the help text as the default }@@ -221,18 +231,25 @@ mappend (Completer c1) (Completer c2) = Completer $ \s -> (++) <$> c1 s <*> c2 s --- | Result after a parse error.-data ParserFailure = ParserFailure- { errMessage :: String -> IO String -- ^ Function which takes the program name- -- as input and returns an error message- , errExitCode :: ExitCode -- ^ Exit code to use for this error- }+newtype CompletionResult = CompletionResult+ { execCompletion :: String -> IO String } -instance Error ParserFailure where- strMsg msg = ParserFailure- { errMessage = \_ -> return msg- , errExitCode = ExitFailure 1 }+newtype ParserFailure = ParserFailure+ { execFailure :: String -> (String, ExitCode) } +-- | Result of 'execParserPure'.+data ParserResult a+ = Success a+ | Failure ParserFailure+ | CompletionInvoked CompletionResult++type Args = [String]++data ArgPolicy+ = SkipOpts+ | AllowOpts+ deriving Eq+ data OptHelpInfo = OptHelpInfo { hinfoMulti :: Bool , hinfoDefault :: Bool }@@ -246,7 +263,7 @@ optVisibility :: Option a -> OptVisibility optVisibility = propVisibility . optProps -optHelp :: Option a -> String+optHelp :: Option a -> Chunk Doc optHelp = propHelp . optProps optMetaVar :: Option a -> String
− Options/Applicative/Utils.hs
@@ -1,32 +0,0 @@-module Options.Applicative.Utils (- (<+>),- vcat,- tabulate,- pad- ) where--import Data.List (intercalate)---- | Concatenate two strings with a space in the middle.-(<+>) :: String -> String -> String-"" <+> s = s-s <+> "" = s-s1 <+> s2 = s1 ++ " " ++ s2---- | Concatenate strings vertically with empty lines in between.-vcat :: [String] -> String-vcat = intercalate "\n\n" . filter (not . null)--tabulate' :: Int -> [(String, String)] -> [String]-tabulate' size table =- [ " " ++ pad size key ++ " " ++ value- | (key, value) <- table ]---- | Display pairs of strings in a table.-tabulate :: [(String, String)] -> [String]-tabulate = tabulate' 24---- | Pad a string to a fixed size with whitespace.-pad :: Int -> String -> String-pad size str = str ++ replicate (size - n `max` 0) ' '- where n = length str
README.md view
@@ -212,11 +212,17 @@ for which the reader succeeds. For example ```haskell-argument str ( metavar "FILE" )+argument str (metavar "FILE") ``` -creates an argument accepting any string.+creates an argument accepting any string. To accept an arbitrary number of+arguments, combine the `argument` builder with either the `many` or `some`+combinator: +```haskell+some (argument str (metavar "FILES..."))+```+ Arguments are only displayed in the brief help text, so there's no need to attach a description to them. They should be manually documented in the program description.@@ -303,39 +309,13 @@ See the haddock documentation for `Options.Applicative.Builder` for a full list of builders and modifiers. -## Arrow interface--It is also possible to use the [Arrow syntax][arrows] to combine basic parsers.--This can be particularly useful when the structure holding parse results is-deeply nested, or when the order of fields differs from the order in which the-parsers should be applied.--Using functions from the `Options.Applicative.Arrows` module, one can write,-for example:--```haskell-data Options = Options- { optArgs :: [String]- , optVerbose :: Bool }--opts :: Parser Options-opts = runA $ proc () -> do- verbosity <- asA (option (short 'v' <> value 0)) -< ()- let verbose = verbosity > 0- args <- asA (arguments str idm) -< ()- returnA -< Options args verbose-```--where parsers are converted to arrows using `asA`, and the resulting composed-arrow is converted back to a `Parser` with `runA`.+## Advanced features -See `tests/Examples/Cabal.hs` for a slightly more elaborate example using the-arrow syntax for defining parsers.+* [Bash completion][bash]+* [Arrow interface][arrows] -Note that the `Arrow` interface is provided only for convenience. The API based-on `Applicative` is just as expressive, although it might be cumbersome to use-in certain cases.+ [bash]: https://github.com/pcapriotti/optparse-applicative/wiki/Bash-Completion+ [arrows]: https://github.com/pcapriotti/optparse-applicative/wiki/Arrows ## How it works
optparse-applicative.cabal view
@@ -1,5 +1,5 @@ name: optparse-applicative-version: 0.7.0.2+version: 0.8.0 synopsis: Utilities and combinators for parsing command line options description: Here is a simple example of an applicative option parser:@@ -96,21 +96,26 @@ Options.Applicative.Common, Options.Applicative.Extra, Options.Applicative.Help,+ Options.Applicative.Help.Pretty,+ Options.Applicative.Help.Chunk,+ Options.Applicative.Help.Core, Options.Applicative.Types,- Options.Applicative.Utils- other-modules: Options.Applicative.Internal+ Options.Applicative.Internal ghc-options: -Wall build-depends: base == 4.*, transformers >= 0.2 && < 0.4,- process >= 1.0 && < 1.3+ process >= 1.0 && < 1.3,+ ansi-wl-pprint >= 0.6 && < 0.7 test-suite tests type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: Tests.hs- ghc-options: -Wall+ ghc-options: -Wall -fno-warn-orphans build-depends: base == 4.*, HUnit == 1.2.*, optparse-applicative,+ QuickCheck == 2.6.*, test-framework >= 0.6 && < 0.9, test-framework-hunit >= 0.2 && < 0.4,+ test-framework-quickcheck2 == 0.3.*, test-framework-th-prime == 0.0.*
tests/Examples/Cabal.hs view
@@ -118,7 +118,8 @@ returnA -< BuildOpts bdir pinfo :: ParserInfo Args-pinfo = info parser idm+pinfo = info parser+ ( progDesc "An example modelled on cabal" ) main :: IO () main = do
tests/Examples/Commands.hs view
@@ -16,7 +16,7 @@ deriving Show hello :: Parser Sample-hello = Hello <$> arguments str (metavar "TARGET...")+hello = Hello <$> many (argument str (metavar "TARGET...")) sample :: Parser Sample sample = subparser
tests/Tests.hs view
@@ -1,54 +1,71 @@-{-# LANGUAGE TemplateHaskell, CPP #-}+{-# LANGUAGE StandaloneDeriving, GeneralizedNewtypeDeriving,+ TemplateHaskell, CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Main where import qualified Examples.Hello as Hello import qualified Examples.Commands as Commands import qualified Examples.Cabal as Cabal import qualified Examples.Alternatives as Alternatives+import qualified Examples.Formatting as Formatting import Control.Monad-import Data.List-import Options.Applicative+import Data.List hiding (group)+import Data.Monoid import System.Exit import Test.HUnit import Test.Framework.Providers.HUnit+import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.Framework.TH.Prime+import Test.QuickCheck (Positive (..))+import Test.QuickCheck.Arbitrary +import Options.Applicative+import Options.Applicative.Help.Pretty (Doc, SimpleDoc(..))+import qualified Options.Applicative.Help.Pretty as Doc+import Options.Applicative.Help.Chunk+ #if __GLASGOW_HASKELL__ <= 702 import Data.Monoid (<>) :: Monoid a => a -> a -> a (<>) = mappend #endif -run :: ParserInfo a -> [String] -> Either ParserFailure a+run :: ParserInfo a -> [String] -> ParserResult a run = execParserPure (prefs idm) -assertLeft :: Show b => Either a b -> (a -> Assertion) -> Assertion-assertLeft x f = either f err x- where- err b = assertFailure $ "expected Left, got " ++ show b+assertError :: Show a => ParserResult a -> (ParserFailure -> Assertion) -> Assertion+assertError x f = case x of+ Success r -> assertFailure $ "expected failure, got success: " ++ show r+ Failure e -> f e+ CompletionInvoked _ -> assertFailure $ "expected failure, got completion" -assertRight :: Either ParserFailure b -> (b -> Assertion) -> Assertion-assertRight x f = either err f x- where- err (ParserFailure e _) = do- msg <- e "test"- assertFailure $ "unexpected parse error\n" ++ msg+assertResult :: ParserResult a -> (a -> Assertion) -> Assertion+assertResult x f = case x of+ Success r -> f r+ Failure e -> do+ let (msg, _) = execFailure e "test"+ assertFailure $ "unexpected parse error\n" ++ msg+ CompletionInvoked _ -> assertFailure $ "expected result, got completion" assertHasLine :: String -> String -> Assertion assertHasLine l s | l `elem` lines s = return () | otherwise = assertFailure $ "expected line:\n\t" ++ l ++ "\nnot found" -checkHelpText :: Show a => String -> ParserInfo a -> [String] -> Assertion-checkHelpText name p args = do- let result = run p args- assertLeft result $ \(ParserFailure err code) -> do+checkHelpTextWith :: Show a => ExitCode -> ParserPrefs -> String+ -> ParserInfo a -> [String] -> Assertion+checkHelpTextWith ecode pprefs name p args = do+ let result = execParserPure pprefs p args+ assertError result $ \(ParserFailure err) -> do expected <- readFile $ "tests/" ++ name ++ ".err.txt"- msg <- err name- expected @=? msg- ExitFailure 1 @=? code+ let (msg, code) = err name+ expected @=? msg ++ "\n"+ ecode @=? code +checkHelpText :: Show a => String -> ParserInfo a -> [String] -> Assertion+checkHelpText = checkHelpTextWith ExitSuccess (prefs idm)+ case_hello :: Assertion case_hello = checkHelpText "hello" Hello.opts ["--help"] @@ -62,42 +79,41 @@ case_args = do let result = run Commands.opts ["hello", "foo", "bar"] case result of- Left _ ->- assertFailure "unexpected parse error"- Right (Commands.Hello args) ->+ Success (Commands.Hello args) -> ["foo", "bar"] @=? args- Right Commands.Goodbye ->+ Success Commands.Goodbye -> assertFailure "unexpected result: Goodbye"+ _ ->+ assertFailure "unexpected parse error" case_args_opts :: Assertion case_args_opts = do let result = run Commands.opts ["hello", "foo", "--bar"] case result of- Left _ -> return ()- Right (Commands.Hello xs) ->+ Success (Commands.Hello xs) -> assertFailure $ "unexpected result: Hello " ++ show xs- Right Commands.Goodbye ->+ Success Commands.Goodbye -> assertFailure "unexpected result: Goodbye"+ _ -> return () case_args_ddash :: Assertion case_args_ddash = do let result = run Commands.opts ["hello", "foo", "--", "--bar", "baz"] case result of- Left _ ->- assertFailure "unexpected parse error"- Right (Commands.Hello args) ->+ Success (Commands.Hello args) -> ["foo", "--bar", "baz"] @=? args- Right Commands.Goodbye ->+ Success Commands.Goodbye -> assertFailure "unexpected result: Goodbye"+ _ -> assertFailure "unexpected parse error" case_alts :: Assertion case_alts = do let result = run Alternatives.opts ["-b", "-a", "-b", "-a", "-a", "-b"] case result of- Left _ -> assertFailure "unexpected parse error"- Right xs -> [b, a, b, a, a, b] @=? xs+ Success xs -> [b, a, b, a, a, b] @=? xs where a = Alternatives.A b = Alternatives.B+ _ -> assertFailure "unexpected parse error" case_show_default :: Assertion case_show_default = do@@ -108,12 +124,13 @@ i = info (p <**> helper) idm result = run i ["--help"] case result of- Left (ParserFailure err _) -> do- msg <- err "test"+ Failure (ParserFailure err) -> do+ let (msg, _) = err "test" assertHasLine " -n ARG set count (default: 0)" msg- Right r -> assertFailure $ "unexpected result: " ++ show r+ Success r -> assertFailure $ "unexpected result: " ++ show r+ CompletionInvoked _ -> assertFailure "unexpected completion" case_alt_cont :: Assertion case_alt_cont = do@@ -121,8 +138,8 @@ i = info p idm result = run i ["-a", "-b"] case result of- Left _ -> return ()- Right r -> assertFailure $ "unexpected result: " ++ show r+ Success r -> assertFailure $ "unexpected result: " ++ show r+ _ -> return () case_alt_help :: Assertion case_alt_help = do@@ -145,17 +162,17 @@ p2 = subparser (command "b" (info p3 idm)) p1 = subparser (command "c" (info p2 idm)) i = info (p1 <**> helper) idm- checkHelpText "nested" i ["c", "b"]+ checkHelpTextWith (ExitFailure 1) (prefs idm) "nested" i ["c", "b"] case_many_args :: Assertion case_many_args = do- let p = arguments str idm+ let p = many (argument str idm) i = info p idm nargs = 20000 result = run i (replicate nargs "foo") case result of- Left _ -> assertFailure "unexpected parse error"- Right xs -> nargs @=? length xs+ Success xs -> nargs @=? length xs+ _ -> assertFailure "unexpected parse error" case_disambiguate :: Assertion case_disambiguate = do@@ -165,8 +182,8 @@ i = info p idm result = execParserPure (prefs disambiguate) i ["--f"] case result of- Left _ -> assertFailure "unexpected parse error"- Right val -> 1 @=? val+ Success val -> 1 @=? val+ _ -> assertFailure "unexpected parse error" case_ambiguous :: Assertion case_ambiguous = do@@ -176,8 +193,8 @@ i = info p idm result = execParserPure (prefs disambiguate) i ["--ba"] case result of- Left _ -> return ()- Right val -> assertFailure $ "unexpected result " ++ show val+ Success val -> assertFailure $ "unexpected result " ++ show val+ _ -> return () case_completion :: Assertion case_completion = do@@ -187,24 +204,24 @@ i = info p idm result = run i ["--bash-completion-index", "0"] case result of- Left (ParserFailure err code) -> do- ExitSuccess @=? code+ CompletionInvoked (CompletionResult err) -> do completions <- lines <$> err "test" ["--foo", "--bar"] @=? completions- Right val ->- assertFailure $ "unexpected result " ++ show val+ Failure _ -> assertFailure "unexpected failure"+ Success val -> assertFailure $ "unexpected result " ++ show val case_bind_usage :: Assertion case_bind_usage = do- let p = arguments str (metavar "ARGS...")+ let p = many (argument str (metavar "ARGS...")) i = info (p <**> helper) briefDesc result = run i ["--help"] case result of- Left (ParserFailure err _) -> do- text <- head . lines <$> err "test"+ Failure (ParserFailure err) -> do+ let text = head . lines . fst $ err "test" "Usage: test [ARGS...]" @=? text- Right val ->+ Success val -> assertFailure $ "unexpected result " ++ show val+ CompletionInvoked _ -> assertFailure "unexpected completion" case_issue_19 :: Assertion case_issue_19 = do@@ -215,32 +232,32 @@ i = info (p <**> helper) idm result = run i ["-x", "foo"] case result of- Left _ -> assertFailure "unexpected parse error"- Right r -> Just "foo" @=? r+ Success r -> Just "foo" @=? r+ _ -> assertFailure "unexpected parse error" case_arguments1_none :: Assertion case_arguments1_none = do- let p = arguments1 str idm+ let p = some (argument str idm) i = info (p <**> helper) idm result = run i []- assertLeft result $ \(ParserFailure _ _) -> return ()+ assertError result $ \(ParserFailure _) -> return () case_arguments1_some :: Assertion case_arguments1_some = do- let p = arguments1 str idm+ let p = some (argument str idm) i = info (p <**> helper) idm result = run i ["foo", "--", "bar", "baz"] case result of- Left _ -> assertFailure "unexpected parse error"- Right r -> ["foo", "bar", "baz"] @=? r+ Success r -> ["foo", "bar", "baz"] @=? r+ _ -> assertFailure "unexpected parse error" case_arguments_switch :: Assertion case_arguments_switch = do let p = switch (short 'x')- *> arguments str idm+ *> many (argument str idm) i = info p idm result = run i ["--", "-x"]- assertRight result $ \args -> ["-x"] @=? args+ assertResult result $ \args -> ["-x"] @=? args case_issue_35 :: Assertion case_issue_35 = do@@ -248,12 +265,9 @@ <|> flag' False (short 'f') i = info p idm result = run i []- case result of- Left (ParserFailure err _) -> do- text <- head . lines <$> err "test"- "Usage: test -f" @=? text- Right val ->- assertFailure $ "unexpected result " ++ show val+ assertError result $ \(ParserFailure err) -> do+ let text = head . lines . fst . err $ "test"+ "Usage: test -f" @=? text case_backtracking :: Assertion case_backtracking = do@@ -263,7 +277,7 @@ <*> switch (short 'b') i = info (p1 <**> helper) idm result = execParserPure (prefs noBacktrack) i ["c", "-b"]- assertLeft result $ \ _ -> return ()+ assertError result $ \ _ -> return () case_error_context :: Assertion case_error_context = do@@ -271,16 +285,13 @@ <*> option (long "key") i = info p idm result = run i ["--port", "foo", "--key", "291"]- case result of- Left (ParserFailure err _) -> do- msg <- err "test"+ assertError result $ \(ParserFailure err) -> do+ let (msg, _) = err "test" let errMsg = head $ lines msg assertBool "no context in error message (option)" ("port" `isInfixOf` errMsg) assertBool "no context in error message (value)" ("foo" `isInfixOf` errMsg)- Right val ->- assertFailure $ "unexpected result " ++ show val where pk :: Int -> Int -> (Int, Int) pk = (,)@@ -298,7 +309,7 @@ <*> argument (condr odd) idm i = info p idm result = run i ["3", "6"]- assertLeft result $ \_ -> return ()+ assertError result $ \_ -> return () case_arg_order_2 :: Assertion case_arg_order_2 = do@@ -309,8 +320,8 @@ i = info p idm result = run i ["2", "-b", "3", "-a", "6"] case result of- Left _ -> assertFailure "unexpected parse error"- Right res -> (2, 6, 3) @=? res+ Success res -> (2, 6, 3) @=? res+ _ -> assertFailure "unexpected parse error" case_arg_order_3 :: Assertion case_arg_order_3 = do@@ -321,35 +332,102 @@ i = info p idm result = run i ["-n", "3", "5"] case result of- Left _ ->- assertFailure "unexpected parse error"- Right res -> (3, 5) @=? res+ Success res -> (3, 5) @=? res+ _ -> assertFailure "unexpected parse error" case_issue_47 :: Assertion case_issue_47 = do let p = nullOption (long "test" <> reader r <> value 9) :: Parser Int r _ = readerError "error message" result = run (info p idm) ["--test", "x"]- assertLeft result $ \(ParserFailure err _) -> do- text <- head . lines <$> err "test"+ assertError result $ \(ParserFailure err) -> do+ let text = head . lines . fst . err $ "test" assertBool "no error message" ("error message" `isInfixOf` text) +case_long_help :: Assertion+case_long_help = do+ let p = Formatting.opts <**> helper+ i = info p+ ( progDesc (concat+ [ "This is a very long program description. "+ , "This text should be automatically wrapped "+ , "to fit the size of the terminal" ]) )+ checkHelpTextWith ExitSuccess (prefs (columns 50)) "formatting" i ["--help"]+ case_issue_50 :: Assertion case_issue_50 = do let p = argument str (metavar "INPUT") <* switch (long "version") result = run (info p idm) ["--version", "test"]- assertRight result $ \r -> "test" @=? r+ assertResult result $ \r -> "test" @=? r case_intersperse_1 :: Assertion case_intersperse_1 = do- let p = arguments str (metavar "ARGS")+ let p = many (argument str (metavar "ARGS")) <* switch (short 'x')- result = execParserPure (prefs noIntersperse)- (info p idm)- ["a", "-x", "b"]- assertRight result $ \args -> ["a", "-x", "b"] @=? args+ result = run (info p noIntersperse)+ ["a", "-x", "b"]+ assertResult result $ \args -> ["a", "-x", "b"] @=? args++case_intersperse_2 :: Assertion+case_intersperse_2 = do+ let p = subparser+ ( command "run"+ ( info (many (argument str (metavar "OPTIONS")))+ noIntersperse )+ <> command "test"+ ( info (many (argument str (metavar "ARGS")))+ idm ) )+ i = info p idm+ result1 = run i ["run", "-x", "foo"]+ result2 = run i ["test", "-x", "bar"]+ assertResult result1 $ \args -> ["-x", "foo"] @=? args+ assertError result2 $ \_ -> return ()++case_issue_52 :: Assertion+case_issue_52 = do+ let p = subparser+ ( metavar "FOO"+ <> command "run" (info (pure "foo") idm) )+ i = info p idm+ assertError (run i []) $ \(ParserFailure err) -> do+ let text = head . lines . fst . err $ "test"+ "Usage: test FOO" @=? text++---++deriving instance Arbitrary a => Arbitrary (Chunk a)+deriving instance Eq SimpleDoc++equalDocs :: Float -> Int -> Doc -> Doc -> Bool+equalDocs f w d1 d2 = Doc.renderPretty f w d1+ == Doc.renderPretty f w d2++prop_listToChunk_1 :: [String] -> Bool+prop_listToChunk_1 xs = isEmpty (listToChunk xs) == null xs++prop_listToChunk_2 :: [String] -> Bool+prop_listToChunk_2 xs = listToChunk xs == mconcat (fmap pure xs)++prop_extractChunk_1 :: String -> Bool+prop_extractChunk_1 x = extractChunk (pure x) == x++prop_extractChunk_2 :: Chunk String -> Bool+prop_extractChunk_2 x = extractChunk (fmap pure x) == x++prop_stringChunk_1 :: Positive Float -> Positive Int -> String -> Bool+prop_stringChunk_1 (Positive f) (Positive w) s =+ equalDocs f w (extractChunk (stringChunk s))+ (Doc.string s)++prop_stringChunk_2 :: String -> Bool+prop_stringChunk_2 s = isEmpty (stringChunk s) == null s++prop_paragraph :: String -> Bool+prop_paragraph s = isEmpty (paragraph s) == null (words s)++--- main :: IO () main = $(defaultMainGenerator)