content: {-
  pandoc-crossref is a pandoc filter for numbering figures,
  equations, tables and cross-references to them.
  Copyright (C) 2015  Nikolay Yakimov <root@livid.pp.ru>
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  -}
  
  {-# LANGUAGE CPP, OverloadedLists #-}
  {-# OPTIONS_GHC -Wno-orphans #-}
  import Test.Hspec
  import Text.Pandoc hiding (getDataFileName)
  import Text.Pandoc.Builder hiding (figure)
  import qualified Text.Pandoc.Builder as B
  import Data.List
  import Control.Arrow
  import qualified Data.Map as M
  import qualified Data.Text as T
  import Data.Maybe
  import Lens.Micro.Mtl
  
  import Text.Pandoc.CrossRef
  import Text.Pandoc.CrossRef.Util.Options
  import Text.Pandoc.CrossRef.Util.Util
  import Text.Pandoc.CrossRef.References.Types
  import Lens.Micro
  import Text.Pandoc.CrossRef.References.Monad
  import qualified Text.Pandoc.CrossRef.References.Blocks as References.Blocks
  import qualified Text.Pandoc.CrossRef.References.Refs as References.Refs
  import qualified Text.Pandoc.CrossRef.References.List as References.List
  import qualified Text.Pandoc.CrossRef.Util.Template as Util.Template
  import qualified Text.Pandoc.CrossRef.Util.CodeBlockCaptions as Util.CodeBlockCaptions
  import Data.String (IsString(..))
  
  import qualified Native
  import Paths_pandoc_crossref
  
  import Prelude
  
  main :: IO ()
  main = hspec $ do
      describe "References.Blocks.replaceInlines" $ do
        it "Labels equations" $
          testAll (equation' "a^2+b^2=c^2" "equation")
          (spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" ""),
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the middle of text" $
          testAll (
                  text "This is an equation: "
               <> equation' "a^2+b^2=c^2" "equation"
               <> text " it should be labeled")
          (
             text "This is an equation: "
          <> spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" "")
          <> text " it should be labeled",
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the beginning of text" $
          testAll (
                  equation' "a^2+b^2=c^2" "equation"
               <> text " it should be labeled")
          (
             spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" "")
          <> text " it should be labeled",
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the end of text" $
          testAll (
                  text "This is an equation: "
               <> equation' "a^2+b^2=c^2" "equation")
          (
             text "This is an equation: "
          <> spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" ""),
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
  
      -- TODO:
      -- describe "References.Blocks.spanInlines"
      -- describe "References.Blocks.divBlocks"
  
      describe "References.Blocks.replaceBlocks" $ do
        it "Labels images" $
          testAll (figure "test.jpg" "" "Test figure" Nothing "figure")
          (figure "test.jpg" "" "Figure 1: Test figure" (Just "Figure 1: Test figure") "figure",
            PfxImg =: M.fromList $ refRec' "fig:figure" 1 "Test figure")
        it "Labels subfigures" $
          testAll (
            divWith ("fig:subfigure",[],[]) (
              para (figure' "test1.jpg" "" "Test figure 1" "figure1")
            <>para (figure' "test2.jpg" "" "Test figure 2" "figure2")
            <>para (text "figure caption")
              ) <>
            divWith ("fig:subfigure2",[],[]) (
              para (figure' "test21.jpg" "" "Test figure 21" "figure21")
            <>para (figure' "test22.jpg" "" "Test figure 22" "figure22")
            <>para (text "figure caption 2")
              )
            )
          (
            figureWith ("fig:subfigure",["subfigures"],[])
              (caption Nothing $ para (text "Figure 1: figure caption. a — Test figure 1, b — Test figure 2"))
              (  figure "test1.jpg" "" "a" Nothing "figure1"
              <> figure "test2.jpg" "" "b" Nothing "figure2"
              ) <>
            figureWith ("fig:subfigure2",["subfigures"],[])
              (caption Nothing $ para (text "Figure 2: figure caption 2. a — Test figure 21, b — Test figure 22"))
              (  figure "test21.jpg" "" "a" Nothing "figure21"
              <> figure "test22.jpg" "" "b" Nothing "figure22"
              )
          , PfxImg =: M.fromList [("fig:figure1",RefRec {
                                              refIndex = [(1,Nothing)],
                                              refGlobal = 1,
                                              refTitle = [Str "Test",Space,Str "figure",Space,Str "1"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:figure1",
                                              refSubfigure = Just [(1, Just "a")]}),
                                      ("fig:figure2",RefRec {
                                              refIndex = [(1,Nothing)],
                                              refGlobal = 2,
                                              refTitle = [Str "Test",Space,Str "figure",Space,Str "2"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:figure2",
                                              refSubfigure = Just [(2, Just "b")]}),
                                      ("fig:subfigure",RefRec {
                                              refIndex = [(1,Nothing)],
                                              refGlobal = 0,
                                              refTitle = [Str "figure",Space,Str "caption"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:subfigure",
                                              refSubfigure = Nothing}),
                                      ("fig:figure21",RefRec {
                                              refIndex = [(2,Nothing)],
                                              refGlobal = 4,
                                              refTitle = [Str "Test",Space,Str "figure",Space,Str "21"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:figure21",
                                              refSubfigure = Just [(1, Just "a")]}),
                                      ("fig:figure22",RefRec {
                                              refIndex = [(2,Nothing)],
                                              refGlobal = 5,
                                              refTitle = [Str "Test",Space,Str "figure",Space,Str "22"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:figure22",
                                              refSubfigure = Just [(2, Just "b")]}),
                                      ("fig:subfigure2",RefRec {
                                              refIndex = [(2,Nothing)],
                                              refGlobal = 3,
                                              refTitle = [Str "figure",Space,Str "caption",Space,Str "2"],
                                              refHideFromList = False,
                                              refLabel = Just "fig:subfigure2",
                                              refSubfigure = Nothing})
                                     ]
              )
        it "Labels equations" $
          testAll (equation "a^2+b^2=c^2" "equation")
          (para $ spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" ""),
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the middle of text" $
          testAll (para $
                  text "This is an equation: "
               <> equation' "a^2+b^2=c^2" "equation"
               <> text " it should be labeled")
          (para $
             text "This is an equation: "
          <> spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" "")
          <> text " it should be labeled",
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the beginning of text" $
          testAll (para $
                  equation' "a^2+b^2=c^2" "equation"
               <> text " it should be labeled")
          (para $
             spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" "")
          <> text " it should be labeled",
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels equations in the end of text" $
          testAll (para $
                  text "This is an equation: "
               <> equation' "a^2+b^2=c^2" "equation")
          (para $
             text "This is an equation: "
          <> spanWith ("eq:equation", [], []) (equation' "a^2+b^2=c^2\\qquad{(1)}" ""),
            PfxEqn =: M.fromList $ refRec'' "eq:equation" 1)
        it "Labels tables" $
          testAll (table' "Test table" "table")
          (table'' ("tbl:table", [], []) "Table 1: Test table" "",
            PfxTbl =: M.fromList $ refRec' "tbl:table" 1 "Test table")
        it "Labels code blocks" $
          testAll (codeBlock' "Test code block" "codeblock")
          (codeBlockDiv "Listing 1: Test code block" "codeblock",
            PfxLst =: M.fromList $ refRec' "lst:codeblock" 1 "Test code block")
        it "Labels code block divs" $
          testAll (codeBlockDiv "Test code block" "codeblock")
          (codeBlockDiv "Listing 1: Test code block" "codeblock",
            PfxLst =: M.fromList $ refRec' "lst:codeblock" 1 "Test code block")
        it "Labels sections divs" $
          testAll (section "Section Header" 1 "section")
          (section "Section Header" 1 "section",
            PfxSec =: M.fromList (refRec' "sec:section" 1 "Section Header"))
  
      describe "References.Refs.replaceRefs" $ do
        it "References one image" $
          testRefs' "fig:" [1] [4] PfxImg "fig.\160\&4"
        it "References multiple images" $
          testRefs' "fig:" [1..3] [4..6] PfxImg "figs.\160\&4-6"
        it "References one equation" $
          testRefs' "eq:" [1] [4] PfxEqn "eq.\160\&4"
        it "References multiple equations" $
          testRefs' "eq:" [1..3] [4..6] PfxEqn "eqns.\160\&4-6"
        it "References one table" $
          testRefs' "tbl:" [1] [4] PfxTbl "tbl.\160\&4"
        it "References multiple tables" $
          testRefs' "tbl:" [1..3] [4..6] PfxTbl "tbls.\160\&4-6"
        it "References one listing" $
          testRefs' "lst:" [1] [4] PfxLst "lst.\160\&4"
        it "References multiple listings" $
          testRefs' "lst:" [1..3] [4..6] PfxLst "lsts.\160\&4-6"
        it "References one section" $
          testRefs' "sec:" [1] [4] PfxSec "sec.\160\&4"
        it "References multiple sections" $
          testRefs' "sec:" [1..3] [4..6] PfxSec "secs.\160\&4-6"
        it "Separates references to different chapter items by a comma" $
          testRefs'' "lst:" [1..6] (zip [1,1..] [4..6] <> zip [2,2..] [7..9]) PfxLst "lsts.\160\&1.4-1.6, 2.7-2.9"
  
      describe "References.Refs.replaceRefs capitalization" $ do
        it "References one image" $
          testRefs' "Fig:" [1] [4] PfxImg "Fig.\160\&4"
        it "References multiple images" $
          testRefs' "Fig:" [1..3] [4..6] PfxImg "Figs.\160\&4-6"
        it "References one equation" $
          testRefs' "Eq:" [1] [4] PfxEqn "Eq.\160\&4"
        it "References multiple equations" $
          testRefs' "Eq:" [1..3] [4..6] PfxEqn "Eqns.\160\&4-6"
        it "References one table" $
          testRefs' "Tbl:" [1] [4] PfxTbl "Tbl.\160\&4"
        it "References multiple tables" $
          testRefs' "Tbl:" [1..3] [4..6] PfxTbl "Tbls.\160\&4-6"
        it "References one listing" $
          testRefs' "Lst:" [1] [4] PfxLst "Lst.\160\&4"
        it "References multiple listings" $
          testRefs' "Lst:" [1..3] [4..6] PfxLst "Lsts.\160\&4-6"
        it "References one listing" $
          testRefs' "Sec:" [1] [4] PfxSec "Sec.\160\&4"
        it "References multiple listings" $
          testRefs' "Sec:" [1..3] [4..6] PfxSec "Secs.\160\&4-6"
  
      describe "References.List.listOf" $ do
        it "Generates list of tables" $
          testList (rawBlock "latex" "\\listoftables")
                   (PfxTbl =: M.fromList $ refRec' "tbl:1" 4 "4" <> refRec' "tbl:2" 5 "5" <> refRec' "tbl:3" 6 "6")
                   (header 1 (text "List of Tables") <> divWith ("", ["list", "list-of-tbl"], [])
                     (mconcat $ map (\n -> plain (str (T.pack $ show n <> ".") <> space <> str (T.pack $ show n) <> linebreak)) [4..6 :: Int]))
        it "Generates list of figures" $
          testList (rawBlock "latex" "\\listoffigures")
                   (PfxImg =: M.fromList $ refRec' "fig:1" 4 "4" <> refRec' "fig:2" 5 "5" <> refRec' "fig:3" 6 "6")
                   (header 1 (text "List of Figures") <> divWith ("", ["list", "list-of-fig"], [])
                     (mconcat $ map (\n -> plain (str (T.pack $ show n <> ".") <> space <> str (T.pack $ show n) <> linebreak)) [4..6 :: Int]))
  
      describe "Util.CodeBlockCaptions" $
        it "Transforms table-style codeBlock captions to codeblock divs" $ do
          let t x = testCBCaptions x (codeBlockDiv' "Code Block" "cb")
          t (codeBlockForTable "cb" <> paraText ": Code Block")
          t (codeBlockForTable "cb" <> paraText "Listing: Code Block")
          t (paraText ": Code Block" <> codeBlockForTable "cb")
          t (paraText "Listing: Code Block" <> codeBlockForTable "cb")
  
      describe "Util.Template" $
        it "Applies templates" $
          let (template :: Util.Template.Template)=Util.Template.makeTemplate defaultMeta (toList $ displayMath "figureTitle" <> displayMath "i" <> displayMath "t")
          in Util.Template.applyTemplate [Str "1"] [Str "title"] template `shouldBe`
             toList (str "Figure" <> str "1" <> str "title")
  
      describe "Citation groups shouldn't be separated (#22 regression test)" $ do
        it "Should not separate citation groups" $ do
          let cits = para $ citeGen "" [1..3]
          testRefs cits def cits
  
        it "Should not separate citation groups with unknown prefix" $ do
          let cits = para $ citeGen "unk:" [1..3]
          testRefs cits def cits
  
        it "Should not separate citation groups with different unknown prefixes" $ do
          let cits = para $ cite (mconcat $ map (cit . uncurry (<>) . second (T.pack . show)) l) $ text $
                "[" <> T.intercalate "; " (map (("@" <>) . uncurry (<>) . second (T.pack . show)) l) <> "]"
              l = zip ["unk1:", "unk2:"] [1,2::Int]
          testRefs cits def cits
  
  #ifdef FLAKY
      describe "Test files" $ do
  
        it "demo.md matches demo.native" $ do
          demomd <- readFile =<< getDataFileName "docs/demo/demo.md"
          Pandoc m b <- handleError $ runPure $ readMarkdown def {readerExtensions = pandocExtensions} $ T.pack demomd
          runCrossRef m Nothing crossRefBlocks b `shouldBe` Native.demo
  
        it "demo.md with chapters matches demo-chapters.native" $ do
          demomd <- readFile =<< getDataFileName "docs/demo/demo.md"
          Pandoc m b <- handleError $ runPure $ readMarkdown def {readerExtensions = pandocExtensions} $ T.pack demomd
          let m' = setMeta "chapters" True m
          runCrossRef m' Nothing crossRefBlocks b `shouldBe` Native.demochapters
  #endif
  
      describe "LaTeX" $ do
        let test = test' nullMeta
            infixr 5 `test`
            test' m i o = getLatex m i `shouldBe` o
            getLatex m i = either (fail . show) T.unpack (runPure $ writeLaTeX def (Pandoc m $ runCrossRef m (Just $ Format "latex") crossRefBlocks (toList i)))
  
        describe "Labels" $ do
  
          it "Section labels" $
            headerWith ("sec:section_label1", [], []) 1 (text "Section")
              <> para (citeGen "sec:section_label" [1])
              `test` "\\section{Section}\\label{sec:section_label1}\n\nsec.~\\ref{sec:section_label1}"
  
          it "Image labels" $
            figure "img.png" "" "Title" Nothing "figure_label1"
              <> para (citeGen "fig:figure_label" [1])
              `test` "\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio,alt={Title}]{img.png}}\n\\caption{Title}\\label{fig:figure_label1}\n\\end{figure}\n\nfig.~\\ref{fig:figure_label1}"
  
  #ifdef FLAKY
          it "Eqn labels" $
            equation "x^2" "some_equation1"
              <> para (citeGen "eq:some_equation" [1])
              `test` "\\begin{equation}\\protect\\phantomsection\\label{eq:some_equation1}{x^2}\\end{equation}\n\neq.~\\ref{eq:some_equation1}"
  
          it "Tbl labels" $
            table' "A table" "some_table1"
              <> para (citeGen "tbl:some_table" [1])
              `test` concat (
                [ "\\begin{longtable}[]{@{}l@{}}\n"
                , "\\caption{A table}\\label{tbl:some_table1}\\tabularnewline\n"
                , "\\toprule\\noalign{}\n"
                , "\\endfirsthead\n"
                , "\\endhead\n"
                , "\\bottomrule\\noalign{}\n"
                , "\\endlastfoot\n"
                , " \\\\\n"
                , "\\end{longtable}\n\n"
                , "tbl.~\\ref{tbl:some_table1}"
                ] :: [String])
  #endif
  
          it "Code block labels" $ do
            codeBlock' "A code block" "some_codeblock1"
              <> para (citeGen "lst:some_codeblock" [1])
              `test` "\\begin{codelisting}\n\n\\caption{A code block}\\label{lst:some_codeblock1}\n\n\\begin{Shaded}\n\\begin{Highlighting}[]\n\\OtherTok{main ::} \\DataTypeTok{IO}\\NormalTok{ ()}\n\\end{Highlighting}\n\\end{Shaded}\n\n\\end{codelisting}\n\nlst.~\\ref{lst:some_codeblock1}"
            codeBlock' "A code block with under_score" "some_codeblock1"
              <> para (citeGen "lst:some_codeblock" [1])
              `test` "\\begin{codelisting}\n\n\\caption{A code block with under\\_score}\\label{lst:some_codeblock1}\n\n\\begin{Shaded}\n\\begin{Highlighting}[]\n\\OtherTok{main ::} \\DataTypeTok{IO}\\NormalTok{ ()}\n\\end{Highlighting}\n\\end{Shaded}\n\n\\end{codelisting}\n\nlst.~\\ref{lst:some_codeblock1}"
            let test1 = test' $ setMeta "codeBlockCaptions" True nullMeta
                infixr 5 `test1`
            codeBlockForTable "some_codeblock1" <> paraText ": A code block"
              <> para (citeGen "lst:some_codeblock" [1])
              `test1` "\\begin{codelisting}\n\n\\caption{A code block}\\label{lst:some_codeblock1}\n\n\\begin{Shaded}\n\\begin{Highlighting}[]\n\\OtherTok{main ::} \\DataTypeTok{IO}\\NormalTok{ ()}\n\\end{Highlighting}\n\\end{Shaded}\n\n\\end{codelisting}\n\nlst.~\\ref{lst:some_codeblock1}"
  
  instance IsString RefIndex where
    fromString (T.pack -> s) =
      case find @[] (\pfx -> s `hasPfx` pfx) [minBound..] of
        Just pfx -> IdxRef pfx s
        Nothing -> error $ "Invalid reference: " <> T.unpack s
  
  citeGen :: T.Text -> [Int] -> Inlines
  citeGen p l = cite (mconcat $ map (cit . (p<>) . T.pack . show) l) $ text $
    "[" <> T.intercalate "; " (map (("@"<>) . (p<>) . T.pack . show) l) <> "]"
  
  refGen :: T.Text -> [Int] -> [Int] -> M.Map RefIndex RefRec
  refGen p l1 l2 = M.fromList $ mconcat $ zipWith refRec'' (((uncapitalizeFirst p<>) . T.pack . show) `map` l1) l2
  
  refGen' :: T.Text -> [Int] -> [(Int, Int)] -> M.Map RefIndex RefRec
  refGen' p l1 l2 = M.fromList $ mconcat $ zipWith refRec''' (((uncapitalizeFirst p<>) . T.pack . show) `map` l1) l2
  
  refRec' :: T.Text -> Int -> T.Text -> [(RefIndex, RefRec)]
  refRec' ref i tit = [(fromString $ T.unpack ref, RefRec{refLabel=Just ref,refIndex=[(i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text tit,refSubfigure=Nothing})]
  
  refRec'' :: T.Text -> Int -> [(RefIndex, RefRec)]
  refRec'' ref i = refRec' ref i ""
  
  refRec''' :: T.Text -> (Int, Int) -> [(RefIndex, RefRec)]
  refRec''' ref (c,i) = [(fromString $ T.unpack ref, RefRec{refLabel=Just ref,refIndex=[(c,Nothing), (i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text "",refSubfigure=Nothing})]
  
  testRefs' :: T.Text -> [Int] -> [Int] -> Prefix -> T.Text -> Expectation
  testRefs' p l1 l2 prop res = testRefs (para $ citeGen p l1) (set (refsAt prop) (refGen p l1 l2) def) (para $ text res)
  
  testRefs'' :: T.Text -> [Int] -> [(Int, Int)] -> Prefix -> T.Text -> Expectation
  testRefs'' p l1 l2 prop res = testRefs (para $ citeGen p l1) (set (refsAt prop) (refGen' p l1 l2) def) (para $ text res)
  
  testAll :: (Eq a, Data a, Show a) => Many a -> (Many a, References) -> Expectation
  testAll = testState References.Blocks.replaceAll def
  
  testState :: (Show a1, Eq a1) => ([a2] -> WS [a1]) -> References -> Many a2 -> (Many a1, References) -> Expectation
  testState f init' arg res = runWSWithOptsInit defaultOptions init' (f $ toList arg) `shouldBe` first toList res
  
  runWSWithOptsInit :: Options -> References -> WS a -> (a, References)
  runWSWithOptsInit opts st act = runWS (WState opts st) act & _2 %~ view wsReferences
  
  testRefs :: Blocks -> References -> Blocks -> Expectation
  testRefs bs st rbs = testState (bottomUpM replaceRefs') st bs (rbs, st)
  
  replaceRefs' :: [Inline] -> WS [Inline]
  replaceRefs' (x:xs) = do
    x' <- replaceRefOne x
    pure $ toList $ x' <> fromList xs
  replaceRefs' [] = pure []
  
  replaceRefOne :: Inline -> WS Inlines
  replaceRefOne = \case
    Cite cits _ -> References.Refs.replaceRefs cits <$> use wsOptions <*> use wsReferences
    x -> pure $ fromList [x]
  
  testCBCaptions :: Blocks -> Blocks -> Expectation
  testCBCaptions bs res =
    let opts = defaultOptions{Text.Pandoc.CrossRef.Util.Options.codeBlockCaptions=True}
    in (bottomUp (\bs' -> fromMaybe bs' $ Util.CodeBlockCaptions.mkCodeBlockCaptions opts bs') (toList bs)) `shouldBe` toList res
  
  testList :: Blocks -> References -> Blocks -> Expectation
  testList bs st res = runWSWithOptsInit defaultOptions st (bottomUpM listOf' (toList bs))
     `shouldBe` (toList res,st)
  
  listOf' :: [Block] -> WS [Block]
  listOf' (x:xs) = References.List.listOf x <$> use wsOptions <*> use wsReferences <&> \case
    Just res' -> res' <> xs
    Nothing -> x : xs
  listOf' [] = pure []
  
  figure :: T.Text -> T.Text -> T.Text -> Maybe T.Text -> T.Text -> Blocks
  figure src title cap malt ref = B.figureWith ("fig:" <> ref, [], [])
    (caption Nothing $ plain $ text cap) $ plain $ figure' src title (fromMaybe cap malt) ""
  
  figure' :: T.Text -> T.Text -> T.Text -> T.Text -> Inlines
  figure' src title alt ref = imageWith (if T.null ref then mempty else "fig:" <> ref, [], []) src title (text alt)
  
  section :: T.Text -> Int -> T.Text -> Blocks
  section text' level label = headerWith ("sec:" <> label,[],[]) level (text text')
  
  equation :: T.Text -> T.Text -> Blocks
  equation = (para .) . equation'
  
  equation' :: T.Text -> T.Text -> Inlines
  equation' eq ref = displayMath eq <> ref' "eq" ref
  
  table' :: T.Text -> T.Text -> Blocks
  table' title ref = tableWith ("tbl:" <> ref, [], []) (simpleCaption . plain $ text title) []
     (TableHead nullAttr [Row nullAttr $ map (Cell nullAttr AlignDefault (RowSpan 0) (ColSpan 0) . toList) [para $ str "H1", para $ str "H2"]])
    [TableBody nullAttr (RowHeadColumns 0) [] [Row nullAttr $ map (Cell nullAttr AlignDefault (RowSpan 0) (ColSpan 0) . toList) [para $ str "C1", para $ str "C2"]]]
    (TableFoot nullAttr [])
  
  table'' :: Attr -> T.Text -> T.Text -> Blocks
  table'' attr title ref = tableWith attr (simpleCaption . plain $ text title <> ref' "tbl" ref) []
     (TableHead nullAttr [Row nullAttr $ map (Cell nullAttr AlignDefault (RowSpan 0) (ColSpan 0) . toList) [para $ str "H1", para $ str "H2"]])
    [TableBody nullAttr (RowHeadColumns 0) [] [Row nullAttr $ map (Cell nullAttr AlignDefault (RowSpan 0) (ColSpan 0) . toList) [para $ str "C1", para $ str "C2"]]]
    (TableFoot nullAttr [])
  
  codeBlock' :: T.Text -> T.Text -> Blocks
  codeBlock' title ref = codeBlockWith
    ("lst:"<>ref,["haskell"],[("caption",title)]) "main :: IO ()"
  
  codeBlockForTable :: T.Text -> Blocks
  codeBlockForTable ref = codeBlockWith
       ("lst:"<>ref,["haskell"],[]) "main :: IO ()"
  
  paraText :: T.Text -> Blocks
  paraText s = para $ text s
  
  codeBlockDiv :: T.Text -> T.Text -> Blocks
  codeBlockDiv title ref = divWith ("lst:"<>ref, ["listing","haskell"],[]) $
    para (text title) <>
    codeBlockWith
      ("",["haskell"],[]) "main :: IO ()"
  
  codeBlockDiv' :: T.Text -> T.Text -> Blocks
  codeBlockDiv' title ref = divWith ("lst:"<>ref, ["listing"],[]) $
    para (text title) <>
    codeBlockWith
      ("",["haskell"],[]) "main :: IO ()"
  
  ref' :: T.Text -> T.Text -> Inlines
  ref' p n | T.null n  = mempty
           | otherwise = space <> str ("{#"<>p<>":"<>n<>"}")
  
  defaultOptions :: Options
  defaultOptions = getOptions defaultMeta Nothing
  
  defCit :: Citation
  defCit = Citation{citationId = ""
                   ,citationPrefix = []
                   ,citationSuffix = []
                   ,citationHash = 0
                   ,citationNoteNum = 0
                   ,citationMode = NormalCitation
                   }
  
  cit :: T.Text -> [Citation]
  cit r = [defCit{citationId=r}]
  
  infixr 0 =:
  (=:) :: Prefix -> M.Map RefIndex RefRec -> References
  a =: b = def
    & ctrsAt a .~ (refIndex $ last $ M.elems b)
    & refsAt a .~ b
    & stGlob %~ (+ fromIntegral (M.size b))
    & stHiddenHeaderLevel .~
      if a == PfxSec
      then [HiddenHeader { hhLevel = 1, hhHidden = False }]
      else []
  