computer-playbook/sphinx/extensions/local_subfolders.py

72 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import re
from sphinx.util import logging
from docutils.parsers.rst import Directive
from .nav_utils import natural_sort_key, extract_headings_from_file, group_headings, sort_tree, MAX_HEADING_LEVEL, DEFAULT_MAX_NAV_DEPTH
logger = logging.getLogger(__name__)
def collect_subfolder_tree(dir_path, base_url, current_depth, max_depth):
"""
Recursively collects navigation items from subdirectories.
For each subfolder, it looks for a candidate file (prefer "index.rst" then "README.md")
and extracts its first level1 heading as the title. If no candidate or heading is found,
the folder name is used.
Returns a list representing the subfolder tree.
"""
items = []
for item in sorted(os.listdir(dir_path), key=lambda s: s.lower()):
full_path = os.path.join(dir_path, item)
if os.path.isdir(full_path):
candidate = None
for cand in ['index.rst', 'README.md']:
candidate_path = os.path.join(full_path, cand)
if os.path.isfile(candidate_path):
candidate = candidate_path
break
if candidate:
headings = extract_headings_from_file(candidate, max_level=MAX_HEADING_LEVEL)
title = headings[0]['text'] if headings else item
else:
title = item
link = os.path.join(base_url, item)
entry = {
'level': 1,
'text': title,
'link': link,
'anchor': '',
'priority': 0,
'filename': item
}
if current_depth < max_depth:
children = collect_subfolder_tree(full_path, os.path.join(base_url, item), current_depth + 1, max_depth)
if children:
entry['children'] = children
items.append(entry)
return items
def add_local_subfolders(app, pagename, templatename, context, doctree):
"""
Collects a tree of subfolder navigation items from the current directory.
For each subfolder, the title is determined by scanning for a candidate file (prefer "index.rst" then "README.md")
and extracting its first level1 heading, or using the folder name if none is found.
The resulting tree is stored in context['local_subfolders'].
"""
srcdir = app.srcdir
directory = os.path.dirname(pagename)
abs_dir = os.path.join(srcdir, directory)
if not os.path.isdir(abs_dir):
logger.warning(f"Directory {abs_dir} not found for page {pagename}.")
context['local_subfolders'] = []
return
max_nav_depth = getattr(app.config, 'local_nav_max_depth', DEFAULT_MAX_NAV_DEPTH)
subfolder_tree = collect_subfolder_tree(abs_dir, directory, current_depth=0, max_depth=max_nav_depth)
sort_tree(subfolder_tree)
context['local_subfolders'] = subfolder_tree
def setup(app):
# Do not add the config value here to avoid conflicts.
app.connect('html-page-context', add_local_subfolders)
return {'version': '0.1', 'parallel_read_safe': True}