From fac6a9a457e69f47a6b1f8fb897a9e90d78f8d2c Mon Sep 17 00:00:00 2001 From: John McCardle Date: Thu, 30 Oct 2025 11:39:54 -0400 Subject: [PATCH] feat: add link transformation to documentation generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds transform_doc_links() function that converts MCRF_LINK patterns to appropriate format (HTML links, Markdown links, or plain text). Addresses issue #97. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tools/generate_dynamic_docs.py | 52 +++++++++++++++++++++++++++----- tools/test_link_transform.py | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 tools/test_link_transform.py diff --git a/tools/generate_dynamic_docs.py b/tools/generate_dynamic_docs.py index 92e65cc..4b79315 100644 --- a/tools/generate_dynamic_docs.py +++ b/tools/generate_dynamic_docs.py @@ -12,6 +12,40 @@ import html import re from pathlib import Path +def transform_doc_links(docstring, format='html', base_url=''): + """Transform MCRF_LINK patterns based on output format. + + Detects pattern: "See also: TEXT (docs/path.md)" + Transforms to appropriate format for output type. + """ + if not docstring: + return docstring + + link_pattern = r'See also: ([^(]+) \(([^)]+)\)' + + def replace_link(match): + text, ref = match.group(1).strip(), match.group(2).strip() + + if format == 'html': + # Convert docs/foo.md → foo.html + href = ref.replace('docs/', '').replace('.md', '.html') + return f'

See also: {text}

' + + elif format == 'web': + # Link to hosted docs + web_path = ref.replace('docs/', '').replace('.md', '') + return f'

See also: {text}

' + + elif format == 'markdown': + # Markdown link + return f'\n**See also:** [{text}]({ref})' + + else: # 'python' or default + # Keep as plain text for Python docstrings + return match.group(0) + + return re.sub(link_pattern, replace_link, docstring) + # Must be run with McRogueFace as interpreter try: import mcrfpy @@ -304,8 +338,9 @@ def generate_html_docs(): html_content += f"""

{func_name}{parsed['signature'] if parsed['signature'] else '(...)'}

-

{html.escape(parsed['description'])}

""" + description = transform_doc_links(parsed['description'], format='html') + html_content += f"

{description}

\n" if parsed['args']: html_content += "

Arguments:

\n
    \n" @@ -361,9 +396,10 @@ def generate_html_docs():
    {method_name}{parsed['signature'] if parsed['signature'] else '(...)'}
    """ - + if parsed['description']: - html_content += f"

    {html.escape(parsed['description'])}

    \n" + description = transform_doc_links(parsed['description'], format='html') + html_content += f"

    {description}

    \n" if parsed['args']: html_content += "
    \n" @@ -429,9 +465,10 @@ def generate_markdown_docs(): parsed = func_info["parsed"] md_content += f"### `{func_name}{parsed['signature'] if parsed['signature'] else '(...)'}`\n\n" - + if parsed['description']: - md_content += f"{parsed['description']}\n\n" + description = transform_doc_links(parsed['description'], format='markdown') + md_content += f"{description}\n\n" if parsed['args']: md_content += "**Arguments:**\n" @@ -479,9 +516,10 @@ def generate_markdown_docs(): parsed = method_info['parsed'] md_content += f"#### `{method_name}{parsed['signature'] if parsed['signature'] else '(...)'}`\n\n" - + if parsed['description']: - md_content += f"{parsed['description']}\n\n" + description = transform_doc_links(parsed['description'], format='markdown') + md_content += f"{description}\n\n" if parsed['args']: md_content += "**Arguments:**\n" diff --git a/tools/test_link_transform.py b/tools/test_link_transform.py new file mode 100644 index 0000000..37e3b05 --- /dev/null +++ b/tools/test_link_transform.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Test script for link transformation function.""" + +import re + +def transform_doc_links(docstring, format='html', base_url=''): + """Transform MCRF_LINK patterns based on output format. + + Detects pattern: "See also: TEXT (docs/path.md)" + Transforms to appropriate format for output type. + """ + if not docstring: + return docstring + + link_pattern = r'See also: ([^(]+) \(([^)]+)\)' + + def replace_link(match): + text, ref = match.group(1).strip(), match.group(2).strip() + + if format == 'html': + # Convert docs/foo.md → foo.html + href = ref.replace('docs/', '').replace('.md', '.html') + return f'

    See also: {text}

    ' + + elif format == 'web': + # Link to hosted docs + web_path = ref.replace('docs/', '').replace('.md', '') + return f'

    See also: {text}

    ' + + elif format == 'markdown': + # Markdown link + return f'\n**See also:** [{text}]({ref})' + + else: # 'python' or default + # Keep as plain text for Python docstrings + return match.group(0) + + return re.sub(link_pattern, replace_link, docstring) + +# Test cases +test_doc = "Description text.\n\nSee also: Tutorial Guide (docs/guide.md)\n\nMore text." + +html_result = transform_doc_links(test_doc, format='html') +print("HTML:", html_result) +assert 'Tutorial Guide' in html_result + +md_result = transform_doc_links(test_doc, format='markdown') +print("Markdown:", md_result) +assert '[Tutorial Guide](docs/guide.md)' in md_result + +plain_result = transform_doc_links(test_doc, format='python') +print("Python:", plain_result) +assert 'See also: Tutorial Guide (docs/guide.md)' in plain_result + +print("\nSUCCESS: All transformations work correctly")