]> gitweb @ CieloNegro.org - Rakka.git/blobdiff - Rakka/Wiki/Formatter.hs
Resurrection from bitrot
[Rakka.git] / Rakka / Wiki / Formatter.hs
index 1054d174582a535acafb2a4a88c1d448f790ae30..c1e63f5c05138ab1a4caa4b4de7f84b3dd8a49fe 100644 (file)
+{-# LANGUAGE
+    Arrows
+  , OverloadedStrings
+  , TypeOperators
+  , UnicodeSyntax
+  , ViewPatterns
+  #-}
 module Rakka.Wiki.Formatter
-    ( formatWikiElements
+    ( formatWikiBlocks
     )
     where
+import Control.Arrow
+import Control.Arrow.ArrowIf
+import Control.Arrow.ArrowList
+import Control.Arrow.ArrowTree
+import Control.Arrow.Unicode
+import qualified Data.CaseInsensitive as CS
+import Data.Maybe
+import Data.Monoid.Unicode
+import Data.Text (Text)
+import qualified Data.Text as T
+import Network.URI hiding (fragment)
+import Prelude.Unicode
+import Rakka.Page
+import Rakka.Wiki
+import Text.XML.HXT.Arrow.XmlArrow
+import Text.XML.HXT.DOM.TypeDefs
 
-import           Control.Arrow
-import           Control.Arrow.ArrowList
-import           Data.List
-import           Rakka.Wiki
-import           Text.XML.HXT.Arrow.XmlArrow
-import           Text.XML.HXT.DOM.TypeDefs
+formatWikiBlocks :: (ArrowXml a, ArrowChoice a) => a (URI, [BlockElement]) XmlTree
+formatWikiBlocks
+    = proc (baseURI, blocks)
+    -> do block   <- arrL id     -< blocks
+          tree    <- formatBlock -< (baseURI, block)
+          attachXHtmlNS -< tree
 
 
--- 複數の Inline を一つに纏める
-packParagraph :: [WikiElement] -> [Either BlockElement [InlineElement]]
-packParagraph elems = map pack grp
+formatElement :: (ArrowXml a, ArrowChoice a) => a (URI, Element) XmlTree
+formatElement 
+    = proc (baseURI, e)
+    -> case e of
+         Block  b -> formatBlock  -< (baseURI, b)
+         Inline i -> formatInline -< (baseURI, i)
+
+formatBlock ∷ (ArrowXml (⇝), ArrowChoice (⇝)) ⇒ (URI, BlockElement) ⇝ XmlTree
+formatBlock 
+    = proc (baseURI, block)
+    → case block of
+         Heading level text
+             → formatHeading ⤙ (level, text)
+
+         HorizontalLine
+             -> eelem "hr" -< ()
+
+         list@(List _ _)
+             -> formatListElement -< (baseURI, list)
+
+         DefinitionList list
+             -> formatDefinitionList -< (baseURI, list)
+
+         Preformatted inlines
+             -> formatPreformatted -< (baseURI, inlines)
+                
+         Paragraph inlines
+             -> formatParagraph -< (baseURI, inlines)
+
+         Div attrs contents
+             -> formatElem "div" -< (baseURI, attrs, contents)
+
+         EmptyBlock
+             -> none -< ()
+
+         _   -> arr (error . ("formatBlock: unsupported BlockElement: " ++) . show) -< block
     where
-      grp :: [[WikiElement]]
-      grp = groupBy criteria elems
+      formatElem :: (ArrowXml a, ArrowChoice a) =>
+                    String
+                 -> a (URI, [Attribute], [Element]) XmlTree
+      formatElem name
+          = proc (baseURI, attrs, contents)
+          -> ( eelem name
+               += ( arrL (fst . snd)
+                       >>>
+                       attrFromPair
+                  )
+               += ( (arr fst &&& arrL (snd . snd))
+                    >>>
+                    formatElement
+                  )
+             ) -< (baseURI, (attrs, contents))
 
-      criteria :: WikiElement -> WikiElement -> Bool
-      criteria (Inline _) (Inline _) = True
-      criteria _ _                   = False
 
-      pack :: [WikiElement] -> Either BlockElement [InlineElement]
-      pack (Block b : []) = Left b
-      pack xs             = Right [ case x of
-                                      Inline i -> i | x <- xs ]
-                                                       
+formatHeading ∷ ArrowXml (⇝) ⇒ (Int, Text) ⇝ XmlTree
+formatHeading 
+    = proc (level, T.unpack → text)
+    -> mkelem ("h" ⊕ show level)
+       [ sattr "id" text ]
+       [ txt text        ] ⤛ ()
 
-formatWikiElements :: (ArrowXml a, ArrowChoice a) => a [WikiElement] XmlTree
-formatWikiElements
-    = proc elems
-    -> do chunk <- arrL id -< packParagraph elems
-          case chunk of
-            Left  x  -> formatBlock     -< x
-            Right xs -> formatParagraph -< xs
 
+formatListElement :: (ArrowXml a, ArrowChoice a) => a (URI, BlockElement) XmlTree
+formatListElement 
+    = proc (baseURI, list)
+    -> let tag = case listType list of
+                   Bullet   -> "ul"
+                   Numbered -> "ol"
+       in
+         ( eelem tag
+           += ( (constA baseURI &&& constL (listItems list))
+                >>>
+                formatListItem
+              )
+         ) -<< ()
+      where
+        formatListItem :: (ArrowXml a, ArrowChoice a) => a (URI, ListItem) XmlTree
+        formatListItem 
+            = proc (baseURI, item)
+            -> eelem "li"
+               += ( (arr fst &&& arrL snd)
+                    >>>
+                    formatElement
+                  ) -< (baseURI, item)
 
-formatBlock :: (ArrowXml a, ArrowChoice a) => a BlockElement XmlTree
-formatBlock 
-    = proc b
-    -> do case b of
-            Header level text
-                -> formatHeader -< (level, text)
-            EmptyLine
-                -> none -< ()
 
+formatDefinitionList :: (ArrowXml a, ArrowChoice a) => a (URI, [Definition]) XmlTree
+formatDefinitionList 
+    = proc (baseURI, list)
+    -> ( eelem "dl"
+         += ( (arr fst &&& arrL snd)
+              >>>
+              formatDefinition
+            )
+       ) -< (baseURI, list)
+    where
+      formatDefinition :: (ArrowXml a, ArrowChoice a) => a (URI, Definition) XmlTree
+      formatDefinition 
+          = proc (baseURI, def)
+          -> ( eelem "dt"
+               += ( (arr fst &&& arrL (defTerm . snd))
+                    >>>
+                    formatInline
+                  )
+               <+>
+               eelem "dd"
+               += ( (arr fst &&& arrL (defDesc . snd))
+                    >>>
+                    formatInline
+                  )
+             ) -< (baseURI, def)
 
-formatHeader :: ArrowXml a => a (Int, String) XmlTree
-formatHeader 
-    = proc (level, text)
-    -> selem ("h" ++ show level) [txt text] -<< ()
 
+formatPreformatted :: (ArrowXml a, ArrowChoice a) => a (URI, [InlineElement]) XmlTree
+formatPreformatted
+    = eelem "pre"
+      += ( (arr fst &&& arrL snd)
+           >>>
+           formatInline
+         )
 
-formatParagraph :: (ArrowXml a, ArrowChoice a) => a [InlineElement] XmlTree
-formatParagraph 
-    = proc xs
-    -> do elem <- arrL id -< xs
-          tree <- ( eelem "p"
-                    += formatInline ) -< elem
-          returnA -< tree
 
+formatParagraph :: (ArrowXml a, ArrowChoice a) => a (URI, [InlineElement]) XmlTree
+formatParagraph 
+    = eelem "p"
+      += ( (arr fst &&& arrL snd)
+           >>>
+           formatInline
+         )
 
-formatInline :: (ArrowXml a, ArrowChoice a) => a InlineElement XmlTree
+formatInline ∷ (ArrowXml (⇝), ArrowChoice (⇝)) ⇒ (URI, InlineElement) ⇝ XmlTree
 formatInline 
-    = proc i
-    -> do case i of
-            Text text
-                -> mkText -< text
+    = proc (baseURI, i)
+    → case i of
+         Text text
+             → mkText ⤙ T.unpack text
+
+         Italic contents
+             -> formatElem "i" -< (baseURI, [], contents)
+
+         Bold contents
+             -> formatElem "b" -< (baseURI, [], contents)
+
+         link@(ObjectLink _ _)
+             -> formatObjectLink -< (baseURI, link)
+
+         link@(PageLink _ _ _)
+             -> formatPageLink -< (baseURI, link)
+
+         link@(ExternalLink _ _)
+             -> formatExternalLink -< link
+
+         LineBreak attrs
+             -> formatElem "br" -< (baseURI, attrs, [])
+
+         Span attrs contents
+             -> formatElem "span" -< (baseURI, attrs, contents)
+
+         img@(Image _ _)
+             -> formatImage -< (baseURI, img)
+
+         Anchor attrs contents
+             -> formatElem "a" -< (baseURI, attrs, contents)
+
+         Input attrs
+             -> formatElem "input" -< (baseURI, attrs, [])
+
+         EmptyInline
+             -> none -< ()
+
+         _   -> arr (error . ("formatInline: unsupported InlineElement: " ++) . show) -< i
+    where
+      formatElem ∷ (ArrowXml (⇝), ArrowChoice (⇝)) ⇒
+                    String
+                 → (URI, [Attribute], [InlineElement]) ⇝ XmlTree
+      formatElem name
+          = proc (baseURI, attrs, contents)
+          → ( eelem name
+               += ( arrL (fst ∘ snd)
+                    ⋙
+                    attrFromPair
+                  )
+               += ( (arr fst &&& arrL (snd . snd))
+                    ⋙
+                    formatInline
+                  )
+             ) ⤙ (baseURI, (attrs, contents))
+
+attrFromPair ∷ ArrowXml (⇝) ⇒ Attribute ⇝ XmlTree
+attrFromPair = proc (T.unpack ∘ CS.original → name, T.unpack → value)
+             → attr name (txt value) ⤛ ()
+
+formatObjectLink ∷ ArrowXml (⇝) ⇒ (URI, InlineElement) ⇝ XmlTree
+formatObjectLink 
+    = proc (baseURI, ObjectLink page text)
+    → let uri   = mkObjectURI baseURI page
+          label = fromMaybe ("{" ⊕ page ⊕ "}") text
+      in
+        mkAnchor ⤙ (uri, label)
+
+formatPageLink ∷ ArrowXml (⇝) ⇒ (URI, InlineElement) ⇝ XmlTree
+formatPageLink 
+    = proc (baseURI, PageLink page fragment text)
+    → let uri    = case (page, fragment) of
+                      (Just  x, Just  y) → mkPageFragmentURI baseURI x y
+                      (Just  x, Nothing) → mkPageURI baseURI x
+                      (Nothing, Just  y) → mkFragmentURI y
+                      _                  → (⊥)
+          dLabel = fromMaybe (∅) page ⊕ maybe (∅) (T.cons '#') fragment
+          label  = fromMaybe dLabel text
+      in
+        mkAnchor ⤙ (uri, label)
+
+formatImage ∷ ArrowXml (⇝) ⇒ (URI, InlineElement) ⇝ XmlTree
+formatImage = proc (baseURI, Image src alt)
+            → let uri  = case src of
+                           Left  u    → u
+                           Right name → mkObjectURI baseURI name
+                  href = uriToString id uri ""
+              in
+                ( eelem "img"
+                  += sattr "src" href
+                  += ( case alt of
+                         Just x  → sattr "alt" (T.unpack x)
+                         Nothing → none
+                     )
+                ) ⤛ ()
+
+
+formatExternalLink ∷ ArrowXml (⇝) ⇒ InlineElement ⇝ XmlTree
+formatExternalLink 
+    = proc (ExternalLink uri text)
+    → let href  = uriToString id uri ""
+          label = fromMaybe (T.pack href) text
+      in
+        mkAnchor -< (uri, label)
+
+mkAnchor ∷ ArrowXml (⇝) ⇒ (URI, Text) ⇝ XmlTree
+mkAnchor = eelem "a"
+           += attr "href" (arr (flip (uriToString id) "" ∘ fst) ⋙ mkText)
+           += (arr (T.unpack ∘ snd) ⋙ mkText)
+
+attachXHtmlNS ∷ ArrowXml (⇝) ⇒ XmlTree ⇝ XmlTree
+attachXHtmlNS = processTopDown (changeQName attach `when` isElem)
+    where
+      attach ∷ QName → QName
+      attach = setNamespaceUri' (newXName "http://www.w3.org/1999/xhtml")