Skip to content

Commit

Permalink
Merge pull request #707 from ae-utbm/update-mistune
Browse files Browse the repository at this point in the history
Update mistune (0.8 => 3.0)
  • Loading branch information
imperosol authored Jul 8, 2024
2 parents 8bcf59a + 7f6c4f6 commit 7fe4951
Show file tree
Hide file tree
Showing 11 changed files with 369 additions and 366 deletions.
2 changes: 1 addition & 1 deletion core/management/commands/install_xapian.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd
# first argument of the script is Xapian version (e.g. 1.2.19)
VERSION=$1
VERSION="$1"

# Cleanup env vars for auto discovery mechanism
export CPATH=
Expand Down
258 changes: 111 additions & 147 deletions core/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,161 +12,125 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from __future__ import annotations

import os
import re
from typing import TYPE_CHECKING

import mistune
from django.urls import reverse
from mistune import InlineGrammar, InlineLexer, Markdown, Renderer, escape, escape_link


class SithRenderer(Renderer):
def file_link(self, pk, suffix):
return reverse("core:file_detail", kwargs={"file_id": pk}) + suffix

def exposant(self, text):
return """<sup>%s</sup>""" % text

def indice(self, text):
return """<sub>%s</sub>""" % text

def underline(self, text):
return """<u>%s</u>""" % text

def image(self, original_src, title, text):
"""Rendering a image with title and text.
:param src: source link of the image.
:param title: title text of the image.
:param text: alt text of the image.
"""
style = None
if "?" in original_src:
src, params = original_src.rsplit("?", maxsplit=1)
m = re.search(r"(\d+%?)(x(\d+%?))?", params)
if not m:
src = original_src
else:
width = m.group(1)
if not width.endswith("%"):
width += "px"
style = "width: %s; " % width
height = m.group(3)
if height is not None:
if not height.endswith("%"):
height += "px"
style += "height: %s; " % height
else:
params = None
src = original_src
src = escape_link(src)
text = escape(text, quote=True)
if title:
title = escape(title, quote=True)
html = '<img src="%s" alt="%s" title="%s"' % (src, text, title)
else:
html = '<img src="%s" alt="%s"' % (src, text)
if style:
html = '%s style="%s"' % (html, style)
if self.options.get("use_xhtml"):
return "%s />" % html
return "%s>" % html


class SithInlineGrammar(InlineGrammar):
double_emphasis = re.compile(r"^\*{2}([\s\S]+?)\*{2}(?!\*)") # **word**
emphasis = re.compile(r"^\*((?:\*\*|[^\*])+?)\*(?!\*)") # *word*
underline = re.compile(r"^_{2}([\s\S]+?)_{2}(?!_)") # __word__
exposant = re.compile(r"^<sup>([\s\S]+?)</sup>") # <sup>text</sup>
indice = re.compile(r"^<sub>([\s\S]+?)</sub>") # <sub>text</sub>


class SithInlineLexer(InlineLexer):
grammar_class = SithInlineGrammar

