Blame CMarkGFM.hsc

Packit 1f51f5
{-# LANGUAGE CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving,
Packit 1f51f5
    DeriveGeneric, DeriveDataTypeable, FlexibleContexts #-}
Packit 1f51f5
Packit 1f51f5
module CMarkGFM (
Packit 1f51f5
    commonmarkToHtml
Packit 1f51f5
  , commonmarkToXml
Packit 1f51f5
  , commonmarkToMan
Packit 1f51f5
  , commonmarkToLaTeX
Packit 1f51f5
  , commonmarkToNode
Packit 1f51f5
  , nodeToHtml
Packit 1f51f5
  , nodeToXml
Packit 1f51f5
  , nodeToMan
Packit 1f51f5
  , nodeToLaTeX
Packit 1f51f5
  , nodeToCommonmark
Packit 1f51f5
  , optSourcePos
Packit 1f51f5
  , optHardBreaks
Packit 1f51f5
  , optSmart
Packit 1f51f5
  , optSafe
Packit 1f51f5
  , extStrikethrough
Packit 1f51f5
  , extTable
Packit 1f51f5
  , extAutolink
Packit 1f51f5
  , extTagfilter
Packit 1f51f5
  , Node(..)
Packit 1f51f5
  , NodeType(..)
Packit 1f51f5
  , PosInfo(..)
Packit 1f51f5
  , DelimType(..)
Packit 1f51f5
  , ListType(..)
Packit 1f51f5
  , ListAttributes(..)
Packit 1f51f5
  , Url
Packit 1f51f5
  , Title
Packit 1f51f5
  , Level
Packit 1f51f5
  , Info
Packit 1f51f5
  , TableCellAlignment(..)
Packit 1f51f5
  , CMarkOption
Packit 1f51f5
  , CMarkExtension
Packit 1f51f5
  ) where
Packit 1f51f5
Packit 1f51f5
import Foreign
Packit 1f51f5
import Foreign.C.Types
Packit 1f51f5
import Foreign.C.String (CString, withCString)
Packit 1f51f5
import qualified System.IO.Unsafe as Unsafe
Packit 1f51f5
import Data.Maybe (fromMaybe)
Packit 1f51f5
import GHC.Generics (Generic)
Packit 1f51f5
import Data.Data (Data)
Packit 1f51f5
import Data.Typeable (Typeable)
Packit 1f51f5
import Data.Text (Text, empty)
Packit 1f51f5
import qualified Data.Text.Foreign as TF
Packit 1f51f5
import qualified Data.ByteString as B
Packit 1f51f5
import Data.Text.Encoding (encodeUtf8)
Packit 1f51f5
import Control.Applicative ((<$>), (<*>))
Packit 1f51f5
Packit 1f51f5
#include <cmark.h>
Packit 1f51f5
#include <core-extensions.h>
Packit 1f51f5
Packit 1f51f5
-- | Ensure core extensions are registered.
Packit 1f51f5
ensurePluginsRegistered :: IO ()
Packit 1f51f5
ensurePluginsRegistered = c_core_extensions_ensure_registered
Packit 1f51f5
Packit 1f51f5
-- | Frees a cmark linked list, produced by extsToLlist.
Packit 1f51f5
freeLlist :: LlistPtr a -> IO ()
Packit 1f51f5
freeLlist = c_cmark_llist_free c_CMARK_DEFAULT_MEM_ALLOCATOR
Packit 1f51f5
Packit 1f51f5
-- | Converts a list of resolved extension pointers to a single cmark
Packit 1f51f5
-- linked list, which can be passed to functions requiring a list of
Packit 1f51f5
-- extensions.
Packit 1f51f5
extsToLlist :: [ExtensionPtr] -> IO (LlistPtr ExtensionPtr)
Packit 1f51f5
extsToLlist [] = return nullPtr
Packit 1f51f5
extsToLlist (h:t) = do
Packit 1f51f5
  t' <- extsToLlist t
Packit 1f51f5
  c_cmark_llist_append c_CMARK_DEFAULT_MEM_ALLOCATOR t' (castPtr h)
Packit 1f51f5
Packit 1f51f5
-- | Resolves CMarkExtensions to pointers.
Packit 1f51f5
resolveExts :: [CMarkExtension] -> IO [ExtensionPtr]
Packit 1f51f5
resolveExts exts = do
Packit 1f51f5
  ensurePluginsRegistered
Packit 1f51f5
  mapM resolveExt exts
Packit 1f51f5
  where resolveExt ext = do p <- withCString (unCMarkExtension ext) c_cmark_find_syntax_extension
Packit 1f51f5
                            if p == nullPtr then
Packit 1f51f5
                              fail $ "could not load extension " ++ unCMarkExtension ext
Packit 1f51f5
                            else
Packit 1f51f5
                              return p
Packit 1f51f5
Packit 1f51f5
-- | Convert CommonMark formatted text to Html, using cmark's
Packit 1f51f5
-- built-in renderer.
Packit 1f51f5
commonmarkToHtml :: [CMarkOption] -> [CMarkExtension] -> Text -> Text
Packit 1f51f5
commonmarkToHtml opts exts =
Packit 1f51f5
  commonmarkToX render_html opts exts Nothing
Packit 1f51f5
  where exts' = Unsafe.unsafePerformIO $ resolveExts exts
Packit 1f51f5
        render_html n o _ = do
Packit 1f51f5
          llist <- extsToLlist exts'
Packit 1f51f5
          r <- c_cmark_render_html n o llist
Packit 1f51f5
          freeLlist llist
Packit 1f51f5
          return r
Packit 1f51f5
Packit 1f51f5
-- | Convert CommonMark formatted text to CommonMark XML, using cmark's
Packit 1f51f5
-- built-in renderer.
Packit 1f51f5
commonmarkToXml :: [CMarkOption] -> [CMarkExtension] -> Text -> Text
Packit 1f51f5
commonmarkToXml opts exts = commonmarkToX render_xml opts exts Nothing
Packit 1f51f5
  where render_xml n o _ = c_cmark_render_xml n o
Packit 1f51f5
Packit 1f51f5
-- | Convert CommonMark formatted text to groff man, using cmark's
Packit 1f51f5
-- built-in renderer.
Packit 1f51f5
commonmarkToMan :: [CMarkOption] -> [CMarkExtension] -> Maybe Int -> Text -> Text
Packit 1f51f5
commonmarkToMan = commonmarkToX c_cmark_render_man
Packit 1f51f5
Packit 1f51f5
-- | Convert CommonMark formatted text to latex, using cmark's
Packit 1f51f5
-- built-in renderer.
Packit 1f51f5
commonmarkToLaTeX :: [CMarkOption] -> [CMarkExtension] -> Maybe Int -> Text -> Text
Packit 1f51f5
commonmarkToLaTeX = commonmarkToX c_cmark_render_latex
Packit 1f51f5
Packit 1f51f5
-- | Convert CommonMark formatted text to a structured 'Node' tree,
Packit 1f51f5
-- which can be transformed or rendered using Haskell code.
Packit 1f51f5
commonmarkToNode :: [CMarkOption] -> [CMarkExtension] -> Text -> Node
Packit 1f51f5
commonmarkToNode opts exts s = Unsafe.unsafePerformIO $ do
Packit 1f51f5
  exts' <- resolveExts exts
Packit 1f51f5
  parser <- c_cmark_parser_new (combineOptions opts)
Packit 1f51f5
  mapM_ (c_cmark_parser_attach_syntax_extension parser) exts'
Packit 1f51f5
  TF.withCStringLen s $! \(ptr, len) ->
Packit 1f51f5
             c_cmark_parser_feed parser ptr len
Packit 1f51f5
  nptr <- c_cmark_parser_finish parser
Packit 1f51f5
  c_cmark_parser_free parser
Packit 1f51f5
  fptr <- newForeignPtr c_cmark_node_free nptr
Packit 1f51f5
  withForeignPtr fptr toNode
Packit 1f51f5
Packit 1f51f5
nodeToHtml :: [CMarkOption] -> [CMarkExtension] -> Node -> Text
Packit 1f51f5
nodeToHtml opts exts =
Packit 1f51f5
  nodeToX render_html opts Nothing
Packit 1f51f5
  where exts' = Unsafe.unsafePerformIO $ resolveExts exts
Packit 1f51f5
        render_html n o _ = do
Packit 1f51f5
          llist <- extsToLlist exts'
Packit 1f51f5
          r <- c_cmark_render_html n o llist
Packit 1f51f5
          freeLlist llist
Packit 1f51f5
          return r
Packit 1f51f5
Packit 1f51f5
nodeToXml :: [CMarkOption] -> Node -> Text
Packit 1f51f5
nodeToXml opts = nodeToX render_xml opts Nothing
Packit 1f51f5
  where render_xml n o _ = c_cmark_render_xml n o
Packit 1f51f5
Packit 1f51f5
nodeToMan :: [CMarkOption] -> Maybe Int -> Node -> Text
Packit 1f51f5
nodeToMan = nodeToX c_cmark_render_man
Packit 1f51f5
Packit 1f51f5
nodeToLaTeX :: [CMarkOption] -> Maybe Int -> Node -> Text
Packit 1f51f5
nodeToLaTeX = nodeToX c_cmark_render_latex
Packit 1f51f5
Packit 1f51f5
nodeToCommonmark :: [CMarkOption] -> Maybe Int -> Node -> Text
Packit 1f51f5
nodeToCommonmark = nodeToX c_cmark_render_commonmark
Packit 1f51f5
Packit 1f51f5
type Renderer = NodePtr -> CInt -> Int -> IO CString
Packit 1f51f5
Packit 1f51f5
nodeToX :: Renderer -> [CMarkOption] -> Maybe Int -> Node -> Text
Packit 1f51f5
nodeToX renderer opts mbWidth node = Unsafe.unsafePerformIO $ do
Packit 1f51f5
  nptr <- fromNode node
Packit 1f51f5
  fptr <- newForeignPtr c_cmark_node_free nptr
Packit 1f51f5
  withForeignPtr fptr $ \ptr -> do
Packit 1f51f5
    cstr <- renderer ptr (combineOptions opts) (fromMaybe 0 mbWidth)
Packit 1f51f5
    TF.peekCStringLen (cstr, c_strlen cstr)
Packit 1f51f5
Packit 1f51f5
commonmarkToX :: Renderer
Packit 1f51f5
              -> [CMarkOption]
Packit 1f51f5
              -> [CMarkExtension]
Packit 1f51f5
              -> Maybe Int
Packit 1f51f5
              -> Text
Packit 1f51f5
              -> Text
Packit 1f51f5
commonmarkToX renderer opts exts mbWidth s = Unsafe.unsafePerformIO $
Packit 1f51f5
  TF.withCStringLen s $ \(ptr, len) -> do
Packit 1f51f5
    let opts' = combineOptions opts
Packit 1f51f5
    exts' <- resolveExts exts
Packit 1f51f5
    parser <- c_cmark_parser_new opts'
Packit 1f51f5
    mapM_ (c_cmark_parser_attach_syntax_extension parser) exts'
Packit 1f51f5
    c_cmark_parser_feed parser ptr len
Packit 1f51f5
    nptr <- c_cmark_parser_finish parser
Packit 1f51f5
    c_cmark_parser_free parser
Packit 1f51f5
    fptr <- newForeignPtr c_cmark_node_free nptr
Packit 1f51f5
    withForeignPtr fptr $ \p -> do
Packit 1f51f5
      str <- renderer p opts' (fromMaybe 0 mbWidth)
Packit 1f51f5
      t <- TF.peekCStringLen $! (str, c_strlen str)
Packit 1f51f5
      return t
Packit 1f51f5
Packit 1f51f5
data ParserPhantom
Packit 1f51f5
type ParserPtr = Ptr ParserPhantom
Packit 1f51f5
Packit 1f51f5
data NodePhantom
Packit 1f51f5
type NodePtr = Ptr NodePhantom
Packit 1f51f5
Packit 1f51f5
data LlistPhantom a
Packit 1f51f5
type LlistPtr a = Ptr (LlistPhantom a)
Packit 1f51f5
Packit 1f51f5
data MemPhantom
Packit 1f51f5
type MemPtr = Ptr MemPhantom
Packit 1f51f5
Packit 1f51f5
data ExtensionPhantom
Packit 1f51f5
type ExtensionPtr = Ptr ExtensionPhantom
Packit 1f51f5
Packit 1f51f5
data Node = Node (Maybe PosInfo) NodeType [Node]
Packit 1f51f5
     deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
data DelimType =
Packit 1f51f5
    PERIOD_DELIM
Packit 1f51f5
  | PAREN_DELIM
Packit 1f51f5
  deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
data ListType =
Packit 1f51f5
    BULLET_LIST
Packit 1f51f5
  | ORDERED_LIST
Packit 1f51f5
  deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
data ListAttributes = ListAttributes{
Packit 1f51f5
    listType     :: ListType
Packit 1f51f5
  , listTight    :: Bool
Packit 1f51f5
  , listStart    :: Int
Packit 1f51f5
  , listDelim    :: DelimType
Packit 1f51f5
  } deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
type Url = Text
Packit 1f51f5
Packit 1f51f5
type Title = Text
Packit 1f51f5
Packit 1f51f5
type Level = Int
Packit 1f51f5
Packit 1f51f5
type Info = Text
Packit 1f51f5
Packit 1f51f5
type OnEnter = Text
Packit 1f51f5
Packit 1f51f5
type OnExit = Text
Packit 1f51f5
Packit 1f51f5
data TableCellAlignment = NoAlignment | LeftAligned | CenterAligned | RightAligned
Packit 1f51f5
     deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
data NodeType =
Packit 1f51f5
    DOCUMENT
Packit 1f51f5
  | THEMATIC_BREAK
Packit 1f51f5
  | PARAGRAPH
Packit 1f51f5
  | BLOCK_QUOTE
Packit 1f51f5
  | HTML_BLOCK Text
Packit 1f51f5
  | CUSTOM_BLOCK OnEnter OnExit
Packit 1f51f5
  | CODE_BLOCK Info Text
Packit 1f51f5
  | HEADING Level
Packit 1f51f5
  | LIST ListAttributes
Packit 1f51f5
  | ITEM
Packit 1f51f5
  | TEXT Text
Packit 1f51f5
  | SOFTBREAK
Packit 1f51f5
  | LINEBREAK
Packit 1f51f5
  | HTML_INLINE Text
Packit 1f51f5
  | CUSTOM_INLINE OnEnter OnExit
Packit 1f51f5
  | CODE Text
Packit 1f51f5
  | EMPH
Packit 1f51f5
  | STRONG
Packit 1f51f5
  | LINK Url Title
Packit 1f51f5
  | IMAGE Url Title
Packit 1f51f5
  | STRIKETHROUGH
Packit 1f51f5
  | TABLE [TableCellAlignment]
Packit 1f51f5
  | TABLE_ROW
Packit 1f51f5
  | TABLE_CELL
Packit 1f51f5
  deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
data PosInfo = PosInfo{ startLine   :: Int
Packit 1f51f5
                      , startColumn :: Int
Packit 1f51f5
                      , endLine     :: Int
Packit 1f51f5
                      , endColumn   :: Int
Packit 1f51f5
                      }
Packit 1f51f5
  deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
Packit 1f51f5
Packit 1f51f5
newtype CMarkOption = CMarkOption { unCMarkOption :: CInt }
Packit 1f51f5
Packit 1f51f5
-- | Combine a list of options into a single option, using bitwise or.
Packit 1f51f5
combineOptions :: [CMarkOption] -> CInt
Packit 1f51f5
combineOptions = foldr ((.|.) . unCMarkOption) 0
Packit 1f51f5
Packit 1f51f5
-- | Include a @data-sourcepos@ attribute on block elements.
Packit 1f51f5
optSourcePos :: CMarkOption
Packit 1f51f5
optSourcePos = CMarkOption #const CMARK_OPT_SOURCEPOS
Packit 1f51f5
Packit 1f51f5
-- | Render @softbreak@ elements as hard line breaks.
Packit 1f51f5
optHardBreaks :: CMarkOption
Packit 1f51f5
optHardBreaks = CMarkOption #const CMARK_OPT_HARDBREAKS
Packit 1f51f5
Packit 1f51f5
-- | Convert straight quotes to curly, @---@ to em-dash, @--@ to en-dash.
Packit 1f51f5
optSmart :: CMarkOption
Packit 1f51f5
optSmart = CMarkOption #const CMARK_OPT_SMART
Packit 1f51f5
Packit 1f51f5
-- | Suppress rendering of raw HTML and potentially dangerous URLs in links
Packit 1f51f5
-- and images.
Packit 1f51f5
optSafe :: CMarkOption
Packit 1f51f5
optSafe = CMarkOption #const CMARK_OPT_SAFE
Packit 1f51f5
Packit 1f51f5
newtype CMarkExtension = CMarkExtension { unCMarkExtension :: String }
Packit 1f51f5
Packit 1f51f5
extStrikethrough :: CMarkExtension
Packit 1f51f5
extStrikethrough = CMarkExtension "strikethrough"
Packit 1f51f5
Packit 1f51f5
extTable :: CMarkExtension
Packit 1f51f5
extTable = CMarkExtension "table"
Packit 1f51f5
Packit 1f51f5
extAutolink :: CMarkExtension
Packit 1f51f5
extAutolink = CMarkExtension "autolink"
Packit 1f51f5
Packit 1f51f5
extTagfilter :: CMarkExtension
Packit 1f51f5
extTagfilter = CMarkExtension "tagfilter"
Packit 1f51f5
Packit 1f51f5
ptrToNodeType :: NodePtr -> IO NodeType
Packit 1f51f5
ptrToNodeType ptr = do
Packit 1f51f5
  nodeType <- c_cmark_node_get_type ptr
Packit 1f51f5
  case nodeType of
Packit 1f51f5
       #const CMARK_NODE_DOCUMENT
Packit 1f51f5
         -> return DOCUMENT
Packit 1f51f5
       #const CMARK_NODE_THEMATIC_BREAK
Packit 1f51f5
         -> return THEMATIC_BREAK
Packit 1f51f5
       #const CMARK_NODE_PARAGRAPH
Packit 1f51f5
         -> return PARAGRAPH
Packit 1f51f5
       #const CMARK_NODE_BLOCK_QUOTE
Packit 1f51f5
         -> return BLOCK_QUOTE
Packit 1f51f5
       #const CMARK_NODE_HTML_BLOCK
Packit 1f51f5
         -> HTML_BLOCK <$> literal
Packit 1f51f5
       #const CMARK_NODE_CUSTOM_BLOCK
Packit 1f51f5
         -> CUSTOM_BLOCK <$> onEnter <*> onExit
Packit 1f51f5
       #const CMARK_NODE_CODE_BLOCK
Packit 1f51f5
         -> CODE_BLOCK <$> info
Packit 1f51f5
                       <*> literal
Packit 1f51f5
       #const CMARK_NODE_LIST
Packit 1f51f5
         -> LIST <$> listAttr
Packit 1f51f5
       #const CMARK_NODE_ITEM
Packit 1f51f5
         -> return ITEM
Packit 1f51f5
       #const CMARK_NODE_HEADING
Packit 1f51f5
         -> HEADING <$> level
Packit 1f51f5
       #const CMARK_NODE_EMPH
Packit 1f51f5
         -> return EMPH
Packit 1f51f5
       #const CMARK_NODE_STRONG
Packit 1f51f5
         -> return STRONG
Packit 1f51f5
       #const CMARK_NODE_LINK
Packit 1f51f5
         -> LINK <$> url <*> title
Packit 1f51f5
       #const CMARK_NODE_IMAGE
Packit 1f51f5
         -> IMAGE <$> url <*> title
Packit 1f51f5
       #const CMARK_NODE_TEXT
Packit 1f51f5
         -> TEXT <$> literal
Packit 1f51f5
       #const CMARK_NODE_CODE
Packit 1f51f5
         -> CODE <$> literal
Packit 1f51f5
       #const CMARK_NODE_HTML_INLINE
Packit 1f51f5
         -> HTML_INLINE <$> literal
Packit 1f51f5
       #const CMARK_NODE_CUSTOM_INLINE
Packit 1f51f5
         -> CUSTOM_INLINE <$> onEnter <*> onExit
Packit 1f51f5
       #const CMARK_NODE_SOFTBREAK
Packit 1f51f5
         -> return SOFTBREAK
Packit 1f51f5
       #const CMARK_NODE_LINEBREAK
Packit 1f51f5
         -> return LINEBREAK
Packit 1f51f5
       _ -> if nodeType == fromIntegral (Unsafe.unsafePerformIO $ peek c_CMARK_NODE_STRIKETHROUGH) then
Packit 1f51f5
              return STRIKETHROUGH
Packit 1f51f5
            else if nodeType == fromIntegral (Unsafe.unsafePerformIO $ peek c_CMARK_NODE_TABLE) then
Packit 1f51f5
              TABLE <$> alignments
Packit 1f51f5
            else if nodeType == fromIntegral (Unsafe.unsafePerformIO $ peek c_CMARK_NODE_TABLE_ROW) then
Packit 1f51f5
              return TABLE_ROW
Packit 1f51f5
            else if nodeType == fromIntegral (Unsafe.unsafePerformIO $ peek c_CMARK_NODE_TABLE_CELL) then
Packit 1f51f5
              return TABLE_CELL
Packit 1f51f5
            else
Packit 1f51f5
              error $ "Unknown node type " ++ (show nodeType)
Packit 1f51f5
  where literal   = c_cmark_node_get_literal ptr >>= totext
Packit 1f51f5
        level     = c_cmark_node_get_heading_level ptr
Packit 1f51f5
        onEnter    = c_cmark_node_get_on_enter ptr >>= totext
Packit 1f51f5
        onExit     = c_cmark_node_get_on_exit  ptr >>= totext
Packit 1f51f5
        listAttr  = do
Packit 1f51f5
          listtype <- c_cmark_node_get_list_type ptr
Packit 1f51f5
          listdelim <- c_cmark_node_get_list_delim ptr
Packit 1f51f5
          tight <- c_cmark_node_get_list_tight ptr
Packit 1f51f5
          start <- c_cmark_node_get_list_start ptr
Packit 1f51f5
          return ListAttributes{
Packit 1f51f5
            listType  = case listtype of
Packit 1f51f5
                             (#const CMARK_ORDERED_LIST) -> ORDERED_LIST
Packit 1f51f5
                             (#const CMARK_BULLET_LIST)  -> BULLET_LIST
Packit 1f51f5
                             _                           -> BULLET_LIST
Packit 1f51f5
          , listDelim  = case listdelim of
Packit 1f51f5
                             (#const CMARK_PERIOD_DELIM) -> PERIOD_DELIM
Packit 1f51f5
                             (#const CMARK_PAREN_DELIM)  -> PAREN_DELIM
Packit 1f51f5
                             _                           -> PERIOD_DELIM
Packit 1f51f5
          , listTight  = tight
Packit 1f51f5
          , listStart  = start
Packit 1f51f5
          }
Packit 1f51f5
        url       = c_cmark_node_get_url ptr >>= totext
Packit 1f51f5
        title     = c_cmark_node_get_title ptr >>= totext
Packit 1f51f5
        info      = c_cmark_node_get_fence_info ptr >>= totext
Packit 1f51f5
        alignments = do
Packit 1f51f5
          ncols <- c_cmarkextensions_get_table_columns ptr
Packit 1f51f5
          cols <- c_cmarkextensions_get_table_alignments ptr
Packit 1f51f5
          mapM (fmap ucharToAlignment . peekElemOff cols) [0..(fromIntegral ncols) - 1]
Packit 1f51f5
        ucharToAlignment (CUChar 108) = LeftAligned
Packit 1f51f5
        ucharToAlignment (CUChar 99)  = CenterAligned
Packit 1f51f5
        ucharToAlignment (CUChar 114) = RightAligned
Packit 1f51f5
        ucharToAlignment _            = NoAlignment
Packit 1f51f5
Packit 1f51f5
getPosInfo :: NodePtr -> IO (Maybe PosInfo)
Packit 1f51f5
getPosInfo ptr = do
Packit 1f51f5
  startline <- c_cmark_node_get_start_line ptr
Packit 1f51f5
  endline <- c_cmark_node_get_end_line ptr
Packit 1f51f5
  startcol <- c_cmark_node_get_start_column ptr
Packit 1f51f5
  endcol <- c_cmark_node_get_end_column ptr
Packit 1f51f5
  if startline + endline + startcol + endcol == 0
Packit 1f51f5
     then return Nothing
Packit 1f51f5
     else return $ Just PosInfo{ startLine = startline
Packit 1f51f5
                               , startColumn = startcol
Packit 1f51f5
                               , endLine = endline
Packit 1f51f5
                               , endColumn = endcol }
Packit 1f51f5
Packit 1f51f5
toNode :: NodePtr -> IO Node
Packit 1f51f5
toNode ptr = do
Packit 1f51f5
  let handleNodes ptr' =
Packit 1f51f5
        if ptr' == nullPtr
Packit 1f51f5
           then return []
Packit 1f51f5
           else do
Packit 1f51f5
              x  <- toNode ptr'
Packit 1f51f5
              xs <- c_cmark_node_next ptr' >>= handleNodes
Packit 1f51f5
              return $! (x:xs)
Packit 1f51f5
  nodeType <- ptrToNodeType ptr
Packit 1f51f5
  children <- c_cmark_node_first_child ptr >>= handleNodes
Packit 1f51f5
  posinfo <- getPosInfo ptr
Packit 1f51f5
  return $! Node posinfo nodeType children
Packit 1f51f5
Packit 1f51f5
fromNode :: Node -> IO NodePtr
Packit 1f51f5
fromNode (Node _ nodeType children) = do
Packit 1f51f5
  node <- case nodeType of
Packit 1f51f5
            DOCUMENT    -> c_cmark_node_new (#const CMARK_NODE_DOCUMENT)
Packit 1f51f5
            THEMATIC_BREAK -> c_cmark_node_new (#const CMARK_NODE_THEMATIC_BREAK)
Packit 1f51f5
            PARAGRAPH   -> c_cmark_node_new (#const CMARK_NODE_PARAGRAPH)
Packit 1f51f5
            BLOCK_QUOTE -> c_cmark_node_new (#const CMARK_NODE_BLOCK_QUOTE)
Packit 1f51f5
            HTML_BLOCK literal -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_HTML_BLOCK)
Packit 1f51f5
                     c_cmark_node_set_literal n =<< fromtext literal
Packit 1f51f5
                     return n
Packit 1f51f5
            CUSTOM_BLOCK onEnter onExit -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_BLOCK)
Packit 1f51f5
                     c_cmark_node_set_on_enter n =<< fromtext onEnter
Packit 1f51f5
                     c_cmark_node_set_on_exit  n =<< fromtext onExit
Packit 1f51f5
                     return n
Packit 1f51f5
            CODE_BLOCK info literal -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_CODE_BLOCK)
Packit 1f51f5
                     c_cmark_node_set_literal n =<< fromtext literal
Packit 1f51f5
                     c_cmark_node_set_fence_info n =<< fromtext info
Packit 1f51f5
                     return n
Packit 1f51f5
            LIST attr   -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_LIST)
Packit 1f51f5
                     c_cmark_node_set_list_type n $ case listType attr of
Packit 1f51f5
                         ORDERED_LIST -> #const CMARK_ORDERED_LIST
Packit 1f51f5
                         BULLET_LIST  -> #const CMARK_BULLET_LIST
Packit 1f51f5
                     c_cmark_node_set_list_delim n $ case listDelim attr of
Packit 1f51f5
                         PERIOD_DELIM -> #const CMARK_PERIOD_DELIM
Packit 1f51f5
                         PAREN_DELIM  -> #const CMARK_PAREN_DELIM
Packit 1f51f5
                     c_cmark_node_set_list_tight n $ listTight attr
Packit 1f51f5
                     c_cmark_node_set_list_start n $ listStart attr
Packit 1f51f5
                     return n
Packit 1f51f5
            ITEM        -> c_cmark_node_new (#const CMARK_NODE_ITEM)
Packit 1f51f5
            HEADING lev  -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_HEADING)
Packit 1f51f5
                     c_cmark_node_set_heading_level n lev
Packit 1f51f5
                     return n
Packit 1f51f5
            EMPH        -> c_cmark_node_new (#const CMARK_NODE_EMPH)
Packit 1f51f5
            STRONG      -> c_cmark_node_new (#const CMARK_NODE_STRONG)
Packit 1f51f5
            LINK url title -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_LINK)
Packit 1f51f5
                     c_cmark_node_set_url n =<< fromtext url
Packit 1f51f5
                     c_cmark_node_set_title n =<< fromtext title
Packit 1f51f5
                     return n
Packit 1f51f5
            IMAGE url title -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_IMAGE)
Packit 1f51f5
                     c_cmark_node_set_url n =<< fromtext url
Packit 1f51f5
                     c_cmark_node_set_title n =<< fromtext title
Packit 1f51f5
                     return n
Packit 1f51f5
            TEXT literal -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_TEXT)
Packit 1f51f5
                     c_cmark_node_set_literal n =<< fromtext literal
Packit 1f51f5
                     return n
Packit 1f51f5
            CODE literal -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_CODE)
Packit 1f51f5
                     c_cmark_node_set_literal n =<< fromtext literal
Packit 1f51f5
                     return n
Packit 1f51f5
            HTML_INLINE literal -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_HTML_INLINE)
Packit 1f51f5
                     c_cmark_node_set_literal n =<< fromtext literal
Packit 1f51f5
                     return n
Packit 1f51f5
            CUSTOM_INLINE onEnter onExit -> do
Packit 1f51f5
                     n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_INLINE)
Packit 1f51f5
                     c_cmark_node_set_on_enter n =<< fromtext onEnter
Packit 1f51f5
                     c_cmark_node_set_on_exit  n =<< fromtext onExit
Packit 1f51f5
                     return n
Packit 1f51f5
            SOFTBREAK   -> c_cmark_node_new (#const CMARK_NODE_SOFTBREAK)
Packit 1f51f5
            LINEBREAK   -> c_cmark_node_new (#const CMARK_NODE_LINEBREAK)
Packit 1f51f5
            STRIKETHROUGH -> c_cmark_node_new (fromIntegral . Unsafe.unsafePerformIO $ peek c_CMARK_NODE_STRIKETHROUGH)
Packit 1f51f5
            TABLE _       -> error "constructing table not supported"
Packit 1f51f5
            TABLE_ROW     -> error "constructing table row not supported"
Packit 1f51f5
            TABLE_CELL    -> error "constructing table cell not supported"
Packit 1f51f5
  mapM_ (\child -> fromNode child >>= c_cmark_node_append_child node) children
Packit 1f51f5
  return node
Packit 1f51f5
Packit 1f51f5
totext :: CString -> IO Text
Packit 1f51f5
totext str
Packit 1f51f5
  | str == nullPtr = return empty
Packit 1f51f5
  | otherwise      = TF.peekCStringLen (str, c_strlen str)
Packit 1f51f5
Packit 1f51f5
fromtext :: Text -> IO CString
Packit 1f51f5
fromtext t = B.useAsCString (encodeUtf8 t) return
Packit 1f51f5
Packit 1f51f5
foreign import ccall "string.h strlen"
Packit 1f51f5
    c_strlen :: CString -> Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_new"
Packit 1f51f5
    c_cmark_node_new :: Int -> IO NodePtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_render_html"
Packit 1f51f5
    c_cmark_render_html :: NodePtr -> CInt -> LlistPtr ExtensionPtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_render_xml"
Packit 1f51f5
    c_cmark_render_xml :: NodePtr -> CInt -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_render_man"
Packit 1f51f5
    c_cmark_render_man :: NodePtr -> CInt -> Int -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_render_latex"
Packit 1f51f5
    c_cmark_render_latex :: NodePtr -> CInt -> Int -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_render_commonmark"
Packit 1f51f5
    c_cmark_render_commonmark :: NodePtr -> CInt -> Int -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_parser_new"
Packit 1f51f5
    c_cmark_parser_new :: CInt -> IO ParserPtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_parser_feed"
Packit 1f51f5
    c_cmark_parser_feed :: ParserPtr -> CString -> Int -> IO ()
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_parser_finish"
Packit 1f51f5
    c_cmark_parser_finish :: ParserPtr -> IO NodePtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_parser_free"
Packit 1f51f5
    c_cmark_parser_free :: ParserPtr -> IO ()
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_type"
Packit 1f51f5
    c_cmark_node_get_type :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_first_child"
Packit 1f51f5
    c_cmark_node_first_child :: NodePtr -> IO NodePtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_next"
Packit 1f51f5
    c_cmark_node_next :: NodePtr -> IO NodePtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_literal"
Packit 1f51f5
    c_cmark_node_get_literal :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_url"
Packit 1f51f5
    c_cmark_node_get_url :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_title"
Packit 1f51f5
    c_cmark_node_get_title :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_heading_level"
Packit 1f51f5
    c_cmark_node_get_heading_level :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_list_type"
Packit 1f51f5
    c_cmark_node_get_list_type :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_list_tight"
Packit 1f51f5
    c_cmark_node_get_list_tight :: NodePtr -> IO Bool
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_list_start"
Packit 1f51f5
    c_cmark_node_get_list_start :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_list_delim"
Packit 1f51f5
    c_cmark_node_get_list_delim :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_fence_info"
Packit 1f51f5
    c_cmark_node_get_fence_info :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_start_line"
Packit 1f51f5
    c_cmark_node_get_start_line :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_start_column"
Packit 1f51f5
    c_cmark_node_get_start_column :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_end_line"
Packit 1f51f5
    c_cmark_node_get_end_line :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_end_column"
Packit 1f51f5
    c_cmark_node_get_end_column :: NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_on_enter"
Packit 1f51f5
    c_cmark_node_get_on_enter :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_get_on_exit"
Packit 1f51f5
    c_cmark_node_get_on_exit :: NodePtr -> IO CString
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_append_child"
Packit 1f51f5
    c_cmark_node_append_child :: NodePtr -> NodePtr -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_literal"
Packit 1f51f5
    c_cmark_node_set_literal :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_url"
Packit 1f51f5
    c_cmark_node_set_url :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_title"
Packit 1f51f5
    c_cmark_node_set_title :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_heading_level"
Packit 1f51f5
    c_cmark_node_set_heading_level :: NodePtr -> Int -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_list_type"
Packit 1f51f5
    c_cmark_node_set_list_type :: NodePtr -> Int -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_list_tight"
Packit 1f51f5
    c_cmark_node_set_list_tight :: NodePtr -> Bool -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_list_start"
Packit 1f51f5
    c_cmark_node_set_list_start :: NodePtr -> Int -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_list_delim"
Packit 1f51f5
    c_cmark_node_set_list_delim :: NodePtr -> Int -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_fence_info"
Packit 1f51f5
    c_cmark_node_set_fence_info :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_on_enter"
Packit 1f51f5
    c_cmark_node_set_on_enter :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_node_set_on_exit"
Packit 1f51f5
    c_cmark_node_set_on_exit :: NodePtr -> CString -> IO Int
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h &cmark_node_free"
Packit 1f51f5
    c_cmark_node_free :: FunPtr (NodePtr -> IO ())
Packit 1f51f5
Packit 1f51f5
foreign import ccall "core-extensions.h core_extensions_ensure_registered"
Packit 1f51f5
    c_core_extensions_ensure_registered :: IO ()
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark_extension_api.h cmark_find_syntax_extension"
Packit 1f51f5
    c_cmark_find_syntax_extension :: CString -> IO ExtensionPtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_llist_append"
Packit 1f51f5
    c_cmark_llist_append :: MemPtr -> LlistPtr a -> Ptr () -> IO (LlistPtr a)
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h cmark_llist_free"
Packit 1f51f5
    c_cmark_llist_free :: MemPtr -> LlistPtr a -> IO ()
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark.h &CMARK_DEFAULT_MEM_ALLOCATOR"
Packit 1f51f5
    c_CMARK_DEFAULT_MEM_ALLOCATOR :: MemPtr
Packit 1f51f5
Packit 1f51f5
foreign import ccall "cmark_extension_api.h cmark_parser_attach_syntax_extension"
Packit 1f51f5
    c_cmark_parser_attach_syntax_extension :: ParserPtr -> ExtensionPtr -> IO ()
Packit 1f51f5
Packit 1f51f5
foreign import ccall "strikethrough.h &CMARK_NODE_STRIKETHROUGH"
Packit 1f51f5
    c_CMARK_NODE_STRIKETHROUGH :: Ptr CUShort
Packit 1f51f5
Packit 1f51f5
foreign import ccall "table.h &CMARK_NODE_TABLE"
Packit 1f51f5
    c_CMARK_NODE_TABLE :: Ptr CUShort
Packit 1f51f5
Packit 1f51f5
foreign import ccall "table.h &CMARK_NODE_TABLE_ROW"
Packit 1f51f5
    c_CMARK_NODE_TABLE_ROW :: Ptr CUShort
Packit 1f51f5
Packit 1f51f5
foreign import ccall "table.h &CMARK_NODE_TABLE_CELL"
Packit 1f51f5
    c_CMARK_NODE_TABLE_CELL :: Ptr CUShort
Packit 1f51f5
Packit 1f51f5
foreign import ccall "core-extensions.h cmarkextensions_get_table_columns"
Packit 1f51f5
    c_cmarkextensions_get_table_columns :: NodePtr -> IO CUShort
Packit 1f51f5
Packit 1f51f5
foreign import ccall "core-extensions.h cmarkextensions_get_table_alignments"
Packit 1f51f5
    c_cmarkextensions_get_table_alignments :: NodePtr -> IO (Ptr CUChar)