implement a summary property on Content
Allow the user to set a summary and make Article extract its own summary when the Article.render is called and the summary is still missing. Extracted summaries only work when the first item of a markdown file is a paragraph.
This commit is contained in:
+22
-3
@@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
|||||||
import yaml
|
import yaml
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
from markdown_it import MarkdownIt
|
from markdown_it import MarkdownIt
|
||||||
|
from markdown_it.token import Token
|
||||||
from pygments import highlight as pygments_highlight
|
from pygments import highlight as pygments_highlight
|
||||||
from pygments.formatters import HtmlFormatter
|
from pygments.formatters import HtmlFormatter
|
||||||
from pygments.lexers import get_lexer_by_name
|
from pygments.lexers import get_lexer_by_name
|
||||||
@@ -44,6 +45,9 @@ def highlight(code: str, lang: str, attrs: str) -> str:
|
|||||||
return pygments_highlight(code, lexer, html_formatter)
|
return pygments_highlight(code, lexer, html_formatter)
|
||||||
|
|
||||||
|
|
||||||
|
md = MarkdownIt(options_update={"highlight": highlight})
|
||||||
|
|
||||||
|
|
||||||
def read_frontmatter(path: Path) -> dict[str, Any]:
|
def read_frontmatter(path: Path) -> dict[str, Any]:
|
||||||
lines = []
|
lines = []
|
||||||
with path.open("r", encoding="utf-8") as f:
|
with path.open("r", encoding="utf-8") as f:
|
||||||
@@ -88,6 +92,7 @@ class Content(ABC):
|
|||||||
created: date,
|
created: date,
|
||||||
updated: date | None = None,
|
updated: date | None = None,
|
||||||
tags: set[str] | None = None,
|
tags: set[str] | None = None,
|
||||||
|
summary: str | None = None,
|
||||||
):
|
):
|
||||||
if tags is None:
|
if tags is None:
|
||||||
tags = set()
|
tags = set()
|
||||||
@@ -96,6 +101,7 @@ class Content(ABC):
|
|||||||
self.created = created
|
self.created = created
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
self.tags: set[str] = tags
|
self.tags: set[str] = tags
|
||||||
|
self.summary: str | None = summary
|
||||||
self.uri: str = self.build_uri(section, slug)
|
self.uri: str = self.build_uri(section, slug)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -131,14 +137,27 @@ class Article(Content):
|
|||||||
meta["date"],
|
meta["date"],
|
||||||
meta.get("updated", None),
|
meta.get("updated", None),
|
||||||
meta.get("tags", None),
|
meta.get("tags", None),
|
||||||
|
meta.get("summary", None),
|
||||||
)
|
)
|
||||||
self.title: str = meta["title"]
|
self.title: str = meta["title"]
|
||||||
self.content: Markup = Markup("")
|
self.content: Markup = Markup("")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extract_summary(tokens: list[Token]) -> str:
|
||||||
|
if not tokens or tokens[0].type != "paragraph_open" or tokens[0].hidden:
|
||||||
|
raise ContentError(
|
||||||
|
"Body does not start with a paragraph; set 'summary' in frontmatter"
|
||||||
|
)
|
||||||
|
for j in range(1, len(tokens)):
|
||||||
|
if tokens[j].type == "paragraph_close":
|
||||||
|
return cast(str, md.renderer.render(tokens[1:j], md.options, {}))
|
||||||
|
raise ContentError("Content has no paragraph to use as summary")
|
||||||
|
|
||||||
def render(self, site: Site) -> None:
|
def render(self, site: Site) -> None:
|
||||||
md = MarkdownIt()
|
tokens = md.parse(self.read_body())
|
||||||
md.options["highlight"] = highlight
|
if self.summary is None:
|
||||||
self.content = Markup(md.render(self.read_body()))
|
self.summary = Article.extract_summary(tokens)
|
||||||
|
self.content = Markup(md.renderer.render(tokens, md.options, {}))
|
||||||
|
|
||||||
template = site.jinja_env.get_template("article.html")
|
template = site.jinja_env.get_template("article.html")
|
||||||
html = template.render(article=self, site=site)
|
html = template.render(article=self, site=site)
|
||||||
|
|||||||
Reference in New Issue
Block a user