matterhorn-50200.2.0: src/Types/UserNames.hs
module Types.UserNames
( findUsernames
, isNameFragment
, takeWhileNameFragment
)
where
import qualified Cheapskate as C
import Data.Char ( isAlpha )
import qualified Data.Foldable as F
import qualified Data.Set as S
import qualified Data.Text as T
import Prelude ()
import Prelude.MH
findUsernames :: C.Blocks -> S.Set T.Text
findUsernames = S.unions . F.toList . fmap blockFindUsernames
blockFindUsernames :: C.Block -> S.Set T.Text
blockFindUsernames (C.Para is) =
inlineFindUsernames $ F.toList is
blockFindUsernames (C.Header _ is) =
inlineFindUsernames $ F.toList is
blockFindUsernames (C.Blockquote bs) =
findUsernames bs
blockFindUsernames (C.List _ _ bs) =
S.unions $ F.toList $ findUsernames <$> bs
blockFindUsernames _ =
mempty
inlineFindUsernames :: [C.Inline] -> S.Set T.Text
inlineFindUsernames [] = mempty
inlineFindUsernames (C.Str "@" : rest) =
let (strs, remaining) = takeWhileNameFragment rest
in if null strs
then inlineFindUsernames remaining
else S.insert (T.concat $ getInlineStr <$> strs) $ inlineFindUsernames remaining
inlineFindUsernames (_ : rest) =
inlineFindUsernames rest
getInlineStr :: C.Inline -> T.Text
getInlineStr (C.Str s) = s
getInlineStr _ = ""
takeWhileNameFragment :: [C.Inline] -> ([C.Inline], [C.Inline])
takeWhileNameFragment [] = ([], [])
takeWhileNameFragment rest =
let (strs, remaining) = break (not . isNameFragment) rest
-- Does the last element in strs start with a letter? If
-- not, move it to the remaining list. This avoids pulling
-- punctuation-only tokens into usernames, e.g. "Hello,
-- @foobar."
(strs', remaining') =
if length strs <= 1
then (strs, remaining)
else let (initStrs, [lastStr]) = splitAt (length strs - 1) strs
in if isAlpha $ T.head $ getInlineStr lastStr
then (strs, remaining)
else (initStrs, lastStr : remaining)
in (strs', remaining')
isValidNameChar :: Char -> Bool
isValidNameChar c = isAlpha c || c == '_' || c == '.' || c == '-'
isNameFragment :: C.Inline -> Bool
isNameFragment (C.Str t) =
not (T.null t) && isValidNameChar (T.head t)
isNameFragment _ = False