dotfiles
My PC Dotfiles & Websitegit clone https://github.com/maydayv7/dotfiles
site/git/stagit/render.py (4125B)
1#!/usr/bin/env python3
2"""Render file contents"""
3
4import html
5import sys
6
7import nh3
8from pygments import highlight
9from pygments.formatters import HtmlFormatter
10from pygments.lexers import get_lexer_for_filename, TextLexer
11from pygments.util import ClassNotFound
12
13# Preview Limits
14MAX_HIGHLIGHT = 512 * 1024
15MAX_OUTPUT = 8 * 1024 * 1024
16FORMAT = HtmlFormatter(
17 cssclass="highlight",
18 linenos="inline",
19 lineanchors="loc",
20 anchorlinenos=True,
21 linespans="line",
22)
23
24
25def lexer_for(filename):
26 try:
27 return get_lexer_for_filename(filename)
28 except ClassNotFound:
29 return TextLexer()
30
31
32def markdown_parser():
33 from markdown import Markdown
34 from markdown_callouts.github_callouts import GitHubCalloutsExtension
35
36 return Markdown(
37 extensions=[
38 "markdown.extensions.tables",
39 "markdown.extensions.toc",
40 "pymdownx.arithmatex",
41 "pymdownx.betterem",
42 "pymdownx.magiclink",
43 "pymdownx.superfences",
44 "pymdownx.tasklist",
45 "pymdownx.tilde",
46 GitHubCalloutsExtension(),
47 ],
48 extension_configs={
49 "pymdownx.arithmatex": {"generic": True},
50 "pymdownx.magiclink": {
51 "repo_url_shortener": True,
52 "repo_url_shorthand": True,
53 },
54 },
55 )
56
57
58def safe_attribute(tag, attr, value):
59 # Keep Markdown separated
60 if attr == "id" and value in {
61 "main-content",
62 "theme-select",
63 "mobile-menu",
64 "more-menu",
65 }:
66 return None
67 if attr == "class":
68 return " ".join(
69 name
70 for name in value.split()
71 if name
72 not in {
73 "header",
74 "header-logo",
75 "logo",
76 "loading",
77 "footer",
78 "container",
79 "hidden",
80 "js-only",
81 }
82 )
83 return value
84
85
86def render(filename, contents):
87 lexer = TextLexer() if len(contents) > MAX_HIGHLIGHT else lexer_for(filename)
88 print(f"Filename: {filename!r}; Lexer: {lexer.name}.", file=sys.stderr, flush=True)
89 rendered = ""
90 if len(contents) <= MAX_HIGHLIGHT and filename.lower().endswith(
91 (".md", ".markdown", ".mdown")
92 ):
93 markup = markdown_parser().reset().convert(contents)
94 attrs = {tag: set(values) for tag, values in nh3.ALLOWED_ATTRIBUTES.items()}
95 attrs["*"] = {"class", "id", "title"}
96 attrs["input"] = {"type", "checked", "disabled"}
97 markup = nh3.clean(
98 markup,
99 tags=nh3.ALLOWED_TAGS | {"input"},
100 attributes=attrs,
101 attribute_filter=safe_attribute,
102 url_schemes={"http", "https", "mailto"},
103 set_tag_attribute_values={"input": {"type": "checkbox", "disabled": ""}},
104 )
105 rendered = (
106 '<h3>Rendered</h3><article class="post-content markdown">'
107 + markup
108 + "</article><h3>Code</h3>"
109 )
110 print("Markdown was rendered in addition.", file=sys.stderr, flush=True)
111 if len(contents) > MAX_HIGHLIGHT:
112 # Plain Text
113 code = (
114 "<pre>"
115 + "".join(
116 f'<span id="loc-{number}"><a class="linenos" href="#loc-{number}">{number}</a> {html.escape(line)}</span>'
117 for number, line in enumerate(contents.splitlines(keepends=True), 1)
118 )
119 + "</pre>"
120 )
121 else:
122 code = highlight(contents, lexer, FORMAT)
123 result = rendered + '<div id="blob">' + code + "</div>"
124 if len(result.encode("utf-8")) > MAX_OUTPUT:
125 print(
126 f"Skipping {filename!r}: rendered HTML exceeds 8 MiB.",
127 file=sys.stderr,
128 flush=True,
129 )
130 return "<p>File is too large to display.</p>"
131 return result
132
133
134if __name__ == "__main__":
135 if len(sys.argv) != 2:
136 sys.exit("usage: render filename")
137 contents = sys.stdin.buffer.read().decode("utf-8", errors="replace")
138 sys.stdout.write(render(sys.argv[1], contents))