1+ {-# LANGUAGE OverloadedStrings #-}
2+
13-- | Helpers for building optparse-applicative parsers and subcommands.
24module Data.CLI.Impl (
35 unboundEnumError ,
@@ -11,6 +13,13 @@ module Data.CLI.Impl (
1113 mkCommand ,
1214 mkCommands ,
1315 usage ,
16+ usageWith ,
17+ HelpStyle (.. ),
18+ defaultHelpStyle ,
19+ plainHelpStyle ,
20+ styled ,
21+ colorEnabled ,
22+ execCLIParser ,
1423) where
1524
1625import Data.Bifunctor (first )
@@ -21,9 +30,80 @@ import Data.UUID (UUID)
2130import Data.UUID qualified as UUID
2231import Data.Void (Void )
2332import Options.Applicative
33+ import Options.Applicative.Help.Pretty (
34+ AnsiStyle ,
35+ Color (.. ),
36+ Doc ,
37+ annotate ,
38+ bold ,
39+ color ,
40+ pretty ,
41+ unAnnotate ,
42+ underlined ,
43+ (<+>) ,
44+ )
45+ import System.Environment (getArgs , lookupEnv )
46+ import System.IO (hIsTerminalDevice , stderr , stdout )
2447import Text.Megaparsec (Parsec , eof , errorBundlePretty , runParser )
2548import Text.Printf (printf )
2649
50+ -- | ANSI styles applied to help texts.
51+ --
52+ -- @since 0.2.2.0
53+ data HelpStyle = HelpStyle
54+ { _styleName :: AnsiStyle
55+ -- ^ program name in the header
56+ , _styleVersion :: AnsiStyle
57+ -- ^ version in the header
58+ , _styleValue :: AnsiStyle
59+ -- ^ accepted values and option references
60+ , _styleWarning :: AnsiStyle
61+ -- ^ dangerous behavior
62+ }
63+
64+ -- | Bold underlined name, green version, cyan values, bold red warnings.
65+ --
66+ -- @since 0.2.2.0
67+ defaultHelpStyle :: HelpStyle
68+ defaultHelpStyle = HelpStyle (bold <> underlined) (color Green ) (color Cyan ) (bold <> color Red )
69+
70+ -- | No styling.
71+ --
72+ -- @since 0.2.2.0
73+ plainHelpStyle :: HelpStyle
74+ plainHelpStyle = HelpStyle mempty mempty mempty mempty
75+
76+ -- | Render a string with the selected style of a t'HelpStyle'.
77+ --
78+ -- @since 0.2.2.0
79+ styled :: (HelpStyle -> AnsiStyle ) -> HelpStyle -> String -> Doc
80+ styled f s
81+ | f s == mempty = pretty
82+ | otherwise = annotate (f s) . pretty
83+
84+ -- | Whether colors should be emitted: stdout and stderr are terminals and @NO_COLOR@ is unset or empty.
85+ --
86+ -- @since 0.2.2.0
87+ colorEnabled :: IO Bool
88+ colorEnabled = do
89+ noColor <- maybe False (not . null ) <$> lookupEnv " NO_COLOR"
90+ ttys <- and <$> traverse hIsTerminalDevice [stdout, stderr]
91+ pure (ttys && not noColor)
92+
93+ -- | Like 'execParser', stripping help styles when 'colorEnabled' is false.
94+ --
95+ -- @since 0.2.2.0
96+ execCLIParser :: ParserInfo a -> IO a
97+ execCLIParser pinfo = do
98+ c <- colorEnabled
99+ res <- execParserPure defaultPrefs pinfo <$> getArgs
100+ handleParseResult (if c then res else strip res)
101+ where
102+ strip (Failure (ParserFailure f)) = Failure (ParserFailure (\ p -> let (h, e, w) = f p in (plain h, e, w)))
103+ strip r = r
104+ plain (ParserHelp a b c d e f g h) = ParserHelp (u a) (u b) (u c) (u d) (u e) (u f) (u g) (u h)
105+ u = fmap unAnnotate
106+
27107-- | Error for 'succ' called on 'maxBound' of the named enum.
28108--
29109-- @since 0.1.0.0
@@ -63,7 +143,12 @@ noArgs = pure
63143--
64144-- @since 0.1.0.0
65145forceSwitch :: Parser Bool
66- forceSwitch = switch (short ' f' <> long " force" <> help " force unsafe behavior" )
146+ forceSwitch =
147+ switch
148+ ( short ' f'
149+ <> long " force"
150+ <> helpDoc (Just (" force" <+> styled _styleWarning defaultHelpStyle " unsafe" <+> " behavior" ))
151+ )
67152
68153-- | @-n@/@--noop@ switch for dry runs.
69154--
@@ -103,7 +188,24 @@ mkCommands f = foldMap (mkCommand . f) (sortOn cmdname [minBound ..])
103188--
104189-- @since 0.1.0.0
105190usage :: String -> String -> String -> Parser a -> ParserInfo a
106- usage name version hdr p =
191+ usage = usageWith defaultHelpStyle
192+
193+ -- | 'usage' with a custom t'HelpStyle'.
194+ --
195+ -- @since 0.2.2.0
196+ usageWith :: HelpStyle -> String -> String -> String -> Parser a -> ParserInfo a
197+ usageWith s name version hdr p =
107198 info
108199 (p <**> helper)
109- (fullDesc <> progDesc hdr <> header (printf " %s - %s - version %s" name hdr version))
200+ ( fullDesc
201+ <> progDesc hdr
202+ <> headerDoc
203+ ( Just
204+ ( styled _styleName s name
205+ <+> " -"
206+ <+> pretty hdr
207+ <+> " - version"
208+ <+> styled _styleVersion s version
209+ )
210+ )
211+ )
0 commit comments