optparse-applicative 0.17.1.0 → 0.18.0.0
raw patch · 14 files changed
+165/−85 lines, 14 filesdep +prettyprinterdep +prettyprinter-ansi-terminaldep −ansi-wl-pprintdep ~base
Dependencies added: prettyprinter, prettyprinter-ansi-terminal
Dependencies removed: ansi-wl-pprint
Dependency ranges changed: base
Files
- CHANGELOG.md +22/−1
- optparse-applicative.cabal +4/−3
- src/Options/Applicative/BashCompletion.hs +13/−16
- src/Options/Applicative/Builder.hs +2/−2
- src/Options/Applicative/Builder/Internal.hs +2/−2
- src/Options/Applicative/Common.hs +11/−6
- src/Options/Applicative/Extra.hs +4/−4
- src/Options/Applicative/Help/Chunk.hs +1/−2
- src/Options/Applicative/Help/Core.hs +11/−13
- src/Options/Applicative/Help/Pretty.hs +36/−21
- src/Options/Applicative/Help/Types.hs +1/−2
- src/Options/Applicative/Internal.hs +7/−4
- src/Options/Applicative/Types.hs +2/−2
- tests/test.hs +49/−7
CHANGELOG.md view
@@ -1,5 +1,24 @@-## Version 0.17.1.0 (21 May 2023)+## Version 0.18.0.0 (22 May 2023) +- Move to 'prettyprinter` library for pretty printing.++ This is a potentially breaking change when one uses the '*Doc' family of functions+ (like `headerDoc`) from `Options.Applicative`. However, as versions of+ 'ansi-wl-pprint > 1.0' export a compatible `Doc` type, this can be mitigated by+ using a recent version.++ One can also either import directly from `Options.Applicative.Help` or from the+ `Prettyprinter` module of 'prettyprinter'.++- Allow commands to be disambiguated in a similar manner to flags when the+ `disambiguate` modifier is used.++ This is a potentially breaking change as the internal `CmdReader` constructor+ has been adapted so it is able to be inspected to a greater degree to support+ finding prefix matches.++## Version 0.17.1.0 (22 May 2023)+ - Widen bounds for `ansi-wl-pprint`. This supports the use of `prettyprinter` in a non-breaking way, as the `ansi-wl-pprint > 1.0` support the newer library.@@ -11,6 +30,8 @@ - Add `simpleVersioner` utility for adding a '--version' option to a parser. - Improve documentation.++- Drop support for GHC 7.0 and 7.2. ## Version 0.17.0.0 (1 Feb 2022)
optparse-applicative.cabal view
@@ -1,5 +1,5 @@ name: optparse-applicative-version: 0.17.1.0+version: 0.18.0.0 synopsis: Utilities and combinators for parsing command line options description: optparse-applicative is a haskell library for parsing options@@ -100,10 +100,11 @@ , Options.Applicative.Types , Options.Applicative.Internal - build-depends: base == 4.*+ build-depends: base >= 4.5 && < 5 , transformers >= 0.2 && < 0.7 , transformers-compat >= 0.3 && < 0.8- , ansi-wl-pprint >= 0.6.8 && < 1.1+ , prettyprinter >= 1.7 && < 1.8+ , prettyprinter-ansi-terminal >= 1.1 && < 1.2 if flag(process) build-depends: process >= 1.0 && < 1.7
src/Options/Applicative/BashCompletion.hs view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} -- | You don't need to import this module to enable bash completion. -- -- See@@ -114,11 +113,11 @@ -> return [] | otherwise -> run_completer (crCompleter rdr)- CmdReader _ ns p+ CmdReader _ ns | argumentIsUnreachable reachability -> return [] | otherwise- -> return . add_cmd_help p $ filter_names ns+ -> return . with_cmd_help $ filter (is_completion . fst) ns -- When doing enriched completions, add any help specified -- to the completion variables (tab separated).@@ -133,29 +132,27 @@ -- When doing enriched completions, add the command description -- to the completion variables (tab separated).- add_cmd_help :: Functor f => (String -> Maybe (ParserInfo a)) -> f String -> f String- add_cmd_help p = case richness of- Standard ->- id- Enriched _ len ->- fmap $ \cmd ->- let h = p cmd >>= unChunk . infoProgDesc- in maybe cmd (\h' -> cmd ++ "\t" ++ render_line len h') h+ with_cmd_help :: Functor f => f (String, ParserInfo a) -> f String+ with_cmd_help =+ case richness of+ Standard ->+ fmap fst+ Enriched _ len ->+ fmap $ \(cmd, cmdInfo) ->+ let h = unChunk (infoProgDesc cmdInfo)+ in maybe cmd (\h' -> cmd ++ "\t" ++ render_line len h') h show_names :: [OptName] -> [String]- show_names = filter_names . map showOption+ show_names = filter is_completion . map showOption -- We only want to show a single line in the completion results description. -- If there was a line break, it would come across as a different completion -- possibility. render_line :: Int -> Doc -> String- render_line len doc = case lines (displayS (renderPretty 1 len doc) "") of+ render_line len doc = case lines (prettyString 1 len doc) of [] -> "" [x] -> x x : _ -> x ++ "..."-- filter_names :: [String] -> [String]- filter_names = filter is_completion run_completer :: Completer -> IO [String] run_completer c = runCompleter c (fromMaybe "" (listToMaybe ws''))
src/Options/Applicative/Builder.hs view
@@ -282,8 +282,8 @@ subparser m = mkParser d g rdr where Mod _ d g = metavar "COMMAND" `mappend` m- (groupName, cmds, subs) = mkCommand m- rdr = CmdReader groupName cmds subs+ (groupName, cmds) = mkCommand m+ rdr = CmdReader groupName cmds -- | Builder for an argument parser. argument :: ReadM a -> Mod ArgumentFields a -> Parser a
src/Options/Applicative/Builder/Internal.hs view
@@ -152,8 +152,8 @@ , propShowGlobal = True } -mkCommand :: Mod CommandFields a -> (Maybe String, [String], String -> Maybe (ParserInfo a))-mkCommand m = (group, map fst cmds, (`lookup` cmds))+mkCommand :: Mod CommandFields a -> (Maybe String, [(String, ParserInfo a)])+mkCommand m = (group, cmds) where Mod f _ _ = m CommandFields cmds group = f (CommandFields [] Nothing)
src/Options/Applicative/Common.hs view
@@ -166,23 +166,28 @@ searchParser $ \opt -> do when (isArg (optMain opt)) cut case optMain opt of- CmdReader _ _ f ->- case (f arg, prefBacktrack prefs) of- (Just subp, NoBacktrack) -> lift $ do+ CmdReader _ cs -> do+ subp <- hoistList (cmdMatches cs)+ case prefBacktrack prefs of+ NoBacktrack -> lift $ do args <- get <* put [] fmap pure . lift $ enterContext arg subp *> runParserInfo subp args <* exitContext - (Just subp, Backtrack) -> fmap pure . lift . StateT $ \args ->+ Backtrack -> fmap pure . lift . StateT $ \args -> enterContext arg subp *> runParser (infoPolicy subp) CmdStart (infoParser subp) args <* exitContext - (Just subp, SubparserInline) -> lift $ do+ SubparserInline -> lift $ do lift $ enterContext arg subp return $ infoParser subp - (Nothing, _) -> mzero ArgReader rdr -> fmap pure . lift . lift $ runReadM (crReader rdr) arg _ -> mzero++ where+ cmdMatches cs+ | prefDisambiguate prefs = snd <$> filter (isPrefixOf arg . fst) cs+ | otherwise = maybeToList (lookup arg cs) stepParser :: MonadP m => ParserPrefs -> ArgPolicy -> String -> Parser a -> NondetT (StateT Args m) (Parser a)
src/Options/Applicative/Extra.hs view
@@ -89,8 +89,8 @@ hsubparser m = mkParser d g rdr where Mod _ d g = metavar "COMMAND" `mappend` m- (groupName, cmds, subs) = mkCommand m- rdr = CmdReader groupName cmds (fmap add_helper . subs)+ (groupName, cmds) = mkCommand m+ rdr = CmdReader groupName ((fmap . fmap) add_helper cmds) add_helper pinfo = pinfo { infoParser = infoParser pinfo <**> helper } @@ -317,10 +317,10 @@ OptReader ns _ _ -> fmap showOption ns FlagReader ns _ -> fmap showOption ns ArgReader _ -> []- CmdReader _ ns _ | argumentIsUnreachable reachability+ CmdReader _ ns | argumentIsUnreachable reachability -> [] | otherwise- -> ns+ -> fst <$> ns _ -> mempty
src/Options/Applicative/Help/Chunk.hs view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} module Options.Applicative.Help.Chunk ( Chunk(..) , chunked@@ -116,7 +115,7 @@ -- > extractChunk . stringChunk = string stringChunk :: String -> Chunk Doc stringChunk "" = mempty-stringChunk s = pure (string s)+stringChunk s = pure (pretty 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
src/Options/Applicative/Help/Core.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} module Options.Applicative.Help.Core ( cmdDesc, briefDesc,@@ -25,7 +24,7 @@ import Data.Function (on) import Data.List (sort, intersperse, groupBy) import Data.Foldable (any, foldl')-import Data.Maybe (catMaybes, fromMaybe, maybeToList)+import Data.Maybe (catMaybes, fromMaybe) #if !MIN_VERSION_base(4,8,0) import Data.Monoid (mempty) #endif@@ -58,7 +57,7 @@ meta = stringChunk $ optMetaVar opt descs =- map (string . showOption) names+ map (pretty . showOption) names descriptions = listToChunk (intersperse (descSep style) descs) desc@@ -95,12 +94,11 @@ where desc _ opt = case optMain opt of- CmdReader gn cmds p ->+ CmdReader gn cmds -> (,) gn $ tabulate (prefTabulateFill pprefs)- [ (string cmd, align (extractChunk d))- | cmd <- reverse cmds,- d <- maybeToList . fmap infoProgDesc $ p cmd+ [ (pretty nm, align (extractChunk (infoProgDesc cmd)))+ | (nm, cmd) <- reverse cmds ] _ -> mempty @@ -128,7 +126,7 @@ | otherwise = filterOptional style = OptDescStyle- { descSep = string "|",+ { descSep = pretty '|', descHidden = False, descGlobal = False }@@ -205,9 +203,9 @@ n = fst $ optDesc pprefs style info opt h = optHelp opt hdef = Chunk . fmap show_def . optShowDefault $ opt- show_def s = parens (string "default:" <+> string s)+ show_def s = parens (pretty "default:" <+> pretty s) style = OptDescStyle- { descSep = string ",",+ { descSep = pretty ',', descHidden = True, descGlobal = global }@@ -252,7 +250,7 @@ group_title _ = mempty with_title :: String -> Chunk Doc -> Chunk Doc- with_title title = fmap (string title .$.)+ with_title title = fmap (pretty title .$.) parserGlobals :: ParserPrefs -> Parser a -> ParserHelp@@ -268,8 +266,8 @@ parserUsage pprefs p progn = group $ hsep- [ string "Usage:",- string progn,+ [ pretty "Usage:",+ pretty progn, hangAtIfOver 9 35 (extractChunk (briefDesc pprefs p)) ]
src/Options/Applicative/Help/Pretty.hs view
@@ -1,40 +1,41 @@ {-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} module Options.Applicative.Help.Pretty- ( module Text.PrettyPrint.ANSI.Leijen+ ( module Prettyprinter+ , module Prettyprinter.Render.Terminal , Doc- , indent- , renderPretty- , displayS+ , SimpleDoc+ , (.$.)+ , (</>)+ , groupOrNestLine , altSep , hangAtIfOver++ , prettyString ) where #if !MIN_VERSION_base(4,11,0)-import Data.Semigroup ((<>))+import Data.Semigroup ((<>), mempty) #endif -import Text.PrettyPrint.ANSI.Leijen hiding (Doc, (<$>), (<>), columns, indent, renderPretty, displayS)-import qualified Text.PrettyPrint.ANSI.Leijen as PP+import Prettyprinter hiding (Doc)+import qualified Prettyprinter as PP+import qualified Prettyprinter.Render.String as PP+import Prettyprinter.Render.Terminal import Prelude -type Doc = PP.Doc--indent :: Int -> PP.Doc -> PP.Doc-indent = PP.indent--renderPretty :: Float -> Int -> PP.Doc -> SimpleDoc-renderPretty = PP.renderPretty+type Doc = PP.Doc Prettyprinter.Render.Terminal.AnsiStyle+type SimpleDoc = SimpleDocStream AnsiStyle -displayS :: SimpleDoc -> ShowS-displayS = PP.displayS+linebreak :: Doc+linebreak = flatAlt line mempty (.$.) :: Doc -> Doc -> Doc-(.$.) = (PP.<$>)-+x .$. y = x <> line <> y+(</>) :: Doc -> Doc -> Doc+x </> y = x <> softline <> y -- | Apply the function if we're not at the -- start of our nesting level.@@ -58,7 +59,6 @@ then f doc else g doc - -- | Render flattened text on this line, or start -- a new line before rendering any text. --@@ -81,7 +81,7 @@ -- next line. altSep :: Doc -> Doc -> Doc altSep x y =- group (x <+> char '|' <> line) <//> y+ group (x <+> pretty '|' <> line) <> group linebreak <> y -- | Printer hacks to get nice indentation for long commands@@ -102,3 +102,18 @@ align d else linebreak <> ifAtRoot (indent i) d+++renderPretty :: Double -> Int -> Doc -> SimpleDocStream AnsiStyle+renderPretty ribbonFraction lineWidth+ = layoutSmart LayoutOptions+ { layoutPageWidth = AvailablePerLine lineWidth ribbonFraction }++prettyString :: Double -> Int -> Doc -> String+prettyString ribbonFraction lineWidth+ = streamToString+ . renderPretty ribbonFraction lineWidth++streamToString :: SimpleDocStream AnsiStyle -> String+streamToString stream =+ PP.renderShowS stream ""
src/Options/Applicative/Help/Types.hs view
@@ -42,6 +42,5 @@ -- | Convert a help text to 'String'. renderHelp :: Int -> ParserHelp -> String renderHelp cols- = (`displayS` "")- . renderPretty 1.0 cols+ = prettyString 1.0 cols . helpText
src/Options/Applicative/Internal.hs view
@@ -18,6 +18,7 @@ , ListT , takeListT , runListT+ , hoistList , NondetT , cut@@ -172,9 +173,6 @@ bimapTStep _ _ TNil = TNil bimapTStep f g (TCons a x) = TCons (f a) (g x) -hoistList :: Monad m => [a] -> ListT m a-hoistList = foldr (\x xt -> ListT (return (TCons x xt))) mzero- takeListT :: Monad m => Int -> ListT m a -> ListT m a takeListT 0 = const mzero takeListT n = ListT . liftM (bimapTStep id (takeListT (n - 1))) . stepListT@@ -192,7 +190,7 @@ . stepListT instance Monad m => Applicative (ListT m) where- pure = hoistList . pure+ pure a = ListT (return (TCons a mzero)) (<*>) = ap instance Monad m => Monad (ListT m) where@@ -263,3 +261,8 @@ return $ case xs' of [x] -> Just x _ -> Nothing++hoistList :: Alternative m => [a] -> m a+hoistList = foldr cons empty+ where+ cons x xs = pure x <|> xs
src/Options/Applicative/Types.hs view
@@ -242,14 +242,14 @@ -- ^ flag reader | ArgReader (CReader a) -- ^ argument reader- | CmdReader (Maybe String) [String] (String -> Maybe (ParserInfo a))+ | CmdReader (Maybe String) [(String, ParserInfo a)] -- ^ command reader instance Functor OptReader where fmap f (OptReader ns cr e) = OptReader ns (fmap f cr) e fmap f (FlagReader ns x) = FlagReader ns (f x) fmap f (ArgReader cr) = ArgReader (fmap f cr)- fmap f (CmdReader n cs g) = CmdReader n cs ((fmap . fmap) f . g)+ fmap f (CmdReader n cs) = CmdReader n ((fmap . fmap . fmap) f cs) -- | A @Parser a@ is an option parser returning a value of type 'a'. data Parser a
tests/test.hs view
@@ -28,7 +28,7 @@ import qualified Options.Applicative.Help as H-import Options.Applicative.Help.Pretty (Doc, SimpleDoc(..))+import Options.Applicative.Help.Pretty (Doc) import qualified Options.Applicative.Help.Pretty as Doc import Options.Applicative.Help.Chunk import Options.Applicative.Help.Levenshtein@@ -318,6 +318,49 @@ result = execParserPure (prefs disambiguate) i ["--ba"] in assertError result (\_ -> property succeeded) ++prop_disambiguate_in_same_subparsers :: Property+prop_disambiguate_in_same_subparsers = once $+ let p0 = subparser (command "oranges" (info (pure "oranges") idm) <> command "apples" (info (pure "apples") idm) <> metavar "B")+ i = info (p0 <**> helper) idm+ result = execParserPure (prefs disambiguate) i ["orang"]+ in assertResult result ((===) "oranges")++prop_disambiguate_commands_in_separate_subparsers :: Property+prop_disambiguate_commands_in_separate_subparsers = once $+ let p2 = subparser (command "oranges" (info (pure "oranges") idm) <> metavar "B")+ p1 = subparser (command "apples" (info (pure "apples") idm) <> metavar "C")+ p0 = p1 <|> p2+ i = info (p0 <**> helper) idm+ result = execParserPure (prefs disambiguate) i ["orang"]+ in assertResult result ((===) "oranges")++prop_fail_ambiguous_commands_in_same_subparser :: Property+prop_fail_ambiguous_commands_in_same_subparser = once $+ let p0 = subparser (command "oranges" (info (pure ()) idm) <> command "orangutans" (info (pure ()) idm) <> metavar "B")+ i = info (p0 <**> helper) idm+ result = execParserPure (prefs disambiguate) i ["orang"]+ in assertError result (\_ -> property succeeded)++prop_fail_ambiguous_commands_in_separate_subparser :: Property+prop_fail_ambiguous_commands_in_separate_subparser = once $+ let p2 = subparser (command "oranges" (info (pure ()) idm) <> metavar "B")+ p1 = subparser (command "orangutans" (info (pure ()) idm) <> metavar "C")+ p0 = p1 <|> p2+ i = info (p0 <**> helper) idm+ result = execParserPure (prefs disambiguate) i ["orang"]+ in assertError result (\_ -> property succeeded)++prop_without_disambiguation_same_named_commands_should_parse_in_order :: Property+prop_without_disambiguation_same_named_commands_should_parse_in_order = once $+ let p3 = subparser (command "b" (info (pure ()) idm) <> metavar "B")+ p2 = subparser (command "a" (info (pure ()) idm) <> metavar "B")+ p1 = subparser (command "a" (info (pure ()) idm) <> metavar "C")+ p0 = (,,) <$> p1 <*> p2 <*> p3+ i = info (p0 <**> helper) idm+ result = execParserPure defaultPrefs i ["b", "a", "a"]+ in assertResult result ((===) ((), (), ()))+ prop_completion :: Property prop_completion = once . ioProperty $ let p = (,)@@ -903,15 +946,14 @@ , "to fit the size of the terminal" ]) ) in checkHelpTextWith ExitSuccess (prefs (columns 50)) "formatting-long-subcommand" i ["hello-very-long-sub", "--help"] - --- deriving instance Arbitrary a => Arbitrary (Chunk a) -equalDocs :: Float -> Int -> Doc -> Doc -> Property-equalDocs f w d1 d2 = Doc.displayS (Doc.renderPretty f w d1) ""- === Doc.displayS (Doc.renderPretty f w d2) ""+equalDocs :: Double -> Int -> Doc -> Doc -> Property+equalDocs f w d1 d2 = Doc.prettyString f w d1+ === Doc.prettyString f w d2 prop_listToChunk_1 :: [String] -> Property prop_listToChunk_1 xs = isEmpty (listToChunk xs) === null xs@@ -925,10 +967,10 @@ prop_extractChunk_2 :: Chunk String -> Property prop_extractChunk_2 x = extractChunk (fmap pure x) === x -prop_stringChunk_1 :: Positive Float -> Positive Int -> String -> Property+prop_stringChunk_1 :: Positive Double -> Positive Int -> String -> Property prop_stringChunk_1 (Positive f) (Positive w) s = equalDocs f w (extractChunk (stringChunk s))- (Doc.string s)+ (Doc.pretty s) prop_stringChunk_2 :: String -> Property prop_stringChunk_2 s = isEmpty (stringChunk s) === null s