default_rules = [
"escape",
# 'inline_html',
"autolink",
"url",
"footnote",
"link",
"reflink",
"nolink",
"exposant",
"double_emphasis",
"emphasis",
from mistune import HTMLRenderer, Markdown

if TYPE_CHECKING:
from mistune import InlineParser, InlineState

# match __text__, without linebreak in the text, nor backslash prepending an underscore
# Examples :
# - "__text__" : OK
# - "__te xt__" : OK
# - "__te_xt__" : nope (underscore in the middle)
# - "__te\_xt__" : Ok (the middle underscore is escaped)
# - "__te\nxt__" : nope (there is a linebreak in the text)
# - "\__text__" : nope (one of the underscores have a backslash prepended)
# - "\\__text__" : Ok (the backslash is ignored, because there is another backslash before)
UNDERLINED_RE = (
r"(?<!\\)(?:\\{2})*" # ignore if there is an odd number of backslashes before
r"_{2}" # two underscores
r"(?P<underlined>([^\\_]|\\.)+)" # the actual text
r"_{2}" # closing underscores
)

SITH_LINK_RE = (
r"\[(?P<page_name>[\w\s]+)\]" # [nom du lien]
r"\(page:\/\/" # (page://
r"(?P<page_slug>[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])" # actual page name
r"\)" # )
)

CUSTOM_DIMENSIONS_IMAGE_RE = (
r"\[(?P<img_name>[\w\s]+)\]" # [nom du lien]
r"\(img:\/\/" # (img://
r"(?P<img_slug>[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])" # actual page name
r"\)" # )
)


def parse_underline(_inline: InlineParser, m: re.Match, state: InlineState):
state.append_token({"type": "underline", "raw": m.group("underlined")})
return m.end()


def underline(md_instance: Markdown):
md_instance.inline.register(
"underline",
"indice",
"code",
"linebreak",
UNDERLINED_RE,
parse_underline,
before="emphasis",
)
md_instance.renderer.register("underline", lambda _, text: f"<u>{text}</u>")


def parse_sith_link(_inline: InlineParser, m: re.Match, state: InlineState):
page_name = m.group("page_name")
page_slug = m.group("page_slug")
state.append_token(
{
"type": "link",
"children": [{"type": "text", "raw": page_name}],
"attrs": {"url": reverse("core:page", kwargs={"page_name": page_slug})},
}
)
return m.end()


def sith_link(md_instance: Markdown):
md_instance.inline.register(
"sith_link",
SITH_LINK_RE,
parse_sith_link,
before="emphasis",
)
# no custom renderer here.
# we just add another parsing rule, but render it as if it was
# a regular markdown link


class SithRenderer(HTMLRenderer):
def image(self, text: str, url: str, title=None) -> str:
if "?" not in url:
return super().image(text, url, title)

new_url, params = url.rsplit("?", maxsplit=1)
m = re.match(r"^(?P<width>\d+(%|px)?)(x(?P<height>\d+(%|px)?))?$", params)
if not m:
return super().image(text, url, title)

width, height = m.group("width"), m.group("height")
if not width.endswith(("%", "px")):
width += "px"
style = f"width:{width};"
if height is not None:
if not height.endswith(("%", "px")):
height += "px"
style += f"height:{height};"
return super().image(text, new_url, title).replace("/>", f'style="{style}" />')


markdown = mistune.create_markdown(
renderer=SithRenderer(escape=True),
plugins=[
underline,
sith_link,
"strikethrough",
"text",
]
inline_html_rules = [
"escape",
"autolink",
"footnotes",
"table",
"spoiler",
"subscript",
"superscript",
"url",
"link",
"reflink",
"nolink",
"exposant",
"double_emphasis",
"emphasis",
"underline",
"indice",
"code",
"linebreak",
"strikethrough",
"text",
]

def output_underline(self, m):
text = m.group(1)
return self.renderer.underline(text)

def output_exposant(self, m):
text = m.group(1)
return self.renderer.exposant(text)

def output_indice(self, m):
text = m.group(1)
return self.renderer.indice(text)

# Double emphasis rule changed
def output_double_emphasis(self, m):
text = m.group(1)
text = self.output(text)
return self.renderer.double_emphasis(text)

# Emphasis rule changed
def output_emphasis(self, m):
text = m.group(1)
text = self.output(text)
return self.renderer.emphasis(text)

def _process_link(self, m, link, title=None):
try: # Add page:// support for links
page = re.compile(r"^page://(\S*)") # page://nom_de_ma_page
match = page.search(link)
page = match.group(1) or ""
link = reverse("core:page", kwargs={"page_name": page})
except:
pass
try: # Add file:// support for links
file_link = re.compile(r"^file://(\d*)/?(\S*)?") # file://4000/download
match = file_link.search(link)
pk = match.group(1)
suffix = match.group(2) or ""
link = reverse("core:file_detail", kwargs={"file_id": id}) + suffix
except:
pass
return super()._process_link(m, link, title)


renderer = SithRenderer(escape=True)
inline = SithInlineLexer(renderer)

markdown = Markdown(renderer, inline=inline)
],
)

if __name__ == "__main__":
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down
75 changes: 75 additions & 0 deletions core/static/core/markdown.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.markdown {
--bg-lightgrey: rgb(240, 241, 245);
--border-lightgrey: rgb(215, 216, 220);

h1,
h2,
h3,
h4,
h5,
h6 {
padding-top: 20px;
}

ul,
ol,
p {
line-height: 22px;
}

code {
overflow: auto;
max-width: 100%;
font-family: Consolas;
font-size: 14px;
border-radius: 4px;
border: 1px solid var(--border-lightgrey);
background-color: var(--bg-lightgrey);
padding: 1px 2px;
}

pre code {
// code that is not inlined
margin: 15px;
padding: 10px;
display: block;
border-radius: 5px;
font-size: 1em;

@media screen and (max-width: 500px) {
margin: 10px 0;
}
}

blockquote {
background-color: var(--bg-lightgrey);
border: unset;
border-left: 5px solid #ccc;
border-radius: 4px;
margin: 15px;
padding: 10px 15px;
display: block;
width: fit-content;

p:first-of-type {
margin-top: 0;
}

@media screen and (max-width: 500px) {
margin: 10px 0;
padding: 8px 10px;
}
}

table {
width: auto;
min-width: 30%;
}

a:hover {
text-decoration: underline;
}
.footnotes {
font-size: 85%;
}
}
Loading

0 comments on commit 7fe4951

Please sign in to comment.