module Text.MMark.Extension.TableOfContents
( Toc
, tocScanner
, toc )
where
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (maybeToList)
import Data.Text (Text)
import Text.MMark.Extension (Extension, Block (..), Inline (..), Bni)
import qualified Control.Foldl as L
import qualified Data.List.NonEmpty as NE
import qualified Text.MMark.Extension as Ext
newtype Toc = Toc [(Int, NonEmpty Inline)]
tocScanner
:: (Int -> Bool)
-> L.Fold Bni Toc
tocScanner p = fmap (Toc . ($ [])) . Ext.scanner id $ \xs block ->
case block of
Heading1 x -> f 1 x xs
Heading2 x -> f 2 x xs
Heading3 x -> f 3 x xs
Heading4 x -> f 4 x xs
Heading5 x -> f 5 x xs
Heading6 x -> f 6 x xs
_ -> xs
where
f n a as =
if p n
then as . ((n, a):)
else as
toc
:: Text
-> Toc
-> Extension
toc label (Toc xs) = Ext.blockTrans $ \case
old@(CodeBlock mlabel _) ->
case NE.nonEmpty xs of
Nothing -> old
Just ns ->
if mlabel == pure label
then renderToc ns
else old
other -> other
renderToc :: NonEmpty (Int, NonEmpty Inline) -> Bni
renderToc = UnorderedList . NE.unfoldr f
where
f ((n,x) :| xs) =
let (sitems, fitems) = span ((> n) . fst) xs
url = Ext.headerFragment (Ext.headerId x)
in ( Naked (Link x url Nothing :| [])
: maybeToList (renderToc <$> NE.nonEmpty sitems)
, NE.nonEmpty fitems )