{-# LANGUAGE OverloadedStrings #-} module MdBlocks.Parser where import Data.Text (Text) import qualified Data.Text as T import MdBlocks.Metadata import MdBlocks.Types parseFile :: FilePath -> Text -> [Block] parseFile fp txt = numberBlocks $ parseFenced fp ls ++ parseIndented fp ls where ls = zip [1..] (T.lines txt) numberBlocks :: [Block] -> [Block] numberBlocks = zipWith (\n b -> b { blockNumber = n }) [1..] parseFenced :: FilePath -> [(Int,Text)] -> [Block] parseFenced fp = go False 0 "" [] where go _ _ _ acc [] = reverse acc go False _ _ acc ((ln,t):xs) | "```" `T.isPrefixOf` T.stripStart t = go True ln t acc xs | otherwise = go False 0 "" acc xs go True start hdr acc ((ln,t):xs) | "```" `T.isPrefixOf` T.stripStart t = let body = T.unlines $ map snd $ takeWhile (\(_,x) -> not ("```" `T.isPrefixOf` T.stripStart x)) ((ln,t):xs) in go False 0 "" (mk start ln hdr body:acc) xs | otherwise = go True start hdr acc xs mk s e hdr body = let meta = parseMetadataLine $ "@" <> T.drop 3 (T.strip hdr) in Block fp 0 Fenced meta s e body parseIndented :: FilePath -> [(Int,Text)] -> [Block] parseIndented fp = go where go [] = [] go ((ln,t):xs) | isIndented t = let (blk,rest) = span (isIndented . snd) xs chunk = T.unlines $ map (T.drop 4 . snd) ((ln,t):blk) meta = case T.lines chunk of [] -> emptyMetadata (x:_) | "@" `T.isPrefixOf` x -> parseMetadataLine x | otherwise -> emptyMetadata code = case T.lines chunk of [] -> "" (x:rs) | "@" `T.isPrefixOf` x -> T.unlines rs | otherwise -> chunk in Block fp 0 Indented meta ln (fst $ last (blk++[(ln,t)])) code : go rest | otherwise = go xs isIndented t = " " `T.isPrefixOf` t