mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-03-29 04:23:34 +01:00
72 lines
3.0 KiB
Python
72 lines
3.0 KiB
Python
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 level‑1 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 level‑1 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}
|