1 {-# LANGUAGE ConstraintKinds #-}
2 {-# LANGUAGE NoMonomorphismRestriction #-}
3 {-# LANGUAGE Rank2Types #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE UndecidableInstances #-} -- for From (Word *) d
6 module Symantic.CLI.Layout where
8 import Control.Applicative (Applicative(..))
9 import Control.Monad (Monad(..), (>>))
10 import Control.Monad.Trans.State.Strict
12 import Data.Function (($), (.), id)
13 import Data.Functor (Functor(..), (<$>))
14 import Data.Maybe (Maybe(..), fromMaybe)
15 import Data.Monoid (Monoid(..))
16 import Data.Semigroup (Semigroup(..))
17 import Data.Tree (Tree(..), Forest)
18 import Text.Show (Show(..))
19 import qualified Data.List as List
20 import qualified Data.Tree as Tree
21 import qualified Symantic.Document as Doc
23 import Symantic.CLI.API
24 import Symantic.CLI.Schema
27 data Layout d f k = Layout
28 { layoutSchema :: Schema d f k
29 -- ^ Synthetized (bottom-up) 'Schema'.
30 -- Useful for complex grammar rules or 'alt'ernatives associated
31 -- to the left of a 'response'.
33 -- ^ Synthetized (bottom-up) 'help'.
34 -- Useful in 'LayoutPerm' to merge nested 'help'
35 -- and nesting 'help' of the permutation.
36 , layoutMonad :: LayoutInh d -> State (LayoutState d) ()
39 runLayout :: LayoutDoc d => Bool -> Layout d f k -> d
40 runLayout full (Layout _s _h l) =
41 runLayoutForest full $
47 -- ** Type 'LayoutInh'
48 newtype LayoutInh d = LayoutInh
49 { layoutInh_message :: {-!-}[d]
52 defLayoutInh :: LayoutInh d
53 defLayoutInh = LayoutInh
54 { layoutInh_message = []
57 -- ** Type 'LayoutState'
58 type LayoutState d = Diff (Tree.Forest (LayoutNode d))
61 -- | A continuation-passing-style constructor,
62 -- (each constructor prepending something),
63 -- augmented with 'Maybe' to change the prepending
64 -- according to what the following parts are.
65 -- Used in '<!>' and 'alt' to know if branches
66 -- lead to at least one route (ie. contain at least one 'response').
67 type Diff a = Maybe a -> Maybe a
69 -- ** Type 'LayoutDoc'
75 runLayoutForest :: LayoutDoc d => Bool -> Forest (LayoutNode d) -> d
76 runLayoutForest full = (<> Doc.newline) . Doc.catV . (runLayoutTree full <$>)
78 runLayoutForest' :: LayoutDoc d => Bool -> Forest (LayoutNode d) -> d
79 runLayoutForest' full = Doc.catV . (runLayoutTree full <$>)
81 runLayoutTree :: LayoutDoc d => Bool -> Tree (LayoutNode d) -> d
83 -- Doc.setIndent mempty 0 .
84 Doc.catV . runLayoutNode full
86 runLayoutNode :: LayoutDoc d => Bool -> Tree (LayoutNode d) -> [d]
87 runLayoutNode full (Tree.Node n ts0) =
89 LayoutNode_Single sch mh ->
93 _ | not full -> Doc.whiter sch
94 h -> Doc.whiter sch <> Doc.newline <> Doc.justify (Doc.catV h)
96 LayoutNode_List ns ds ->
97 ((if full then ns else []) <>) $
98 (<$> ds) $ \(sch, mh) ->
102 _ | not full -> Doc.whiter sch
104 Doc.fillOrBreak 15 (Doc.whiter sch) <>
105 Doc.space <> Doc.align (Doc.justify (Doc.catV h))
106 LayoutNode_Forest sch ds ts ->
108 (if List.null ds then [] else [Doc.catV ds]) <>
109 (if List.null ts then [] else [runLayoutForest' full ts])
115 shift (Doc.blacker "└──"<>Doc.space)
117 (Doc.incrIndent (Doc.spaces 4) 4 <$> runLayoutNode full t)
120 shift (Doc.blacker "├──"<>Doc.space)
121 (Doc.blacker "│"<>Doc.spaces 3)
122 (Doc.incrIndent (Doc.blacker "│"<>Doc.spaces 3) 4 <$> runLayoutNode full t)
129 instance LayoutDoc d => App (Layout d) where
130 Layout xs xh xm <.> Layout ys yh ym =
131 Layout (xs<.>ys) (xh<>yh) $ \inh ->
133 instance LayoutDoc d => Alt (Layout d) where
134 Layout ls lh lm <!> Layout rs rh rm = Layout sch [] $ \inh -> do
146 case (lk Nothing, rk Nothing) of
147 (Nothing, Nothing) -> \case
149 Just ts -> k $ Just [Tree.Node (LayoutNode_Single (docSchema sch) (lh<>rh)) ts]
150 (Just lt, Just rt) -> \case
151 Nothing -> k $ Just (lt<>rt)
152 Just ts -> k $ Just (lt<>rt<>ts)
153 (Just lt, Nothing) -> \case
154 Nothing -> k $ Just lt
155 Just ts -> k $ Just (lt<>ts)
156 (Nothing, Just rt) -> \case
157 Nothing -> k $ Just rt
158 Just ts -> k $ Just (rt<>ts)
160 Layout ls lh lm `alt` Layout rs rh rm =
161 (Layout ls lh lm <!> Layout rs rh rm)
163 where sch = ls`alt`rs
164 opt (Layout xs xh xm) = Layout sch xh $ \inh -> do
165 modify' $ \k -> \case
167 Just _ts -> k $ Just [Tree.Node (LayoutNode_Single (docSchema sch) []{-FIXME-}) mempty]
170 instance LayoutDoc d => AltApp (Layout d) where
171 many0 (Layout xs xh xm) = Layout sch xh $ \inh -> do
172 modify' $ \k -> \case
174 Just ts -> k $ Just [Tree.Node nod mempty]
175 where nod = LayoutNode_Forest (docSchema sch) (layoutInh_message inh) ts
176 xm inh{layoutInh_message=[]}
178 many1 (Layout xs xh xm) = Layout sch xh $ \inh -> do
179 modify' $ \k -> \case
181 Just ts -> k $ Just [Tree.Node nod mempty]
182 where nod = LayoutNode_Forest (docSchema sch) (layoutInh_message inh) ts
183 xm inh{layoutInh_message=[]}
185 instance (LayoutDoc d, Doc.Justifiable d) => Permutable (Layout d) where
186 type Permutation (Layout d) = LayoutPerm d
187 runPermutation (LayoutPerm h ps) = Layout sch h $ \inh -> do
188 modify' $ \k -> \case
190 Just ts -> k $ Just [Tree.Node nod ts]
191 where nod = LayoutNode_List (layoutInh_message inh) (ps inh{layoutInh_message=[]})
192 where sch = runPermutation $ SchemaPerm id []
193 toPermutation (Layout xl xh _xm) = LayoutPerm [] $ \inh ->
194 [(docSchema xl, layoutInh_message inh <> xh)]
195 toPermDefault _a (Layout xl xh _xm) = LayoutPerm [] $ \inh ->
196 [(Doc.brackets (docSchema xl), layoutInh_message inh <> xh)]
197 instance (LayoutDoc d, Doc.Justifiable d) => Sequenceable (Layout d) where
198 type Sequence (Layout d) = LayoutSeq d
199 runSequence (LayoutSeq s h m) = Layout (runSequence s) h m
200 toSequence (Layout s h m) = LayoutSeq (toSequence s) h m
202 runSequence (LayoutSeq s h ps) = Layout sch h $ \inh -> do
203 modify' $ \k -> \case
205 Just ts -> k $ Just [Tree.Node nod mempty]
206 -- where nod = LayoutNode_List (layoutInh_message inh) (ps inh{layoutInh_message=[]})
208 nod = LayoutNode_Forest mempty {-(docSchema sch)-}
209 (layoutInh_message inh) (gs <> ts)
210 gs = (<$> ps inh{layoutInh_message=[]}) $ \(d,ds) ->
211 Tree.Node (LayoutNode_Single d ds) mempty
213 where sch = runSequence s
214 toSequence (Layout s h _m) = LayoutSeq (toSequence s) h $ \inh ->
215 [(docSchema s, layoutInh_message inh <> h)]
217 instance Pro (Layout d) where
218 dimap a2b b2a (Layout s h l) = Layout (dimap a2b b2a s) h l
219 instance (LayoutDoc d, Doc.From Name d) => CLI_Command (Layout d) where
220 command n (Layout xl xh xm) = Layout sch xh $ \inh -> do
221 modify' $ \k -> \case
226 (Doc.magentaer $ docSchema $ command n nothing)
227 (layoutInh_message inh)
230 xm inh{layoutInh_message=[]}
231 where sch = command n xl
232 instance (LayoutDoc d, Doc.Justifiable d) => CLI_Tag (Layout d) where
233 type TagConstraint (Layout d) a = TagConstraint (Schema d) a
234 tagged n (Layout xs xh xm) = Layout (tagged n xs) xh $ \inh -> do
235 modify' $ \k -> \case
239 ( LayoutNode_List [] [
240 ( docSchema (tagged n nothing)
241 , layoutInh_message inh
246 xm inh{layoutInh_message=[]}
247 endOpts = Layout sch [] $ \_inh -> do
248 modify' $ \k -> \case
250 Just ts -> k $ Just [Tree.Node (LayoutNode_Single (docSchema sch) []) ts]
252 instance LayoutDoc d => CLI_Var (Layout d) where
253 type VarConstraint (Layout d) a = VarConstraint (Schema d) a
254 var' n = Layout sch [] $ \inh -> do
255 modify' $ \k -> \case
257 Just ts -> k $ Just [Tree.Node (LayoutNode_List [] h) ts]
258 where h | List.null (layoutInh_message inh) = []
259 | otherwise = [(docSchema sch, layoutInh_message inh)]
261 just a = Layout (just a) [] $ \_inh -> pure ()
262 nothing = Layout nothing [] $ \_inh -> pure ()
263 instance LayoutDoc d => CLI_Env (Layout d) where
264 type EnvConstraint (Layout d) a = EnvConstraint (Schema d) a
265 env' n = Layout (env' n) [] $ \_inh -> pure ()
266 instance LayoutDoc d => CLI_Help (Layout d) where
267 type HelpConstraint (Layout d) d' = HelpConstraint (Schema d) d'
268 help msg (Layout s _h m) = Layout
270 (\inh -> m inh{layoutInh_message=[msg]})
271 program n (Layout xl xh xm) = Layout sch xh $ \inh -> do
272 modify' $ \k -> \case
276 (LayoutNode_Single (Doc.magentaer $ docSchema $ program n nothing) [])
280 where sch = program n xl
282 instance LayoutDoc d => CLI_Response (Layout d) where
283 type ResponseConstraint (Layout d) a = ResponseConstraint (Schema d) a
284 type ResponseArgs (Layout d) a = ResponseArgs (Schema d) a
285 type Response (Layout d) = Response (Schema d)
286 response' = Layout response' [] $ \_inh -> do
287 modify' $ \k -> \case
288 Nothing -> k $ Just []
289 Just ts -> k $ Just ts
291 -- ** Type 'LayoutSeq'
292 data LayoutSeq d k a = LayoutSeq
293 { layoutSeq_schema :: SchemaSeq d k a
294 , layoutSeq_help :: [d]
295 , layoutSeq_monad :: LayoutInh d -> State (LayoutState d) ()
297 instance Functor (LayoutSeq d k) where
298 f`fmap`LayoutSeq s h m = LayoutSeq (f<$>s) h $ \inh -> m inh
299 instance Applicative (LayoutSeq d k) where
300 pure a = LayoutSeq (pure a) [] $ \_inh -> return ()
301 LayoutSeq fs fh f <*> LayoutSeq xs xh x =
302 LayoutSeq (fs<*>xs) (fh<>xh) $ \inh -> f inh >> x inh
303 instance LayoutDoc d => CLI_Help (LayoutSeq d) where
304 type HelpConstraint (LayoutSeq d) d' = HelpConstraint (SchemaSeq d) d'
305 help msg (LayoutSeq s _h m) = LayoutSeq (help msg s) [msg] $ \inh ->
306 m inh{layoutInh_message=[msg]}
307 program n (LayoutSeq s h m) = LayoutSeq (program n s) h m
308 rule n (LayoutSeq s h m) = LayoutSeq (rule n s) h m
310 -- ** Type 'LayoutPerm'
311 data LayoutPerm d k a = LayoutPerm
312 { layoutPerm_help :: [d]
313 , layoutPerm_elem :: LayoutInh d -> [(d, {-help-}[d])]
315 instance Functor (LayoutPerm d k) where
316 _f`fmap`LayoutPerm h ps = LayoutPerm h $ \inh -> ps inh
317 instance Applicative (LayoutPerm d k) where
318 pure _a = LayoutPerm [] $ \_inh -> []
319 LayoutPerm _fh f <*> LayoutPerm _xh x =
320 LayoutPerm [] $ \inh -> f inh <> x inh
321 instance LayoutDoc d => CLI_Help (LayoutPerm d) where
322 type HelpConstraint (LayoutPerm d) d' = HelpConstraint (SchemaPerm d) d'
323 help msg (LayoutPerm _h m) = LayoutPerm [msg] $ \inh ->
324 m inh{layoutInh_message=[msg]}
328 -- ** Type 'LayoutNode'
330 = LayoutNode_Help [d] d
331 | LayoutNode_Tags [([d], d)]