Skip to content

Commit

Permalink
fix(#13): do not modify internal spacing in code blocks (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleKing authored Dec 31, 2023
1 parent fb0132a commit 807b9d6
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 30 deletions.
66 changes: 44 additions & 22 deletions mdformat_mkdocs/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import argparse
import re
from typing import Dict, Mapping
from typing import Dict, Mapping, Tuple

from markdown_it import MarkdownIt
from mdformat.renderer import RenderContext, RenderTreeNode
Expand Down Expand Up @@ -51,15 +51,22 @@ def update_mdit(mdit: MarkdownIt) -> None:
"""Default indent."""


def _separate_indent(line: str) -> Tuple[str, str]:
"""Separate leading indent from content. Also used by the test suite."""
match = _RE_INDENT.match(line)
assert match is not None # for pylint
return (match["indent"], match["content"])


class _MarkdownList:
"""Markdown list."""

this_indent = ""
is_numbered = False
is_semantic_indent = False
is_list_match = False

_numbered = None
_this_indent_depth = 0

def __init__(self, increment_number_mode: bool) -> None:
"""Store relevant 'mdformat' context."""
Expand All @@ -68,7 +75,7 @@ def __init__(self, increment_number_mode: bool) -> None:
def _number(self) -> int:
"""Return the number."""
if self.increment_number_mode:
idx = len(self.this_indent)
idx = self._this_indent_depth
pad = [0] * (idx + 1)
# FYI: on de-dent, clip previously saved _numbered
self._numbered = [*(self._numbered or []), *pad][: (idx + 1)]
Expand All @@ -78,10 +85,9 @@ def _number(self) -> int:

def add_bullet(self, line: str) -> str:
"""Add bullet to the line."""
match = _RE_INDENT.match(line)
assert match is not None # for pylint
self.this_indent = match["indent"]
list_match = _RE_LIST_ITEM.match(match["content"])
indent, content = _separate_indent(line)
self._this_indent_depth = len(indent)
list_match = _RE_LIST_ITEM.match(content)
self.is_list_match = bool(list_match)
new_line = line
if list_match:
Expand All @@ -100,27 +106,47 @@ class _MarkdownIndent:
_last_indent = ""
_counter = 0
_lookup: Dict[str, int] = {}
_code_block_indent: str = ""

def __init__(self) -> None:
self._lookup = {}

def calculate(self, this_indent: str) -> str:
def _get_code_indent(self, indent: str, content: str) -> str:
if content.startswith("```"):
# Remove tracked indent on end of code block
self._code_block_indent = "" if self._code_block_indent else indent
return self._code_block_indent

def calculate(self, line: str) -> str:
"""Calculate the new indent."""
if this_indent:
diff = len(this_indent) - len(self._last_indent)
raw_indent, content = _separate_indent(line)
code_indent = self._get_code_indent(raw_indent, content)
working_indent = code_indent or raw_indent
extra_indent = ""

if working_indent:
diff = len(working_indent) - len(self._last_indent)
print( # FIXME: Remove all print debugging before merge!
f"diff={diff} // indent={len(working_indent)} //_lookup={self._lookup}"
)

if not diff:
...
elif diff > 0:
self._counter += 1
self._lookup[this_indent] = self._counter
elif this_indent in self._lookup:
self._counter = self._lookup[this_indent]
self._lookup[working_indent] = self._counter
elif working_indent in self._lookup:
self._counter = self._lookup[working_indent]
else:
raise NotImplementedError("No indentation")
raise ValueError(f"Error in list indentation at '{line}'")

if code_indent:
extra_indent = "".join(raw_indent[len(code_indent) :])
else:
self._counter = 0
self._last_indent = this_indent
return _DEFAULT_INDENT * self._counter
self._last_indent = working_indent

return _DEFAULT_INDENT * self._counter + extra_indent if content else ""


def _normalize_list(text: str, node: RenderTreeNode, context: RenderContext) -> str:
Expand All @@ -132,13 +158,9 @@ def _normalize_list(text: str, node: RenderTreeNode, context: RenderContext) ->
md_list = _MarkdownList(increment_number_mode=number_mode)
md_indent = _MarkdownIndent()
for line in text.split(eol):
new_line = md_list.add_bullet(line)

try:
new_indent = md_indent.calculate(this_indent=md_list.this_indent)
except NotImplementedError:
raise NotImplementedError(f"Error in indentation of: `{line}`") from None
new_indent = md_indent.calculate(line=line)

new_line = md_list.add_bullet(line)
if (
_ALIGN_SEMANTIC_BREAKS_IN_LISTS
and not md_list.is_list_match
Expand Down
Empty file added tests/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions tests/fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,26 @@ You can also use words, to fit your writing style more closely\[^note\].
\[^note\]: Named footnotes will still render with numbers instead of the text but allow easier identification and linking.\
This footnote also has been made with a different syntax using 4 spaces for new lines.
.

Nested Python Classes. Resolves #13: https://github.com/KyleKing/mdformat-mkdocs/issues/13
.
1. Add a serializer class

```python
class RecurringEventSerializer(serializers.ModelSerializer): # (1)!
"""Used to retrieve recurring_event info"""
class Meta:
model = RecurringEvent # (2)!
```
.
1. Add a serializer class

```python
class RecurringEventSerializer(serializers.ModelSerializer): # (1)!
"""Used to retrieve recurring_event info"""
class Meta:
model = RecurringEvent # (2)!
```
.
18 changes: 18 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from mdformat_mkdocs.plugin import _separate_indent

_SHOW_TEXT = True # PLANNED: Make configurable based on pytest CLI


def _show_indent(content: str) -> None:
for line in content.split("\n"):
indent, content = _separate_indent(line)
print(indent.replace(" ", "→").replace("\t", "➤") + content)


def print_text(output: str, expected: str) -> None:
if _SHOW_TEXT:
print("-- Output --")
_show_indent(output.strip())
print("-- Expected --")
_show_indent(expected.strip())
print("-- <End> --")
3 changes: 3 additions & 0 deletions tests/test_align_semantic_breaks_in_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import mdformat
import pytest

from .helpers import print_text

FIXTURE_PATH = Path(__file__).parent / "fixtures-semantic-indent.md"
fixtures = read_fixture_file(FIXTURE_PATH)

Expand All @@ -17,4 +19,5 @@ def test_align_semantic_breaks_in_lists(line, title, text, expected):
options={"align_semantic_breaks_in_lists": True, "wrap": "keep"},
extensions={"mkdocs"},
)
print_text(output, expected)
assert output.rstrip() == expected.rstrip()
31 changes: 31 additions & 0 deletions tests/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import mdformat
import pytest

from .helpers import print_text

FIXTURE_PATH = Path(__file__).parent / "fixtures.md"
fixtures = read_fixture_file(FIXTURE_PATH)

Expand All @@ -13,4 +15,33 @@
)
def test_fixtures(line, title, text, expected):
output = mdformat.text(text, extensions={"mkdocs"}, options={"wrap": "keep"})
print_text(output, expected)
assert output.rstrip() == expected.rstrip()


