Skip to content

feat: add inline code, ordered lists, horizontal rules, bold+italic, strikethrough - #1

Open
cyclingwithelephants wants to merge 2 commits into
mainfrom
feat/extended-markdown-support
Open

feat: add inline code, ordered lists, horizontal rules, bold+italic, strikethrough#1
cyclingwithelephants wants to merge 2 commits into
mainfrom
feat/extended-markdown-support

Conversation

@cyclingwithelephants

Copy link
Copy Markdown
Owner

Summary

Extends parse_inline() and from_markdown() with missing markdown features, plus fixes a bug where list items and blockquotes produced invalid ProseMirror JSON.

Changes

New inline formatting in parse_inline()

  • Inline code (`code`) → {"type": "code"} mark
  • Bold+italic combo (***text***) → [{"type": "strong"}, {"type": "em"}] marks
  • Strikethrough (~~text~~) → {"type": "strikethrough"} mark
  • Pattern ordering: code > links > bold+italic > bold > italic > strikethrough

New block elements in from_markdown()

  • Ordered lists (1. item) → {"type": "ordered_list"} node, mirroring existing bullet list pattern
  • Horizontal rules (---, ***, ___) → calls existing horizontal_rule() method

Bug fix: tokens_to_text_nodes()

List items and blockquotes were using raw parse_inline() tokens ({"content": "text"}) instead of valid ProseMirror text nodes ({"type": "text", "text": "text"}). This caused Substack's editor to render empty bodies. Marks were also being dropped in blockquotes.

Testing

  • 33 new unit tests in tests/substack/test_post_extended.py
  • All 16 existing test_post.py tests pass unchanged
  • All features E2E verified against live Substack API — drafts render correctly

…strikethrough

Extend parse_inline() with new inline formatting patterns:
- Inline code (`code`) with correct pattern ordering to prevent
  backtick content from being parsed as bold/italic
- Bold+italic combo (***text***) matched before bold/italic
- Strikethrough (~~text~~)

Extend from_markdown() with new block-level elements:
- Ordered lists (1. item) mirroring existing bullet list pattern
- Horizontal rules (---, ***, ___) using existing horizontal_rule() method

Fix parse_inline() token format in list items and blockquotes:
- Add tokens_to_text_nodes() helper that converts parse_inline() tokens
  ({"content": "text"}) to valid ProseMirror text nodes
  ({"type": "text", "text": "text"}) with marks preserved
- Apply to flush_bullets, flush_ordered, flush_quotes, and single-line
  variants — fixes Substack rendering empty body for lists/blockquotes

All features verified against live Substack API.
Includes 33 new unit tests covering all additions plus regression tests.
Comment thread substack/post.py Outdated
if not any(start <= match.start() < end for start, end, _, _, _ in matches):
matches.append((match.start(), match.end(), "bold_italic", match.group(1), None))

# 4. Bold

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the numbering, keep the section titles

Comment thread substack/post.py Outdated
Comment on lines +5 to +6
Post Utilities

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is repeated

Comment thread substack/post.py Outdated
self.add({"type": "paragraph", "content": tokens})
# Check for single-line ordered list
ordered_match = re.match(r'^(\d+)\.\s+(.*)', text_content)
if ordered_match:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use an early return to simplify this?

- Fix duplicate "Post Utilities" in module docstring
- Remove numbering from parse_inline section comments
- Flatten single-line blockquote/ordered/paragraph branch with elif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant