diff --git a/callbacks.py b/callbacks.py index b96b24c..05cc87b 100644 --- a/callbacks.py +++ b/callbacks.py @@ -31,12 +31,34 @@ async def msg_cb(room: MatrixRoom, event: RoomMessageText) -> None: args = shlex.split(event.body[4:]) if len(args) >= 1: await tex_router.handle_command(args[0], room.room_id, event, args[1:]) + + return if event.body.startswith("lily!"): import router args = shlex.split(event.body[5:]) if len(args) >= 1: await lily_router.handle_command(args[0], room.room_id, event, args[1:]) - + + return + + if event.formatted_body is not None: + import lilypond + import parser + + code = parser.CodeParser() + code.feed(event.formatted_body) + code.close() + + for block in code.blocks: + content = block['content'] + if block['lang'] == 'tex': + filename = latex.render(event.sender, content) + await utils.send_png(room.room_id, filename) + if block['lang'] == 'ly': + filename = lilypond.render(event.sender, content, template=False) + await utils.send_png(room.room_id, filename) + + for tex in re.findall(latex_regex, event.body, re.M): try: filename = latex.render(event.sender, tex) diff --git a/latex.py b/latex.py index 7abac90..713bdd8 100644 --- a/latex.py +++ b/latex.py @@ -20,7 +20,7 @@ def render(user: str, source: str, png: bool = True) -> str: import subprocess import utils import os - + # Put the user's packages in the format too. directory = utils.create_user_dir(user) pkgs = "" @@ -38,7 +38,8 @@ def render(user: str, source: str, png: bool = True) -> str: # Parse our templated file thru' LaTeX and dvipng. # TODO: Allow arbitrary DPI and foreground color. - ret = subprocess.run(["latex", "-halt-on-error", tmp.name], cwd="/tmp", capture_output=True) + ret = subprocess.run(["latex", "-halt-on-error", tmp.name], + cwd="/tmp", capture_output=True) if ret.returncode != 0: raise FileNotFoundError(ret.stdout.decode()) diff --git a/lilypond.py b/lilypond.py index b1652d0..8d08bb4 100644 --- a/lilypond.py +++ b/lilypond.py @@ -5,23 +5,26 @@ template = r""" {content} """ -def render(user: str, source: str, png: bool = True) -> str: + +def render(user: str, source: str, png: bool = True, template: bool = False) -> str: import tempfile import subprocess import utils import os - fmt = template.format(content=source) + fmt = template.format(content=source) if template else source tmp = tempfile.NamedTemporaryFile(delete=False) tmp.write(bytes(fmt, encoding="utf8")) tmp.close() if png: - ret = subprocess.run(["lilypond", "-d", "preview", "--png", "-dresolution=500", tmp.name], cwd="/tmp", capture_output=True) + ret = subprocess.run(["lilypond", "-d", "preview", "--png", + "-dresolution=500", tmp.name], cwd="/tmp", capture_output=True) if ret.returncode != 0: raise FileNotFoundError(ret.stderr.decode()) return tmp.name + ".preview.png" - subprocess.run(["lilypond", "-d", "preview", "--svg", tmp.name], cwd="/tmp") + subprocess.run(["lilypond", "-d", "preview", + "--svg", tmp.name], cwd="/tmp") return tmp.name + "preview.svg" diff --git a/parser.py b/parser.py new file mode 100644 index 0000000..2d08f84 --- /dev/null +++ b/parser.py @@ -0,0 +1,53 @@ +from html.parser import HTMLParser + +STATE_NONE = 0 +STATE_PRE = 1 +STATE_CODE = 2 + +class CodeParser(HTMLParser): + def __init__(self, **kwargs): + super().__init__() + + # This shall be a list of dictionnaries, which + # look like this: + # [ + # {'lang': 'ly', 'content': '...'}, + # {'lang': 'ly', 'content': '...'}, + # {'lang': 'tex', 'content': '...'}, + # ... + # ] + self.blocks = [] + + # Yes, this thing acts kinda like a state machine. + self.state = STATE_NONE + self.language = '' + self.data = '' + + def handle_starttag(self, tag, attrs): + if self.state == STATE_NONE and tag == "pre": + self.state = STATE_PRE + elif self.state == STATE_PRE and tag == "code": + self.state = STATE_CODE + for val in attrs: + v = val[1] + if val[0] == "class": + if v == "language-lilypond": + self.language = 'ly' + if v == "language-tex" or v == "language-latex": + self.language = 'tex' + + + + def handle_endtag(self, tag): + if self.state == STATE_CODE and tag == "pre": + self.blocks.append({'lang': self.language, 'content': self.data}) + + self.state = STATE_NONE + self.data = '' + self.language = '' + + def handle_data(self, data): + if self.state == STATE_CODE: + self.data += data + + diff --git a/routes.py b/routes.py index 02594c0..3c351ad 100644 --- a/routes.py +++ b/routes.py @@ -45,3 +45,6 @@ async def route_lshow(router: Router, client: Client, room: str, event: RoomMess "formatted_body": f"Couldn't parse LilyPond correctly.
{e.args[0]}
", } await client.room_send(room, message_type="m.room.message", content=content) + except Exception: + # ??? + pass