added syntax highlighting config

This commit is contained in:
Daniel Fichtinger 2025-06-29 23:42:35 -04:00
parent c792a6bb07
commit 3bd1eddfac
4 changed files with 58 additions and 15 deletions

View file

@ -12,7 +12,9 @@ from rich import print
class ZonaBuilder: class ZonaBuilder:
def __init__( 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.layout: Layout = discover_layout(cli_root, cli_output)
self.config: ZonaConfig = ZonaConfig.from_file( self.config: ZonaConfig = ZonaConfig.from_file(
@ -37,7 +39,9 @@ class ZonaBuilder:
destination=destination, destination=destination,
url=str(destination.relative_to(layout.output)), 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" layout.root / "content" / "static"
): ):
item.metadata, item.content = parse_metadata(path) item.metadata, item.content = parse_metadata(path)
@ -52,9 +56,13 @@ class ZonaBuilder:
item.copy = False item.copy = False
name = destination.stem name = destination.stem
if name == "index": if name == "index":
item.destination = item.destination.with_suffix(".html") item.destination = (
item.destination.with_suffix(".html")
)
else: else:
relative = path.relative_to(base).with_suffix("") relative = path.relative_to(base).with_suffix(
""
)
name = relative.stem name = relative.stem
item.destination = ( item.destination = (
layout.output layout.output
@ -62,9 +70,13 @@ class ZonaBuilder:
/ name / name
/ "index.html" / "index.html"
) )
rel_url = item.destination.parent.relative_to(layout.output) rel_url = item.destination.parent.relative_to(
layout.output
)
item.url = ( item.url = (
"" if rel_url == Path(".") else rel_url.as_posix() ""
if rel_url == Path(".")
else rel_url.as_posix()
) )
items.append(item) items.append(item)
# print(item) # print(item)
@ -74,13 +86,17 @@ class ZonaBuilder:
assert self.items assert self.items
post_list: list[Item] = sorted( post_list: list[Item] = sorted(
[item for item in self.items if item.post], [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, reverse=True,
) )
templater = Templater( templater = Templater(
template_dir=self.layout.templates, post_list=post_list 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) # print(item_map)
for item in self.item_map.values(): for item in self.item_map.values():
dst = item.destination dst = item.destination
@ -90,6 +106,7 @@ class ZonaBuilder:
assert item.content is not None assert item.content is not None
# parse markdown and render as html # parse markdown and render as html
raw_html = zmd.md_to_html( raw_html = zmd.md_to_html(
config=self.config,
content=item.content, content=item.content,
resolve_links=True, resolve_links=True,
source=item.source, source=item.source,

View file

@ -19,15 +19,24 @@ class BlogConfig:
dir: str = "blog" dir: str = "blog"
@dataclass
class HighlightingConfig:
enabled: bool = True
theme: str = "ashen"
wrap: bool = False
@dataclass @dataclass
class MarkdownConfig: class MarkdownConfig:
image_labels: bool = True image_labels: bool = True
syntax_highlighting: HighlightingConfig = field(
default_factory=HighlightingConfig
)
@dataclass @dataclass
class ThemeConfig: class ThemeConfig:
name: str = "default" name: str = "default"
syntax_highlighting: bool = True
@dataclass @dataclass

View file

@ -5,6 +5,7 @@ from marko.inline import Link, Image
from marko.block import FencedCode from marko.block import FencedCode
from marko.html_renderer import HTMLRenderer from marko.html_renderer import HTMLRenderer
from marko.parser import Parser from marko.parser import Parser
from zona.config import ZonaConfig
from zona.layout import Layout from zona.layout import Layout
from pygments import highlight from pygments import highlight
@ -18,6 +19,7 @@ from zona.models import Item
class ZonaRenderer(HTMLRenderer): class ZonaRenderer(HTMLRenderer):
def __init__( def __init__(
self, self,
config: ZonaConfig | None,
resolve: bool = False, resolve: bool = False,
source: Path | None = None, source: Path | None = None,
layout: Layout | None = None, layout: Layout | None = None,
@ -33,6 +35,7 @@ class ZonaRenderer(HTMLRenderer):
self.source: Path = source.resolve() self.source: Path = source.resolve()
self.layout: Layout = layout self.layout: Layout = layout
self.item_map: dict[Path, Item] = item_map self.item_map: dict[Path, Item] = item_map
self.config: ZonaConfig | None = config
@override @override
def render_link(self, element: Link): def render_link(self, element: Link):
@ -80,16 +83,23 @@ class ZonaRenderer(HTMLRenderer):
@override @override
def render_fenced_code(self, element: FencedCode): 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 code = "".join(child.children for child in element.children) # type: ignore
lang = element.lang or "text" lang = element.lang or "text"
if not config.enabled:
return f"<pre><code>{code}</code></pre>"
try: try:
lexer = get_lexer_by_name(lang, stripall=False) lexer = get_lexer_by_name(lang, stripall=False)
except Exception: except Exception:
lexer = TextLexer(stripall=False) # type: ignore 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 highlighted = highlight(code, lexer, formatter) # type: ignore
return ( return (
@ -100,18 +110,25 @@ class ZonaRenderer(HTMLRenderer):
def md_to_html( def md_to_html(
content: str, content: str,
config: ZonaConfig | None,
resolve_links: bool = False, resolve_links: bool = False,
source: Path | None = None, source: Path | None = None,
layout: Layout | None = None, layout: Layout | None = None,
item_map: dict[Path, Item] | None = None, item_map: dict[Path, Item] | None = None,
) -> str: ) -> 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( raise TypeError(
"md_to_html() missing source and ctx when resolve_links is true" "md_to_html() missing source and ctx when resolve_links is true"
) )
parser = Parser() parser = Parser()
ast = parser.parse(content) ast = parser.parse(content)
renderer = ZonaRenderer( 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) return renderer.render(ast)

View file

@ -9,7 +9,7 @@ def get_header(template_dir: Path) -> str | None:
md_header = template_dir / "header.md" md_header = template_dir / "header.md"
html_header = template_dir / "header.html" html_header = template_dir / "header.html"
if md_header.exists(): 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(): elif html_header.exists():
return html_header.read_text() return html_header.read_text()
@ -18,7 +18,7 @@ def get_footer(template_dir: Path) -> str | None:
md_footer = template_dir / "footer.md" md_footer = template_dir / "footer.md"
html_footer = template_dir / "footer.html" html_footer = template_dir / "footer.html"
if md_footer.exists(): 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(): elif html_footer.exists():
return html_footer.read_text() return html_footer.read_text()