From 3bd1eddfac22ed9c00e2fcd9e3ca56824d68164e Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Sun, 29 Jun 2025 23:42:35 -0400 Subject: [PATCH] added syntax highlighting config --- src/zona/builder.py | 33 +++++++++++++++++++++++++-------- src/zona/config.py | 11 ++++++++++- src/zona/markdown.py | 25 +++++++++++++++++++++---- src/zona/templates.py | 4 ++-- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/zona/builder.py b/src/zona/builder.py index 568d452..01c411f 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -12,7 +12,9 @@ from rich import print class ZonaBuilder: def __init__( - self, cli_root: Path | None = None, cli_output: Path | None = None + self, + cli_root: Path | None = None, + cli_output: Path | None = None, ): self.layout: Layout = discover_layout(cli_root, cli_output) self.config: ZonaConfig = ZonaConfig.from_file( @@ -37,7 +39,9 @@ class ZonaBuilder: destination=destination, url=str(destination.relative_to(layout.output)), ) - if path.name.endswith(".md") and not path.is_relative_to( + if path.name.endswith( + ".md" + ) and not path.is_relative_to( layout.root / "content" / "static" ): item.metadata, item.content = parse_metadata(path) @@ -52,9 +56,13 @@ class ZonaBuilder: item.copy = False name = destination.stem if name == "index": - item.destination = item.destination.with_suffix(".html") + item.destination = ( + item.destination.with_suffix(".html") + ) else: - relative = path.relative_to(base).with_suffix("") + relative = path.relative_to(base).with_suffix( + "" + ) name = relative.stem item.destination = ( layout.output @@ -62,9 +70,13 @@ class ZonaBuilder: / name / "index.html" ) - rel_url = item.destination.parent.relative_to(layout.output) + rel_url = item.destination.parent.relative_to( + layout.output + ) item.url = ( - "" if rel_url == Path(".") else rel_url.as_posix() + "" + if rel_url == Path(".") + else rel_url.as_posix() ) items.append(item) # print(item) @@ -74,13 +86,17 @@ class ZonaBuilder: assert self.items post_list: list[Item] = sorted( [item for item in self.items if item.post], - key=lambda item: item.metadata.date if item.metadata else date.min, + key=lambda item: item.metadata.date + if item.metadata + else date.min, reverse=True, ) templater = Templater( template_dir=self.layout.templates, post_list=post_list ) - self.item_map = {item.source.resolve(): item for item in self.items} + self.item_map = { + item.source.resolve(): item for item in self.items + } # print(item_map) for item in self.item_map.values(): dst = item.destination @@ -90,6 +106,7 @@ class ZonaBuilder: assert item.content is not None # parse markdown and render as html raw_html = zmd.md_to_html( + config=self.config, content=item.content, resolve_links=True, source=item.source, diff --git a/src/zona/config.py b/src/zona/config.py index 9524cc2..ed51f43 100644 --- a/src/zona/config.py +++ b/src/zona/config.py @@ -19,15 +19,24 @@ class BlogConfig: dir: str = "blog" +@dataclass +class HighlightingConfig: + enabled: bool = True + theme: str = "ashen" + wrap: bool = False + + @dataclass class MarkdownConfig: image_labels: bool = True + syntax_highlighting: HighlightingConfig = field( + default_factory=HighlightingConfig + ) @dataclass class ThemeConfig: name: str = "default" - syntax_highlighting: bool = True @dataclass diff --git a/src/zona/markdown.py b/src/zona/markdown.py index 87ea02a..8faf7db 100644 --- a/src/zona/markdown.py +++ b/src/zona/markdown.py @@ -5,6 +5,7 @@ from marko.inline import Link, Image from marko.block import FencedCode from marko.html_renderer import HTMLRenderer from marko.parser import Parser +from zona.config import ZonaConfig from zona.layout import Layout from pygments import highlight @@ -18,6 +19,7 @@ from zona.models import Item class ZonaRenderer(HTMLRenderer): def __init__( self, + config: ZonaConfig | None, resolve: bool = False, source: Path | None = None, layout: Layout | None = None, @@ -33,6 +35,7 @@ class ZonaRenderer(HTMLRenderer): self.source: Path = source.resolve() self.layout: Layout = layout self.item_map: dict[Path, Item] = item_map + self.config: ZonaConfig | None = config @override def render_link(self, element: Link): @@ -80,16 +83,23 @@ class ZonaRenderer(HTMLRenderer): @override def render_fenced_code(self, element: FencedCode): - # code = element.children + assert self.config + config = self.config.markdown.syntax_highlighting code = "".join(child.children for child in element.children) # type: ignore lang = element.lang or "text" + if not config.enabled: + return f"
{code}
" try: lexer = get_lexer_by_name(lang, stripall=False) except Exception: lexer = TextLexer(stripall=False) # type: ignore - formatter = HtmlFormatter(style="ashen", nowrap=True, noclasses=True) + formatter = HtmlFormatter( + style=config.theme, + nowrap=not config.wrap, + noclasses=True, + ) highlighted = highlight(code, lexer, formatter) # type: ignore return ( @@ -100,18 +110,25 @@ class ZonaRenderer(HTMLRenderer): def md_to_html( content: str, + config: ZonaConfig | None, resolve_links: bool = False, source: Path | None = None, layout: Layout | None = None, item_map: dict[Path, Item] | None = None, ) -> str: - if resolve_links and (source is None or layout is None or item_map is None): + if resolve_links and ( + source is None or layout is None or item_map is None + ): raise TypeError( "md_to_html() missing source and ctx when resolve_links is true" ) parser = Parser() ast = parser.parse(content) renderer = ZonaRenderer( - resolve_links, source, layout=layout, item_map=item_map + config, + resolve_links, + source, + layout=layout, + item_map=item_map, ) return renderer.render(ast) diff --git a/src/zona/templates.py b/src/zona/templates.py index 27b3b89..0147df3 100644 --- a/src/zona/templates.py +++ b/src/zona/templates.py @@ -9,7 +9,7 @@ def get_header(template_dir: Path) -> str | None: md_header = template_dir / "header.md" html_header = template_dir / "header.html" if md_header.exists(): - return md_to_html(md_header.read_text()) + return md_to_html(md_header.read_text(), None) elif html_header.exists(): return html_header.read_text() @@ -18,7 +18,7 @@ def get_footer(template_dir: Path) -> str | None: md_footer = template_dir / "footer.md" html_footer = template_dir / "footer.html" if md_footer.exists(): - return md_to_html(md_footer.read_text()) + return md_to_html(md_footer.read_text(), None) elif html_footer.exists(): return html_footer.read_text()