module Text.Ptolemy.Slackdown.Reader
( SlackdownOpts(..)
, defaultOpts
, inlineOpts
, readSlackdown
) where
import Control.Applicative (empty)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector as V
import Text.Megaparsec
import Text.Megaparsec.Text
import Text.Ptolemy.Core (PtolemyError, Document)
import qualified Text.Ptolemy.Core as P
data SlackdownOpts = SlackdownOpts
{ tdBlockElems :: Bool
} deriving (Eq, Show)
defaultOpts :: SlackdownOpts
defaultOpts = SlackdownOpts
{ tdBlockElems = True
}
inlineOpts :: SlackdownOpts
inlineOpts = SlackdownOpts
{ tdBlockElems = False
}
readSlackdown :: SlackdownOpts -> Text -> Either PtolemyError Document
readSlackdown opts tx = case runParser (pSlackdown opts) "[]" tx of
Right x -> Right x
Left err -> Left (P.PtolemyError (show err))
enables :: Bool -> Parser a -> Parser a
enables True p = p
enables False _ = empty
pSlackdown :: SlackdownOpts -> Parser Document
pSlackdown SlackdownOpts { tdBlockElems = blockElems } =
P.vec <$> (many pBlock <* eof)
where pBlock =
blockElems `enables` (pCodeBlock <|> pQuote) <|>
pLine
pLine = (P.Plain . flip V.snoc P.LineBreak . P.vec) <$>
manyTill pInline (char '\n')
pQuote = (P.BlockQuote . P.vec) <$> some (char '>' *> pLine)
pCodeBlock = (P.CodeBlock P.emptyAttr . T.concat) <$>
(string "```\n" *> manyTill pPlainLine (string "```\n"))
pPlainLine = T.pack <$> manyTill (noneOf ("\n\r" :: String))
(char '\n')
pInline = pWhitespace
<|> pString
<|> (P.Code P.emptyAttr . T.pack) <$>
(char '`' *> many (satisfy (/= '`')) <* char '`')
<|> P.Strong <$> pSurrounded '*'
<|> P.Emph <$> pSurrounded '_'
<|> P.Strikeout <$> pSurrounded '~'
pWhitespace = (P.Space) <$ some (oneOf (" \t" :: String))
pString = (P.Str . T.pack) <$> some (noneOf ("*_~` \t\r\n" :: String))
pSurrounded :: Char -> Parser P.Chunk
pSurrounded c = try (char c *> rest)
where rest = P.vec <$> (many (notFollowedBy (char c) *> pInline)
<* char c)