linkify.py 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. """Process links like https://example.org/"""
  2. import re
  3. from .state_inline import StateInline
  4. # RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
  5. SCHEME_RE = re.compile(r"(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$", re.IGNORECASE)
  6. def linkify(state: StateInline, silent: bool) -> bool:
  7. """Rule for identifying plain-text links."""
  8. if not state.md.options.linkify:
  9. return False
  10. if state.linkLevel > 0:
  11. return False
  12. if not state.md.linkify:
  13. raise ModuleNotFoundError("Linkify enabled but not installed.")
  14. pos = state.pos
  15. maximum = state.posMax
  16. if (
  17. (pos + 3) > maximum
  18. or state.src[pos] != ":"
  19. or state.src[pos + 1] != "/"
  20. or state.src[pos + 2] != "/"
  21. ):
  22. return False
  23. if not (match := SCHEME_RE.match(state.pending)):
  24. return False
  25. proto = match.group(1)
  26. if not (link := state.md.linkify.match_at_start(state.src[pos - len(proto) :])):
  27. return False
  28. url: str = link.url
  29. # disallow '*' at the end of the link (conflicts with emphasis)
  30. url = url.rstrip("*")
  31. full_url = state.md.normalizeLink(url)
  32. if not state.md.validateLink(full_url):
  33. return False
  34. if not silent:
  35. state.pending = state.pending[: -len(proto)]
  36. token = state.push("link_open", "a", 1)
  37. token.attrs = {"href": full_url}
  38. token.markup = "linkify"
  39. token.info = "auto"
  40. token = state.push("text", "", 0)
  41. token.content = state.md.normalizeLinkText(url)
  42. token = state.push("link_close", "a", -1)
  43. token.markup = "linkify"
  44. token.info = "auto"
  45. state.pos += len(url) - len(proto)
  46. return True