1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE RecordWildCards #-}
3 {-# LANGUAGE ViewPatterns #-}
4 {-# OPTIONS_GHC -fno-warn-orphans #-}
5 -- | Render a TCT source file in HTML5.
6 module Language.TCT.HTML5.Source where
8 import Control.Applicative (Applicative(..))
9 import Control.Monad (Monad(..), forM_, mapM)
11 import Data.Eq (Eq(..))
12 import Data.Foldable (Foldable(..))
13 import Data.Function (($), (.))
15 import Data.Maybe (Maybe(..))
16 import Data.Monoid (Monoid(..))
17 import Data.Ord (Ord(..))
18 import Data.Semigroup (Semigroup(..))
19 import Data.Sequence (ViewL(..))
20 import Data.String (IsString(..))
21 import Data.Text (Text)
22 import Prelude (Num(..), undefined)
23 import Text.Blaze ((!))
24 import Text.Blaze.Html (Html)
25 import Text.Show (Show(..))
26 import qualified Control.Monad.Trans.State as S
27 import qualified Data.List as L
28 import qualified Data.Sequence as Seq
29 import qualified Data.Text as Text
30 import qualified Text.Blaze.Html5 as H
31 import qualified Text.Blaze.Html5.Attributes as HA
33 import Language.TCT.Tree
34 import Language.TCT.Token
35 import Language.TCT.Elem
38 class HTML5able a where
41 class Textable a where
43 instance HTML5able TCT where
45 instance Semigroup H.AttributeValue where
48 -- * Class 'Attributable'
49 class AttrValue a where
50 attrValue :: a -> H.AttributeValue
51 instance AttrValue Text where
52 attrValue = fromString . Text.unpack
53 instance AttrValue Int where
54 attrValue = fromString . show
55 instance AttrValue Group where
56 attrValue = fromString . show
58 whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m ()
59 whenJust Nothing _f = pure ()
60 whenJust (Just a) f = f a
62 html5 :: Trees (Cell Key) (Cell Token) -> Html
67 H.meta ! HA.httpEquiv "Content-Type"
68 ! HA.content "text/html; charset=UTF-8"
69 whenJust (titleTCT tct) $ \(unCell -> t) ->
70 H.title $ H.toMarkup $ t_Token t
71 -- link ! rel "Chapter" ! title "SomeTitle">
72 H.link ! HA.rel "stylesheet"
74 ! HA.href "style/tct-html5-source.css"
76 H.a ! HA.id ("line-1") $ return ()
77 forM_ (treePosLastCell tct) $ h_TreeCell
79 titleTCT :: Trees (Cell Key) (Cell a) -> Maybe (Cell a)
82 TreeN (unCell -> KeySection{}) _ts -> True
85 TreeN (unCell -> KeySection _lvl) (Seq.viewl -> Tree0 title:<_) -> Just title
88 h_Text :: Text -> Html
91 h_Spaces :: Int -> Html
92 h_Spaces 0 = return ()
93 h_Spaces sp = H.span $ h_Text $ Text.replicate sp " "
95 h_TreeCell :: Tree (Pos,Cell Key) (Pos,Cell Token) -> Html
96 h_TreeCell (TreeN c@(_, unCell -> KeySection lvl) ts) = do
99 H.span ! HA.class_ "section-title" $ do
100 H.span $ h_Text $ Text.replicate lvl "#" <> " "
102 Tree0 (_,Cell posTitle _ title) :< _ -> h lvl $ h_IndentToken posTitle title
104 forM_ (case Seq.viewl ts of {Tree0{} :< ts' -> ts'; _ -> ts}) $ h_TreeCell
112 h n | n > 0 = H.span ! HA.class_ ("h h"<>attrValue n)
114 h_TreeCell (Tree0 c@(_,cell)) = do
117 h_TreeCell (TreeN c@(_,cell) cs) = do
121 h_IndentCell :: (Pos,Cell a) -> Html
122 h_IndentCell ((lineLast,colLast),posCell -> (line,col))
123 | lineLast < line = do
124 forM_ [lineLast+1..line] $ \lnum -> do
126 H.a ! HA.id ("line-"<>attrValue lnum) $ return ()
127 H.toMarkup $ Text.replicate (col - 1) " "
129 && colLast <= col = H.toMarkup $ Text.replicate (col - colLast) " "
130 | otherwise = undefined
132 h_CellKey :: Cell Key -> Trees (Pos,Cell Key) (Pos,Cell Token) -> Html
133 h_CellKey (Cell _pos _posEnd key) cells = do
135 KeyColon n wh -> h_Key n wh ":" "colon"
136 KeyGreat n wh -> h_Key n wh ">" "great"
137 KeyEqual n wh -> h_Key n wh "=" "equal"
138 KeyBar n wh -> h_Key n wh "|" "bar"
140 H.toMarkup ("- "::Text)
141 forM_ cells h_TreeCell
142 KeyLower name attrs -> do
143 H.span ! HA.class_ (mconcat ["key key-lower"," key-name-",attrValue name]) $ do
144 H.span ! HA.class_ "key-mark" $ H.toMarkup '<'
145 H.span ! HA.class_ "key-name" $ H.toMarkup name
147 forM_ cells h_TreeCell
149 h_Key :: Text -> White -> Text -> H.AttributeValue -> Html
150 h_Key name wh mark cl = do
151 -- h_Spaces $ colPos posEnd - (colPos pos + Text.length name + 1)
152 H.span ! HA.class_ (mconcat ["key key-",cl," key-name-",attrValue name]) $ do
153 H.span ! HA.class_ "key-name" $ H.toMarkup name
155 H.span ! HA.class_ "key-mark" $ H.toMarkup mark
156 forM_ cells h_TreeCell
158 h_CellToken :: Cell Token -> Html
159 h_CellToken (Cell pos _posEnd mrk) =
160 h_IndentToken pos mrk
162 h_IndentToken :: Pos -> Token -> Html
163 h_IndentToken pos mrk = go mrk `S.evalState` linePos pos
165 indent = Text.replicate (columnPos pos - 1) " "
166 go :: Token -> S.State Int Html
167 go (TokenPlain txt) = do
169 let lines = Text.splitOn "\n" txt
170 let lnums = H.toMarkup :
173 H.a ! HA.id ("line-"<>attrValue lnum) $ return ()
178 S.put (lin - 1 + L.length lines)
179 return $ mconcat $ L.zipWith ($) lnums lines
182 H.span ! HA.class_ "tag" $
184 go (TokenEscape c) = return $ H.toMarkup ['\\',c]
185 go (TokenLink lnk) = do
187 H.a ! HA.href (attrValue lnk) $
189 go (TokenGroup (GroupElem name attrs) t) = do
192 let cl = mconcat ["group-GroupElem", " group-elem-", attrValue name]
193 H.span ! HA.class_ cl $ do
194 H.span ! HA.class_ "group-open" $ H.toMarkup o
195 H.span ! HA.class_ "group-content" $ h
196 H.span ! HA.class_ "group-close" $ H.toMarkup c
198 h_name = H.span ! HA.class_ "elem-name" $ H.toMarkup name
202 Tokens ts | Seq.null ts ->
203 ( "<"<>h_name<>h_Attrs attrs<>"/>"
206 ( "<"<>h_name<>h_Attrs attrs<>">"
207 , "</"<>h_name<>">" )
208 go (TokenGroup grp t) = do
211 let (o,c) = groupBorders grp t
212 H.span ! HA.class_ (mconcat ["group-", attrValue grp]) $ do
213 H.span ! HA.class_ "group-open" $ H.toMarkup o
214 H.span ! HA.class_ "group-content" $ h
215 H.span ! HA.class_ "group-close" $ H.toMarkup c
218 return $ foldr (<>) mempty ts'
220 h_Attrs :: Attrs -> Html
221 h_Attrs = foldMap h_Attr
223 h_Attr :: (Text,Attr) -> Html
224 h_Attr (attr_white,Attr{..}) = do
225 H.toMarkup attr_white
226 H.span ! HA.class_ "attr-name" $
229 H.span ! HA.class_ "attr-value" $
230 H.toMarkup attr_value
231 H.toMarkup attr_close
233 t_Token :: Token -> Text
234 t_Token (TokenPlain t) = t
235 t_Token (TokenTag v) = "#"<>v
236 t_Token (TokenEscape c) = Text.pack ['\\',c]
237 t_Token (TokenLink lnk) = lnk
238 t_Token (TokenGroup grp t) = o<>t_Token t<>c
239 where (o,c) = groupBorders grp t
240 t_Token (Tokens ts) = foldMap t_Token ts
242 t_Value :: Text -> Text
245 treePosLastCell :: Trees (Cell k) (Cell a) -> Trees (Pos,Cell k) (Pos,Cell a)
246 treePosLastCell t = S.evalState (go`mapM`t) (1,1)
248 go :: Tree (Cell k) (Cell a) ->
249 S.State (Int, Int) (Tree (Pos, Cell k) (Pos, Cell a))
252 S.put $ posEndCell cell
253 return $ Tree0 (lastPos,cell)
254 go (TreeN cell ts) = do
256 S.put $ posEndCell cell
258 return $ TreeN (lastPos,cell) ts'