123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- import re
- import markdown
- from markdown.extensions.attr_list import AttrListExtension
- from markdown.extensions.fenced_code import FencedBlockPreprocessor
- from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension
- from markdown.serializers import _escape_attrib_html
- class CodeBlockExtension(markdown.Extension):
- def extendMarkdown(self, md):
- md.registerExtension(self)
- md.preprocessors.register(
- CodeBlockPreprocessor(md, self.getConfigs()), "misago_code_bbcode", 24
- )
- class CodeBlockPreprocessor(FencedBlockPreprocessor):
- FENCED_BLOCK_RE = re.compile(
- r"""
- \[code(=("?)(?P<lang>.*?)("?))?](([ ]*\n)+)?(?P<code>.*?)((\s|\n)+)?\[/code\]
- """,
- re.IGNORECASE | re.MULTILINE | re.DOTALL | re.VERBOSE,
- )
- def run(self, lines):
- """Match and store Fenced Code Blocks in the HtmlStash."""
- # Check for dependent extensions
- if not self.checked_for_deps:
- for ext in self.md.registeredExtensions:
- if isinstance(ext, CodeHiliteExtension):
- self.codehilite_conf = ext.getConfigs()
- if isinstance(ext, AttrListExtension):
- self.use_attr_list = True
- self.checked_for_deps = True
- text = "\n".join(lines)
- while 1:
- m = self.FENCED_BLOCK_RE.search(text)
- if m:
- lang, id, classes, config = None, "", [], {}
- if m.group("lang"):
- lang = m.group("lang")
- # If config is not empty, then the codehighlite extension
- # is enabled, so we call it to highlight the code
- if (
- self.codehilite_conf
- and self.codehilite_conf["use_pygments"]
- and config.get("use_pygments", True)
- ):
- local_config = self.codehilite_conf.copy()
- local_config.update(config)
- # Combine classes with cssclass. Ensure cssclass is at end
- # as pygments appends a suffix under certain circumstances.
- # Ignore ID as Pygments does not offer an option to set it.
- if classes:
- local_config["css_class"] = "{} {}".format(
- " ".join(classes), local_config["css_class"]
- )
- highliter = CodeHilite(
- m.group("code"),
- lang=lang,
- style=local_config.pop("pygments_style", "default"),
- **local_config,
- )
- code = highliter.hilite(shebang=False)
- else:
- id_attr = lang_attr = class_attr = kv_pairs = ""
- if lang:
- lang_attr = f' class="{_escape_attrib_html(lang)}"'
- if classes:
- class_attr = (
- f' class="{_escape_attrib_html(" ".join(classes))}"'
- )
- if id:
- id_attr = f' id="{_escape_attrib_html(id)}"'
- if (
- self.use_attr_list
- and config
- and not config.get("use_pygments", False)
- ):
- # Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs
- # were defined on the code block, and the `use_pygments` key was not set to True. The
- # `use_pygments` key could be either set to False or not defined. It is omitted from output.
- kv_pairs = "".join(
- f' {k}="{_escape_attrib_html(v)}"'
- for k, v in config.items()
- if k != "use_pygments"
- )
- code = self._escape(m.group("code"))
- code = f"<pre{id_attr}{class_attr}><code{lang_attr}{kv_pairs}>{code}</code></pre>"
- placeholder = self.md.htmlStash.store(code)
- text = f"{text[:m.start()]}\n{placeholder}\n{text[m.end():]}"
- else:
- break
- return text.split("\n")
|