TABBED_CODE_BLOCK = '''
1. Add a serializer class
```python
class RecurringEventSerializer(serializers.ModelSerializer): # (1)!
\t"""Used to retrieve recurring_event info"""
\tclass Meta:
\t\tmodel = RecurringEvent # (2)!
```
'''


@pytest.mark.parametrize(
"text,expected",
[
(TABBED_CODE_BLOCK, TABBED_CODE_BLOCK),
],
ids=[
"TABBED_CODE_BLOCK",
],
)
def test_tabbed_code_block(text: str, expected: str):
output = mdformat.text(text, extensions={"mkdocs"}, options={"wrap": "keep"})
print_text(output, expected)
assert output.strip() == expected.strip()
7 changes: 3 additions & 4 deletions tests/test_number.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import mdformat
import pytest

from .helpers import print_text

CASE_1 = """
1. One
1. AAA
Expand Down Expand Up @@ -64,8 +66,5 @@ def test_number(text: str, expected: str):
options={"number": True},
extensions={"mkdocs"},
)

print(output.strip())
print("-- Expected --")
print(expected.strip())
print_text(output, expected)
assert output.strip() == expected.strip()
7 changes: 3 additions & 4 deletions tests/test_wrap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import mdformat
import pytest

from .helpers import print_text

# Note: indented text that starts with a number is parsed as the start of a numbered list

CASE_1 = """
Expand Down Expand Up @@ -109,8 +111,5 @@ def test_wrap(text: str, expected: str, align_lists: bool, wrap: int):
options={"align_semantic_breaks_in_lists": align_lists, "wrap": wrap},
extensions={"mkdocs"},
)

print(output.strip())
print("-- Expected --")
print(expected.strip())
print_text(output, expected)
assert output.strip() == expected.strip()

0 comments on commit 807b9d6

Please sign in to comment.