From 756407d407d893d0f2db1660e7699cad1b717cc5 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach <kevin@veen.world> Date: Mon, 17 Mar 2025 14:42:29 +0100 Subject: [PATCH] Implemented -v parameter --- .gitignore | 3 +- Makefile | 10 ++-- sphinx/README.md | 7 ++- sphinx/conf.py | 33 ++++------ sphinx/extensions/local_file_headings.py | 76 +++++++++++++++++++++++- 5 files changed, 96 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 22c3e70d..4b791a41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ site.retry *__pycache__ docs/* -venv \ No newline at end of file +venv +*.log \ No newline at end of file diff --git a/Makefile b/Makefile index fb1e565c..a920ce17 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ .PHONY: install deinstall refresh install: - $(MAKE) -C sphinx html - $(MAKE) -C sphinx install + $(MAKE) -C sphinx html $(MAKEFLAGS) + $(MAKE) -C sphinx install $(MAKEFLAGS) deinstall: - $(MAKE) -C sphinx clean + $(MAKE) -C sphinx clean $(MAKEFLAGS) refresh: - $(MAKE) -C sphinx clean - $(MAKE) -C sphinx html + $(MAKE) -C sphinx clean $(MAKEFLAGS) + $(MAKE) -C sphinx html $(MAKEFLAGS) diff --git a/sphinx/README.md b/sphinx/README.md index e47b31b1..04cc6c18 100644 --- a/sphinx/README.md +++ b/sphinx/README.md @@ -22,4 +22,9 @@ This command cleans the previous build and generates the updated documentation. #### On Server -In your inventory file, enable the **Sphinx** role. When activated, the documentation will be automatically generated and deployed under the **docs** subdomain of your CyMaIS instance. This ensures your documentation is always current and easily accessible 🔄🌐. \ No newline at end of file + +### Debug +To debug and produce an .log execute: +```bash +pkgmgr shell cymais -c "make refresh SPHINXOPTS='-v -c .' 2>&1 | tee debug.log" +``` \ No newline at end of file diff --git a/sphinx/conf.py b/sphinx/conf.py index 5f9b0fbc..dba4f26e 100644 --- a/sphinx/conf.py +++ b/sphinx/conf.py @@ -1,12 +1,14 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - import sys +import logging + +# Check if a verbose flag is present in the command line arguments. +if any(arg in sys.argv for arg in ["-v", "--verbose"]): + logging_level = logging.DEBUG +else: + logging_level = logging.INFO + +logging.basicConfig(level=logging_level) + import os sys.path.insert(0, os.path.abspath('.')) @@ -15,32 +17,20 @@ copyright = '2025, Kevin Veen-Birkenbach' author = 'Kevin Veen-Birkenbach' # -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - templates_path = ['templates'] exclude_patterns = ['docs', 'venv', 'venv/**'] - - # -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - html_theme = 'sphinxawesome_theme' html_static_path = ['static'] html_sidebars = { '**': [ - #'globaltoc.html', - # 'relations.html', - # 'sourcelink.html', 'structure.html', # Include your custom template - # 'searchbox.html', ] } - html_theme_options = { - # 'fixed_sidebar': True, "show_prev_next": False, } @@ -73,6 +63,3 @@ def setup(app): if directive is not None: directive.optional_arguments = 10 return {'version': '1.0', 'parallel_read_safe': True} - - - diff --git a/sphinx/extensions/local_file_headings.py b/sphinx/extensions/local_file_headings.py index eb97f199..1c41ef23 100644 --- a/sphinx/extensions/local_file_headings.py +++ b/sphinx/extensions/local_file_headings.py @@ -1,17 +1,68 @@ import os -from sphinx.util import logging +import sys +import logging as std_logging # Use the standard logging module +from sphinx.util import logging # Sphinx logging is used elsewhere if needed 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__) +# Use standard logging to set the level based on command-line args. +logger = std_logging.getLogger(__name__) +if any(arg in sys.argv for arg in ["-v", "--verbose"]): + logger.setLevel(std_logging.DEBUG) +else: + logger.setLevel(std_logging.INFO) + DEFAULT_MAX_NAV_DEPTH = 4 +def postprocess_current(nodes, current_file, current_anchor): + """ + Recursively process the tree nodes in post-order. + If a node or any child node matches the current file and anchor, + mark that node as current. + Returns True if the current subtree contains a current node. + """ + found_in_subtree = False + for node in nodes: + # Process children first (post-order) + child_found = False + if 'children' in node and node['children']: + child_found = postprocess_current(node['children'], current_file, current_anchor) + + # Get the file and anchor for the current node, trimming trailing slashes and whitespace. + node_file = node.get('link', '').rstrip('/') + node_anchor = node.get('anchor', '').strip() + + # Debug: output the current node's values and the comparison values. + logger.debug("Checking node: text=%s, link=%s, anchor=%s", + node.get('text', ''), + node_file, + node_anchor) + logger.debug("Comparing with current_file=%s, current_anchor=%s", + current_file.rstrip('/'), + current_anchor.strip()) + + # Mark node as current if it exactly matches the current file and anchor. + if node_file == current_file.rstrip('/') and node_anchor == current_anchor.strip(): + node['current'] = True + logger.debug("Node '%s' marked as current (exact match).", node.get('text', '')) + found = True + else: + node['current'] = child_found + if child_found: + logger.debug("Node '%s' marked as current (child match).", node.get('text', '')) + + if node['current']: + found_in_subtree = True + return found_in_subtree + def add_local_file_headings(app, pagename, templatename, context, doctree): + logger.debug("add_local_file_headings called with pagename: %s", pagename) + 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}.") + logger.warning("Directory %s not found for page %s.", abs_dir, pagename) context['local_md_headings'] = [] return @@ -41,6 +92,25 @@ def add_local_file_headings(app, pagename, templatename, context, doctree): }) tree = group_headings(file_items) sort_tree(tree) + + logger.debug("Generated tree: %s", tree) + + # Determine current file and anchor. + # This implementation assumes that if an anchor is present, it is appended to pagename as "#anchor". + if '#' in pagename: + current_file, current_anchor = pagename.split('#', 1) + else: + current_file, current_anchor = pagename, '' + + logger.debug("Current file: %s, Current anchor: %s", current_file, current_anchor) + + # Postprocess the tree: bubble up the 'current' flag from children to parents. + if current_anchor: + postprocess_current(tree, current_file, current_anchor) + else: + logger.debug("No anchor provided; skipping current marking.") + + logger.debug("Final tree after postprocessing: %s", tree) context['local_md_headings'] = tree def setup(app):