Skip to content
Open
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
7 changes: 6 additions & 1 deletion src/codealmanac/services/wiki/sections.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import re

from markdown_it import MarkdownIt

from codealmanac.core.models import CodeAlmanacModel

MARKDOWN = MarkdownIt("commonmark", {"html": False, "linkify": False})
# Token.map line numbers only count these breaks. str.splitlines() also splits
# on form feeds, U+2028 and friends, which would shift every slice after them.
SOURCE_LINE = re.compile(r"[^\r\n]*(?:\r\n?|\n)|[^\r\n]+$")


class WikiSection(CodeAlmanacModel):
Expand All @@ -29,7 +34,7 @@ def project_sections(body: str, page_title: str) -> tuple[WikiSection, ...]:
if not boundaries:
return (section(0, (page_title,), body),)

lines = body.splitlines(keepends=True)
lines = SOURCE_LINE.findall(body)
sections: list[WikiSection] = []
headings: dict[int, str] = {}

Expand Down
21 changes: 21 additions & 0 deletions tests/test_wiki_sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,24 @@ def test_heading_free_page_still_has_one_searchable_section():
assert len(sections) == 1
assert sections[0].heading_path == ("Note",)
assert sections[0].body == "Only prose.\n"


def test_section_slices_count_lines_the_way_the_markdown_parser_does():
body = (
"Pasted note
with a line separator.\n\n"
"## Setup\n\n"
"Install it.\x0cNext page.\n\n"
"## Usage\n\n"
"Run it.\n"
)

sections = project_sections(body, page_title="Tool")

assert [section.heading_path for section in sections] == [
("Tool",),
("Setup",),
("Usage",),
]
assert sections[0].body == "Pasted note
with a line separator.\n\n"
assert sections[1].body == "\nInstall it.\x0cNext page.\n\n"
assert sections[2].body == "\nRun it.\n"