Skip to content

Commit

Permalink
Add on register event and allow adding block specific configs
Browse files Browse the repository at this point in the history
  • Loading branch information
facelessuser committed Oct 28, 2022
1 parent 3ce4931 commit 10c9ed2
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 12 deletions.
19 changes: 18 additions & 1 deletion pymdownx/blocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ def __init__(self, parser, md, config):
Tabs
]

self.config = {b.NAME: self.set_configs(b.CONFIG, config['block_configs'].get(b.NAME, {})) for b in blocks}

for b in blocks:
b.on_register(md, self.config.get(b.NAME, {}))

self.empty_tags = set(['hr'])
self.block_level_tags = set(md.block_level_elements.copy())
# Block-level tags in which the content only gets span level parsing
Expand Down Expand Up @@ -170,6 +175,17 @@ def __init__(self, parser, md, config):
self.cached_block = None
self.require_yaml_fences = config['require_yaml_fences']

def set_configs(self, default, config):
"""Set config for a block extension."""

d = {}
for key in default.keys():
if key in config:
d[key] = config[key]
else:
d[key] = default[key]
return d

def test(self, parent, block):
"""Test to see if we should process the block."""

Expand All @@ -186,7 +202,7 @@ def test(self, parent, block):
# Create a block object
name = m.group(2).lower()
if name in self.blocks:
generic_block = self.blocks[name](len(m.group(1)), self.trackers[name], self.md)
generic_block = self.blocks[name](len(m.group(1)), self.trackers[name], self.md, self.config[name])
# Remove first line
block = block[m.end():]

Expand Down Expand Up @@ -455,6 +471,7 @@ def __init__(self, *args, **kwargs):

self.config = {
'blocks': [[], "Blocks extensions to load, if not defined, the default ones will be loaded."],
'block_configs': [{}, "Global configuration for a given block."],
'require_yaml_fences': [False, "Require YAML fences in generic blocks."]
}

Expand Down
11 changes: 10 additions & 1 deletion pymdownx/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,14 @@ class Block(metaclass=ABCMeta):
# Arguments will be split and white space stripped.
NAME = ''

# Instance arguments and options
ARGUMENTS = {}
OPTIONS = {}

def __init__(self, length, tracker, md):
# Extension config
CONFIG = {}

def __init__(self, length, tracker, md, config):
"""
Initialize.
Expand All @@ -218,6 +222,7 @@ def __init__(self, length, tracker, md):
self.md = md
self.args = []
self.options = {}
self.config = config
self.on_init()

def on_init(self):
Expand Down Expand Up @@ -313,6 +318,10 @@ def on_parse(self):

return True

@classmethod
def on_register(cls, md, config):
"""Handle registration events."""

@abstractmethod
def on_create(self, parent):
"""Create the needed element and return it."""
Expand Down
4 changes: 2 additions & 2 deletions pymdownx/blocks/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class HTML(Block):
'markdown': ['auto', type_string_in(['auto', 'span', 'block', 'raw'])]
}

def __init__(self, length, tracker, md):
def __init__(self, length, tracker, md, config):
"""Initialize."""

self.markdown = None
super().__init__(length, tracker, md)
super().__init__(length, tracker, md, config)

def on_markdown(self):
"""Check if this is atomic."""
Expand Down
78 changes: 70 additions & 8 deletions pymdownx/blocks/tabs.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,51 @@
"""Tabs."""
import xml.etree.ElementTree as etree
from markdown.extensions import toc
from markdown.treeprocessors import Treeprocessor
from .block import Block, type_boolean


class TabbedTreeprocessor(Treeprocessor):
"""Tab tree processor."""

def __init__(self, md, config):
"""Initialize."""

super().__init__(md)

self.slugify = config["slugify"]
self.sep = config["separator"]

def run(self, doc):
"""Update tab IDs."""

# Get a list of id attributes
used_ids = set()
for el in doc.iter():
if "id" in el.attrib:
used_ids.add(el.attrib["id"])

for el in doc.iter():
if isinstance(el.tag, str) and el.tag.lower() == 'div':
classes = el.attrib.get('class', '').split()
if 'tabbed-set' in classes and 'tabbed-alternate' in classes:
inputs = []
labels = []
for i in list(el):
if i.tag == 'input':
inputs.append(i)
if i.tag == 'div' and i.attrib.get('class', '') == 'tabbed-labels':
labels = [j for j in list(i) if j.tag == 'label']

# Generate slugged IDs
for inpt, label in zip(inputs, labels):
text = toc.get_name(label)
innertext = toc.unescape(toc.stashedHTML2text(text, self.md))
slug = toc.unique(self.slugify(innertext, self.sep), used_ids)
inpt.attrib["id"] = slug
label.attrib["for"] = slug


class Tabs(Block):
"""
Tabbed container.
Expand All @@ -26,15 +69,32 @@ class Tabs(Block):
'select': [False, type_boolean]
}

