task_lists.py 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import re
  2. from typing import TYPE_CHECKING, Any, Dict, Iterable
  3. if TYPE_CHECKING:
  4. from ..core import BaseRenderer, BlockState
  5. from ..markdown import Markdown
  6. __all__ = ["task_lists"]
  7. TASK_LIST_ITEM = re.compile(r"^(\[[ xX]\])\s+")
  8. def task_lists_hook(md: "Markdown", state: "BlockState") -> Iterable[Dict[str, Any]]:
  9. return _rewrite_all_list_items(state.tokens)
  10. def render_task_list_item(renderer: "BaseRenderer", text: str, checked: bool = False) -> str:
  11. checkbox = '<input class="task-list-item-checkbox" type="checkbox" disabled'
  12. if checked:
  13. checkbox += " checked/>"
  14. else:
  15. checkbox += "/>"
  16. if text.startswith("<p>"):
  17. text = text.replace("<p>", "<p>" + checkbox, 1)
  18. else:
  19. text = checkbox + text
  20. return '<li class="task-list-item">' + text + "</li>\n"
  21. def task_lists(md: "Markdown") -> None:
  22. """A mistune plugin to support task lists. Spec defined by
  23. GitHub flavored Markdown and commonly used by many parsers:
  24. .. code-block:: text
  25. - [ ] unchecked task
  26. - [x] checked task
  27. :param md: Markdown instance
  28. """
  29. md.before_render_hooks.append(task_lists_hook)
  30. if md.renderer and md.renderer.NAME == "html":
  31. md.renderer.register("task_list_item", render_task_list_item)
  32. def _rewrite_all_list_items(tokens: Iterable[Dict[str, Any]]) -> Iterable[Dict[str, Any]]:
  33. for tok in tokens:
  34. if tok["type"] == "list_item":
  35. _rewrite_list_item(tok)
  36. if "children" in tok:
  37. _rewrite_all_list_items(tok["children"])
  38. return tokens
  39. def _rewrite_list_item(tok: Dict[str, Any]) -> None:
  40. children = tok["children"]
  41. if children:
  42. first_child = children[0]
  43. text = first_child.get("text", "")
  44. m = TASK_LIST_ITEM.match(text)
  45. if m:
  46. mark = m.group(1)
  47. first_child["text"] = text[m.end() :]
  48. tok["type"] = "task_list_item"
  49. tok["attrs"] = {"checked": mark != "[ ]"}