all2md.ast.sections

Core section utilities for document AST structures.

This module provides the fundamental Section dataclass and basic section extraction and querying operations. Sections are the building blocks for document manipulation, representing a heading and all content up to the next same-or-higher level heading.

Functions

Section : Dataclass representing a document section get_all_sections : Extract all sections from a document with level filtering get_preamble : Get content before first heading parse_section_ranges : Parse section range specifications like “1-3,5” query_sections : Universal section query function (consolidates finding by heading, index, predicate) find_heading : Find a heading node in the document count_sections : Count sections in a document

Examples

Get all sections:
>>> sections = get_all_sections(doc)
>>> for section in sections:
...     print(f"Level {section.level}: {section.get_heading_text()}")
Query sections flexibly:
>>> sections = query_sections(doc, "Introduction")  # by name
>>> sections = query_sections(doc, 0)  # by index
>>> sections = query_sections(doc, lambda s: s.level == 2)  # by predicate
>>> sections = query_sections(doc, level=2)  # by level
Parse section ranges:
>>> indices = parse_section_ranges("1-3,5", total_sections=10)
>>> indices
[0, 1, 2, 4]
class all2md.ast.sections.Section

Bases: object

Represents a document section with heading and content.

A section consists of a heading node and all content nodes that follow it until the next heading of the same or higher level.

Parameters:
  • heading (Heading) – The heading node for this section

  • content (list of Node) – All nodes between this heading and the next same-or-higher level heading

  • level (int) – Heading level (1-6)

  • start_index (int) – Index of the heading in the parent document’s children list

  • end_index (int) – Exclusive end index (one past the last content node)

Examples

>>> section = Section(
...     heading=Heading(level=1, content=[Text("Title")]),
...     content=[Paragraph(content=[Text("Content")])],
...     level=1,
...     start_index=0,
...     end_index=2
... )
heading: Heading
content: list[Node]
level: int = 1
start_index: int = 0
end_index: int = 0
to_document() Document

Convert this section to a standalone document.

Returns:

New document containing the heading and content

Return type:

Document

Examples

>>> doc = section.to_document()
>>> len(doc.children)
2
get_heading_text() str

Extract plain text from the heading.

Returns:

Plain text content of the heading

Return type:

str

Examples

>>> section.get_heading_text()
'Introduction'
__init__(heading: Heading, content: list[Node] = <factory>, level: int = 1, start_index: int = 0, end_index: int = 0) None
all2md.ast.sections.get_all_sections(doc: Document, min_level: int = 1, max_level: int = 6) list[Section]

Extract all sections from a document.

Parameters:
  • doc (Document) – Document to extract sections from

  • min_level (int, default = 1) – Minimum heading level to include (1 is highest)

  • max_level (int, default = 6) – Maximum heading level to include (6 is lowest)

Returns:

All sections in document order

Return type:

list of Section

Notes

A section is defined as a heading plus all content until the next same-or-higher level heading. Content before the first heading is not included in any section (see get_preamble).

Examples

>>> sections = get_all_sections(doc)
>>> for section in sections:
...     print(f"Level {section.level}: {section.get_heading_text()}")
Get only top-level sections:
>>> top_sections = get_all_sections(doc, min_level=1, max_level=1)
all2md.ast.sections.get_preamble(doc: Document) list[Node]

Get all content before the first heading.

Parameters:

doc (Document) – Document to extract preamble from

Returns:

All nodes before the first heading (empty if document starts with heading)

Return type:

list of Node

Examples

>>> preamble = get_preamble(doc)
>>> if preamble:
...     print(f"Found {len(preamble)} preamble nodes")
all2md.ast.sections.parse_section_ranges(section_spec: str, total_sections: int) list[int]

Parse section range specification into list of 0-based section indices.

Supports various formats (all 1-indexed input): - “1-3” -> [0, 1, 2] - “5” -> [4] - “10-” -> [9, 10, …, total_sections-1] - “1-3,5,10-” -> combined ranges - “5-3” -> [2, 3, 4] (automatically swaps to “3-5”)

Reversed ranges (where start > end) are automatically corrected by swapping the values. For example, “10-5” is treated as “5-10”.

