optparse-applicative 0.16.1.0 → 0.17.0.0
raw patch · 17 files changed
+222/−65 lines, 17 files
Files
- CHANGELOG.md +13/−0
- README.md +23/−18
- optparse-applicative.cabal +9/−5
- src/Options/Applicative/Builder.hs +17/−6
- src/Options/Applicative/Extra.hs +26/−8
- src/Options/Applicative/Help/Chunk.hs +4/−7
- src/Options/Applicative/Help/Core.hs +20/−10
- src/Options/Applicative/Help/Pretty.hs +39/−3
- src/Options/Applicative/Help/Types.hs +10/−6
- src/Options/Applicative/NonEmpty.hs +0/−1
- src/Options/Applicative/Types.hs +2/−1
- tests/Examples/LongSub.hs +34/−0
- tests/cabal.err.txt +1/−0
- tests/formatting-long-subcommand.err.txt +9/−0
- tests/formatting.err.txt +1/−0
- tests/hello.err.txt +1/−0
- tests/test.hs +13/−0
CHANGELOG.md view
@@ -1,3 +1,16 @@+## Version 0.17.0.0 (1 Feb 2022)++- Make tabulation width configurable in usage texts.++- Separate program name and description in ParserHelp type.++- Add `helperWith` function, which can be easily used to+ localize the help flag.++- Improve usage texts when command names are long.++- Improve Documentation.+ ## Version 0.16.1.0 (21 Nov 2020) - Guard `process` dependency behind an on by default flag.
README.md view
@@ -527,22 +527,6 @@ global options that apply to all of them. Typical examples are version control systems like `git`, or build tools like `cabal`. -A command can be created using the `subparser` builder (or `hsubparser`,-which is identical but for an additional `--help` option on each-command), and commands can be added with the `command` modifier.-For example--```haskell-subparser- ( command "add" (info addOptions ( progDesc "Add a file to the repository" ))- <> command "commit" (info commitOptions ( progDesc "Record changes to the repository" ))- )-```--Each command takes a full `ParserInfo` structure, which will be-used to extract a description for this command when generating a-help text.- Note that all the parsers appearing in a command need to have the same type. For this reason, it is often best to use a sum type which has the same structure as the command itself. For example,@@ -559,6 +543,22 @@ ... ``` +A command can then be created using the `subparser` builder (or+`hsubparser`, which is identical but for an additional `--help` option+on each command), and commands can be added with the `command`+modifier. For example,++```haskell+subparser+ ( command "add" (info addCommand ( progDesc "Add a file to the repository" ))+ <> command "commit" (info commitCommand ( progDesc "Record changes to the repository" ))+ )+```++Each command takes a full `ParserInfo` structure, which will be+used to extract a description for this command when generating a+help text.+ Alternatively, you can directly return an `IO` action from a parser, and execute it using `join` from `Control.Monad`. @@ -702,6 +702,11 @@ ``` +**Note**. If an option name is a prefix of another option, then it+will never be matched when disambiguation is on. See+[#419](https://github.com/pcapriotti/optparse-applicative/issues/419)+for more details.+ ### Customising the help screen optparse-applicative has a number of combinators to help customise@@ -1020,6 +1025,6 @@ [monoid]: http://hackage.haskell.org/package/base/docs/Data-Monoid.html [semigroup]: http://hackage.haskell.org/package/base/docs/Data-Semigroup.html [parsec]: http://hackage.haskell.org/package/parsec- [status]: http://travis-ci.org/pcapriotti/optparse-applicative?branch=master- [status-png]: https://api.travis-ci.org/pcapriotti/optparse-applicative.svg?branch=master+ [status]: https://github.com/pcapriotti/optparse-applicative/actions/workflows/haskell-ci.yml+ [status-png]: https://github.com/pcapriotti/optparse-applicative/workflows/Haskell-CI/badge.svg [ansi-wl-pprint]: http://hackage.haskell.org/package/ansi-wl-pprint
optparse-applicative.cabal view
@@ -1,5 +1,5 @@ name: optparse-applicative-version: 0.16.1.0+version: 0.17.0.0 synopsis: Utilities and combinators for parsing command line options description: optparse-applicative is a haskell library for parsing options@@ -36,6 +36,7 @@ tests/helponemptysub.err.txt tests/long_equals.err.txt tests/formatting.err.txt+ tests/formatting-long-subcommand.err.txt tests/nested.err.txt tests/optional.err.txt tests/nested_optional.err.txt@@ -54,7 +55,9 @@ GHC==8.2.2, GHC==8.4.4, GHC==8.6.5,- GHC==8.8.1+ GHC==8.8.4,+ GHC==8.10.4,+ GHC==9.0.1 source-repository head type: git@@ -95,15 +98,15 @@ , Options.Applicative.Internal build-depends: base == 4.*- , transformers >= 0.2 && < 0.6- , transformers-compat >= 0.3 && < 0.7+ , transformers >= 0.2 && < 0.7+ , transformers-compat >= 0.3 && < 0.8 , ansi-wl-pprint >= 0.6.8 && < 0.7 if flag(process) build-depends: process >= 1.0 && < 1.7 if !impl(ghc >= 8)- build-depends: semigroups >= 0.10 && < 0.20+ build-depends: semigroups >= 0.10 && < 0.21 , fail == 4.9.* test-suite tests@@ -122,6 +125,7 @@ , Examples.Commands , Examples.Formatting , Examples.Hello+ , Examples.LongSub build-depends: base , optparse-applicative
src/Options/Applicative/Builder.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-}- module Options.Applicative.Builder ( -- * Parser builders --@@ -89,6 +88,7 @@ columns, helpLongEquals, helpShowGlobals,+ helpIndent, prefs, defaultPrefs, @@ -219,7 +219,7 @@ -- -- /NOTE/: This builder is more flexible than its name and example -- allude. One of the motivating examples for its addition was to--- used `const` to completely replace the usage text of an option.+-- use `const` to completely replace the usage text of an option. style :: ( Doc -> Doc ) -> Mod f a style x = optionMod $ \p -> p { propDescMod = Just x }@@ -273,6 +273,11 @@ -- | Builder for a command parser. The 'command' modifier can be used to -- specify individual commands.+--+-- By default, sub-parsers allow backtracking to their parent's options when+-- they are completed. To allow full mixing of parent and sub-parser options,+-- turn on 'subparserInline'; otherwise, to disable backtracking completely,+-- use 'noBacktrack'. subparser :: Mod CommandFields a -> Parser a subparser m = mkParser d g rdr where@@ -385,7 +390,7 @@ instance Semigroup (InfoMod a) where m1 <> m2 = InfoMod $ applyInfoMod m2 . applyInfoMod m1 --- | Show a full description in the help text of this parser.+-- | Show a full description in the help text of this parser (default). fullDesc :: InfoMod a fullDesc = InfoMod $ \i -> i { infoFullDesc = True } @@ -517,11 +522,16 @@ helpLongEquals :: PrefsMod helpLongEquals = PrefsMod $ \p -> p { prefHelpLongEquals = True } --- | Show global help information in subparser usage+-- | Show global help information in subparser usage. helpShowGlobals :: PrefsMod-helpShowGlobals = PrefsMod $ \p -> p { prefHelpShowGlobal = True}+helpShowGlobals = PrefsMod $ \p -> p { prefHelpShowGlobal = True } +-- | Set fill width in help text presentation.+helpIndent :: Int -> PrefsMod+helpIndent w = PrefsMod $ \p -> p { prefTabulateFill = w } ++ -- | Create a `ParserPrefs` given a modifier prefs :: PrefsMod -> ParserPrefs prefs m = applyPrefsMod m base@@ -534,7 +544,8 @@ , prefBacktrack = Backtrack , prefColumns = 80 , prefHelpLongEquals = False- , prefHelpShowGlobal = False }+ , prefHelpShowGlobal = False+ , prefTabulateFill = 24 } -- Convenience shortcuts
src/Options/Applicative/Extra.hs view
@@ -4,6 +4,7 @@ -- -- | This module contains high-level functions to run parsers. helper,+ helperWith, hsubparser, execParser, customExecParser,@@ -47,16 +48,32 @@ helper :: Parser (a -> a) helper =+ helperWith (mconcat [+ long "help",+ short 'h',+ help "Show this help text"+ ])++-- | Like helper, but with a minimal set of modifiers that can be extended+-- as desired.+--+-- > opts :: ParserInfo Sample+-- > opts = info (sample <**> helperWith (mconcat [+-- > long "help",+-- > short 'h',+-- > help "Show this help text",+-- > hidden+-- > ])) mempty+helperWith :: Mod OptionFields (a -> a) -> Parser (a -> a)+helperWith modifiers = option helpReader $ mconcat- [ long "help",- short 'h',- help "Show this help text",- value id,+ [ value id, metavar "", noGlobal, noArgError (ShowHelpText Nothing),- hidden+ hidden,+ modifiers ] where helpReader = do@@ -197,9 +214,10 @@ InfoMsg _ -> mempty _- -> usageHelp $ vcatChunks- [ pure . parserUsage pprefs (infoParser i) . unwords $ progn : names- , fmap (indent 2) . infoProgDesc $ i ]+ -> mconcat [+ usageHelp (pure . parserUsage pprefs (infoParser i) . unwords $ progn : names)+ , descriptionHelp (infoProgDesc i)+ ] error_help = errorHelp $ case msg of ShowHelpText {}
src/Options/Applicative/Help/Chunk.hs view
@@ -128,12 +128,9 @@ paragraph = foldr (chunked (</>) . stringChunk) mempty . words -tabulate' :: Int -> [(Doc, Doc)] -> Chunk Doc-tabulate' _ [] = mempty-tabulate' size table = pure $ vcat+-- | Display pairs of strings in a table.+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
src/Options/Applicative/Help/Core.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} module Options.Applicative.Help.Core ( cmdDesc, briefDesc,@@ -9,6 +10,7 @@ headerHelp, suggestionsHelp, usageHelp,+ descriptionHelp, bodyHelp, footerHelp, globalsHelp,@@ -23,8 +25,12 @@ import Data.List (sort, intersperse, groupBy) import Data.Foldable (any, foldl') import Data.Maybe (maybeToList, catMaybes, fromMaybe)+#if !MIN_VERSION_base(4,8,0) import Data.Monoid (mempty)+#endif+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup (..))+#endif import Prelude hiding (any) import Options.Applicative.Common@@ -83,14 +89,14 @@ in (modified, wrapping) -- | Generate descriptions for commands.-cmdDesc :: Parser a -> [(Maybe String, Chunk Doc)]-cmdDesc = mapParser desc+cmdDesc :: ParserPrefs -> Parser a -> [(Maybe String, Chunk Doc)]+cmdDesc pprefs = mapParser desc where desc _ opt = case optMain opt of CmdReader gn cmds p -> (,) gn $- tabulate+ tabulate (prefTabulateFill pprefs) [ (string cmd, align (extractChunk d)) | cmd <- reverse cmds, d <- maybeToList . fmap infoProgDesc $ p cmd@@ -188,7 +194,7 @@ -- | Common generator for full descriptions and globals optionsDesc :: Bool -> ParserPrefs -> Parser a -> Chunk Doc-optionsDesc global pprefs = tabulate . catMaybes . mapParser doc+optionsDesc global pprefs = tabulate (prefTabulateFill pprefs) . catMaybes . mapParser doc where doc info opt = do guard . not . isEmpty $ n@@ -220,6 +226,9 @@ usageHelp :: Chunk Doc -> ParserHelp usageHelp chunk = mempty { helpUsage = chunk } +descriptionHelp :: Chunk Doc -> ParserHelp+descriptionHelp chunk = mempty { helpDescription = chunk }+ bodyHelp :: Chunk Doc -> ParserHelp bodyHelp chunk = mempty { helpBody = chunk } @@ -234,7 +243,7 @@ : (group_title <$> cs) where def = "Available commands:"- cs = groupBy ((==) `on` fst) $ cmdDesc p+ cs = groupBy ((==) `on` fst) $ cmdDesc pprefs p group_title a@((n, _) : _) = with_title (fromMaybe def n) $@@ -256,11 +265,12 @@ -- | Generate option summary. parserUsage :: ParserPrefs -> Parser a -> String -> Doc parserUsage pprefs p progn =- hsep- [ string "Usage:",- string progn,- align (extractChunk (briefDesc pprefs p))- ]+ group $+ hsep+ [ string "Usage:",+ string progn,+ hangAtIfOver 9 35 (extractChunk (briefDesc pprefs p))+ ] -- | Peek at the structure of the rendered tree within. --
src/Options/Applicative/Help/Pretty.hs view
@@ -1,12 +1,16 @@+{-# LANGUAGE CPP #-} module Options.Applicative.Help.Pretty ( module Text.PrettyPrint.ANSI.Leijen , (.$.) , groupOrNestLine , altSep+ , hangAtIfOver ) where import Control.Applicative+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>), columns) import Text.PrettyPrint.ANSI.Leijen.Internal (Doc (..), flatten)@@ -21,12 +25,24 @@ -- | Apply the function if we're not at the -- start of our nesting level. ifNotAtRoot :: (Doc -> Doc) -> Doc -> Doc-ifNotAtRoot f doc =+ifNotAtRoot =+ ifElseAtRoot id++-- | Apply the function if we're not at the+-- start of our nesting level.+ifAtRoot :: (Doc -> Doc) -> Doc -> Doc+ifAtRoot =+ flip ifElseAtRoot id++-- | Apply the function if we're not at the+-- start of our nesting level.+ifElseAtRoot :: (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc+ifElseAtRoot f g doc = Nesting $ \i -> Column $ \j -> if i == j- then doc- else f doc+ then f doc+ else g doc -- | Render flattened text on this line, or start@@ -54,3 +70,23 @@ altSep :: Doc -> Doc -> Doc altSep x y = group (x <+> char '|' <> line) <//> y+++-- | Printer hacks to get nice indentation for long commands+-- and subcommands.+--+-- If we're starting this section over the desired width+-- (usually 1/3 of the ribbon), then we will make a line+-- break, indent all of the usage, and go.+--+-- The ifAtRoot is an interesting clause. If this whole+-- operation is put under a `group` then the linebreak+-- will disappear; then item d will therefore not be at+-- the starting column, and it won't be indented more.+hangAtIfOver :: Int -> Int -> Doc -> Doc+hangAtIfOver i j d =+ Column $ \k ->+ if k <= j then+ align d+ else+ linebreak <> ifAtRoot (indent i) d
src/Options/Applicative/Help/Types.hs view
@@ -14,26 +14,30 @@ , helpSuggestions :: Chunk Doc , helpHeader :: Chunk Doc , helpUsage :: Chunk Doc+ , helpDescription :: Chunk Doc , helpBody :: Chunk Doc , helpGlobals :: Chunk Doc- , helpFooter :: Chunk Doc }+ , helpFooter :: Chunk Doc+ } instance Show ParserHelp where showsPrec _ h = showString (renderHelp 80 h) instance Monoid ParserHelp where- mempty = ParserHelp mempty mempty mempty mempty mempty mempty mempty+ mempty = ParserHelp mempty mempty mempty mempty mempty mempty mempty mempty mappend = (<>) instance Semigroup ParserHelp where- (ParserHelp e1 s1 h1 u1 b1 g1 f1) <> (ParserHelp e2 s2 h2 u2 b2 g2 f2)+ (ParserHelp e1 s1 h1 u1 d1 b1 g1 f1) <> (ParserHelp e2 s2 h2 u2 d2 b2 g2 f2) = ParserHelp (mappend e1 e2) (mappend s1 s2) (mappend h1 h2) (mappend u1 u2)- (mappend b1 b2) (mappend g1 g2)- (mappend f1 f2)+ (mappend d1 d2) (mappend b1 b2)+ (mappend g1 g2) (mappend f1 f2) helpText :: ParserHelp -> Doc-helpText (ParserHelp e s h u b g f) = extractChunk . vsepChunks $ [e, s, h, u, b, g, f]+helpText (ParserHelp e s h u d b g f) =+ extractChunk $+ vsepChunks [e, s, h, u, fmap (indent 2) d, b, g, f] -- | Convert a help text to 'String'. renderHelp :: Int -> ParserHelp -> String
src/Options/Applicative/NonEmpty.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP, Rank2Types, ExistentialQuantification #-} module Options.Applicative.NonEmpty ( some1 ) where
src/Options/Applicative/Types.hs view
@@ -125,7 +125,8 @@ -- single space (default: False) , prefHelpShowGlobal :: Bool -- ^ when displaying subparsers' usage help, -- show parent options under a "global options"- -- section (default: True)+ -- section (default: False)+ , prefTabulateFill ::Int -- ^ Indentation width for tables } deriving (Eq, Show) data OptName = OptShort !Char
+ tests/Examples/LongSub.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE CPP #-}+module Examples.LongSub where++import Data.Monoid+import Options.Applicative++#if __GLASGOW_HASKELL__ <= 702+(<>) :: Monoid a => a -> a -> a+(<>) = mappend+#endif++data Sample+ = Hello [String]+ | Goodbye+ deriving (Eq, Show)++hello :: Parser Sample+hello =+ Hello+ <$> many (argument str (metavar "TARGET..."))+ <* switch (long "first-flag")+ <* switch (long "second-flag")+ <* switch (long "third-flag")+ <* switch (long "fourth-flag")++sample :: Parser Sample+sample = hsubparser+ ( command "hello-very-long-sub"+ (info hello+ (progDesc "Print greeting"))+ )++opts :: ParserInfo Sample+opts = info (sample <**> helper) idm
tests/cabal.err.txt view
@@ -1,4 +1,5 @@ Usage: cabal configure [--enable-tests] [-f|--flags FLAGS]+ Prepare to build the package Available options:
+ tests/formatting-long-subcommand.err.txt view
@@ -0,0 +1,9 @@+Usage: formatting-long-subcommand hello-very-long-sub + [TARGET...] [--first-flag] + [--second-flag] [--third-flag] + [--fourth-flag]++ Print greeting++Available options:+ -h,--help Show this help text
tests/formatting.err.txt view
@@ -1,4 +1,5 @@ Usage: formatting [-t|--test FOO_BAR_BAZ_LONG_METAVARIABLE]+ This is a very long program description. This text should be automatically wrapped to fit the size of the terminal
tests/hello.err.txt view
@@ -1,6 +1,7 @@ hello - a test for optparse-applicative Usage: hello --hello TARGET [-q|--quiet] [--repeat INT]+ Print a greeting for TARGET Available options:
tests/test.hs view
@@ -9,6 +9,7 @@ import qualified Examples.Cabal as Cabal import qualified Examples.Alternatives as Alternatives import qualified Examples.Formatting as Formatting+import qualified Examples.LongSub as LongSub import Control.Applicative import Control.Monad@@ -890,6 +891,18 @@ pre = run i ["--help"] post = run i ["--help", "not-a-command"] in grabHelpMessage pre === grabHelpMessage post+++prop_long_command_line_flow :: Property+prop_long_command_line_flow = once $+ let p = LongSub.sample <**> 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" ]) )+ in checkHelpTextWith ExitSuccess (prefs (columns 50)) "formatting-long-subcommand" i ["hello-very-long-sub", "--help"]+ ---