another working version

This commit is contained in:
relikd
2022-04-05 20:13:02 +02:00
parent 68b5fb406a
commit d3ae0e5800

View File

@@ -1,86 +1,77 @@
# -*- coding: utf-8 -*-
from lektor.markdown import Markdown, Markup
from lektor.context import get_ctx
from lektor.markdown import Markup
from lektor.pluginsystem import Plugin
from lektor.reporter import reporter, style
from lektor.types.formats import MarkdownDescriptor
from lektor.utils import slugify
from lektor.sourceobj import VirtualSourceObject as VObj
import re
from typing import Set, Dict, Iterator, Tuple
from collections import namedtuple
InlineTag = namedtuple('InlineTag', ['slug', 'title', 'link'])
from typing import Set, Dict, Iterator, Generator
from lektor_groupby import report_config_error
class InlineTagsPlugin(Plugin):
name = 'InlineTags'
name = 'inlinetags'
description = 'Auto-detect and reference tags inside written text.'
def on_setup_env(self, **extra) -> None:
def _iter_inlinetags(record, *, children=False) -> Set[InlineTag]:
res = set()
if hasattr(record, 'inlinetags'):
res.update(record.inlinetags.values())
if children and hasattr(record, 'children'):
for child in record.children:
res.update(_iter_inlinetags(child, children=True))
return res
def _get_tags(record, *, recursive=False) -> Iterator[VObj]:
fn = self.env.jinja_env.filters['groupby']
yield from fn(record, *self.config_keys, recursive=recursive)
self.env.jinja_env.filters.update(inlinetags=_iter_inlinetags)
self.env.jinja_env.filters.update(inlinetags=_get_tags)
def on_process_template_context(self, context, **extra) -> None:
# track dependency for observer replacement below
if hasattr(context.get('this'), '_inlinetag_modified'):
ctx = get_ctx()
if ctx:
ctx.record_dependency(self.config_filename)
def on_groupby_before_build_all(self, groupby, **extra) -> None:
self.config_keys = set() # type: Set[str]
# load config
config = self.get_config()
root = config.get('root', '/') # type: str
temp = config.get('template', 'inlinetag.html') # type: str
regex = config.get('regex', r'{{([^}]{1,32})}}') # type: re.Pattern
rel_url = config.get('link', 'tags/{tag}/') # type: str
link_format = config.get('replace', '<a href="{link}">{title}</a>') # type: str
slug_map = config.section_as_dict('slugs') # type: Dict
for sect in config.sections():
if '.' in sect: # e.g., sect.fields and sect.key_map
continue
if self.add_observer(groupby, config, sect):
self.config_keys.add(sect)
# normalize and validate input
def add_observer(self, groupby, config, sect_key) -> bool:
# get regex
pattern = config.section_as_dict(sect_key + '.pattern')
regex_str = pattern.get('match', r'{{([^}]{1,32})}}') # type: str
tag_replace = pattern.get('replace', '{name}') # type: str
# validate input
try:
regex = re.compile(regex)
regex = re.compile(regex_str)
except Exception as e:
err = 'inlinetags.regex not valid: ' + str(e)
reporter._write_line(style(err, fg='red'))
return
if rel_url.endswith('/index.html'):
rel_url = rel_url[:-10]
root = root.rstrip('/') + '/' # ensure end slash
abs_url = rel_url if rel_url.startswith('/') else root + rel_url
rel_url = rel_url.replace('{tag}', '{group}')
report_config_error(sect_key, 'pattern.match', regex_str, e)
return False
# detect and replace tags
@groupby.depends_on(self.config_filename)
@groupby.watch(root, 'inlinetags', slug=rel_url, template=temp)
def convert_inlinetags(args) -> Iterator[Tuple[str, InlineTag]]:
# add observer
watcher = groupby.add_watcher(sect_key, config)
@watcher.grouping()
def convert_inlinetags(args) -> Generator[str, str, None]:
arr = args.field if isinstance(args.field, list) else [args.field]
tmptags = {} # type: Dict[str, InlineTag]
_tags = {} # type: Dict[str, str]
for obj in arr:
if isinstance(obj, (Markdown, MarkdownDescriptor)):
if hasattr(obj, 'source'):
obj = obj.source
if isinstance(obj, str) and str:
for match in regex.finditer(obj):
title = match.group(1)
slug = slug_map.get(title, slugify(title))
link = abs_url.replace('{tag}', slug)
tmptags[title] = InlineTag(slug, title, link)
yield slug, tmptags[title]
name = match.group(1)
_tags[name] = yield name
# ignore other types (int, float, date, url, undefined)
# Create new attribute on page record.
# All tagged records are guaranteed to have this attribute.
if not hasattr(args.record, 'inlinetags'):
args.record.inlinetags = tmptags
elif tmptags:
args.record.inlinetags.update(tmptags)
# replace inline-tags with hyperlink
if tmptags:
if _tags:
def _repl_tags(match: re.Match) -> str:
inl_tag = tmptags[match.group(1)]
return link_format.format(**inl_tag._asdict())
name = match.group(1)
return tag_replace.format(key=_tags[name], name=name)
args.record._inlinetag_modified = True
# get field value
key, b_idx, b_key = args.key
obj = args.record[key]
@@ -88,7 +79,7 @@ class InlineTagsPlugin(Plugin):
obj = obj.blocks[b_idx][b_key]
# type = markdown
if isinstance(obj, (Markdown, MarkdownDescriptor)):
if hasattr(obj, 'source'):
obj.source = regex.sub(_repl_tags, obj.source)
# type = checkboxes, strings
elif isinstance(obj, list):
@@ -102,8 +93,9 @@ class InlineTagsPlugin(Plugin):
newval = Markup(newval)
if b_idx is None:
# _data is only writable in source info update
# during build, write to _bound_data is necessary
# thus, during build, _bound_data is necessary
args.record._bound_data[key] = newval
else:
# here, using _data seems fine...
args.record[key].blocks[b_idx]._data[b_key] = newval
return True