Skip to content

Commit

Permalink
Merge pull request #486 from tbidne/parser-groups
Browse files Browse the repository at this point in the history
Add Option groups
  • Loading branch information
HuwCampbell authored Sep 3, 2024
2 parents 9da7065 + db1f771 commit 2ea40a9
Show file tree
Hide file tree
Showing 22 changed files with 1,289 additions and 33 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.19.0.0

- Add `parserOptionGroup` for grouping Options together, similar to command
groups. Requires the breaking change of adding the `propGroup :: OptGroup`
field to `OptProperties`.

## Version 0.18.1.0 (29 May 2023)

- Change pretty printer layout algorithm used.
Expand Down
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,52 @@ main = customExecParser p opts
p = prefs showHelpOnEmpty
```

#### Option groups

The `parserOptionGroup` function can be used to group options together under
a common heading. For example, if we have:

```haskell
Args
<$> parseMain
<*> parserOptionGroup "Group A" parseA
<*> parserOptionGroup "Group B" parseB
<*> parseOther
```

Then the `--help` page `Available options` will look like:

```
Available options:
<main options>
<other options>
Group A
<A options>
Group B
<B options>
```

Caveats:

- Parser groups are like command groups in that groups are listed in creation
order, and duplicate groups are consolidated.

- Nested groups are indented:

```haskell
parserOptionGroup "Group Outer" (parserOptionGroup "Group Inner" parseA)
```

Will render as:

```
Group Outer
- Group Inner
...
```

### Command groups

One experimental feature which may be useful for programs with many
Expand Down
12 changes: 12 additions & 0 deletions optparse-applicative.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ extra-source-files: CHANGELOG.md
tests/formatting-long-subcommand.err.txt
tests/nested.err.txt
tests/optional.err.txt
tests/parser_group_all_grouped.err.txt
tests/parser_group_basic.err.txt
tests/parser_group_command_groups.err.txt
tests/parser_group_duplicate_command_groups.err.txt
tests/parser_group_duplicates.err.txt
tests/parser_group_nested.err.txt
tests/nested_optional.err.txt
tests/subparsers.err.txt

Expand Down Expand Up @@ -131,6 +137,12 @@ test-suite tests
, Examples.Formatting
, Examples.Hello
, Examples.LongSub
, Examples.ParserGroup.AllGrouped
, Examples.ParserGroup.Basic
, Examples.ParserGroup.CommandGroups
, Examples.ParserGroup.DuplicateCommandGroups
, Examples.ParserGroup.Duplicates
, Examples.ParserGroup.Nested

build-depends: base
, optparse-applicative
Expand Down
1 change: 1 addition & 0 deletions src/Options/Applicative.hs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ module Options.Applicative (
completer,
idm,
mappend,
parserOptionGroup,

OptionFields,
FlagFields,
Expand Down
56 changes: 54 additions & 2 deletions src/Options/Applicative/Builder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ module Options.Applicative.Builder (
completer,
idm,
mappend,
parserOptionGroup,

-- * Readers
--
Expand Down Expand Up @@ -107,8 +108,8 @@ module Options.Applicative.Builder (
) where

import Control.Applicative
#if __GLASGOW_HASKELL__ <= 802
import Data.Semigroup hiding (option)
#if __GLASGOW_HASKELL__ < 804
import Data.Semigroup hiding (Option, option)
#endif
import Data.String (fromString, IsString)

Expand All @@ -118,6 +119,7 @@ import Options.Applicative.Common
import Options.Applicative.Types
import Options.Applicative.Help.Pretty
import Options.Applicative.Help.Chunk
import Options.Applicative.Internal (mapParserOptions)

-- Readers --

Expand Down Expand Up @@ -379,6 +381,56 @@ option r m = mkParser d g rdr
crdr = CReader (optCompleter fields) r
rdr = OptReader (optNames fields) crdr (optNoArgError fields)

-- | Prepends a group to 'OptProperties'. Nested groups are indented e.g.
--
-- @
-- optPropertiesGroup "Group Outer" (optPropertiesGroup "Group Inner" o)
-- @
--
-- will render as:
--
-- @
-- Group Outer
-- - Group Inner
-- ...
-- @
optPropertiesGroup :: String -> OptProperties -> OptProperties
optPropertiesGroup g o = o { propGroup = OptGroup (g : oldGroup) }
where
OptGroup oldGroup = propGroup o

-- | Prepends a group per 'optPropertiesGroup'.
optionGroup :: String -> Option a -> Option a
optionGroup grp o = o { optProps = props' }
where
props' = optPropertiesGroup grp (optProps o)

-- | Group options together under a common heading in the help text.
--
-- For example, if we have:
--
-- > Args
-- > <$> parseMain
-- > <*> parserOptionGroup "Group A" parseA
-- > <*> parserOptionGroup "Group B" parseB
-- > <*> parseOther
--
-- Then the help page will look like:
--
-- > Available options:
-- > <main options>
-- > <other options>
-- >
-- > Group A
-- > <A options>
-- >
-- > Group B
-- > <B options>
--
-- @since 0.19.0.0
parserOptionGroup :: String -> Parser a -> Parser a
parserOptionGroup g = mapParserOptions (optionGroup g)

-- | Modifier for 'ParserInfo'.
newtype InfoMod a = InfoMod
{ applyInfoMod :: ParserInfo a -> ParserInfo a }
Expand Down
1 change: 1 addition & 0 deletions src/Options/Applicative/Builder/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ baseProps = OptProperties
, propShowDefault = Nothing
, propDescMod = Nothing
, propShowGlobal = True
, propGroup = OptGroup []
}

mkCommand :: Mod CommandFields a -> (Maybe String, [(String, ParserInfo a)])
Expand Down
Loading

0 comments on commit 2ea40a9

Please sign in to comment.