1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ViewPatterns #-}
4 {-# OPTIONS_GHC -fno-warn-orphans #-}
5 module Language.TCT.Write.Plain where
7 import Control.Applicative (liftA2)
8 import Control.Monad (Monad(..))
10 import Data.Char (Char)
11 import Data.Default.Class (Default(..))
12 import Data.Eq (Eq(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($))
15 import Data.Functor ((<$>))
17 import Data.Maybe (Maybe(..))
18 import Data.Monoid (Monoid(..))
19 import Data.Ord (Ord(..), Ordering(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence (ViewL(..))
22 import Data.String (String, IsString(..))
23 import Data.Tuple (fst)
24 import Prelude (Num(..), error)
25 import Text.Show (Show(..))
26 import qualified Control.Monad.Trans.State as S
27 import qualified Data.List as List
28 import qualified Data.Text.Lazy as TL
29 import qualified Data.Text.Lazy.Builder as TLB
30 import qualified Data.Sequence as Seq
33 import Language.TCT.Utils
34 -- import Language.TCT.Debug
37 type Plain = S.State State TLB.Builder
38 -- NOTE: To get maximum performance when building lazy Text values using a builder,
39 -- associate mappend calls to the right.
40 -- NOTE: (Semigroup.<>) associates to the right.
41 instance IsString Plain where
43 instance Semigroup Plain where
45 instance Monoid Plain where
49 runPlain :: Plain -> State -> TL.Text
50 runPlain p s = TLB.toLazyText $ fst $ S.runState p s
52 text :: Plainify a => State -> a -> TL.Text
53 text s a = runPlain (plainify a) s
55 plainDocument :: Roots -> TL.Text
56 plainDocument doc = text (setStart doc def) doc
61 { state_escape :: Bool -- FIXME: useful?
63 -- ^ current position,
64 -- always in sync annotated 'Pos' of the input,
65 -- not with the output (whose colmuns may be shifted left by 'state_unindent')
66 , state_indent :: TL.Text
67 -- ^ indentation, which contain horizontal spaces,
68 -- but also any repeated prefix introduced by 'HeaderBar' or 'HeaderGreat'
69 , state_unindent :: Int
70 -- ^ initial 'pos_column' set by 'setStart',
71 -- useful to shift everything to the left
73 instance Default State where
81 -- | Set the starting 'Pos' of given 'State'
82 -- by using the first 'cell_begin'.
83 setStart :: Roots -> State -> State
86 , state_unindent = pos_column pos
91 Tree Cell{cell_begin} _ :< _ -> cell_begin
94 class Plainify a where
95 plainify :: a -> Plain
96 instance Plainify () where
98 instance Plainify Char where
101 S.modify' $ \s@State{state_pos=Pos line _col, state_indent, state_unindent} ->
102 s{state_pos=Pos (line + 1) $ state_unindent + int (TL.length state_indent)}
104 return $ TLB.singleton '\n' <> TLB.fromLazyText state_indent
106 S.modify' $ \s@State{state_pos=Pos line col} ->
107 s{state_pos=Pos line (col + 1)}
108 return $ TLB.singleton c
109 instance Plainify String where
110 plainify = foldMap plainify
111 instance Plainify TL.Text where
115 let (h,ts) = TL.span (/='\n') t in
118 S.modify' $ \s@State{state_pos=Pos line col} ->
119 s{state_pos=Pos line $ col + int (TL.length h)}
120 return $ TLB.fromLazyText h
122 return (TLB.fromLazyText h) <>
123 -- NOTE: useless to increment the pos_column for h,
124 -- since the following '\n' will reset the pos_column.
127 instance Plainify Pos where
128 plainify new@(Pos lineNew colNew) = do
130 { state_pos=old@(Pos lineOld colOld)
134 S.modify' $ \s -> s{state_pos=new}
135 return $ TLB.fromLazyText $
136 case lineNew`compare`lineOld of
137 GT -> lines <> state_indent <> hspaces
139 lines = TL.replicate (int64 $ lineNew - lineOld) "\n"
140 hspaces = TL.replicate (int64 (colNew - state_unindent) - TL.length state_indent) " "
141 EQ | colNew >= colOld ->
142 TL.replicate (int64 $ colNew - colOld) " "
143 _ -> error $ "plainify: non-ascending Pos:"
144 <> "\n old: " <> show old
145 <> "\n new: " <> show new
146 instance Plainify Roots where
147 plainify = foldMap plainify
148 instance Plainify Root where
149 plainify (Tree (Cell bp _ep nod) ts) =
152 ----------------------
153 NodeGroup -> plainify ts
154 ----------------------
155 NodeLower n as -> "<" <> plainify n <> plainify as <> plainify ts
156 ----------------------
159 HeaderGreat{} -> plainHeaderRepeated
160 HeaderBar{} -> plainHeaderRepeated
161 _ -> plainify hdr <> plainify ts
163 plainHeaderRepeated = do
166 S.modify' $ \s -> s{state_indent =
168 TL.replicate (int64 (pos_column bp - state_unindent) - TL.length state_indent) " " <>
172 S.modify' $ \s -> s{state_indent}
174 ----------------------
177 S.modify' $ \s -> s{state_indent =
179 TL.replicate (int64 (pos_column bp - state_unindent) - TL.length state_indent) " "
182 S.modify' $ \s -> s{state_indent}
184 ----------------------
187 S.modify' $ \s -> s{state_indent =
189 TL.replicate (int64 (pos_column bp - state_unindent) - TL.length state_indent) " "
192 S.modify' $ \s -> s{state_indent}
194 ----------------------
195 NodeToken t -> plainify t <> plainify ts
196 ----------------------
198 plainify o <> plainify ts <> plainify c
199 where (o,c) = pairBorders p ts
200 instance Plainify Header where
203 HeaderColon n wh -> plainify n <> plainify wh <> ":"
204 HeaderGreat n wh -> plainify n <> plainify wh <> ">"
205 HeaderEqual n wh -> plainify n <> plainify wh <> "="
206 HeaderBar n wh -> plainify n <> plainify wh <> "|"
207 HeaderDot n -> plainify n <> "."
208 HeaderBrackets n -> "[" <> plainify n <> "]"
210 HeaderDashDash -> "-- "
211 HeaderSection lvl -> plainify (List.replicate lvl '#')
212 HeaderDotSlash n -> "./" <> plainify n
213 instance Plainify Token where
215 TokenText t -> plainify t
216 TokenTag t -> plainify '#' <> plainify t
217 TokenLink l -> plainify l
219 esc <- S.gets state_escape
221 then plainify ['\\', c]
223 instance Plainify ElemAttrs where
224 plainify = foldMap plainify
225 instance Plainify (White,ElemAttr) where
226 plainify (elemAttr_white,ElemAttr{..}) =
227 mconcat $ plainify <$>
236 -- * Class 'RackUpLeft'
237 class RackUpLeft a where
238 rackUpLeft :: a -> S.State (Maybe Pos) a
239 instance RackUpLeft Pos where
240 rackUpLeft pos@Pos{..} = do
242 Nothing -> return pos
245 { pos_line = pos_line - l0 + 1
246 , pos_column = pos_column - c0 + 1
248 instance RackUpLeft (Cell a) where
249 rackUpLeft (Cell bp ep a) = do
257 instance RackUpLeft a => RackUpLeft (Seq a) where
258 rackUpLeft = mapM rackUpLeft
259 instance RackUpLeft a => RackUpLeft (Tree a) where
260 rackUpLeft (Tree n ts) =