1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE RecordWildCards #-}
3 {-# LANGUAGE ViewPatterns #-}
4 {-# OPTIONS_GHC -fno-warn-orphans #-}
5 -- | Render a TCT file in plain Text.
6 module Language.TCT.Write.Text where
8 import Control.Monad (Monad(..), mapM)
10 import Data.Eq (Eq(..))
11 import Data.Foldable (Foldable(..))
12 import Data.Function (($), (.))
13 import Data.Functor ((<$>))
15 import Data.Int (Int64)
16 import Data.Monoid (Monoid(..))
17 import Data.Ord (Ord(..))
18 import Data.Semigroup (Semigroup(..))
19 import Data.Sequence (ViewL(..), ViewR(..))
20 import Data.String (String)
21 import Data.Text (Text)
22 import Data.TreeSeq.Strict (Tree(..),Trees)
23 import Prelude (Num(..), undefined, Integral(..))
24 import Text.Show (Show(..))
25 import qualified Control.Monad.Trans.State as S
26 import qualified Data.List as L
27 import qualified Data.Sequence as Seq
28 import qualified Data.Text as Text
29 import qualified Data.Text.Lazy as TL
31 import Language.TCT.Tree
32 import Language.TCT.Cell
33 import Language.TCT.Token
34 import Language.TCT.Elem hiding (trac,dbg)
36 import Debug.Trace (trace)
37 trac :: String -> a -> a
40 dbg :: Show a => String -> a -> a
41 dbg m x = trac (m <> ": " <> show x) x
46 -- * Type 'Config_Text'
49 { config_text_escape :: Bool
52 config_text :: Config_Text
55 { config_text_escape = True
58 text :: Config_Text -> Trees (Cell Key) Tokens -> TL.Text
59 text cfg = textTreesCell cfg . treePosLastCell
61 treeRackUpLeft :: Tree (Cell k) (Cell a) -> Tree (Cell k) (Cell a)
62 treeRackUpLeft t = go t
67 (linePos pos - l0 + 1)
68 (columnPos pos - c0 + 1)
69 go :: Tree (Cell k) (Cell a) -> Tree (Cell k) (Cell a)
70 go (Tree0 (Cell pos posEnd c)) =
71 Tree0 $ Cell (rackUpLeft pos) (rackUpLeft posEnd) c
72 go (TreeN (Cell pos posEnd c) ts) =
74 (Cell (rackUpLeft pos) (rackUpLeft posEnd) c)
78 Trees (Cell k) Tokens ->
79 Trees (Pos,Cell k) (Pos,Tokens)
80 treePosLastCell t = S.evalState (go`mapM`t) (Pos 1 1)
82 go :: Tree (Cell k) Tokens ->
83 S.State Pos (Tree (Pos,Cell k) (Pos,Tokens))
88 return $ Tree0 (lastPos,ts)
90 S.put $ posEndCell cell
91 return $ Tree0 (lastPos,ts)
92 go (TreeN cell ts) = do
94 S.put $ posEndCell cell
96 return $ TreeN (lastPos,cell) ts'
98 int64 :: Integral i => i -> Int64
99 int64 = fromInteger . toInteger
103 Tree (Pos,Cell Key) (Pos,Tokens) ->
105 textTreeCell cfg (TreeN (posEnd, Cell pos _ (KeySection lvl)) ts) =
106 textIndentCell (posEnd,pos) <>
107 TL.replicate (int64 lvl) "#" <> " " <>
108 (case Seq.viewl ts of
109 Tree0 (_,title) :< _ ->
110 textIndentToken cfg title
113 (case Seq.viewl ts of
114 Tree0{} :< ts' -> ts'
116 textTreeCell cfg (Tree0 (posEnd,toks)) =
117 case Seq.viewl toks of
118 EmptyL -> textIndentToken cfg toks
119 t0:<_ -> textIndentCell (posEnd,posCell t0) <> textIndentToken cfg toks
120 textTreeCell cfg (TreeN (posEnd,cell@(Cell pos _ _)) cs) =
121 textIndentCell (posEnd,pos) <>
122 textCellKey cfg cell cs
124 textIndentCell :: (Pos,Pos) -> TL.Text
125 textIndentCell (Pos lineLast colLast,Pos line col)
127 TL.replicate (int64 $ line - (lineLast+1)) "\n" <>
128 TL.replicate (int64 $ col - 1) " "
129 | lineLast == line && colLast <= col =
130 TL.replicate (int64 $ col - colLast) " "
131 | otherwise = undefined
136 Trees (Pos,Cell Key) (Pos,Tokens) ->
138 textCellKey cfg (Cell _pos _posEnd key) cells = do
140 KeyColon n wh -> textKey n wh ":"
141 KeyGreat n wh -> textKey n wh ">"
142 KeyEqual n wh -> textKey n wh "="
143 KeyBar n wh -> textKey n wh "|"
144 KeyDash -> textKey "" "" "- "
145 KeyDashDash -> textKey "" "" "-- "
146 KeyLower name attrs ->
147 "<" <> tl name <> textAttrs attrs <>
148 textTreesCell cfg cells
149 KeySection{} -> undefined
152 textTreesCell cfg cells
154 textKey :: Text -> White -> TL.Text -> TL.Text
155 textKey name wh mark =
156 tl name <> tl wh <> mark <>
157 textTreesCell cfg cells
161 Trees (Pos,Cell Key) (Pos,Tokens) ->
163 textTreesCell cfg = foldMap (textTreeCell cfg)
165 textIndentToken :: Config_Text -> Tokens -> TL.Text
166 textIndentToken _cfg (Seq.viewl -> EmptyL) = ""
167 textIndentToken cfg toks@(Seq.viewl -> Cell pos _ _ :< _) =
168 goTokens toks `S.evalState` linePos pos
170 indent = TL.replicate (int64 $ columnPos pos - 1) " "
171 go :: Cell Token -> S.State Int TL.Text
176 let lines = Text.splitOn "\n" txt
177 S.put (lnum - 1 + L.length lines)
181 (l0:ls) -> tl l0 <> mconcat ((\l -> "\n"<>indent<>tl l)<$>ls)
182 TokenTag v -> return $ "#"<>tl v
185 if config_text_escape cfg
186 then tl $ Text.pack ['\\',c]
188 TokenLink lnk -> return $ tl lnk
189 TokenPair grp ts -> do
191 return $ tl o<>ts'<>tl c
192 where (o,c) = pairBorders grp ts
193 goTokens :: Tokens -> S.State Int TL.Text
196 return $ foldr (<>) mempty ts'
198 textAttrs :: Attrs -> TL.Text
199 textAttrs = foldMap textAttr
201 textAttr :: (Text,Attr) -> TL.Text
202 textAttr (attr_white,Attr{..}) =
211 textToken :: Token -> TL.Text
212 textToken (TokenPlain txt) = tl txt
213 textToken (TokenTag v) = "#"<>tl v
214 textToken (TokenEscape c) = TL.singleton c -- tl $ Text.pack ['\\',c]
215 textToken (TokenLink lnk) = tl lnk
216 textToken (TokenPair grp t) = tl o<>textTokens t<>tl c
217 where (o,c) = pairBorders grp t
219 textTokens :: Tokens -> TL.Text
220 textTokens ts = foldMap (textToken . unCell) ts