1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE ViewPatterns #-}
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 module Hdoc.TCT.Write.XML where
9 import Control.Monad (Monad(..))
11 import Data.Default.Class (Default(..))
12 import Data.Eq (Eq(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($), (.))
15 import Data.Functor ((<$>), (<$), ($>))
16 import Data.List.NonEmpty (NonEmpty(..))
17 import Data.Maybe (Maybe(..))
18 import Data.Monoid (Monoid(..))
19 import Data.Ord (Ord(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|))
23 import Data.TreeSeq.Strict (Tree(..), tree0)
24 import Data.Tuple (uncurry)
25 import Prelude (Num(..), undefined)
26 import qualified Data.Char as Char
27 import qualified Data.List as List
28 import qualified Data.Sequence as Seq
29 import qualified Data.Text.Lazy as TL
30 import qualified Hdoc.TCT.Write.Plain as Plain
32 -- import Hdoc.TCT.Debug
34 import Hdoc.TCT hiding (Parser)
36 import Text.Blaze.XML ()
40 -- NOTE: 'XmlNode' are still annotated with 'Cell',
41 -- but nothing is done to preserve any ordering amongst them,
42 -- because 'Node's sometimes need to be reordered
43 -- (eg. about/title may have a title from the section before,
44 -- hence outside of about).
45 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
46 writeXML :: Roots -> XMLs
48 -- (`S.evalState` def) $
50 sec@(Tree (unCell -> NodeHeader HeaderSection{}) _body) :< foot ->
51 let (titles, content) = partitionSection sec in
52 case Seq.viewl titles of
53 (unTree -> Cell st _) :< _ ->
61 case Seq.findIndexL isAbout content of
62 Nothing -> Tree (Cell st $ NodeHeader $ HeaderColon "about" "") mempty <| content
65 (unTree -> (unCell -> NodeHeader (HeaderColon "about" _wh))) -> True
70 partitionSection :: Root -> (Roots, Roots)
71 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
72 case Seq.viewl body of
74 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
75 let (subtitles, content) = spanlSubtitles et rest in
76 (title <| (subtitles >>= subTrees), content)
78 spanlSubtitles ep ts =
80 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
82 , pos_line span_begin - pos_line ep <= 1 ->
83 let (subs, ts') = spanlSubtitles span_end rs in
87 partitionSection _ = mempty
93 , inh_para :: [Inh -> Root -> XML]
96 instance Default Inh where
99 , inh_para = List.repeat elementPara
100 , inh_titles = mempty
104 elementPara :: Inh -> Root -> XML
105 elementPara inh (Tree c ts) = Tree (XmlElem "para" <$ c) $ xmlify inh ts
107 elementTitle :: Inh -> Root -> XML
108 elementTitle inh (Tree c ts) = Tree (XmlElem "title" <$ c) $ xmlify inh ts
110 elementName :: Inh -> Root -> XML
111 elementName inh (Tree c ts) = Tree (XmlElem "name" <$ c) $ xmlify inh ts
113 attributeName :: Inh -> Root -> XML
114 attributeName _inh (Tree c ts) = tree0 (XmlAttr "name" (Plain.writePlain ts) <$ c)
118 xmlify :: Inh -> a -> XMLs
119 instance Xmlify Roots where
121 case Seq.viewl roots of
123 r@(Tree cr@(Cell _sr nr) ts) :< rs ->
125 ----------------------
126 -- NOTE: HeaderColon becomes parent
127 -- of any continuous following-sibling HeaderBar or HeaderGreat
128 NodeHeader (HeaderColon n _wh)
129 | (span, rest) <- spanlHeaderColon rs
131 xmlify inh (Tree cr (ts<>span)) <>
134 spanlHeaderColon :: Roots -> (Roots, Roots)
137 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
138 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
140 ----------------------
141 -- NOTE: gather HeaderBrackets
142 NodeHeader HeaderBrackets{}
143 | (span,rest) <- spanlBrackets roots
145 (<| xmlify inh rest) $
146 element "references" $
149 spanlBrackets :: Roots -> (Roots, Roots)
152 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
154 ----------------------
155 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
157 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
158 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
159 ----------------------
160 -- NOTE: detect [some text](http://some.url) or [SomeRef]
162 | Tree (Cell sb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
163 (<| xmlify inh rs') $
165 (toList -> [unTree -> Cell sl (NodeToken (TokenLink lnk))]) ->
167 xmlAttrs [Cell sl ("to",lnk)] <>
171 xmlAttrs [Cell sb ("to",Plain.writePlain bracket)] <>
173 ----------------------
174 -- NOTE: gather HeaderDash
175 _ | (span, rest) <- spanlItems (==HeaderDash) roots
177 (<| xmlify inh rest) $
179 span >>= xmlify inh{inh_para=List.repeat elementPara}
180 ----------------------
181 -- NOTE: gather HeaderDot
182 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
184 (<| xmlify inh rest) $
186 span >>= xmlify inh{inh_para=List.repeat elementPara}
188 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
189 spanlItems liHeader =
190 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
192 NodeHeader (HeaderColon "li" _wh) -> True
193 NodeHeader hdr -> liHeader hdr
194 NodePair (PairElem "li" _as) -> True
196 ----------------------
197 NodePara | para:inh_para <- inh_para inh ->
199 xmlify inh{inh_para} rs
200 ----------------------
201 -- NOTE: context-free Root
203 xmlify inh r `unionXml`
206 element :: XmlName -> XMLs -> XML
207 element n = Tree (XmlElem n <$ cr)
208 instance Xmlify Root where
209 xmlify inh tn@(Tree (Cell ss@(sn:|ssn) nod) ts) =
211 ----------------------
215 para:_ -> Seq.singleton $ para inh tn
216 ----------------------
222 element "section" $ head <> xmlify inh' body
224 (titles, content) = partitionSection tn
225 (attrs, body) = partitionAttrs content
227 case Seq.viewl titles of
229 title@(unTree -> ct) :< subtitles ->
230 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
231 xmlify inh{inh_para=List.repeat elementTitle} title <>
235 subtitles >>= \subtitle@(unTree -> cs) ->
237 Tree (cs $> XmlElem "alias") $
238 xmlAttrs [cs $> ("id",getAttrId subtitle)]
240 { inh_para = List.repeat elementPara
245 let (attrs,body) = partitionAttrs ts in
247 -- NOTE: insert titles into <about>.
251 xmlify inh' (inh_titles inh) <>
253 xmlify inh'{inh_figure=False} body
254 -- NOTE: in <figure> mode, unreserved elements become <figure>
255 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
258 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
259 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_begin sn}:|ssn) ("type", n)) <>
261 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
262 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
263 -- NOTE: reserved elements
266 element (xmlLocalName n) $
273 "about" -> List.repeat elementTitle
274 "reference" -> elementTitle : List.repeat elementPara
275 "serie" -> List.repeat attributeName
276 "author" -> List.repeat attributeName
277 "editor" -> List.repeat attributeName
278 "org" -> List.repeat attributeName
279 "note" -> List.repeat elementPara
284 if inh_figure inh && n`List.notElem`elems || TL.null n
288 xmlAttrs (Seq.singleton $ Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
289 xmlify inh{inh_para=[]} ts
292 Tree (cell $ NodeHeader $ HeaderColon n wh) ts
296 let (attrs,body) = partitionAttrs ts in
298 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
299 xmlify inh{inh_para=List.repeat elementPara} body
303 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
304 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
309 let span_end = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length n)} in
310 xmlAttrs (Seq.singleton $ Cell (sn{span_end}:|ssn) ("name", n)) <>
313 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
316 Seq.singleton $ Tree0 $ cell $
317 XmlComment $ Plain.writePlain ts
319 HeaderBrackets ident ->
320 let (attrs,body) = partitionAttrs ts in
322 element "reference" $
323 xmlAttrs (setAttr (Cell (sn{span_end=span_end sn}:|ssn) ("id",ident)) attrs) <>
324 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
326 inh' = inh{inh_figure = False}
328 HeaderDotSlash _file -> xmlify inh ts
329 ----------------------
332 PairBracket | to <- Plain.writePlain ts
333 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
336 xmlAttrs [cell ("to",to)]
337 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
338 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
339 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
344 (Seq.viewl -> Tree0 (Cell sl (NodeToken (TokenText l))) :< ls) ->
346 m :> Tree0 (Cell sr (NodeToken (TokenText r))) ->
348 Tree0 (Cell sl (NodeToken (TokenText (TL.dropWhile Char.isSpace l))))
349 Seq.<|(m Seq.|>Tree0 (Cell sr (NodeToken (TokenText (TL.dropWhileEnd Char.isSpace r)))))
352 Tree0 (Cell sl (NodeToken (TokenText (TL.dropAround Char.isSpace l)))) Seq.<| ls
353 (Seq.viewr -> rs :> Tree0 (Cell sr (NodeToken (TokenText r)))) ->
355 rs Seq.|> Tree0 (Cell sr (NodeToken (TokenText (TL.dropAround Char.isSpace r))))
360 xmlAttrs [cell ("to",Plain.writePlain ts)]
361 PairElem name attrs ->
363 element (xmlLocalName name) $
364 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
365 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
368 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XmlText open) `unionXml`
369 xmlify inh ts `unionXml`
370 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XmlText close)
372 (open, close) = pairBorders pair ts
373 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
374 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
375 ----------------------
376 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
377 ----------------------
380 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
381 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
382 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
383 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
384 ----------------------
392 element :: XmlName -> XMLs -> XML
393 element n = Tree (cell $ XmlElem n)
394 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
395 xmlify _inh = xmlAttrs
399 -- | Reserved elements' name
496 -- | Convenient alias, forcing the types
497 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
498 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
500 -- | Extract attributes
501 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
502 partitionAttrs ts = (attrs,cs)
504 (as,cs) = (`Seq.partition` ts) $ \case
505 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
509 Tree (Cell ssn (NodeHeader (HeaderEqual n _wh))) a ->
510 Cell ssn (xmlLocalName n, v)
512 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
515 getAttrId :: Root -> TL.Text
516 getAttrId = Plain.writePlain . Seq.singleton
519 Cell (XmlName, TL.Text) ->
520 Seq (Cell (XmlName, TL.Text)) ->
521 Seq (Cell (XmlName, TL.Text))
522 setAttr a@(unCell -> (k, _v)) as =
523 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
524 Just idx -> Seq.update idx a as
528 Seq (Cell (XmlName, TL.Text)) ->
529 Cell (XmlName, TL.Text) ->
530 Seq (Cell (XmlName, TL.Text))
531 defaultAttr as a@(unCell -> (k, _v)) =
532 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
538 -- | Unify two 'XMLs', merging border 'XmlText's if any.
539 unionXml :: XMLs -> XMLs -> XMLs
541 case (Seq.viewr x, Seq.viewl y) of
542 (xs :> x0, y0 :< ys) ->
544 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XmlText tx))
545 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XmlText ty)) ) | fx == fy ->
547 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
553 unionsXml :: Foldable f => f XMLs -> XMLs
554 unionsXml = foldl' unionXml mempty