Skip to content

Commit

Permalink
TOC extension: Add new boolean option permalink_prepend (#1339)
Browse files Browse the repository at this point in the history
Boolean flag which, when set to True, will cause permalink anchors
to be inserted at the start of heading elements, rather than at the
end (the default).
  • Loading branch information
ferdnyc authored Aug 7, 2023
1 parent 47c978d commit 6662053
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/change_log/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ title: Change Log
Python-Markdown Change Log
=========================


*under development*: version 3.5 ([Notes](release-3.5.md))

July 25, 2023: version 3.4.4 (a bug-fix release).

* Add a special case for initial `'s` to smarty extension (#1305).
* Unescape any backslash escaped inline raw HTML (#1358).
* Unescape backslash escaped TOC token names (#1360).


March 23, 2023: version 3.4.3 (a bug-fix release).

* Restore console script (#1327).
Expand Down
20 changes: 20 additions & 0 deletions docs/change_log/release-3.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
title: Release Notes for v3.5

# Python-Markdown 3.5 Release Notes

Python-Markdown version 3.5 supports Python versions 3.7, 3.8, 3.9, 3.10,
3.11 and PyPy3.

## New features

The following new features have been included in the 3.5 release:

* A new configuration option has been added to the
[toc](../extensions/toc.md) extension (#1339):

* A new boolean option `permalink_leading` controls the position of
the permanent link anchors generated with `permalink`. Setting
`permalink_leading` to `True` will cause the links to be inserted
at the start of the header, before any other header content. The
default behavior for `permalink` is to append permanent links to
the header, placing them after all other header content.
9 changes: 9 additions & 0 deletions docs/extensions/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ The following options are provided to configure the output:
* **`permalink_title`**:
Title attribute of the permanent link. Defaults to `Permanent link`.

* **`permalink_leading`**:
Set to `True` if the extension should generate leading permanent links.
Default is `False`.

Leading permanent links are placed at the start of the header tag,
before any header content. The default `permalink` behavior (when
`permalink_leading` is unset or set to `False`) creates trailing
permanent links, which are placed at the end of the header content.

* **`baselevel`**:
Base level for headers. Defaults to `1`.

Expand Down
12 changes: 11 additions & 1 deletion markdown/extensions/toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def __init__(self, md, config):
self.use_permalinks = config["permalink"]
self.permalink_class = config["permalink_class"]
self.permalink_title = config["permalink_title"]
self.permalink_leading = parseBoolValue(config["permalink_leading"], False)
self.header_rgx = re.compile("[Hh][123456]")
if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]:
self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')]
Expand Down Expand Up @@ -235,7 +236,12 @@ def add_permalink(self, c, elem_id):
permalink.attrib["class"] = self.permalink_class
if self.permalink_title:
permalink.attrib["title"] = self.permalink_title
c.append(permalink)
if self.permalink_leading:
permalink.tail = c.text
c.text = ""
c.insert(0, permalink)
else:
c.append(permalink)

def build_toc_div(self, toc_list):
""" Return a string div given a toc list. """
Expand Down Expand Up @@ -347,6 +353,10 @@ def __init__(self, **kwargs):
"permalink_title": ["Permanent link",
"Title attribute of the permalink - "
"Defaults to 'Permanent link'"],
"permalink_leading": [False,
"True if permalinks should be placed at "
"the start of the header, rather than the "
"end - Defaults to False."],
"baselevel": ['1', 'Base level for headers.'],
"slugify": [slugify,
"Function to generate anchors based on header text - "
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ nav:
- Test Tools: test_tools.md
- Contributing to Python-Markdown: contributing.md
- Change Log: change_log/index.md
- Release Notes for v.3.5: change_log/release-3.5.md
- Release Notes for v.3.4: change_log/release-3.4.md
- Release Notes for v.3.3: change_log/release-3.3.md
- Release Notes for v.3.2: change_log/release-3.2.md
Expand Down
114 changes: 114 additions & 0 deletions tests/test_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,117 @@ def testTocInHeaders(self):
'</div>\n' # noqa
'<h1 id="toc"><em>[TOC]</em></h1>' # noqa
)

def testPermalink(self):
""" Test TOC `permalink` feature. """
text = '# Hd 1\n\n## Hd 2'
md = markdown.Markdown(
extensions=[markdown.extensions.toc.TocExtension(
permalink=True, permalink_title="PL")]
)
self.assertEqual(
md.convert(text),
'<h1 id="hd-1">'
'Hd 1' # noqa
'<a class="headerlink" href="#hd-1" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'</h1>\n'
'<h2 id="hd-2">'
'Hd 2' # noqa
'<a class="headerlink" href="#hd-2" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'</h2>'
)

def testPermalinkLeading(self):
""" Test TOC `permalink` with `permalink_leading` option. """
text = '# Hd 1\n\n## Hd 2'
md = markdown.Markdown(extensions=[
markdown.extensions.toc.TocExtension(
permalink=True, permalink_title="PL", permalink_leading=True)]
)
self.assertEqual(
md.convert(text),
'<h1 id="hd-1">'
'<a class="headerlink" href="#hd-1" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'Hd 1' # noqa
'</h1>\n'
'<h2 id="hd-2">'
'<a class="headerlink" href="#hd-2" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'Hd 2' # noqa
'</h2>'
)

def testInlineMarkupPermalink(self):
""" Test TOC `permalink` with headers containing markup. """
text = '# Code `in` hd'
md = markdown.Markdown(
extensions=[markdown.extensions.toc.TocExtension(
permalink=True, permalink_title="PL")]
)
self.assertEqual(
md.convert(text),
'<h1 id="code-in-hd">'
'Code <code>in</code> hd' # noqa
'<a class="headerlink" href="#code-in-hd" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'</h1>'
)

def testInlineMarkupPermalinkLeading(self):
""" Test TOC `permalink_leading` with headers containing markup. """
text = '# Code `in` hd'
md = markdown.Markdown(extensions=[
markdown.extensions.toc.TocExtension(
permalink=True, permalink_title="PL", permalink_leading=True)]
)
self.assertEqual(
md.convert(text),
'<h1 id="code-in-hd">'
'<a class="headerlink" href="#code-in-hd" title="PL">' # noqa
'&para;' # noqa
'</a>' # noqa
'Code <code>in</code> hd' # noqa
'</h1>'
)


class TestSmarty(unittest.TestCase):
def setUp(self):
config = {
'smarty': [
('smart_angled_quotes', True),
('substitutions', {
'ndash': '\u2013',
'mdash': '\u2014',
'ellipsis': '\u2026',
'left-single-quote': '&sbquo;', # `sb` is not a typo!
'right-single-quote': '&lsquo;',
'left-double-quote': '&bdquo;',
'right-double-quote': '&ldquo;',
'left-angle-quote': '[',
'right-angle-quote': ']',
}),
]
}
self.md = markdown.Markdown(
extensions=['smarty'],
extension_configs=config
)

def testCustomSubstitutions(self):
text = """<< The "Unicode char of the year 2014"
is the 'mdash': ---
Must not be confused with 'ndash' (--) ... >>
"""
correct = """<p>[ The &bdquo;Unicode char of the year 2014&ldquo;
is the &sbquo;mdash&lsquo;: \u2014
Must not be confused with &sbquo;ndash&lsquo; (\u2013) \u2026 ]</p>"""
self.assertEqual(self.md.convert(text), correct)

0 comments on commit 6662053

Please sign in to comment.