1 {-# LANGUAGE InstanceSigs #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE UndecidableInstances #-} -- for From (Word *) d
4 module Symantic.CLI.Help where
6 import Control.Applicative (Applicative(..))
8 import Data.Foldable (null)
9 import Data.Function (($), (.))
10 import Data.Functor (Functor(..), (<$>))
11 import Data.Maybe (Maybe(..), maybe, isJust)
12 import Data.Monoid (Monoid(..))
13 import Data.Semigroup (Semigroup(..))
14 import Text.Show (Show(..))
15 import Data.Tree as Tree
16 import qualified Symantic.Document as Doc
18 import Symantic.CLI.API
19 import Symantic.CLI.Schema as Schema
24 { help_result :: HelpInh d -> HelpResult d
25 -- ^ The 'HelpResult' of the current symantic.
26 , help_schema :: Schema d f k
27 -- ^ The 'Schema' of the current symantic.
30 runHelp :: Docable d => HelpInh d -> Help d f k -> d
31 runHelp def (Help h _p) = runHelpNodes def (h def) <> Doc.newline
33 docHelp :: Docable d => Doc.Indentable d => Docable d => Help d f k -> d
34 docHelp = runHelp defHelpInh
36 coerceHelp :: Help d f k -> Help d f' k'
37 coerceHelp Help{help_schema, ..} = Help
38 { help_schema = Schema.coerceSchema help_schema
43 -- | Configuration inherited top-down.
46 { helpInh_message :: !(Maybe d)
47 -- ^ The message inherited from 'help's.
48 , helpInh_command_indent :: !Doc.Indent
49 -- ^ 'Doc.Indent'ation for 'command's.
50 , helpInh_tag_indent :: !Doc.Indent
51 -- ^ 'Doc.Indent'ation for 'Tag's.
52 , helpInh_schema :: !(SchemaInh d)
53 -- ^ The inherited 'SchemaInh' for 'runSchema'.
54 , helpInh_helpless_options :: !Bool
55 -- ^ Whether to include options without help in the listing.
56 , helpInh_command_rule :: !Bool
57 -- ^ Whether to print the name of the rule.
58 , helpInh_full :: !Bool
59 -- ^ Whether to print full help.
62 defHelpInh :: Docable d => HelpInh d
64 { helpInh_message = Nothing
65 , helpInh_command_indent = 2
66 , helpInh_tag_indent = 16
67 , helpInh_schema = defSchemaInh
68 , helpInh_helpless_options = False
69 , helpInh_command_rule = False
73 -- ** Type 'HelpResult'
74 type HelpResult d = Tree.Forest (HelpNode, d)
76 defHelpResult :: Monoid d => HelpResult d
77 defHelpResult = mempty
79 -- *** Type 'HelpNode'
91 Tree (HelpNode, d) -> d
92 runHelpNode (Tree.Node (n,d) _ts) = d -- "[" <> Doc.stringH (show n) <> "]" <> d
94 -- | Introduce 'Doc.newline' according to the 'HelpNode's
95 -- put next to each others.
100 Tree.Forest (HelpNode, d) -> d
101 runHelpNodes inh [] = mempty
102 runHelpNodes inh ( t0@(Tree.Node _ t0s)
103 : t1@(Tree.Node (HelpNode_Command, _) _) : ts ) =
106 (if null t0s || not (helpInh_full inh) then mempty else Doc.newline) <>
107 runHelpNodes inh (t1:ts)
108 runHelpNodes inh ( t0@(Tree.Node (HelpNode_Tag, _) _)
109 : t1@(Tree.Node (HelpNode_Tag, _) _) : ts ) =
112 runHelpNodes inh (t1:ts)
113 runHelpNodes inh ( t0@(Tree.Node (HelpNode_Rule, _) t0s)
114 : t1@(Tree.Node (_, _) _) : ts ) =
117 (if null t0s || not (helpInh_full inh) then mempty else Doc.newline) <>
118 runHelpNodes inh (t1:ts)
119 runHelpNodes inh ( t0@(Tree.Node (HelpNode_Message, _) _)
124 runHelpNodes inh (t1:ts)
125 runHelpNodes inh ( t0 : t1@(Tree.Node (HelpNode_Tag, _) _) : ts ) =
129 runHelpNodes inh (t1:ts)
130 runHelpNodes inh ( t0 : t1@(Tree.Node (HelpNode_Env, _) _) : ts ) =
134 runHelpNodes inh (t1:ts)
135 runHelpNodes inh ( t0 : t1@(Tree.Node (HelpNode_Env, _) _) : ts ) =
138 runHelpNodes inh (t1:ts)
139 runHelpNodes inh ( t0 : t1@(Tree.Node (HelpNode_Rule, _) _) : ts ) =
142 runHelpNodes inh (t1:ts)
143 runHelpNodes inh (t:ts) = runHelpNode t <> runHelpNodes inh ts
145 instance Semigroup d => Semigroup (Help d f k) where
146 Help hx px <> Help hy py = Help (hx<>hy) (px<>py)
147 instance Monoid d => Monoid (Help d f k) where
148 mempty = Help mempty mempty
151 instance (Semigroup d, IsString d) => IsString (Help d e s a) where
152 fromString "" = Help $ \_ro -> Nothing
153 fromString s = Help $ \_ro -> Just $ fromString s
154 instance Show (Help Doc.Term e s a) where
155 show = TL.unpack . Doc.textTerm . runHelp
156 instance Docable d => Functor (Help d f) where
157 f <$$> Help h s = Help h (f<$$>s)
159 instance Docable d => App (Help d) where
160 Help hf pf <.> Help hx px = Help (hf<>hx) (pf<.>px)
161 instance Docable d => Alt (Help d) where
162 Help hl pl <!> Help hr pr = Help (hl<>hr) (pl<!>pr)
163 opt (Help h s) = Help h (opt s)
165 try (Help h s) = Help h (try s)
166 choice hs = Help (mconcat $ help_result <$> hs) (choice (help_schema <$> hs))
167 option a (Help h s) = Help h (option a s)
169 instance Docable d => Permutable (Help d) where
170 type Permutation (Help d) = HelpPerm d
171 runPermutation (HelpPerm h s) = Help h $ runPermutation s
172 toPermutation (Help h s) = HelpPerm h $ toPermutation s
173 toPermDefault a (Help h s) = HelpPerm h $ toPermDefault a s
174 instance Pro (Help d) where
175 dimap a2b b2a (Help h s) = Help h $ dimap a2b b2a s
176 instance Docable d => AltApp (Help d) where
177 many0 (Help h s) = Help h (many0 s)
178 many1 (Help h s) = Help h (many1 s)
179 instance Docable d => CLI_Var (Help d) where
180 type VarConstraint (Help d) a = ()
181 var' n = Help mempty (var' n)
182 just a = Help mempty (just a)
183 nothing = Help mempty nothing
184 instance Docable d => CLI_Env (Help d) where
185 type EnvConstraint (Help d) a = ()
190 then maybe [] (pure . pure . (HelpNode_Message,)) (helpInh_message inh)
193 Doc.breakfill (helpInh_tag_indent inh)
194 (Doc.bold (Doc.green (Doc.from (Doc.Word n)))
196 <> (if null ts then mempty else Doc.space)
197 <> Doc.align (runHelpNodes inh ts)
199 [ Tree.Node (HelpNode_Env, d) ts ]
201 where schema = env' n
202 instance Docable d => CLI_Command (Help d) where
203 -- type CommandConstraint (Help d) a = ()
204 command n (Help h s) =
208 then maybe [] (pure . pure . (HelpNode_Message,)) (helpInh_message inh)
211 { helpInh_message = Nothing
212 , helpInh_command_rule = True
215 (if not (null n) && helpInh_command_rule inh
216 then ref<>Doc.space<>"::= " else mempty)
217 <> Schema.runSchema schema (helpInh_schema inh)
218 <> (if null ts || not (helpInh_full inh) then mempty else Doc.newline)
219 <> Doc.incrIndent (helpInh_command_indent inh)
220 ((if null ts then mempty else Doc.newline) <> runHelpNodes inh ts)
222 [ Tree.Node (HelpNode_Command, d) ts ]
230 Doc.from (Doc.Word n)
231 instance Docable d => CLI_Tag (Help d) where
232 type TagConstraint (Help d) a = ()
233 tagged n (Help h s) =
235 if (isJust (helpInh_message inh)
236 || helpInh_helpless_options inh)
240 maybe [] (pure . pure . (HelpNode_Message,)) (helpInh_message inh) <>
241 h inh{helpInh_message=Nothing} in
243 Doc.breakfill (helpInh_tag_indent inh)
245 Schema.runSchema schema (helpInh_schema inh)
246 <> Doc.space) -- FIXME: space is not always needed
247 <> (if null ts then mempty else Doc.space)
248 <> Doc.align (runHelpNodes inh ts)
250 [ Tree.Node (HelpNode_Tag, d) ts ]
253 where schema = tagged n s
254 endOpts = Help mempty endOpts
255 instance Docable d => CLI_Help (Help d) where
256 type HelpConstraint (Help d) d' = d ~ d'
257 help msg (Help h s) = Help
258 (\inh -> h inh{helpInh_message=Just msg})
260 program n (Help h s) =
264 then maybe [] (pure . pure . (HelpNode_Message,)) (helpInh_message inh)
267 { helpInh_message = Nothing
268 , helpInh_command_rule = True
271 Schema.runSchema schema (helpInh_schema inh)
272 <> (if null ts {-|| not (helpInh_full inh)-} then mempty else Doc.newline)
274 (helpInh_command_indent inh)
275 ((if null ts then mempty else Doc.newline) <> runHelpNodes inh ts)
277 [ Tree.Node (HelpNode_Rule, d) ts ]
285 then maybe [] (pure . pure . (HelpNode_Message,)) (helpInh_message inh)
288 { helpInh_message = Nothing
289 , helpInh_command_rule = True
292 ref<>Doc.space<>"::= "
293 <> Schema.runSchema schema (helpInh_schema inh)
294 <> (if null ts || not (helpInh_full inh) then mempty else Doc.newline)
296 (helpInh_command_indent inh)
297 ((if null ts then mempty else Doc.newline) <> runHelpNodes inh ts)
299 [ Tree.Node (HelpNode_Rule, d) ts ]
307 Doc.from (Doc.Word n)
308 type HelpResponseArgs = SchemaResponseArgs
309 instance Docable d => CLI_Response (Help d) where
310 type ResponseConstraint (Help d) a = () -- ResponseConstraint (Schema d)
311 type ResponseArgs (Help d) a = SchemaResponseArgs a -- ResponseArgs (Schema d) a
312 type Response (Help d) = () -- Response (Schema d)
316 ResponseConstraint repr a =>
317 repr (ResponseArgs repr a)
319 response' = Help mempty $ response' @(Schema d) @a
322 instance Docable d => Sym_AltApp (Help d) where
323 many (Help h s) = Help h (many s)
324 some (Help h s) = Help h (many s)
329 = HelpPerm (HelpInh d -> HelpResult d)
331 instance Functor (HelpPerm d k) where
332 f`fmap`HelpPerm h ps = HelpPerm h (f<$>ps)
333 instance Applicative (HelpPerm d k) where
334 pure a = HelpPerm mempty (pure a)
335 HelpPerm fh f <*> HelpPerm xh x =
336 HelpPerm (fh<>xh) (f<*>x)
337 instance Docable d => CLI_Help (HelpPerm d) where
338 type HelpConstraint (HelpPerm d) d' = d ~ d'
339 help msg (HelpPerm h s) = HelpPerm
340 (\inh -> h inh{helpInh_message=Just msg})
342 program n (HelpPerm h s) = HelpPerm
343 (help_result $ program n (Help h (runPermutation s)))
345 rule n (HelpPerm h s) = HelpPerm
346 (help_result $ rule n (Help h (runPermutation s)))