git.iko.soy/typeable/pandoc
on master help sign in
Tighten up parsing of raw email addresses.
bf72a48John MacFarlane
src/Text/Pandoc/Parsing.hs+13−4
@@ -178,7 +178,7 @@ import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
178import Text.Parsec hiding (token)
178import Text.Parsec hiding (token)
179import Text.Parsec.Pos (newPos)
179import Text.Parsec.Pos (newPos)
180import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum,
180import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum,
181− isHexDigit, isSpace )
181+ isHexDigit, isSpace, isPunctuation )
182import Data.List ( intercalate, transpose, isSuffixOf )
182import Data.List ( intercalate, transpose, isSuffixOf )
183import Text.Pandoc.Shared
183import Text.Pandoc.Shared
184import qualified Data.Map as M
184import qualified Data.Map as M
@@ -405,9 +405,18 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain)
405 domain = intercalate "." <$> (subdomain `sepby1` dot)
405 domain = intercalate "." <$> (subdomain `sepby1` dot)
406 dot = char '.'
406 dot = char '.'
407 subdomain = many1 $ alphaNum <|> innerPunct
407 subdomain = many1 $ alphaNum <|> innerPunct
408+ -- this excludes some valid email addresses, since an
409+ -- email could contain e.g. '__', but gives better results
410+ -- for our purposes, when combined with markdown parsing:
408− innerPunct = try (satisfy (\c -> isEmailPunct c || c == '@') <*
411+ innerPunct = try (satisfy (\c -> isEmailPunct c || c == '@')
409− notFollowedBy space)
412+ <* notFollowedBy space
413+ <* notFollowedBy (satisfy isPunctuation))
414+ -- technically an email address could begin with a symbol,
415+ -- but allowing this creates too many problems.
416+ -- See e.g. https://github.com/jgm/pandoc/issues/2940
417+ emailWord = do x <- satisfy isAlphaNum
410− emailWord = many1 $ satisfy isEmailChar
418+ xs <- many (satisfy isEmailChar)
419+ return (x:xs)
411 isEmailChar c = isAlphaNum c || isEmailPunct c
420 isEmailChar c = isAlphaNum c || isEmailPunct c
412 isEmailPunct c = c `elem` "!\"#$%&'*+-/=?^_{|}~;"
421 isEmailPunct c = c `elem` "!\"#$%&'*+-/=?^_{|}~;"
413 -- note: sepBy1 from parsec consumes input when sep
422 -- note: sepBy1 from parsec consumes input when sep
tests/Tests/Readers/Markdown.hs+5
@@ -185,6 +185,11 @@ tests = [ testGroup "inline code"
185 "<\n\na>" =?>
185 "<\n\na>" =?>
186 para (text "<") <> para (text "a>")
186 para (text "<") <> para (text "a>")
187 ]
187 ]
188+ , testGroup "raw email addresses"
189+ [ test markdownGH "issue 2940" $
190+ "**@user**" =?>
191+ para (strong (text "@user"))
192+ ]
188 , testGroup "emoji"
193 , testGroup "emoji"
189 [ test markdownGH "emoji symbols" $
194 [ test markdownGH "emoji symbols" $
190 ":smile: and :+1:" =?> para (text "😄 and 👍")
195 ":smile: and :+1:" =?> para (text "😄 and 👍")