Skip to content

Conversation

@dahlia
Copy link

@dahlia dahlia commented Jan 18, 2026

Summary

This PR fixes an issue where fenced code blocks inside blockquotes were not rendered correctly. Previously, the following Markdown:

> ```python
> def hello():
>     print("Hello, world!")
> ```

Would render the code as inline <code> elements or plain text instead of a proper <pre><code> block.

Problem

The FencedBlockPreprocessor runs once at the beginning of the parsing pipeline, before any block-level processing occurs. At this point, blockquote lines still have their > prefixes, so the fenced code regex pattern (which expects the fence at the start of a line) doesn't match.

When the BlockQuoteProcessor later strips the > prefixes and recursively parses the content via parseChunk(), only block processors run—not preprocessors. Since fenced code detection was only implemented as a preprocessor, it was never invoked for blockquote content.

Indented code blocks work correctly because they are handled by CodeBlockProcessor, which is a block processor that runs during recursive parsing.

Solution

Add a new FencedCodeBlockProcessor class that handles fenced code blocks as a BlockProcessor. This processor runs during recursive parsing, allowing it to detect fenced code blocks inside blockquotes and other nested contexts.

To avoid code duplication between the existing FencedBlockPreprocessor and the new FencedCodeBlockProcessor, common functionality has been extracted into a FencedCodeMixin class:

  • _check_for_deps() — Check for CodeHilite and AttrList extensions
  • _handle_attrs() — Parse attributes from the fence line
  • _generate_html() — Generate HTML output (with or without syntax highlighting)
  • _escape() — Basic HTML escaping

The preprocessor is retained for top-level fenced code blocks, as it processes them efficiently in a single pass. The block processor complements it by handling nested contexts.

Testing

Added TestFencedCodeInBlockquote test class with 5 test cases:

  • Basic fenced code with backticks in blockquote
  • Basic fenced code with tildes in blockquote
  • Fenced code with language specifier in blockquote
  • Fenced code with surrounding text in blockquote
  • Fenced code in nested blockquotes

The fenced code preprocessor only runs once before block parsing,
so fenced code blocks inside blockquotes were not detected after
the blockquote processor stripped the '>' prefixes.

Add a new FencedCodeBlockProcessor (BlockProcessor) that handles
fenced code blocks in nested contexts like blockquotes. Extract
common functionality into FencedCodeMixin to avoid code duplication
between the preprocessor and block processor.
@dahlia dahlia force-pushed the fix/fenced-code-in-blockquote branch from 4339b68 to a13c849 Compare January 18, 2026 22:07
dahlia added a commit to dahlia/dojang that referenced this pull request Jan 18, 2026
The fenced_code extension doesn't properly render code blocks inside
blockquotes.  This uses a forked version with a fix until the upstream
PR is merged and released:

Python-Markdown/markdown#1585
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