clean up newlines in babycode.py

This commit is contained in:
2025-12-14 08:40:38 +03:00
parent d7a90745f6
commit 36e17c6677

View File

@@ -8,12 +8,15 @@ import re
BABYCODE_VERSION = 8 BABYCODE_VERSION = 8
class BabycodeError(Exception): class BabycodeError(Exception):
pass pass
class BabycodeRenderError(BabycodeError): class BabycodeRenderError(BabycodeError):
pass pass
class UnknownASTElementError(BabycodeRenderError): class UnknownASTElementError(BabycodeRenderError):
def __init__(self, element_type, element=None): def __init__(self, element_type, element=None):
self.element_type = element_type self.element_type = element_type
@@ -24,6 +27,7 @@ class UnknownASTElementError(BabycodeRenderError):
message += f' (element: {element})' message += f' (element: {element})'
super().__init__(message) super().__init__(message)
class BabycodeRenderResult: class BabycodeRenderResult:
def __init__(self, result, mentions=[]): def __init__(self, result, mentions=[]):
self.result = result self.result = result
@@ -232,9 +236,11 @@ NAMED_COLORS = [
'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen',
] ]
def make_emoji(name, code): def make_emoji(name, code):
return f' <img class=emoji src="/static/emoji/{name}.png" alt="{name}" title=":{code}:">' return f' <img class=emoji src="/static/emoji/{name}.png" alt="{name}" title=":{code}:">'
EMOJI = { EMOJI = {
'angry': make_emoji('angry', 'angry'), 'angry': make_emoji('angry', 'angry'),
@@ -281,6 +287,7 @@ EMOJI = {
'wink': make_emoji('wink', 'wink'), 'wink': make_emoji('wink', 'wink'),
} }
RSS_EMOJI = { RSS_EMOJI = {
**EMOJI, **EMOJI,
@@ -329,8 +336,10 @@ RSS_EMOJI = {
'wink': '😉', 'wink': '😉',
} }
TEXT_ONLY = ["code"] TEXT_ONLY = ["code"]
def tag_code(children, attr): def tag_code(children, attr):
is_inline = children.find('\n') == -1 is_inline = children.find('\n') == -1
if is_inline: if is_inline:
@@ -348,11 +357,13 @@ def tag_code(children, attr):
except PygmentsClassNotFound: except PygmentsClassNotFound:
return unhighlighted return unhighlighted
def tag_list(children): def tag_list(children):
list_body = re.sub(r" +\n", "<br>", children.strip()) list_body = re.sub(r" +\n", "<br>", children.strip())
list_body = re.sub(r"\n\n+", "\1", list_body) list_body = re.sub(r"\n\n+", "\1", list_body)
return " ".join([f"<li>{x}</li>" for x in list_body.split("\1") if x]) return " ".join([f"<li>{x}</li>" for x in list_body.split("\1") if x])
def tag_color(children, attr): def tag_color(children, attr):
if not attr: if not attr:
return f"[color]{children}[/color]" return f"[color]{children}[/color]"
@@ -370,16 +381,19 @@ def tag_color(children, attr):
# return just the way it was if we can't parse it # return just the way it was if we can't parse it
return f"[color={attr}]{children}[/color]" return f"[color={attr}]{children}[/color]"
def tag_spoiler(children, attr): def tag_spoiler(children, attr):
spoiler_name = attr if attr else "Spoiler" spoiler_name = attr if attr else "Spoiler"
content = f"<div class='accordion-content post-accordion-content hidden'>{children}</div>" content = f"<div class='accordion-content post-accordion-content hidden'>{children}</div>"
container = f"""<div class='accordion hidden' data-receive='toggleAccordion'><div class='accordion-header'><button type='button' class='accordion-toggle' data-send='toggleAccordion'>+</button><span>{spoiler_name}</span></div>{content}</div>""" container = f"""<div class='accordion hidden' data-receive='toggleAccordion'><div class='accordion-header'><button type='button' class='accordion-toggle' data-send='toggleAccordion'>+</button><span>{spoiler_name}</span></div>{content}</div>"""
return container return container
def tag_image(children, attr): def tag_image(children, attr):
img = f"<img class=\"post-image\" src=\"{attr}\" alt=\"{children}\">" img = f"<img class=\"post-image\" src=\"{attr}\" alt=\"{children}\">"
return f"<div class=post-img-container>{img}</div>" return f"<div class=post-img-container>{img}</div>"
TAGS = { TAGS = {
"b": lambda children, attr: f"<strong>{children}</strong>", "b": lambda children, attr: f"<strong>{children}</strong>",
"i": lambda children, attr: f"<em>{children}</em>", "i": lambda children, attr: f"<em>{children}</em>",
@@ -403,6 +417,7 @@ TAGS = {
"spoiler": tag_spoiler, "spoiler": tag_spoiler,
} }
def tag_code_rss(children, attr): def tag_code_rss(children, attr):
is_inline = children.find('\n') == -1 is_inline = children.find('\n') == -1
if is_inline: if is_inline:
@@ -419,6 +434,7 @@ def tag_url_rss(children, attr):
return f"<a href={attr}>{children}</a>" return f"<a href={attr}>{children}</a>"
def tag_image_rss(children, attr): def tag_image_rss(children, attr):
if attr.startswith('/'): if attr.startswith('/'):
from flask import current_app from flask import current_app
@@ -427,6 +443,7 @@ def tag_image_rss(children, attr):
return f'<img src="{attr}" alt={children} />' return f'<img src="{attr}" alt={children} />'
RSS_TAGS = { RSS_TAGS = {
**TAGS, **TAGS,
'img': tag_image_rss, 'img': tag_image_rss,
@@ -438,6 +455,7 @@ RSS_TAGS = {
'small': lambda children, attr: f'<small>{children}</small>' 'small': lambda children, attr: f'<small>{children}</small>'
} }
VOID_TAGS = { VOID_TAGS = {
'lb': lambda attr: '[', 'lb': lambda attr: '[',
'rb': lambda attr: ']', 'rb': lambda attr: ']',
@@ -445,6 +463,7 @@ VOID_TAGS = {
'd': lambda attr: '-', 'd': lambda attr: '-',
} }
# [img] is considered block for the purposes of collapsing whitespace, # [img] is considered block for the purposes of collapsing whitespace,
# despite being potentially inline (since the resulting <img> tag is inline, but creates a block container around itself and sibling images). # despite being potentially inline (since the resulting <img> tag is inline, but creates a block container around itself and sibling images).
# [code] has a special case in is_inline(). # [code] has a special case in is_inline().
@@ -452,6 +471,7 @@ INLINE_TAGS = {
'b', 'i', 's', 'u', 'color', 'big', 'small', 'url', 'lb', 'rb', 'at', 'd' 'b', 'i', 's', 'u', 'color', 'big', 'small', 'url', 'lb', 'rb', 'at', 'd'
} }
def is_tag(e, tag=None): def is_tag(e, tag=None):
if e is None: if e is None:
return False return False
@@ -465,9 +485,11 @@ def is_tag(e, tag=None):
return e['name'] == tag return e['name'] == tag
def is_text(e): def is_text(e):
return isinstance(e, str) return isinstance(e, str)
def is_inline(e): def is_inline(e):
if e is None: if e is None:
return False # i think return False # i think
@@ -483,24 +505,6 @@ def is_inline(e):
return e['type'] != 'rule' return e['type'] != 'rule'
def make_mention(e, mentions):
from ..models import Users
from flask import url_for, current_app
with current_app.test_request_context('/'):
target_user = Users.find({'username': e['name'].lower()})
if not target_user:
return f"@{e['name']}"
mention_data = {
'mention_text': f"@{e['name']}",
'mentioned_user_id': int(target_user.id),
"start": e['start'],
"end": e['end'],
}
if mention_data not in mentions:
mentions.append(mention_data)
return f"<a class='mention{' display' if target_user.has_display_name() else ''}' href='{url_for('users.page', username=target_user.username)}' title='@{target_user.username}' data-init='highlightMentions' data-username='{target_user.username}'>{'@' if not target_user.has_display_name() else ''}{target_user.get_readable_name()}</a>"
def should_collapse(text, surrounding): def should_collapse(text, surrounding):
if not isinstance(text, str): if not isinstance(text, str):