vextoly/gitman
Fork of patx/gitman.
"""Recognize profile mentions using Markdown's code/link/HTML boundaries."""
from xml.etree.ElementTree import Element
import markdown
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
MENTION_PATTERN = r'(?<![\w@/\\])@([A-Za-z0-9][A-Za-z0-9._-]+)(?![\[email protected]])'
class MentionProcessor(InlineProcessor):
ANCESTOR_EXCLUDES = ('a', 'code', 'pre')
def __init__(self, md, resolve):
super().__init__(MENTION_PATTERN, md)
self.resolve = resolve
def handleMatch(self, match, data):
# A sentence's trailing full stop is punctuation, not part of a handle.
name = match.group(1).rstrip('.')
username = self.resolve(name.lower()) if 2 <= len(name) <= 63 else None
if not username:
return None, None, None
end = match.end(0) - (len(match.group(1)) - len(name))
link = Element('a', {'href': '/' + username})
link.text = data[match.start(0):end]
return link, match.start(0), end
class MentionExtension(Extension):
def __init__(self, resolve):
self.resolve = resolve
super().__init__()
def extendMarkdown(self, md):
# Run after code, escaping, raw HTML and ordinary Markdown links.
md.inlinePatterns.register(MentionProcessor(md, self.resolve), 'mentions', 75)
def text_mentions_user(text, username):
if not text or not username or '@' + username.lower() not in text.lower():
return False
found = False
def resolve(name):
nonlocal found
if name == username.lower():
found = True
return name
return None
markdown.markdown(text, extensions=['extra', 'sane_lists', MentionExtension(resolve)])
return found