packages feed

pandoc-link-context-1.2.0.0: src/Text/Pandoc/LinkContext.hs

module Text.Pandoc.LinkContext (queryLinksWithContext) where

import Data.List (nub)
import qualified Data.Map.Strict as Map
import qualified Text.Pandoc.Builder as B
import Text.Pandoc.Definition (Block, Inline (Link), Pandoc (..))
import qualified Text.Pandoc.Walk as W

type Url = Text

-- | Attributes other than id and class
type OtherAttr = (Text, Text)

-- | Query the pandoc document for all links
--
-- Return a map, containing the "surrounding context" (as Pandoc blocks) for
-- each link.
queryLinksWithContext :: Pandoc -> Map Url (NonEmpty ([OtherAttr], [Block]))
queryLinksWithContext =
  fmap (fmap $ second nub)
    . Map.fromListWith (<>)
    . W.query go
  where
    go :: Block -> [(Url, NonEmpty ([OtherAttr], [Block]))]
    go blk =
      fmap (\(url, attr) -> (url, one (attr, [blk]))) $ case blk of
        B.Para is ->
          queryLinkUrls is
        B.Plain is ->
          queryLinkUrls is
        B.LineBlock is ->
          queryLinkUrls is
        B.Header _ _ is ->
          queryLinkUrls is
        B.DefinitionList xs ->
          -- Gather all filenames linked, and have them put (see above) in the
          -- same definition list block.
          concat $
            xs <&> \(is, bss) ->
              let def = queryLinkUrls is
                  body = fmap (fmap (fmap fst . go)) bss
               in def <> fmap (,[]) (concat (concat body))
        _ -> mempty

    queryLinkUrls :: W.Walkable Inline b => b -> [(Url, [OtherAttr])]
    queryLinkUrls =
      W.query (maybeToList . getLinkUrl)

    getLinkUrl :: Inline -> Maybe (Url, [OtherAttr])
    getLinkUrl = \case
      Link (_, _, attrs) _inlines (url, title) -> do
        -- Put title in attrs, as it *is* an attribute
        pure (url, ("title", title) : attrs)
      _ ->
        Nothing