CONFIG = {
'slugify': None,
'separator': '-'
}

def on_init(self):
"""Handle initialization."""

self.slugify = self.config['slugify'] is not None

# Track tab group count across the entire page.
if 'tab_group_count' not in self.tracker:
self.tracker['tab_group_count'] = 0

self.tab_content = None

@classmethod
def on_register(cls, md, config):
"""Handle registration event."""

print('---config---')
print(config)
if config['slugify'] is not None:
slugs = TabbedTreeprocessor(md, config)
md.treeprocessors.register(slugs, 'tab_slugs', 4)

def last_child(self, parent):
"""Return the last child of an `etree` element."""

Expand Down Expand Up @@ -67,6 +127,9 @@ def on_create(self, parent):
title = self.args[0] if self.args and self.args[0] else ''
sibling = self.last_child(parent)
tabbed_set = 'tabbed-set tabbed-alternate'
index = 0
labels = None
content = None

if (
sibling and sibling.tag.lower() == 'div' and
Expand All @@ -77,8 +140,6 @@ def on_create(self, parent):
tab_group = sibling

index = [index for index, _ in enumerate(tab_group.findall('input'), 1)][-1]
labels = None
content = None
for d in tab_group.findall('div'):
if d.attrib['class'] == 'tabbed-labels':
labels = d
Expand All @@ -95,7 +156,6 @@ def on_create(self, parent):
{'class': tabbed_set, 'data-tabs': '%d:0' % self.tracker['tab_group_count']}
)

index = 0
labels = etree.SubElement(
tab_group,
'div',
Expand All @@ -113,10 +173,14 @@ def on_create(self, parent):

attributes = {
"name": "__tabbed_%d" % tab_set,
"type": "radio",
"id": "__tabbed_%d_%d" % (tab_set, tab_count)
"type": "radio"
}

if not self.slugify:
attributes['id'] = "__tabbed_%d_%d" % (tab_set, tab_count)

attributes2 = {"for": "__tabbed_%d_%d" % (tab_set, tab_count)} if not self.slugify else {}

if first or select:
attributes['checked'] = 'checked'
# Remove any previously assigned "checked states" to siblings
Expand All @@ -133,9 +197,7 @@ def on_create(self, parent):
lab = etree.SubElement(
labels,
"label",
{
"for": "__tabbed_%d_%d" % (tab_set, tab_count)
}
attributes2
)
lab.text = title

Expand Down
97 changes: 97 additions & 0 deletions tests/test_extensions/test_blocks/test_tab.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,102 @@
"""Test cases for Blocks (tab)."""
from ... import util
from pymdownx.slugs import slugify


class TestTabSlugs(util.MdCase):
"""Test legacy tab slug cases."""

extension = ['pymdownx.blocks', 'toc']
extension_configs = {
'pymdownx.blocks': {
'block_configs': {
'tab': {'slugify': slugify(case='lower')}
}
}
}

MD = r"""
### Here is some text
/// tab | Here is some text
content
///
/// tab | Here is some text
content
///
"""

def test_tab_slugs(self):
"""Test tab slugs."""

self.check_markdown(
self.MD,
'''
<h3 id="here-is-some-text">Here is some text</h3>
<div class="tabbed-set tabbed-alternate" data-tabs="1:2"><input checked="checked" id="here-is-some-text_1" name="__tabbed_1" type="radio" /><input id="here-is-some-text_2" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="here-is-some-text_1">Here is some text</label><label for="here-is-some-text_2">Here is some text</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
<p>content</p>
</div>
<div class="tabbed-block">
<p>content</p>
</div>
</div>
</div>
''', # noqa: E501
True
)


class TestTabSlugsSep(util.MdCase):
"""Test legacy tab slug separator cases."""

extension = ['pymdownx.blocks', 'toc']
extension_configs = {
'pymdownx.blocks': {
'block_configs': {
'tab': {'slugify': slugify(case='lower'), 'separator': '_'}
}
}
}

MD = r"""
### Here is some text
/// tab | Here is some text
content
///
/// tab | Here is some text
content
///
"""

def test_slug_with_separator(self):
"""Test tab slugs with separator."""

self.check_markdown(
self.MD,
'''
<h3 id="here-is-some-text">Here is some text</h3>
<div class="tabbed-set tabbed-alternate" data-tabs="1:2"><input checked="checked" id="here_is_some_text" name="__tabbed_1" type="radio" /><input id="here_is_some_text_1" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="here_is_some_text">Here is some text</label><label for="here_is_some_text_1">Here is some text</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
<p>content</p>
</div>
<div class="tabbed-block">
<p>content</p>
</div>
</div>
</div>
''', # noqa: E501
True
)


class TestBlocksTab(util.MdCase):
Expand Down

0 comments on commit 10c9ed2

Please sign in to comment.