Parameters:
  • section_spec (str) – Section range specification (1-based section numbers)

  • total_sections (int) – Total number of sections in document

Returns:

Sorted list of 0-based section indices

Return type:

list of int

Examples

>>> parse_section_ranges("1-3,5", 10)
[0, 1, 2, 4]
>>> parse_section_ranges("8-", 10)
[7, 8, 9]
>>> parse_section_ranges("10-5", 10)
[4, 5, 6, 7, 8, 9]
all2md.ast.sections.query_sections(doc: Document, spec: str | int | list[int] | Callable[[Section], bool] | None = None, *, level: int | None = None, min_level: int = 1, max_level: int = 6, case_sensitive: bool = False) list[Section]

Universal section query function with flexible parameters.

This function consolidates multiple section-finding functions into one powerful interface that handles querying by name, index, pattern, predicate, or level filtering.

Parameters:
  • doc (Document) – Document to search

  • spec (str, int, list[int], callable, or None) – Query specification: - None: return all sections (respects level filters) - str: heading text or pattern (“Introduction”, “Chapter*”) - int: single section index (0-based) - list[int]: multiple section indices (0-based) - callable: predicate function(section) -> bool

  • level (int or None, default = None) – If specified, only return sections at this exact level (1-6)

  • min_level (int, default = 1) – Minimum heading level to include

  • max_level (int, default = 6) – Maximum heading level to include

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string specs)

Returns:

Matching sections in document order

Return type:

list of Section

Raises:
  • ValueError – If spec is invalid or indices are out of range

  • IndexError – If a specified index is out of range

Examples

Find all sections:
>>> sections = query_sections(doc)
Find by exact heading text:
>>> sections = query_sections(doc, "Introduction")
Find by pattern (wildcards):
>>> sections = query_sections(doc, "Chapter*")
Find by single index:
>>> sections = query_sections(doc, 0)  # First section
Find by multiple indices:
>>> sections = query_sections(doc, [0, 2, 4])
Find by predicate:
>>> sections = query_sections(doc, lambda s: len(s.content) > 5)
Find by level:
>>> sections = query_sections(doc, level=2)
Find by level range:
>>> sections = query_sections(doc, min_level=2, max_level=3)
Combined filters:
>>> sections = query_sections(doc, "Chapter*", level=1)
all2md.ast.sections.find_heading(doc: Document, text: str, level: int | None = None, case_sensitive: bool = False) tuple[int, Heading] | None

Find a heading node in the document.

This function finds heading nodes directly, not sections. Use query_sections() if you want to find sections (heading + content).

Parameters:
  • doc (Document) – Document to search

  • text (str) – Text to search for

  • level (int or None, default = None) – If specified, only match headings at this level

  • case_sensitive (bool, default = False) – Whether to perform case-sensitive matching

Returns:

Tuple of (index, heading_node) if found, None otherwise

Return type:

tuple of (int, Heading) or None

Examples

>>> result = find_heading(doc, "Introduction")
>>> if result:
...     idx, heading = result
...     print(f"Found at index {idx}, level {heading.level}")
all2md.ast.sections.resolve_section_indices(sections: list[Section], spec: str | int | list[int], *, case_sensitive: bool = False) list[int]

Resolve an extraction spec to 0-based indices into sections.

This is the selection core shared by extract_sections() (which builds a new Document) and the CLI line-number mapping (which slices the rendered output by section span). Centralizing it keeps the two in lockstep.

Parameters:
  • sections (list of Section) – Sections to select from, in document order.

  • spec (str or int or list of int) – Extraction specification: - Name: “Introduction” (exact match) - Pattern: “Intro*”, “Results” (uses fnmatch) - Single index: 0 (0-based) or “#:1” (1-based with #:) - Range: “#:1-3”, “#:3-” (1-based, inclusive) - Multiple: “#:1,3,5” or [0, 2, 4]

  • case_sensitive (bool, default False) – Whether name matching is case-sensitive.

Returns:

0-based indices into sections. Ordering follows the spec kind: index ranges are sorted ascending; explicit lists keep their order; name patterns are returned in document order.

Return type:

list of int

Raises:
  • ValueError – If the spec is invalid or matches no sections.

  • TypeError – If the spec is of an unsupported type.

