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):