#!/usr/bin/env python3
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
from urllib.parse import urlparse
import urllib.parse
import lxml.html
import argparse
import database
import mistune
import houdini
import jinja2
import parser
import shutil
import sys
ENV = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'))
class MyRenderer(mistune.HTMLRenderer):
def block_code(self, code, info=None):
if not info:
return '\n
{}
\n'.format(houdini.escape_html(code.strip()))
lexer = get_lexer_by_name(info, stripall=True)
formatter = HtmlFormatter()
return highlight(code, lexer, formatter)
def block_quote(self, content):
content = content[3:-5]
out = '\n'
for line in houdini.escape_html(content.strip()).split("\n"):
out += '\n{}
'.format(line)
return out + '\n
'
def image(self, link, text, title):
return "
" % (
urlparse(link)._replace(query='').geturl(), text, link
)
def heading(self, text, level):
hash_ = urllib.parse.quote_plus(text)
return "%s \n" % (
level, hash_, text, hash_, level
)
def get_thought_from_id(db, id_):
title, datetime, markdown, redirect = db.get_thought(id_)
html, headers = parse_text(markdown)
return title, datetime, html, headers, redirect
def parse_file(path):
with open(path, "r") as f:
unformatted = f.read()
return parse_text(unformatted)[0]
def parse_text(unformatted):
md = mistune.create_markdown(
renderer = MyRenderer(),
plugins = ["strikethrough", "table", "url", "task_lists", "def_list"]
)
html = md(unformatted)
if html == "":
return "", ""
return html, get_headers(html)
def get_headers(html):
root = lxml.html.fromstring(html)
headers = []
thesmallestlevel = 7
for node in root.xpath('//h1|//h2|//h3|//h4|//h5//h6'):
level = int(node.tag[-1])
if level < thesmallestlevel:
thesmallestlevel = level
headers.append((
urllib.parse.unquote_plus(node.attrib["id"]),
level,
"#%s" % node.attrib["id"])
)
headers = [(i[0], i[1] - thesmallestlevel, i[2]) for i in headers]
md_template = jinja2.Template("""
{% for text, depth, link in contents %}
{{ " " * depth }} - [{{ text }}]({{ link }})
{% endfor %}
""")
return mistune.html(md_template.render(contents = headers))
def main():
p = argparse.ArgumentParser()
subparse = p.add_subparsers(help = "sub-command help")
save_parser = subparse.add_parser("save", help = "Add a markdown file to the database")
echo_parser = subparse.add_parser("echo", help = "Print markdown render to stdout")
update_parser = subparse.add_parser("update", help = "Replace a markdown file")
export_parser = subparse.add_parser("export", help = "Export a database markdown file to disk")
list_parser = subparse.add_parser("list", help = "List all the markdowns in the database")
for s in [save_parser, echo_parser, update_parser]:
s.add_argument(
"-m", "--markdown",
help = "Path to a markdown file",
type = str,
required = True
)
for s in [save_parser]:
s.add_argument(
"-t", "--title",
help = "Article title",
type = str,
required = True
)
for s in [export_parser, update_parser]:
s.add_argument(
"-i", "--id",
help = "Article's id",
type = int,
required = True
)
export_parser.add_argument(
"-o", "--out",
help = "Path to write the markdown file to",
type = str,
required = True
)
args = vars(p.parse_args())
try:
verb = sys.argv[1]
except IndexError:
print("Error! No verb specified. Nothing to do... Exiting...")
exit()
if verb in ["save", "export", "update", "list"]:
with database.Database() as db:
match verb:
case "save":
with open(args["markdown"], "r") as f:
db.add_thought(args["title"], f.read())
print("Added thought.")
case "export":
with open(args["out"], "w") as f:
f.writelines(db.get_thought(args["id"]))
print(f"Written to {args["out"]}")
case "update":
with open(args["markdown"], "r") as f:
db.update_thought_markdown(args["id"], f.read())
print("Updated thought.")
case "list":
for id_, title, datetime in db.get_all_thoughts():
print(f"{id_}, {title}, {datetime}")
case _:
print("Error! Unknown verb... Exiting...")
exit()
elif verb == "echo":
print(parse_file(args["markdown"]))
if __name__ == '__main__':
main()