Module

rendering.plugins.directives.fenced

Patched FencedDirective to support indentation, code block awareness, and named closers.

Mistune's default FencedDirective has several issues this patch addresses:

  1. Does not support indented directives (e.g. inside lists)
  2. Does not skip ::: sequences inside fenced code blocks
  3. Does not support named closers (:::{/name})

Named Closers:

Instead of counting fence depths (::::, :::::, etc.), users can use
named closers to explicitly close directives:

```markdown
:::{tab-set}
:::{tab-item} First
Content here
:::{/tab-item}
:::{tab-item} Second
More content
:::{/tab-item}
:::{/tab-set}
```

This eliminates the mental overhead of counting colons for deeply nested
structures while remaining backward compatible with fence-depth counting.

Classes

FencedDirective
FencedDirective that allows indentation, skips code blocks, and supports named closers. This is cr…
5

FencedDirective that allows indentation, skips code blocks, and supports named closers.

This is crucial for:

  1. Nesting directives inside lists or other blocks where indentation is required/present
  2. Showing directive syntax examples inside code blocks without the ::: sequences being consumed by the directive parser
  3. Using named closers (:::{/name}) for complex nested structures

Example with fence-depth counting (traditional): ::::{tab-set} :::{tab-item} Example Content here ::: ::::

Example with named closers (new): :::{tab-set} :::{tab-item} Example Content here :::{/tab-item} :::{/tab-set}

Both syntaxes work and can be mixed. Named closers are optional but recommended for deeply nested structures where counting colons is error-prone.

Inherits from BaseFencedDirective

Methods 1

parse_directive
3 int | None
def parse_directive(self, block: BlockParser, m: Match[str], state: BlockState) -> int | None
Parameters 3
block BlockParser
m Match[str]
state BlockState
Returns

int | None

Internal Methods 4
__init__
2 None
def __init__(self, plugins: list[Any], markers: str = ':') -> None
Parameters 2
plugins list[Any]
markers str
_process_directive
Process a directive, supporting named closers and skipping code blocks. This o…
4 int | None
def _process_directive(self, block: BlockParser, marker: str, start: int, state: BlockState) -> int | None

Process a directive, supporting named closers and skipping code blocks.

This overrides the base implementation to handle:

  1. Named closers: :::{/name} explicitly closes :::{name}
  2. Code blocks: ::: sequences inside fenced code blocks are skipped
  3. Traditional fence-depth: :::: closes ::::{name}

Named closers take precedence when found, otherwise falls back to fence-depth counting for backward compatibility.

Parameters 4
block BlockParser
marker str
start int
state BlockState
Returns

int | None

_extract_directive_name
Extract directive name from opening pattern like {name}.
1 str | None
def _extract_directive_name(self, text: str) -> str | None

Extract directive name from opening pattern like {name}.

Parameters 1
text str
Returns

str | None

_find_named_closer
Find a named closer for the given directive. Handles nested directives of the …
3 tuple[str, int] | None
def _find_named_closer(self, text: str, directive_name: str, is_inside_code_block: Any) -> tuple[str, int] | None

Find a named closer for the given directive.

Handles nested directives of the same type by tracking nesting depth.

Parameters 3
text str

Remaining source text after the opening directive

directive_name str

Name of the directive to close (e.g., "tab-set")

is_inside_code_block Any

Function to check if position is in code block

Returns

tuple[str, int] | None

Tuple of (content_before_closer, end_position) or None if not found