From a5e87aefded1ea895ef9f30849f07e2298e98ee4 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Fri, 3 Feb 2023 22:54:33 +0100 Subject: [PATCH] Syntax highlighting and miscellaneous fixes. --- command.py | 7 ++++--- context.py | 24 ++++++++++++++---------- formatitko.py | 2 +- html.py | 21 +++++++++++++++++++-- test-import.md | 4 ++-- test-partial.md | 5 +++++ test.md | 6 +++--- transform.py | 13 ++++++++++--- 8 files changed, 58 insertions(+), 24 deletions(-) diff --git a/command.py b/command.py index 1532846..358d4a2 100644 --- a/command.py +++ b/command.py @@ -33,9 +33,10 @@ def handle_command_define(e: Element, c: Context): return nullify(e) else: raise NameError(f"Command already defined: '{e.attributes['define']}'") - if "redefine" in e.attributes: - c.set_command(e.attributes["redefine"], compile(e.text, '', 'exec')) - return nullify(e) + if "redefine" in e.attributes: + c.set_command(e.attributes["redefine"], compile(e.text, '', 'exec')) + return nullify(e) + return e def executeCommand(source, element: Element, ctx: Context) -> List[Element]: diff --git a/context.py b/context.py index bb8cebb..e9202c0 100644 --- a/context.py +++ b/context.py @@ -2,11 +2,13 @@ from panflute import Doc class Context: - def __init__(self, doc: Doc=None, parent: 'Context'=None): + def __init__(self, doc: Doc, path: str, parent: 'Context'=None): self.parent = parent self._commands = {} - self._flags = {} self.doc = doc + self.path = path + if self.get_metadata("flags", immediate=True) is None: + self.set_metadata("flags", {}) def get_command(self, command: str): if command in self._commands: @@ -23,24 +25,27 @@ class Context: del self._commands[command] def is_flag_set(self, flag: str): - if flag in self._flags and self._flags[flag]: - return True + if self.get_metadata("flags."+flag): + if self.get_metadata("flags."+flag): + return True + else: + return False elif self.parent: - return self.parent.is_set(flag) + return self.parent.is_flag_set(flag) else: return False def set_flag(self, flag: str, val: bool): - self._flags[flag] = val + self.set_metadata("flags."+flag, val) def unset_flag(self, flag): - del self._flags[flag] + self.unset_metadata("flags."+flag) - def get_metadata(self, key, simple=True): + def get_metadata(self, key, simple=True, immediate=False): value = self.doc.get_metadata(key, None, simple) if value is not None: return value - elif self.parent: + elif self.parent and not immediate: return self.parent.get_metadata(key) else: return None @@ -57,6 +62,5 @@ class Context: key = key.split(".") for k in key[:-1]: meta = meta[k] - print(type(meta)) del meta.content[key[-1]] # A hack because MetaMap doesn't have a __delitem__ diff --git a/formatitko.py b/formatitko.py index 2bf4fe9..5d51685 100755 --- a/formatitko.py +++ b/formatitko.py @@ -18,7 +18,7 @@ doc = import_md(open(sys.argv[1], "r").read()) print(show(doc)) -context = Context(doc) +context = Context(doc, sys.argv[1]) doc = doc.walk(transform, context) print("---------------------") #print(show(doc)) diff --git a/html.py b/html.py index b3f4f42..835a4fb 100644 --- a/html.py +++ b/html.py @@ -1,4 +1,9 @@ from panflute import * +from pygments import highlight +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter +from pygments.util import ClassNotFound + from whitespace import NBSP from transform import FQuoted from katex import KatexClient @@ -64,8 +69,20 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") attributes += f' class="{" ".join(e.classes)}"' if isinstance(e, CodeBlock): - # TODO: Syntax highlighting - tag = "pre" + if e.attributes["highlight"] == True or e.attributes["highlight"] == 'True': + for cl in e.classes: + try: + lexer = get_lexer_by_name(cl) + except ClassNotFound: + continue + break + formatter = HtmlFormatter(style=e.attributes["style"]) + result = highlight(e.text, lexer, formatter) + style = formatter.get_style_defs(".highlight") + return f'{result}' + + else: + return f'
{e.text}
' if isinstance(e, Figure): content_foot = html(e.caption, k, indent_level+1, indent_str) diff --git a/test-import.md b/test-import.md index 90da504..678371b 100644 --- a/test-import.md +++ b/test-import.md @@ -1,7 +1,7 @@ -``` {.python define=nop} +```python {define=nop} appendChildren(element.content) ``` -``` {.python define=opendatatask} +```python {define=opendatatask} println("Toto je praktická open-data úloha. V [odevzdávátku](https://ksp.mff.cuni.cz/h/odevzdavatko/) si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.") ``` diff --git a/test-partial.md b/test-partial.md index 2f0dd87..7b7001a 100644 --- a/test-partial.md +++ b/test-partial.md @@ -7,6 +7,10 @@ I am a little piece of content And things... +:::{if=cat} + +::: + ``` {.python .run} # I set my own flags! ctx.set_flag("cat", True) @@ -40,3 +44,4 @@ I am a duck. :::{if=cat} This should be only shown to included cats. ::: + diff --git a/test.md b/test.md index 84e2986..bff9df2 100644 --- a/test.md +++ b/test.md @@ -16,18 +16,18 @@ This should only be shown to cats ::: -``` {.python .run} +```python {.run} ctx.set_flag("cat", True) ``` -``` {.python .run} +```python {.run} println(f"The main document's title is '{ctx.get_metadata('title')}'") ctx.set_metadata("a", {}) ctx.set_metadata("a.b", {}) ctx.set_metadata("a.b.c", "Bruh **bruh** bruh") ``` -```python +```python {style=native} def bruh(no): wat ``` diff --git a/transform.py b/transform.py index 9bd78dd..e9ba497 100644 --- a/transform.py +++ b/transform.py @@ -15,7 +15,6 @@ class FQuoted(Quoted): def transform(e: Element, c: Context) -> Element: # Returns next sibling element to transform """Transform the AST, making format-agnostic changes.""" - if isinstance(e, Whitespace) and bavlna(e, c): e = NBSP() @@ -41,7 +40,7 @@ def transform(e: Element, c: Context) -> Element: # Returns next sibling element # commands without affecting the state of the current document. if (isinstance(e, Div)) and "partial" in e.attributes: includedDoc = import_md(open(e.attributes["partial"], "r").read()) - nContext = Context(includedDoc, c) + nContext = Context(includedDoc, e.attributes["partial"], c) includedDoc = includedDoc.walk(transform, nContext) e = Div(*includedDoc.content) @@ -60,9 +59,17 @@ def transform(e: Element, c: Context) -> Element: # Returns next sibling element ## Command defines # possible TODO: def/longdef? - if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, "attributes"): + if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, "attributes")\ + and ("define" in e.attributes or "redefine" in e.attributes): e = handle_command_define(e, c) + # Pass down metadata 'highlight' and 'highlight_style' as attribute to CodeBlocks + if isinstance(e, CodeBlock): + if not "highlight" in e.attributes: + e.attributes["highlight"] = c.get_metadata("highlight") if c.get_metadata("highlight") is not None else True + if not "style" in e.attributes: + e.attributes["style"] = c.get_metadata("highlight_style") if c.get_metadata("highlight_style") is not None else "default" + ## Shorthands if isinstance(e, Span) and len(e.content) == 1 and isinstance(e.content[0], Str): ## Handle special command shorthand [!commandname]{}