Skip to content

Commit

Permalink
Group commands in CLI (#1692)
Browse files Browse the repository at this point in the history
* Provide groups of subcommands

There are currently 15 subcommands for the `dhall` binary. This is great
because it means the `dhall` binary is full of features. It's not so
great because trying to figure out what to do is non-trivial. We start
grouping commands so we can provide a bit of guidance on what people
might want to do.

The previous `--help` output looked like:

```console
$ dhall --help
Usage: dhall ([version] | [resolve] | [type] | [normalize] | [repl] | [diff] |
             [hash] | [lint] | [tags] | [format] | [freeze] | [encode] |
             [decode] | [text] | [to-directory-tree] | [--file FILE]
             [--output FILE] [--annotate] [--alpha] [--no-cache] [--version])
             [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Available commands:
  version                  Display version
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions
  lint                     Improve Dhall code by using newer language features
                           and removing dead code
  tags                     Generate etags file
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree
```

This new output looks like:
```console
$ dhall --help
Usage: dhall ([version] | [resolve] | [type] | [normalize] | [repl] | [diff] |
             [hash] | [lint] | [tags] | [format] | [freeze] | [encode] |
             [decode] | [text] | [to-directory-tree] | [--file FILE]
             [--output FILE] [--annotate] [--alpha] [--no-cache] [--version])
             [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Miscellaneous
  version                  Display version

Interpret Dhall
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression

Miscellaneous
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions

Manipulate Dhall code
  lint                     Improve Dhall code by using newer language features
                           and removing dead code

Miscellaneous
  tags                     Generate etags file

Manipulate Dhall code
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression

Convert Dhall to and from its binary representation
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary

Generate other formats from Dhall
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree
```

There's still work to do: actually grouping the subcommands, and
displaying the debugging subcommand. But, this gets us on the path.

* Re-order subcommands to make single groups

The way `optparse-applicative` works with command groups is by grouping
adjacent commands together. It doesn't traverse the entire graph and
build groups. We have to move the commands where we want them so it'll
produce the groups.

This is better for us because it means we can display our groups in the
order we want.

The help output changes from:

```console
$ dhall --help
Usage: dhall ([version] | [resolve] | [type] | [normalize] | [repl] | [diff] |
             [hash] | [lint] | [tags] | [format] | [freeze] | [encode] |
             [decode] | [text] | [to-directory-tree] | [--file FILE]
             [--output FILE] [--annotate] [--alpha] [--no-cache] [--version])
             [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Miscellaneous
  version                  Display version

Interpret Dhall
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression

Miscellaneous
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions

Manipulate Dhall code
  lint                     Improve Dhall code by using newer language features
                           and removing dead code

Miscellaneous
  tags                     Generate etags file

Manipulate Dhall code
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression

Convert Dhall to and from its binary representation
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary

Generate other formats from Dhall
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree
```

to:

```console
$ dhall --help
Usage: dhall ([format] | [freeze] | [lint] | [text] | [to-directory-tree] |
             [resolve] | [type] | [normalize] | [encode] | [decode] | [repl] |
             [diff] | [hash] | [tags] | [version] | [--file FILE]
             [--output FILE] [--annotate] [--alpha] [--no-cache] [--version])
             [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Manipulate Dhall code
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression
  lint                     Improve Dhall code by using newer language features
                           and removing dead code

Generate other formats from Dhall
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree

Interpret Dhall
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression

Convert Dhall to and from its binary representation
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary

Miscellaneous
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions
  tags                     Generate etags file
  version                  Display version
```

This is much easier to understand, and should help direct people to the
right place. If we find that the groups should change, we can always do
that.

* Remove external/internal subcommand distinction

Now that we can describe groups of subcommands, we can describe the
subcommands that are for internal use only. These commands should not be
depended upon, and may change without warning. We might want to provide
more of a description around there.

The help output goes from:

```console
$ dhall --help
Usage: dhall ([format] | [freeze] | [lint] | [text] | [to-directory-tree] |
             [resolve] | [type] | [normalize] | [encode] | [decode] | [repl] |
             [diff] | [hash] | [tags] | [version] | [--file FILE]
             [--output FILE] [--annotate] [--alpha] [--no-cache] [--version])
             [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Manipulate Dhall code
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression
  lint                     Improve Dhall code by using newer language features
                           and removing dead code

Generate other formats from Dhall
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree

Interpret Dhall
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression

Convert Dhall to and from its binary representation
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary

Miscellaneous
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions
  tags                     Generate etags file
  version                  Display version
```

to:

```console
$ dhall --help
Usage: dhall ([format] | [freeze] | [lint] | [text] | [to-directory-tree] |
             [resolve] | [type] | [normalize] | [encode] | [decode] | [repl] |
             [diff] | [hash] | [tags] | [version] | [haskell-syntax-tree] |
             [--file FILE] [--output FILE] [--annotate] [--alpha] [--no-cache]
             [--version]) [--explain] [--plain] [--ascii] [--censor]
  Interpreter for the Dhall language

Available options:
  -h,--help                Show this help text
  --file FILE              Read expression from a file instead of standard input
  --output FILE            Write result to a file instead of standard output
  --annotate               Add a type annotation to the output
  --alpha                  α-normalize expression
  --no-cache               Handle protected imports as if the cache was empty
  --version                Display version
  --explain                Explain error messages in more detail
  --plain                  Disable syntax highlighting
  --ascii                  Format code using only ASCII syntax
  --censor                 Hide source code in error messages

Manipulate Dhall code
  format                   Standard code formatter for the Dhall language
  freeze                   Add integrity checks to remote import statements of
                           an expression
  lint                     Improve Dhall code by using newer language features
                           and removing dead code

Generate other formats from Dhall
  text                     Render a Dhall expression that evaluates to a Text
                           literal
  to-directory-tree        Convert nested records of Text literals into a
                           directory tree

Interpret Dhall
  resolve                  Resolve an expression's imports
  type                     Infer an expression's type
  normalize                Normalize an expression

Convert Dhall to and from its binary representation
  encode                   Encode a Dhall expression to binary
  decode                   Decode a Dhall expression from binary

Miscellaneous
  repl                     Interpret expressions in a REPL
  diff                     Render the difference between the normal form of two
                           expressions
  hash                     Compute semantic hashes for Dhall expressions
  tags                     Generate etags file
  version                  Display version

Debugging this interpreter
  haskell-syntax-tree      Output the parsed syntax tree (for debugging)
```

N.B. There's a new section at the end for debugging.

* Apply suggestions from code review

As noticed in review, we want to keep indentation consistent, so we
reformat this to be four spaces instead of two.
  • Loading branch information
joneshf authored Mar 2, 2020
1 parent 7005842 commit 195c440
Showing 1 changed file with 66 additions and 39 deletions.
105 changes: 66 additions & 39 deletions dhall/src/Dhall/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,23 @@ data ResolveMode
| ListTransitiveDependencies
| ListImmediateDependencies

-- | Groups of subcommands
data Group
= Manipulate
| Generate
| Interpret
| Convert
| Miscellaneous
| Debugging

groupDescription :: Group -> String
groupDescription group = case group of
Manipulate -> "Manipulate Dhall code"
Generate -> "Generate other formats from Dhall"
Interpret -> "Interpret Dhall"
Convert -> "Convert Dhall to and from its binary representation"
Miscellaneous -> "Miscellaneous"
Debugging -> "Debugging this interpreter"

-- | `Parser` for the `Options` type
parseOptions :: Parser Options
Expand All @@ -181,12 +198,12 @@ parseOptions =
f True = Censor
f False = NoCensor

subcommand' :: Bool -> String -> String -> Parser a -> Parser a
subcommand' internal name description parser =
subcommand :: Group -> String -> String -> Parser a -> Parser a
subcommand group name description parser =
Options.Applicative.hsubparser
( Options.Applicative.command name parserInfo
<> Options.Applicative.metavar name
<> if internal then Options.Applicative.internal else mempty
<> Options.Applicative.commandGroup (groupDescription group)
)
where
parserInfo =
Expand All @@ -195,75 +212,85 @@ subcommand' internal name description parser =
<> Options.Applicative.progDesc description
)

