Filter for linking Agda identifiers in inline code spans
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.3 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Main where
  3. import qualified Data.HashMap.Strict as HashMap
  4. import Data.HashMap.Strict (HashMap)
  5. import qualified Data.Text as T
  6. import Data.Text (Text)
  7. import Data.Maybe
  8. import Data.List
  9. import System.Environment
  10. import System.Exit
  11. import Text.Pandoc.Definition
  12. import Text.HTML.TagSoup
  13. import Text.Pandoc.Walk
  14. import Text.Pandoc.JSON
  15. main :: IO ()
  16. main = toJSONFilter linkDocument
  17. linkDocument :: Pandoc -> Pandoc
  18. linkDocument (Pandoc meta blocks) =
  19. let hm = parseSymbolRefs blocks
  20. in Pandoc meta (walk (link hm) blocks)
  21. link :: HashMap Text Reference -> Inline -> Inline
  22. link hm inline@(Code (_, classes, kv) text)
  23. | isToBeLinked =
  24. case HashMap.lookup identifier hm of
  25. Just ref -> RawInline "html" (renderReference ref text)
  26. Nothing -> inline
  27. where
  28. classes' = map T.toLower classes
  29. isToBeLinked = ("agda" `elem` classes')
  30. && ("nolink" `notElem` classes')
  31. identifier =
  32. case lookup "ident" kv of
  33. Just id -> id
  34. _ -> text
  35. link _ x = x
  36. renderReference :: Reference -> Text -> Text
  37. renderReference (Reference href cls) t =
  38. renderTags [ TagOpen "span" [("class", "Agda")]
  39. , TagOpen "a" [("href", href), ("class", cls)]
  40. , TagText t
  41. , TagClose "a"
  42. , TagClose "span"
  43. ]
  44. data Reference =
  45. Reference { refHref :: Text
  46. , refClass :: Text
  47. }
  48. deriving (Eq, Show)
  49. parseSymbolRefs :: [Block] -> HashMap Text Reference
  50. parseSymbolRefs = go mempty . concat . mapMaybe getHTML where
  51. getHTML :: Block -> Maybe ([Tag Text])
  52. getHTML (RawBlock (Format x) xs)
  53. | x == "html" = Just (parseTags xs)
  54. getHTML _ = Nothing
  55. go :: HashMap Text Reference -> [Tag Text] -> HashMap Text Reference
  56. go map (TagOpen a meta:TagText t:TagClose a':xs)
  57. | a == "a"
  58. , a' == a
  59. , Just id <- lookup "id" meta
  60. , Just cls <- lookup "class" meta
  61. , Just href <- lookup "href" meta
  62. = go (addIfNotPresent t (Reference href cls) map) xs
  63. | otherwise = go map xs
  64. where
  65. tags = [ TagOpen "span" [("class", "Agda")], TagOpen "a" meta', TagText t, TagClose "a", TagClose "span" ]
  66. meta' = filter ((/= "id") . fst) meta
  67. go map (_:xs) = go map xs
  68. go map [] = map
  69. addIfNotPresent :: Text -> v -> HashMap Text v -> HashMap Text v
  70. addIfNotPresent = HashMap.insertWith (\_ old -> old)