Skip to content

Commit

Permalink
TOC extension: Add new boolean option permalink_prepend (Python-Markd…
Browse files Browse the repository at this point in the history
…own#1339)

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.