subcommand :: String -> String -> Parser a -> Parser a
subcommand = subcommand' False

internalSubcommand :: String -> String -> Parser a -> Parser a
internalSubcommand = subcommand' True

parseMode :: Parser Mode
parseMode =
subcommand
"version"
"Display version"
(pure Version)
Manipulate
"format"
"Standard code formatter for the Dhall language"
(Format <$> parseInplace <*> parseCheck)
<|> subcommand
Manipulate
"freeze"
"Add integrity checks to remote import statements of an expression"
(Freeze <$> parseInplace <*> parseAllFlag <*> parseCacheFlag <*> parseCheck)
<|> subcommand
Manipulate
"lint"
"Improve Dhall code by using newer language features and removing dead code"
(Lint <$> parseInplace <*> parseCheck)
<|> subcommand
Generate
"text"
"Render a Dhall expression that evaluates to a Text literal"
(Text <$> parseFile)
<|> subcommand
Generate
"to-directory-tree"
"Convert nested records of Text literals into a directory tree"
(DirectoryTree <$> parseFile <*> parseDirectoryTreeOutput)
<|> subcommand
Interpret
"resolve"
"Resolve an expression's imports"
(Resolve <$> parseFile <*> parseResolveMode <*> parseSemanticCacheMode)
<|> subcommand
Interpret
"type"
"Infer an expression's type"
(Type <$> parseFile <*> parseQuiet <*> parseSemanticCacheMode)
<|> subcommand
Interpret
"normalize"
"Normalize an expression"
(Normalize <$> parseFile <*> parseAlpha)
<|> subcommand
Convert
"encode"
"Encode a Dhall expression to binary"
(Encode <$> parseFile <*> parseJSONFlag)
<|> subcommand
Convert
"decode"
"Decode a Dhall expression from binary"
(Decode <$> parseFile <*> parseJSONFlag)
<|> subcommand
Miscellaneous
"repl"
"Interpret expressions in a REPL"
(pure Repl)
<|> subcommand
Miscellaneous
"diff"
"Render the difference between the normal form of two expressions"
(Diff <$> argument "expr1" <*> argument "expr2")
<|> subcommand
Miscellaneous
"hash"
"Compute semantic hashes for Dhall expressions"
(Hash <$> parseFile)
<|> subcommand
"lint"
"Improve Dhall code by using newer language features and removing dead code"
(Lint <$> parseInplace <*> parseCheck)
<|> subcommand
Miscellaneous
"tags"
"Generate etags file"
(Tags <$> parseInput <*> parseTagsOutput <*> parseSuffixes <*> parseFollowSymlinks)
<|> subcommand
"format"
"Standard code formatter for the Dhall language"
(Format <$> parseInplace <*> parseCheck)
<|> subcommand
"freeze"
"Add integrity checks to remote import statements of an expression"
(Freeze <$> parseInplace <*> parseAllFlag <*> parseCacheFlag <*> parseCheck)
<|> subcommand
"encode"
"Encode a Dhall expression to binary"
(Encode <$> parseFile <*> parseJSONFlag)
<|> subcommand
"decode"
"Decode a Dhall expression from binary"
(Decode <$> parseFile <*> parseJSONFlag)
<|> subcommand
"text"
"Render a Dhall expression that evaluates to a Text literal"
(Text <$> parseFile)
Miscellaneous
"version"
"Display version"
(pure Version)
<|> subcommand
"to-directory-tree"
"Convert nested records of Text literals into a directory tree"
(DirectoryTree <$> parseFile <*> parseDirectoryTreeOutput)
<|> internalSubcommand
Debugging
"haskell-syntax-tree"
"Output the parsed syntax tree (for debugging)"
(SyntaxTree <$> parseFile)
Expand Down

0 comments on commit 195c440

Please sign in to comment.