diff options
| -rwxr-xr-x | filters/html-converters/md2html | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html index de393c7..faa71f3 100755 --- a/filters/html-converters/md2html +++ b/filters/html-converters/md2html @@ -2,8 +2,62 @@ import markdown import sys import io +import re from pygments.formatters import HtmlFormatter from markdown.extensions.toc import TocExtension +from markdown.extensions import Extension +from markdown.preprocessors import Preprocessor + +# Python-Markdown (unlike CommonMark/GitHub) refuses to let a list interrupt a +# paragraph unless there's a blank line between them, so a very common README +# shape renders as one run-on paragraph instead of a list: +# +# Passing: +# - foo +# - bar +# +# This preprocessor restores the GitHub behavior: when a bullet ( - * + ) or a +# "1."/"1)" ordered-list marker immediately follows a paragraph line, insert a +# blank line so the list starts. We deliberately only interrupt on "1" for +# ordered lists (matching CommonMark) so prose like "... the year\n1985. was +# ..." is not turned into a list. Fenced code, indented code, and lines that +# already follow a list item are left untouched. +_MD_FENCE = re.compile(r'^(\s{0,3})(`{3,}|~{3,})') +_MD_BULLET = re.compile(r'^ {0,3}[-*+][ \t]+\S') +_MD_ORDERED1 = re.compile(r'^ {0,3}1[.)][ \t]+\S') +_MD_ANYITEM = re.compile(r'^ {0,3}(?:[-*+]|\d{1,9}[.)])[ \t]') + + +class _ListInterruptPreprocessor(Preprocessor): + def run(self, lines): + out, in_fence, fence = [], False, None + for line in lines: + m = _MD_FENCE.match(line) + if m: + tok = m.group(2)[0] + if not in_fence: + in_fence, fence = True, tok + elif fence == tok: + in_fence, fence = False, None + out.append(line) + continue + if not in_fence and out: + prev = out[-1] + if (prev.strip() and not _MD_ANYITEM.match(prev) + and not prev.startswith((' ', '\t')) + and (_MD_BULLET.match(line) or _MD_ORDERED1.match(line))): + out.append('') + out.append(line) + return out + + +class ListInterruptExtension(Extension): + def extendMarkdown(self, md): + # priority 21: just above the built-in 'normalize_whitespace' (20) so we + # operate on clean lines before block parsing. + md.preprocessors.register( + _ListInterruptPreprocessor(md), 'list_interrupt', 21) + sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stdout.write(''' @@ -298,6 +352,7 @@ html = markdown.markdown( "markdown.extensions.codehilite", "markdown.extensions.tables", "markdown.extensions.sane_lists", + ListInterruptExtension(), TocExtension(anchorlink=True)], extension_configs={ "markdown.extensions.codehilite":{"css_class":"highlight"}}) |
