From 195c440d8fbbb9157805cc35d4fd5e4564eb11b0 Mon Sep 17 00:00:00 2001 From: Hardy Jones Date: Mon, 2 Mar 2020 04:52:55 -0800 Subject: [PATCH] Group commands in CLI (#1692) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. --- dhall/src/Dhall/Main.hs | 105 +++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/dhall/src/Dhall/Main.hs b/dhall/src/Dhall/Main.hs index 9de9babe2..c4068ed01 100644 --- a/dhall/src/Dhall/Main.hs +++ b/dhall/src/Dhall/Main.hs @@ -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 @@ -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 = @@ -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)