Fix list/blockquote spacing cascades; add nested indent + bullet hierarchy#20
Merged
Conversation
…archy Deeply nested lists (3+ levels) and blockquotes were producing 2+ blank lines between blocks because each close-handler emitted \n independently with no awareness of the cascade. Nested bullets also had no indent, so sub-items sat at column 0 alongside their parents. This change: - Adds a 4-space indent per nest level in list_item_open via _list_depth state. - Uses •/◦/▪ for bullet depths 1/2/3+ to match Slack's native rendering. - Replaces structural newlines from close-handlers (heading, paragraph, list, blockquote) with a sentinel char (\x02 STX), then in render(): caps runs of 3+ sentinels to 2 with a single regex, and materializes sentinels to real \n. Code-block content emits real \n directly, so it's untouched by the cap — multi-blank-line content inside fenced blocks is preserved verbatim. - Scrubs the STX sentinel from user input in slackify() so user-typed control chars can't collide with our cap machinery. - Adds 10 complex edge-case tests + one full-document integration test covering deep nesting, mixed ordered/unordered, code-with-specials, blockquote+list, all heading levels, multi-blank-line collapse with code preservation, link+formatting, mentions+inline-code, and the sentinel scrub. - Adds docs/architecture.md explaining the pipeline, the cap/sentinel rationale (with the trail of alternatives considered — NULL, PUA, noncharacters — and why STX won), known limitations, and the future AST-walker migration tracked in #19. 60 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
*_closehandler emitted\nindependently with no awareness of the cascade. Nested bullets also had no indent.\x02STX sentinel + cap regex inrender()to collapse multi-newline runs from cascading closes down to a single blank line — without touching content inside fenced code blocks.What changed
src/slackify_markdown/slackify.py_list_depthstate drives a 4-space indent per nest level inlist_item_open._BULLETS_BY_DEPTH = ("•", "◦", "▪")— matches Slack's native nested-list glyphs.heading_close,paragraph_close,bullet_list_close,ordered_list_close,blockquote_close) emitNEW_LINE = "\x02"instead of literal\n.render()caps runs of 3+ sentinels to 2 with one regex, then replaces sentinels with real\n. Code-block / fence handlers emit real\ndirectly so their content is preserved verbatim (including multi-blank-line content).slackify()scrubs\x02from user input so a literal STX in a Markdown source can't collide with our cap machinery.tests/test_convert.pytest_full_document_with_all_patterns) that exercises ~everything together.test_complex_markdownexpectations for the cleaner spacing.docs/architecture.md(new)\n,spliton ```, PUA, Unicode noncharacters, NULL\x00, STX\x02) and why STX won — same convention aspython-markdown.Why STX over alternatives
\n{3,}regex on real newlinesPrivate Use Area�Unicode noncharacter\x00NULLos.exec*reject NULL\x02STXTest plan
PYTHONPATH=src python3 -m pytest tests/ -v— all 60 pass.venv/sample.txt(LLM-generated content with 2-level nested lists + links) — clean single-blank-line separation everywhere\x02in input does not corrupt output\n\n\n\ninside a fenced code block is preservedFollow-ups