Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions mdformat_footnote/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,30 @@ def _footnote_ref_renderer(node: RenderTreeNode, context: RenderContext) -> str:
def _footnote_renderer(node: RenderTreeNode, context: RenderContext) -> str:
first_line = f"[^{node.meta['label']}]:"
indent = " " * 4
elements = []

children = [c for c in node.children if c.type != "footnote_anchor"]

if children and children[0].type == "paragraph":
with context.indented(len(first_line) + 1):
first_element = children[0].render(context)

first_para_first_line, *first_para_rest_lines = first_element.split("\n")

with context.indented(len(indent)):
elements = [child.render(context) for child in children[1:]]

result = first_line + " " + first_para_first_line
if first_para_rest_lines:
result += "\n" + textwrap.indent("\n".join(first_para_rest_lines), indent)
if elements:
result += "\n\n" + textwrap.indent("\n\n".join(elements), indent)
return result

with context.indented(len(indent)):
for child in node.children:
if child.type == "footnote_anchor":
continue
elements.append(child.render(context))
elements = [child.render(context) for child in children]
body = textwrap.indent("\n\n".join(elements), indent)
# if the first body element is a paragraph, we can start on the first line,
# otherwise we start on the second line
if body and node.children and node.children[0].type != "paragraph":
if body:
body = "\n" + body
else:
body = " " + body.lstrip()
return first_line + body


Expand Down
55 changes: 55 additions & 0 deletions tests/fixtures_wrap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
wrap at 30
.
[^short]

[^short]: This is a shorter footnote that should wrap at thirty characters max.
.
[^short]

[^short]: This is a shorter
footnote that should
wrap at thirty
characters max.
.


wrap at 40
.
[^a]

[^a]: Ooh no, the first line of this first paragraph is still wrapped too wide
unfortunately. Should fix this.

But this second paragraph is wrapped exactly as expected. Woohooo, awesome!
.
[^a]

[^a]: Ooh no, the first line of this
first paragraph is still wrapped
too wide unfortunately. Should fix
this.

But this second paragraph is wrapped
exactly as expected. Woohooo,
awesome!
.


wrap at 60
.
[^longer]

[^longer]: This footnote has a longer wrap length so it can contain more text per line
before wrapping occurs.

Multiple paragraphs should also respect the wrapping configuration.
.
[^longer]

[^longer]: This footnote has a longer wrap length so it can
contain more text per line before wrapping
occurs.

Multiple paragraphs should also respect the wrapping
configuration.
.
39 changes: 20 additions & 19 deletions tests/test_word_wrap.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import mdformat
from pathlib import Path
import re

from markdown_it.utils import read_fixture_file
import mdformat
import pytest

def test_word_wrap():
input_text = """\
[^a]
FIXTURE_PATH = Path(__file__).parent / "fixtures_wrap.md"
fixtures = read_fixture_file(FIXTURE_PATH)

[^a]: Ooh no, the first line of this first paragraph is still wrapped too wide
unfortunately. Should fix this.

But this second paragraph is wrapped exactly as expected. Woohooo, awesome!
"""
expected_output = """\
[^a]
def _extract_wrap_length(title):
if match := re.search(r"wrap at (\d+)", title):
return int(match.group(1))
return 40

[^a]: Ooh no, the first line of this first
paragraph is still wrapped too wide
unfortunately. Should fix this.

But this second paragraph is wrapped
exactly as expected. Woohooo,
awesome!
"""
output = mdformat.text(input_text, options={"wrap": 40}, extensions={"footnote"})
assert output == expected_output
@pytest.mark.parametrize(
"line,title,text,expected",
fixtures,
ids=[f[1] for f in fixtures],
)
def test_word_wrap(line, title, text, expected):
wrap_length = _extract_wrap_length(title)
output = mdformat.text(text, options={"wrap": wrap_length}, extensions={"footnote"})
assert output.rstrip() == expected.rstrip(), output