added support for tags, both in metadata and inline in the text

This commit is contained in:
Amy G. Bowersox 2024-08-12 00:53:42 -06:00
parent 8c4ab0c4d5
commit 91919dd324
5 changed files with 83 additions and 4 deletions

View File

@ -2,8 +2,7 @@ METADATA VALUES USED BY DRAGONGLASS
aliases aliases
(Obsidian standard metadata) (Obsidian standard metadata)
List of alternative names that can be used to link to a List of alternative names that can be used to link to a particular page.
particular page.
description description
(Obsidian Publish standard metadata) (Obsidian Publish standard metadata)
@ -13,6 +12,10 @@ publish
(Obsidian Publish standard metadata) (Obsidian Publish standard metadata)
If this boolean value is False, the page will not be published. If this boolean value is False, the page will not be published.
tags
(Obsidian standard metadata)
List of tags for this page. Tags may be defined here or inline in the text.
template template
The file name of the template to be used to render this page, overriding the default. The file name of the template to be used to render this page, overriding the default.

View File

@ -17,6 +17,9 @@ dragonglass_version
python_version python_version
The version number of Python that's running dragonglass. The version number of Python that's running dragonglass.
tags:
A list of all tags the page being rendered has, in sorted order.
text text
The text of the page being rendered. The text of the page being rendered.

View File

@ -42,6 +42,8 @@ INLINE_FOOTNOTE_REF_PATTERN = INLINE_FOOTNOTE_REF_PREFIX + "{}" + ETX
# Obsidian comment marker # Obsidian comment marker
COMMENT_MARKER = '%%' COMMENT_MARKER = '%%'
# Tags pattern
OBSTAG_PATTERN = r'#([a-zA-Z0-9/_-]+)'
def is_proper_url(s: str) -> bool: def is_proper_url(s: str) -> bool:
""" """
@ -1033,6 +1035,49 @@ class ObsidianStyleBlockquotes(Extension):
'callout-text', 11) 'callout-text', 11)
class ObsidianTags(Extension):
def __init__(self, context: Context, **kwargs: dict[str, Any]) -> None:
"""
Initialize the ObsidianStyleBlockquotes extension.
Args:
context (Context): Context object that contains configuration information.
**kwargs (dict[str, Any]: Additional configuration information.
"""
super(ObsidianTags, self).__init__(**kwargs)
self._context = context
def stash_tag(self, tagname: str) -> None:
self._context.current_node.stash_tag(tagname)
class ObsidianTagsProcessor(InlineProcessor):
def __init__(self, pattern: str, md: markdown.Markdown, extref: Any) -> None:
"""
Initialize the GenericLinksProc.
Args:
pattern (str): Regular expression pattern to be matched by this processor.
md (markdown.Markdown): Reference to the Markdown parser.
extref (ObsidianLinks): Backreference to the outer object.
"""
super(ObsidianTags.ObsidianTagsProcessor, self).__init__(pattern, md)
self._extref = extref
def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element | None, int | None, int | None]: # noqa: N802
tagname = m.group(1)
if not re.search(r'[^0-9]', tagname):
return None, None, None
self._extref.stash_tag(tagname)
tag = etree.Element('span')
tag.attrib['class'] = "tag"
tag.text = f"#{tagname}"
return tag, m.start(0), m.end(0)
def extendMarkdown(self, md) -> None:
md.inlinePatterns.register(ObsidianTags.ObsidianTagsProcessor(OBSTAG_PATTERN, md, self),
'obsidian-tags', PRIO_BASE + 40)
def create_markdown_parser(context: Context) -> markdown.Markdown: def create_markdown_parser(context: Context) -> markdown.Markdown:
""" """
Creates a Markdown parser with all our extensions loaded. Creates a Markdown parser with all our extensions loaded.
@ -1063,5 +1108,6 @@ def create_markdown_parser(context: Context) -> markdown.Markdown:
ObsidianImages(context), ObsidianImages(context),
ObsidianLinks(context), ObsidianLinks(context),
ObsidianLists(), ObsidianLists(),
ObsidianInlines()], ObsidianInlines(),
ObsidianTags(context)],
extension_configs=extconfig) extension_configs=extconfig)

View File

@ -36,6 +36,17 @@ blockquote {
span.task-list-item-checked { span.task-list-item-checked {
text-decoration-line: line-through; text-decoration-line: line-through;
} }
span.tag {
background-color: hsla(258, 88%, 66%, 0.1);
border: 0px solid hsla(258, 88%, 66%, 0.15);
border-radius: 2em;
color: hsl(258, 88%, 66%);
font-size: 0.875em;
font-weight: inherit;
text-decoration: none;
padding: 0.25em 0.65em;
line-height: 1;
}
div.codehilite { div.codehilite {
padding: 0.1em 0.25em; padding: 0.1em 0.25em;
margin-top: 0.5em; margin-top: 0.5em;

View File

@ -44,6 +44,7 @@ class SourceNode:
self._path = path self._path = path
self._is_dir = is_dir self._is_dir = is_dir
self._is_md = path.match(MARKDOWN_PAT) self._is_md = path.match(MARKDOWN_PAT)
self._tags: dict[str, str] = {}
self.metadata: dict[str, Any] = {} self.metadata: dict[str, Any] = {}
self.text: str | None = None self.text: str | None = None
self.backlinks: set[Any] = set() self.backlinks: set[Any] = set()
@ -117,6 +118,17 @@ class SourceNode:
return urlquote(xpath.relative_to(rel_path.parent, walk_up=True).as_posix()) return urlquote(xpath.relative_to(rel_path.parent, walk_up=True).as_posix())
return urlquote(prefix + xpath.as_posix()) return urlquote(prefix + xpath.as_posix())
def stash_tag(self, tagname: str) -> None:
"""
Add a tag to the set of tags in this node.
Args:
tagname (str): The tag name to be added.
"""
rtag = tagname.lower()
if rtag not in self._tags:
self._tags[rtag] = tagname
def load_metadata(self) -> None: def load_metadata(self) -> None:
""" """
Loads the metadata for this particular node and saves it in the "metadata" attribute. Loads the metadata for this particular node and saves it in the "metadata" attribute.
@ -133,6 +145,9 @@ class SourceNode:
metalines.append(cur_line) metalines.append(cur_line)
cur_line = f.readline() cur_line = f.readline()
self.metadata = yaml.full_load(''.join(metalines)) self.metadata = yaml.full_load(''.join(metalines))
# Load up the initial tags.
for tag in self.metadata.get("tags", []):
self.stash_tag(tag)
def parse_markdown(self, markdown_parser: markdown.Markdown) -> None: def parse_markdown(self, markdown_parser: markdown.Markdown) -> None:
""" """
@ -156,7 +171,8 @@ class SourceNode:
return { return {
"text": self.text, "text": self.text,
"title": self.page_title, "title": self.page_title,
"description": self.metadata.get("description", "") "description": self.metadata.get("description", ""),
"tags": [self._tags[n] for n in sorted(self._tags.keys())]
} }