Skip to content

Commit 9fed7aa

Browse files
committed
Added a configuration option to abbr and
handling for empty abbreviations. - Configuration option `use_last_abbr` allows using the first instance of a term rather than the last. This is useful for sites that auto-append a glossary to pages. - If an abbreviation does not have a definition it will skip the term instead of writing abbr tags with an empty title.
1 parent 37922a7 commit 9fed7aa

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

docs/changelog.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,19 @@ A call to `Markdown.reset()` now clears all previously defined abbreviations.
2525

2626
Abbreviations are now sorted by length before executing `AbbrTreeprocessor`
2727
to ensure that multi-word abbreviations are implemented even if an abbreviation
28-
exists for one of those component words.
28+
exists for one of those component words. (#1465)
29+
30+
Added an optional `use_last_abbr` configuration option to the abbreviations
31+
extension. Default (`True`) maintains the existing behavior. `False` causes
32+
the extension to only use the first instance of an abbreviation, rather than
33+
the last.
34+
35+
Empty abbreviations are now skipped by `AbbrTreeprocessor`. This avoids applying
36+
abbr tags to text without a title value. This also allows disabling an
37+
abbreviation, which may be useful for documents that uses two terms with
38+
identical abbreviations.
39+
40+
2941

3042
### Fixed
3143

docs/extensions/abbreviations.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
title: Abbreviations Extension
22

3+
ABBR
4+
5+
*[ABBR]: Abbreviation
6+
*[ABBR]: Override Ignored
7+
38
Abbreviations
49
=============
510

markdown/extensions/abbr.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from __future__ import annotations
2424

2525
from . import Extension
26+
from ..util import parseBoolValue
2627
from ..blockprocessors import BlockProcessor
2728
from ..inlinepatterns import InlineProcessor
2829
from ..treeprocessors import Treeprocessor
@@ -41,6 +42,14 @@ class AbbrExtension(Extension):
4142

4243
def __init__(self, **kwargs):
4344
""" Initiate Extension and set up configs. """
45+
self.config = {
46+
'use_last_abbr': [
47+
True,
48+
'True to use the last instance of an abbreviation, rather than the first instance.'
49+
'Default: `True`.'
50+
],
51+
}
52+
""" Default configuration options. """
4453
super().__init__(**kwargs)
4554
self.abbrs = {}
4655

@@ -52,7 +61,7 @@ def extendMarkdown(self, md):
5261
""" Insert `AbbrTreeprocessor` and `AbbrBlockprocessor`. """
5362
md.registerExtension(self)
5463
md.treeprocessors.register(AbbrTreeprocessor(md, self.abbrs), 'abbr', 7)
55-
md.parser.blockprocessors.register(AbbrBlockprocessor(md.parser, self.abbrs), 'abbr', 16)
64+
md.parser.blockprocessors.register(AbbrBlockprocessor(md.parser, self.abbrs, self.getConfigs()), 'abbr', 16)
5665

5766

5867
class AbbrTreeprocessor(Treeprocessor):
@@ -69,11 +78,12 @@ def iter_element(self, el: etree.Element, parent: etree.Element | None = None) -
6978
self.iter_element(child, el)
7079
if text := el.text:
7180
for m in reversed(list(self.RE.finditer(text))):
72-
abbr = etree.Element('abbr', {'title': self.abbrs[m.group(0)]})
73-
abbr.text = AtomicString(m.group(0))
74-
abbr.tail = text[m.end():]
75-
el.insert(0, abbr)
76-
text = text[:m.start()]
81+
if self.abbrs[m.group(0)]:
82+
abbr = etree.Element('abbr', {'title': self.abbrs[m.group(0)]})
83+
abbr.text = AtomicString(m.group(0))
84+
abbr.tail = text[m.end():]
85+
el.insert(0, abbr)
86+
text = text[:m.start()]
7787
el.text = text
7888
if parent and el.tail:
7989
tail = el.tail
@@ -104,8 +114,9 @@ class AbbrBlockprocessor(BlockProcessor):
104114

105115
RE = re.compile(r'^[*]\[(?P<abbr>[^\\]*?)\][ ]?:[ ]*\n?[ ]*(?P<title>.*)$', re.MULTILINE)
106116

107-
def __init__(self, parser: BlockParser, abbrs: dict):
117+
def __init__(self, parser: BlockParser, abbrs: dict, config: dict[str, Any]):
108118
self.abbrs: dict = abbrs
119+
self.use_last_abbr: bool = parseBoolValue(config["use_last_abbr"])
109120
super().__init__(parser)
110121

111122
def test(self, parent: etree.Element, block: str) -> bool:
@@ -122,7 +133,8 @@ def run(self, parent: etree.Element, blocks: list[str]) -> bool:
122133
if m:
123134
abbr = m.group('abbr').strip()
124135
title = m.group('title').strip()
125-
self.abbrs[abbr] = title
136+
if self.use_last_abbr or abbr not in self.abbrs:
137+
self.abbrs[abbr] = title
126138
if block[m.end():].strip():
127139
# Add any content after match back to blocks as separate block
128140
blocks.insert(0, block[m.end():].lstrip('\n'))

tests/test_syntax/extensions/test_abbr.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,26 @@ def test_abbr_override(self):
133133
"""
134134
<p><abbr title="The override">ABBR</abbr></p>
135135
"""
136-
)
136+
),
137+
extensions=[AbbrExtension(use_last_abbr=True)]
138+
)
139+
140+
def test_abbr_override_Ignored(self):
141+
self.assertMarkdownRenders(
142+
self.dedent(
143+
"""
144+
ABBR
145+
146+
*[ABBR]: Abbreviation
147+
*[ABBR]: Override Ignored
148+
"""
149+
),
150+
self.dedent(
151+
"""
152+
<p><abbr title="Abbreviation">ABBR</abbr></p>
153+
"""
154+
),
155+
extensions=[AbbrExtension(use_last_abbr=False)]
137156
)
138157

139158
def test_abbr_nested(self):
@@ -401,6 +420,26 @@ def test_abbr_superset_vs_subset(self):
401420
)
402421
)
403422

423+
def test_abbr_empty(self):
424+
self.assertMarkdownRenders(
425+
self.dedent(
426+
"""
427+
*[abbr]: Abbreviation Definition
428+
429+
abbr
430+
431+
*[abbr]:
432+
433+
Testing document text.
434+
"""
435+
),
436+
self.dedent(
437+
"""
438+
<p>abbr</p>\n<p>Testing document text.</p>
439+
"""
440+
)
441+
)
442+
404443
def test_abbr_reset(self):
405444
ext = AbbrExtension()
406445
md = Markdown(extensions=[ext])

0 commit comments

Comments
 (0)