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(..), Alternative(..))
9 import Control.Arrow (first)
10 import Control.Monad (Monad(..), (>>))
11 import Control.Monad.Trans.Class (MonadTrans(..))
12 import Control.Monad.Trans.Reader
13 import Control.Monad.Trans.State.Strict
15 import Data.Char (Char)
16 import Data.Eq (Eq(..))
17 import Data.Function (($), (.), id)
18 import Data.Functor (Functor(..), (<$>))
19 import Data.Maybe (Maybe(..), fromMaybe, catMaybes)
20 import Data.Monoid (Monoid(..))
21 import Data.Ord (Ord(..))
22 import Data.Semigroup (Semigroup(..))
23 import Data.String (String, IsString(..))
24 import Data.Text (Text)
25 import Data.Tree (Tree(..), Forest, drawForest)
26 import Data.Tuple (snd)
28 import Text.Show (Show(..))
29 import Text.Show (Show(..))
30 import qualified Data.List as List
31 import qualified Data.Text.Lazy as TL
32 import qualified Data.Text.Lazy.Builder as TLB
33 import qualified Data.Tree as Tree
34 import qualified Symantic.Document as Doc
35 import qualified System.IO as IO
37 import Symantic.CLI.API
38 import Symantic.CLI.Fixity
39 import Symantic.CLI.Schema
40 import Symantic.CLI.Parser (output)
45 data Layout d f k = Layout
46 { layoutSchema :: Schema d f k
48 , unLayout :: LayoutInh d -> State (LayoutState d) ()
51 runLayout :: LayoutDoc d => Layout d f k -> d
52 runLayout (Layout _s _h l) =
59 -- ** Type 'LayoutInh'
62 { layoutInh_message :: ![d]
65 defLayoutInh :: LayoutInh d
66 defLayoutInh = LayoutInh
67 { layoutInh_message = []
70 -- ** Type 'LayoutState'
71 type LayoutState d = Diff (Tree.Forest (LayoutNode d, d))
74 -- | A continuation-passing-style constructor,
75 -- (each constructor prepending something),
76 -- augmented with 'Maybe' to change the prepending
77 -- according to what the following parts are.
78 -- Used in '<!>' and 'alt' to know if branches
79 -- lead to at least one route (ie. contain at least one 'response').
80 type Diff a = Maybe a -> Maybe a
82 -- ** Type 'LayoutDoc'
88 docTree :: LayoutDoc d => Tree (LayoutNode d, d) -> d
90 Doc.setIndent mempty 0 .
93 docForest :: LayoutDoc d => Forest (LayoutNode d, d) -> d
94 docForest = (<> Doc.newline) . Doc.catV . (docTree <$>)
96 docNode :: LayoutDoc d => Tree (LayoutNode d, d) -> [d]
97 docNode (Tree.Node n ts0) =
99 (LayoutNode_Perms ds, _d) ->
103 h -> Doc.fillOrBreak 16 (Doc.whiter d) <>
104 Doc.align (Doc.space <> Doc.justify (Doc.catV h))
106 -- List.init $ (<> Doc.newline) <$> (ds <> [mempty])
107 (LayoutNode mh, d) ->
111 h -> Doc.whiter d <> Doc.newline <> Doc.justify (Doc.catV h)
118 shift (Doc.blacker "└──"<>Doc.space)
120 (Doc.incrIndent (Doc.spaces 4) 4 <$> docNode t)
123 shift (Doc.blacker "├──"<>Doc.space)
124 (Doc.blacker "│"<>Doc.spaces 3)
125 (Doc.incrIndent (Doc.blacker "│"<>Doc.spaces 3) 4 <$> docNode t)
132 instance LayoutDoc d => App (Layout d) where
133 Layout xs xh xm <.> Layout ys yh ym =
134 Layout (xs<.>ys) (xh<>yh) $ \inh ->
136 instance LayoutDoc d => Alt (Layout d) where
137 Layout ls lh lm <!> Layout rs rh rm = Layout sch [] $ \inh -> do
149 case (lk Nothing, rk Nothing) of
150 (Nothing, Nothing) ->
153 Just ts -> k $ Just [Tree.Node (LayoutNode (lh<>rh), docSchema sch) ts]
154 (Just lt, Just rt) ->
156 Nothing -> k $ Just (lt<>rt)
157 Just ts -> k $ Just (lt<>rt<>ts)
158 (Just lt, Nothing) ->
160 Nothing -> k $ Just lt
161 Just ts -> k $ Just (lt<>ts)
162 (Nothing, Just rt) ->
164 Nothing -> k $ Just rt
165 Just ts -> k $ Just (rt<>ts)
167 Layout ls lh lm `alt` Layout rs rh rm = Layout sch [] $ \inh -> do
179 case (lk Nothing, rk Nothing) of
180 (Nothing, Nothing) ->
183 Just ts -> k $ Just [Tree.Node (LayoutNode (lh<>rh), docSchema sch) ts]
184 (Just lt, Just rt) ->
186 Nothing -> k $ Just (lt<>rt)
187 Just ts -> k $ Just (lt<>rt<>ts)
188 (Just lt, Nothing) ->
190 Nothing -> k $ Just lt
191 Just ts -> k $ Just (lt<>ts)
192 (Nothing, Just rt) ->
194 Nothing -> k $ Just rt
195 Just ts -> k $ Just (rt<>ts)
196 where sch = ls`alt`rs
197 opt (Layout xs xh xm) = Layout sch xh $ \inh -> do
198 modify' $ \k -> \case
200 Just ts -> k $ Just [Tree.Node (LayoutNode [], mempty{-FIXME-}) ts]
203 instance Pro (Layout d) where
204 dimap a2b b2a (Layout s h l) = Layout (dimap a2b b2a s) h l
205 instance (LayoutDoc d, Doc.From Name d) => CLI_Command (Layout d) where
206 command n (Layout xl xh xm) = Layout sch xh $ \inh -> do
207 modify' $ \k -> \case
211 ( LayoutNode (layoutInh_message inh)
212 , Doc.magentaer $ docSchema $ command n nothing
216 where sch = command n xl
217 instance (LayoutDoc d, Doc.Justifiable d) => CLI_Tag (Layout d) where
218 type TagConstraint (Layout d) a = TagConstraint (Schema d) a
219 tagged n (Layout xs xh xm) = Layout (tagged n xs) xh $ \inh -> do
220 modify' $ \k -> \case
222 Just ts -> k $ Just [Tree.Node (LayoutNode [], docSchema (tagged n nothing)) ts]
224 endOpts = Layout sch [] $ \inh -> do
225 modify' $ \k -> \case
227 Just ts -> k $ Just [Tree.Node (LayoutNode [], docSchema sch) ts]
229 instance LayoutDoc d => CLI_Var (Layout d) where
230 type VarConstraint (Layout d) a = VarConstraint (Schema d) a
231 var' n = Layout sch [] $ \inh -> do
232 modify' $ \k -> \case
234 Just ts -> k $ Just [Tree.Node (LayoutNode [], docSchema sch) ts]
236 just a = Layout (just a) [] $ \_inh -> pure ()
237 nothing = Layout nothing [] $ \_inh -> pure ()
238 instance LayoutDoc d => CLI_Env (Layout d) where
239 type EnvConstraint (Layout d) a = EnvConstraint (Schema d) a
240 env' n = Layout (env' n) [] $ \_inh -> pure ()
241 instance LayoutDoc d => CLI_Help (Layout d) where
242 type HelpConstraint (Layout d) d' = HelpConstraint (Schema d) d'
243 help msg (Layout s _h m) = Layout
245 (\inh -> m inh{layoutInh_message=[msg]})
246 program n (Layout xl xh xm) = Layout sch xh $ \inh -> do
247 modify' $ \k -> \case
249 Just ts -> k $ Just [Tree.Node (LayoutNode [], Doc.magentaer $ docSchema $ program n nothing) ts]
251 where sch = program n xl
253 instance LayoutDoc d => CLI_Response (Layout d) where
254 type ResponseConstraint (Layout d) a = ResponseConstraint (Schema d) a
255 type ResponseArgs (Layout d) a = ResponseArgs (Schema d) a
256 type Response (Layout d) = Response (Schema d)
257 response' = Layout response' [] $ \inh -> do
258 modify' $ \k -> \case
259 Nothing -> k $ Just []
260 Just ts -> k $ Just ts
262 -- ** Type 'LayoutPerm'
263 data LayoutPerm d k a = LayoutPerm
264 -- (Permutation (Schema d) k a)
266 (LayoutInh d -> [(d, [d])])
267 instance Functor (LayoutPerm d k) where
268 _f`fmap`LayoutPerm h ps = LayoutPerm h $ \inh -> ps inh
269 instance Applicative (LayoutPerm d k) where
270 pure _a = LayoutPerm [] $ \_inh -> []
271 LayoutPerm fh f <*> LayoutPerm xh x =
272 LayoutPerm [] $ \inh -> f inh <> x inh
273 instance (LayoutDoc d, Doc.Justifiable d) => Permutable (Layout d) where
274 type Permutation (Layout d) = LayoutPerm d
275 runPermutation (LayoutPerm h ps) = Layout sch h $ \inh -> do
276 modify' $ \k -> \case
280 ( LayoutNode_Perms (ps inh)
285 sch = runPermutation $ SchemaPerm id []
286 toPermutation (Layout xl xh xm) = LayoutPerm [] $ \inh ->
287 [(docSchema xl, layoutInh_message inh <> xh)]
288 toPermDefault a (Layout xl xh xm) = LayoutPerm [] $ \inh ->
289 [(Doc.brackets (docSchema xl), layoutInh_message inh <> xh)]
290 instance LayoutDoc d => CLI_Help (LayoutPerm d) where
291 type HelpConstraint (LayoutPerm d) d' = HelpConstraint (SchemaPerm d) d'
292 help msg (LayoutPerm h m) = LayoutPerm [msg] $ \inh ->
293 m inh{layoutInh_message=[msg]}
297 -- ** Type 'LayoutNode'
300 | LayoutNode_Perms [(d, [d])]