pandoc-lua-marshal-0.2.7: src/Text/Pandoc/Lua/Marshal/Block.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{- |
Marshal values of types that make up 'Block' elements.
-}
module Text.Pandoc.Lua.Marshal.Block
( -- * Single Block elements
typeBlock
, peekBlock
, peekBlockFuzzy
, pushBlock
-- * List of Blocks
, peekBlocks
, peekBlocksFuzzy
, pushBlocks
-- * Constructors
, blockConstructors
, mkBlocks
-- * Walk
, walkBlockSplicing
, walkBlocksStraight
) where
import Control.Applicative ((<|>), optional)
import Control.Monad.Catch (throwM)
import Control.Monad ((<$!>))
import Data.Aeson (encode)
import Data.Data (showConstr, toConstr)
import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (Proxy))
import Data.Text (Text)
import HsLua hiding (Div)
import Text.Pandoc.Lua.Marshal.Attr (peekAttr, pushAttr)
import Text.Pandoc.Lua.Marshal.Content
( Content (..), contentTypeDescription, peekContent, pushContent
, peekDefinitionItem )
import Text.Pandoc.Lua.Marshal.Filter (Filter, peekFilter)
import Text.Pandoc.Lua.Marshal.Format (peekFormat, pushFormat)
import Text.Pandoc.Lua.Marshal.Inline (peekInlinesFuzzy)
import Text.Pandoc.Lua.Marshal.List (newListMetatable, pushPandocList)
import Text.Pandoc.Lua.Marshal.ListAttributes
( peekListAttributes, pushListAttributes )
import Text.Pandoc.Lua.Marshal.Shared (walkBlocksAndInlines)
import Text.Pandoc.Lua.Marshal.TableParts
( peekCaptionFuzzy, pushCaption
, peekColSpec, pushColSpec
, peekTableBody, pushTableBody
, peekTableFoot, pushTableFoot
, peekTableHead, pushTableHead
)
import Text.Pandoc.Lua.Walk (SpliceList, Walkable, walkStraight, walkSplicing)
import Text.Pandoc.Definition
-- | Pushes an Block value as userdata object.
pushBlock :: LuaError e => Pusher e Block
pushBlock = pushUD typeBlock
{-# INLINE pushBlock #-}
-- | Retrieves an Block value.
peekBlock :: LuaError e => Peeker e Block
peekBlock = peekUD typeBlock
{-# INLINE peekBlock #-}
-- | Retrieves a list of Block values.
peekBlocks :: LuaError e
=> Peeker e [Block]
peekBlocks = peekList peekBlock
{-# INLINABLE peekBlocks #-}
-- | Pushes a list of Block values.
pushBlocks :: LuaError e
=> Pusher e [Block]
pushBlocks xs = do
pushList pushBlock xs
newListMetatable "Blocks" $ do
pushName "walk"
pushDocumentedFunction $ lambda
### flip walkBlocksAndInlines
<#> parameter peekBlocksFuzzy "Blocks" "self" ""
<#> parameter peekFilter "Filter" "lua_filter" "table of filter functions"
=#> functionResult pushBlocks "Blocks" "modified list"
rawset (nth 3)
pushName "clone"
pushDocumentedFunction $ lambda
### return
<#> parameter peekBlocksFuzzy "Blocks" "self" ""
=#> functionResult pushBlocks "Blocks" "deep copy"
rawset (nth 3)
pushName "__tostring"
pushDocumentedFunction $ lambda
### liftPure show
<#> parameter peekBlocksFuzzy "Blocks" "self" ""
=#> functionResult pushString "string" "native Haskell representation"
rawset (nth 3)
pushName "__tojson"
pushDocumentedFunction $ lambda
### liftPure encode
<#> parameter peekBlocksFuzzy "Blocks" "self" ""
=#> functionResult pushLazyByteString "string" "JSON representation"
rawset (nth 3)
setmetatable (nth 2)
{-# INLINABLE pushBlocks #-}
-- | Try extra hard to retrieve an Block value from the stack. Treats
-- bare strings as @Str@ values.
peekBlockFuzzy :: LuaError e
=> Peeker e Block
peekBlockFuzzy idx =
peekBlock idx
<|> (Plain <$!> peekInlinesFuzzy idx)
<|> (failPeek =<<
typeMismatchMessage "Block or list of Inlines" idx)
{-# INLINABLE peekBlockFuzzy #-}
-- | Try extra-hard to return the value at the given index as a list of
-- inlines.
peekBlocksFuzzy :: LuaError e
=> Peeker e [Block]
peekBlocksFuzzy idx =
peekList peekBlockFuzzy idx
<|> (pure <$!> peekBlockFuzzy idx)
<|> (failPeek =<<
typeMismatchMessage "Block, list of Blocks, or compatible element" idx)
{-# INLINABLE peekBlocksFuzzy #-}
-- | Block object type.
typeBlock :: forall e. LuaError e => DocumentedType e Block
typeBlock = deftype "Block"
[ operation Eq $ lambda
### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))
<#> parameter (optional . peekBlockFuzzy) "Block" "a" ""
<#> parameter (optional . peekBlockFuzzy) "Block" "b" ""
=#> boolResult "whether the two values are equal"
, operation Tostring $ lambda
### liftPure show
<#> udparam typeBlock "self" ""
=#> functionResult pushString "string" "Haskell representation"
, operation (CustomOperation "__tojson") $ lambda
### liftPure encode
<#> udparam typeBlock "self" ""
=#> functionResult pushLazyByteString "string" "JSON representation"
]
[ possibleProperty "attr" "element attributes"
(pushAttr, \case
CodeBlock attr _ -> Actual attr
Div attr _ -> Actual attr
Figure attr _ _ -> Actual attr
Header _ attr _ -> Actual attr
Table attr _ _ _ _ _ -> Actual attr
_ -> Absent)
(peekAttr, \case
CodeBlock _ code -> Actual . flip CodeBlock code
Div _ blks -> Actual . flip Div blks
Figure _ capt blks -> Actual . (\attr -> Figure attr capt blks)
Header lvl _ blks -> Actual . (\attr -> Header lvl attr blks)
Table _ c cs h bs f -> Actual . (\attr -> Table attr c cs h bs f)
_ -> const Absent)
, possibleProperty "bodies" "table bodies"
(pushPandocList pushTableBody, \case
Table _ _ _ _ bs _ -> Actual bs
_ -> Absent)
(peekList peekTableBody, \case
Table attr c cs h _ f -> Actual . (\bs -> Table attr c cs h bs f)
_ -> const Absent)
, possibleProperty "caption" "element caption"
(pushCaption, \case
Figure _ capt _ -> Actual capt
Table _ capt _ _ _ _ -> Actual capt
_ -> Absent)
(peekCaptionFuzzy, \case
Figure attr _ blks -> Actual . (\c -> Figure attr c blks)
Table attr _ cs h bs f -> Actual . (\c -> Table attr c cs h bs f)
_ -> const Absent)
, possibleProperty "colspecs" "column alignments and widths"
(pushPandocList pushColSpec, \case
Table _ _ cs _ _ _ -> Actual cs
_ -> Absent)
(peekList peekColSpec, \case
Table attr c _ h bs f -> Actual . (\cs -> Table attr c cs h bs f)
_ -> const Absent)
, possibleProperty "content" "element content"
(pushContent, getBlockContent)
(peekContent, setBlockContent (Proxy @e))
, possibleProperty "foot" "table foot"
(pushTableFoot, \case {Table _ _ _ _ _ f -> Actual f; _ -> Absent})
(peekTableFoot, \case
Table attr c cs h bs _ -> Actual . Table attr c cs h bs
_ -> const Absent)
, possibleProperty "format" "format of raw content"
(pushFormat, \case {RawBlock f _ -> Actual f; _ -> Absent})
(peekFormat, \case
RawBlock _ txt -> Actual . (`RawBlock` txt)
_ -> const Absent)
, possibleProperty "head" "table head"
(pushTableHead, \case {Table _ _ _ h _ _ -> Actual h; _ -> Absent})
(peekTableHead, \case
Table attr c cs _ bs f -> Actual . (\h -> Table attr c cs h bs f)
_ -> const Absent)
, possibleProperty "level" "heading level"
(pushIntegral, \case {Header lvl _ _ -> Actual lvl; _ -> Absent})
(peekIntegral, \case
Header _ attr inlns -> Actual . \lvl -> Header lvl attr inlns
_ -> const Absent)
, possibleProperty "listAttributes" "ordered list attributes"
(pushListAttributes, \case
OrderedList listAttr _ -> Actual listAttr
_ -> Absent)
(peekListAttributes, \case
OrderedList _ content -> Actual . (`OrderedList` content)
_ -> const Absent)
, possibleProperty "text" "text contents"
(pushText, getBlockText)
(peekText, setBlockText)
, readonly "tag" "type of Block"
(pushString, showConstr . toConstr )
, alias "t" "tag" ["tag"]
, alias "c" "content" ["content"]
, alias "identifier" "element identifier" ["attr", "identifier"]
, alias "classes" "element classes" ["attr", "classes"]
, alias "attributes" "other element attributes" ["attr", "attributes"]
, alias "start" "ordered list start number" ["listAttributes", "start"]
, alias "style" "ordered list style" ["listAttributes", "style"]
, alias "delimiter" "numbering delimiter" ["listAttributes", "delimiter"]
, method $ defun "clone"
### return
<#> parameter peekBlock "Block" "block" "self"
=#> functionResult pushBlock "Block" "cloned Block"
, method $ defun "show"
### liftPure show
<#> parameter peekBlock "Block" "self" ""
=#> functionResult pushString "string" "Haskell string representation"
, method $ defun "walk"
### flip walkBlocksAndInlines
<#> parameter peekBlock "Block" "self" ""
<#> parameter peekFilter "Filter" "lua_filter" "table of filter functions"
=#> functionResult pushBlock "Block" "modified element"
]
getBlockContent :: Block -> Possible Content
getBlockContent = \case
-- inline content
Para inlns -> Actual $ ContentInlines inlns
Plain inlns -> Actual $ ContentInlines inlns
Header _ _ inlns -> Actual $ ContentInlines inlns
-- block content
BlockQuote blks -> Actual $ ContentBlocks blks
Div _ blks -> Actual $ ContentBlocks blks
Figure _ _ blks -> Actual $ ContentBlocks blks
-- lines content
LineBlock lns -> Actual $ ContentLines lns
-- list items content
BulletList itms -> Actual $ ContentListItems itms
OrderedList _ itms -> Actual $ ContentListItems itms
-- definition items content
DefinitionList itms -> Actual $ ContentDefItems itms
_ -> Absent
setBlockContent :: forall e. LuaError e
=> Proxy e -> Block -> Content -> Possible Block
setBlockContent _ = \case
-- inline content
Para _ -> Actual . Para . inlineContent
Plain _ -> Actual . Plain . inlineContent
Header attr lvl _ -> Actual . Header attr lvl . inlineContent
-- block content
BlockQuote _ -> Actual . BlockQuote . blockContent
Div attr _ -> Actual . Div attr . blockContent
Figure attr c _ -> Actual . Figure attr c . blockContent
-- lines content
LineBlock _ -> Actual . LineBlock . lineContent
-- list items content
BulletList _ -> Actual . BulletList . listItemContent
OrderedList la _ -> Actual . OrderedList la . listItemContent
-- definition items content
DefinitionList _ -> Actual . DefinitionList . defItemContent
_ -> const Absent
where
inlineContent = \case
ContentInlines inlns -> inlns
c -> throwM . luaException @e $
"expected Inlines, got " <> contentTypeDescription c
blockContent = \case
ContentBlocks blks -> blks
ContentInlines [] -> []
ContentInlines inlns -> [Plain inlns]
c -> throwM . luaException @e $
"expected Blocks, got " <> contentTypeDescription c
lineContent = \case
ContentLines lns -> lns
c -> throwM . luaException @e $
"expected list of lines (Inlines), got " <> contentTypeDescription c
defItemContent = \case
ContentDefItems itms -> itms
c -> throwM . luaException @e $
"expected definition items, got " <> contentTypeDescription c
listItemContent = \case
ContentBlocks blks -> map (:[]) blks
ContentLines lns -> map ((:[]) . Plain) lns
ContentListItems itms -> itms
c -> throwM . luaException @e $
"expected list of items, got " <> contentTypeDescription c
getBlockText :: Block -> Possible Text
getBlockText = \case
CodeBlock _ lst -> Actual lst
RawBlock _ raw -> Actual raw
_ -> Absent
setBlockText :: Block -> Text -> Possible Block
setBlockText = \case
CodeBlock attr _ -> Actual . CodeBlock attr
RawBlock f _ -> Actual . RawBlock f
_ -> const Absent
-- | Constructor functions for 'Block' elements.
blockConstructors :: LuaError e => [DocumentedFunction e]
blockConstructors =
[ defun "BlockQuote"
### liftPure BlockQuote
<#> blocksParam
=#> blockResult "BlockQuote element"
, defun "BulletList"
### liftPure BulletList
<#> blockItemsParam "list items"
=#> blockResult "BulletList element"
, defun "CodeBlock"
### liftPure2 (\code mattr -> CodeBlock (fromMaybe nullAttr mattr) code)
<#> textParam "text" "code block content"
<#> optAttrParam
=#> blockResult "CodeBlock element"
, defun "DefinitionList"
### liftPure DefinitionList
<#> parameter (choice
[ peekList peekDefinitionItem
, \idx -> (:[]) <$!> peekDefinitionItem idx
])
"{{Inlines, {Blocks,...}},...}"
"content" "definition items"
=#> blockResult "DefinitionList element"
, defun "Div"
### liftPure2 (\content mattr -> Div (fromMaybe nullAttr mattr) content)
<#> blocksParam
<#> optAttrParam
=#> blockResult "Div element"
, defun "Figure"
### liftPure3 (\content mcapt mattr ->
let attr = fromMaybe nullAttr mattr
capt = fromMaybe (Caption mempty mempty) mcapt
in Figure attr capt content)
<#> parameter peekBlocksFuzzy "Blocks" "content" "figure content"
<#> opt (parameter peekCaptionFuzzy "Caption" "caption" "figure caption")
<#> optAttrParam
=#> blockResult "Figure element"
, defun "Header"
### liftPure3 (\lvl content mattr ->
Header lvl (fromMaybe nullAttr mattr) content)
<#> parameter peekIntegral "integer" "level" "heading level"
<#> parameter peekInlinesFuzzy "Inlines" "content" "inline content"
<#> optAttrParam
=#> blockResult "Header element"
, defun "HorizontalRule"
### return HorizontalRule
=#> blockResult "HorizontalRule element"
, defun "LineBlock"
### liftPure LineBlock
<#> parameter (peekList peekInlinesFuzzy) "{Inlines,...}" "content" "lines"
=#> blockResult "LineBlock element"
, defun "OrderedList"
### liftPure2 (\items mListAttrib ->
let defListAttrib = (1, DefaultStyle, DefaultDelim)
in OrderedList (fromMaybe defListAttrib mListAttrib) items)
<#> blockItemsParam "ordered list items"
<#> opt (parameter peekListAttributes "ListAttributes" "listAttributes"
"specifier for the list's numbering")
=#> blockResult "OrderedList element"
, defun "Para"
### liftPure Para
<#> parameter peekInlinesFuzzy "Inlines" "content" "paragraph content"
=#> blockResult "Para element"
, defun "Plain"
### liftPure Plain
<#> parameter peekInlinesFuzzy "Inlines" "content" "paragraph content"
=#> blockResult "Plain element"
, defun "RawBlock"
### liftPure2 RawBlock
<#> parameter peekFormat "Format" "format" "format of content"
<#> textParam "text" "raw content"
=#> blockResult "RawBlock element"
, defun "Table"
### (\capt colspecs thead tbodies tfoot mattr ->
let attr = fromMaybe nullAttr mattr
in return $! attr `seq` capt `seq` colspecs `seq` thead `seq` tbodies
`seq` tfoot `seq` Table attr capt colspecs thead tbodies tfoot)
<#> parameter peekCaptionFuzzy "Caption" "caption" "table caption"
<#> parameter (peekList peekColSpec) "{ColSpec,...}" "colspecs"
"column alignments and widths"
<#> parameter peekTableHead "TableHead" "head" "table head"
<#> parameter (peekList peekTableBody) "{TableBody,...}" "bodies"
"table bodies"
<#> parameter peekTableFoot "TableFoot" "foot" "table foot"
<#> optAttrParam
=#> blockResult "Table element"
]
where
blockResult = functionResult pushBlock "Block"
blocksParam = parameter peekBlocksFuzzy "Blocks" "content" "block content"
blockItemsParam = parameter peekItemsFuzzy "List of Blocks" "content"
peekItemsFuzzy idx = peekList peekBlocksFuzzy idx
<|> ((:[]) <$!> peekBlocksFuzzy idx)
optAttrParam = opt (parameter peekAttr "Attr" "attr" "additional attributes")
-- | Constructor for a list of `Block` values.
mkBlocks :: LuaError e => DocumentedFunction e
mkBlocks = defun "Blocks"
### liftPure id
<#> parameter peekBlocksFuzzy "Blocks" "blocks" "block elements"
=#> functionResult pushBlocks "Blocks" "list of block elements"
--
-- walk
--
walkBlockSplicing :: (LuaError e, Walkable (SpliceList Block) a)
=> Filter -> a -> LuaE e a
walkBlockSplicing = walkSplicing pushBlock peekBlocksFuzzy
walkBlocksStraight :: (LuaError e, Walkable [Block] a)
=> Filter -> a -> LuaE e a
walkBlocksStraight = walkStraight "Blocks" pushBlocks peekBlocksFuzzy