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 $ L.head $ Text.lines (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" $ do
183 H.span ! HA.class_ "tag-open" $ H.toMarkup '#'
185 go (TokenEscape c) = return $ H.toMarkup ['\\',c]
186 go (TokenLink lnk) = do
188 H.a ! HA.href (attrValue lnk) $
190 go (TokenGroup (GroupElem name attrs) t) = do
193 let cl = mconcat ["group-GroupElem", " group-elem-", attrValue name]
194 H.span ! HA.class_ cl $ do
195 H.span ! HA.class_ "group-open" $ H.toMarkup o
196 H.span ! HA.class_ "group-content" $ h
197 H.span ! HA.class_ "group-close" $ H.toMarkup c
199 h_name = H.span ! HA.class_ "elem-name" $ H.toMarkup name
203 Tokens ts | Seq.null ts ->
204 ( "<"<>h_name<>h_Attrs attrs<>"/>"
207 ( "<"<>h_name<>h_Attrs attrs<>">"
208 , "</"<>h_name<>">" )
209 go (TokenGroup grp t) = do
212 let (o,c) = groupBorders grp t
213 H.span ! HA.class_ (mconcat ["group-", attrValue grp]) $ do
214 H.span ! HA.class_ "group-open" $ H.toMarkup o
215 H.span ! HA.class_ "group-content" $ h
216 H.span ! HA.class_ "group-close" $ H.toMarkup c
219 return $ foldr (<>) mempty ts'
221 h_Attrs :: Attrs -> Html
222 h_Attrs = foldMap h_Attr
224 h_Attr :: (Text,Attr) -> Html
225 h_Attr (attr_white,Attr{..}) = do
226 H.toMarkup attr_white
227 H.span ! HA.class_ "attr-name" $
230 H.span ! HA.class_ "attr-value" $
231 H.toMarkup attr_value
232 H.toMarkup attr_close
234 t_Token :: Token -> Text
235 t_Token (TokenPlain t) = t
236 t_Token (TokenTag v) = "#"<>v
237 t_Token (TokenEscape c) = Text.pack ['\\',c]
238 t_Token (TokenLink lnk) = lnk
239 t_Token (TokenGroup grp t) = o<>t_Token t<>c
240 where (o,c) = groupBorders grp t
241 t_Token (Tokens ts) = foldMap t_Token ts
243 t_Value :: Text -> Text
246 treePosLastCell :: Trees (Cell k) (Cell a) -> Trees (Pos,Cell k) (Pos,Cell a)
247 treePosLastCell t = S.evalState (go`mapM`t) (1,1)
249 go :: Tree (Cell k) (Cell a) ->
250 S.State (Int, Int) (Tree (Pos, Cell k) (Pos, Cell a))
253 S.put $ posEndCell cell
254 return $ Tree0 (lastPos,cell)
255 go (TreeN cell ts) = do
257 S.put $ posEndCell cell
259 return $ TreeN (lastPos,cell) ts'