all2md.ast.sections.extract_sections(doc: Document, spec: str | int | list[int], *, case_sensitive: bool = False, combine: bool = True, separator: Node | None = None) Document

Extract sections from document using flexible specification.

This function provides powerful section extraction with support for ranges, wildcards, multiple sections, and fuzzy matching with helpful suggestions.

Parameters:
  • doc (Document) – Source document

  • spec (str or int or list of int) – Extraction specification: - Name: “Introduction” (exact match) - Pattern: “Intro*”, “Results” (uses fnmatch) - Single index: 0 or “#:1” (0-based or 1-based with #:) - Range: “#:1-3”, “#:3-” (1-based, inclusive) - Multiple: “#:1,3,5” or [0, 2, 4] (1-based or 0-based list)

  • case_sensitive (bool, default False) – Whether name matching is case-sensitive

  • combine (bool, default True) – If True, combine sections into single document with separators. If False, return document with just the first match.

  • separator (Node or None, default None) – Node to insert between sections (default: ThematicBreak)

Returns:

Document with extracted sections

Return type:

Document

Raises:

ValueError – If spec is invalid or no sections match

Examples

Extract by name:
>>> extracted = extract_sections(doc, "Introduction")
Extract with wildcards:
>>> extracted = extract_sections(doc, "Chapter*")
Extract by range:
>>> extracted = extract_sections(doc, "#:1-3")
Extract multiple specific sections:
>>> extracted = extract_sections(doc, "#:1,3,5")
>>> extracted = extract_sections(doc, [0, 2, 4])
Extract without separators:
>>> extracted = extract_sections(doc, "#:1-3", separator=None)
all2md.ast.sections.count_sections(doc: Document, level: int | None = None) int

Count the number of sections in a document.

Parameters:
  • doc (Document) – Document to count sections in

  • level (int or None, default = None) – If specified, only count sections at this level

Returns:

Number of sections

Return type:

int

Examples

>>> total_sections = count_sections(doc)
>>> top_level = count_sections(doc, level=1)
all2md.ast.sections.section_or_doc_to_nodes(content: Section | Document | list[Node]) list[Node]

Convert Section, Document, or list to list of nodes.

This helper consolidates the repeated pattern of converting various content types to a flat list of nodes.

Parameters:

content (Section, Document, or list of Node) – Content to convert

Returns:

Flat list of nodes

Return type:

list of Node

Examples

>>> nodes = section_or_doc_to_nodes(section)
>>> nodes = section_or_doc_to_nodes(document)
>>> nodes = section_or_doc_to_nodes([para1, para2])
all2md.ast.sections.generate_toc(doc: Document, max_level: int = 3, style: Literal['markdown', 'list', 'nested'] = 'markdown') str | List

Generate a table of contents from document headings.

Parameters:
  • doc (Document) – Document to generate TOC from

  • max_level (int, default = 3) – Maximum heading level to include (1-6)

  • style ({"markdown", "list", "nested"}, default = "markdown") – Output style for the TOC

Returns:

Table of contents as markdown string (for “markdown” style) or List node (for “list” and “nested” styles)

Return type:

str or List

Notes

  • “markdown”: Returns markdown-formatted TOC as a string

  • “list”: Returns a flat List AST node with all headings

  • “nested”: Returns nested List AST nodes respecting heading hierarchy

Examples

>>> toc = generate_toc(doc, max_level=3)
>>> print(toc)
# Table of Contents
  • [Introduction](#introduction) - [Background](#background) - [Motivation](#motivation)

  • [Methods](#methods)

all2md.ast.sections.insert_toc(doc: Document, position: Literal['start', 'after_first_heading'] = 'start', max_level: int = 3, style: Literal['markdown', 'list', 'nested'] = 'markdown') Document

Insert a table of contents into the document.

Parameters:
  • doc (Document) – Document to modify

  • position ({"start", "after_first_heading"}, default = "start") – Where to insert the TOC

  • max_level (int, default = 3) – Maximum heading level to include

  • style ({"markdown", "list", "nested"}, default = "markdown") – Style of the TOC

Returns:

New document with TOC inserted

Return type:

Document

Examples

>>> doc_with_toc = insert_toc(doc, position="start", max_level=3)