docs: update CLAUDE.md with MCRF_* macro documentation system

Updated documentation guidelines to reflect the new macro-based system:
- Documented MCRF_METHOD and MCRF_PROPERTY usage
- Listed all available macros (MCRF_SIG, MCRF_DESC, MCRF_ARG, etc.)
- Added prose guidelines (concise C++, verbose external docs)
- Updated regeneration workflow (removed references to deleted scripts)
- Emphasized single source of truth and zero-drift architecture

Removed references to obsolete hardcoded documentation scripts that were
deleted in previous commits.

Related: #92 (Inline C++ documentation system)
This commit is contained in:
John McCardle 2025-10-30 12:37:04 -04:00
parent cc80964835
commit 4c61bee512
2 changed files with 221 additions and 54 deletions

105
CLAUDE.md
View File

@ -392,67 +392,102 @@ mcrfpy.setTimer("test", run_test, 100) # 0.1 seconds
## Documentation Guidelines
### Inline C++ Documentation Format
### Documentation Macro System
When adding new methods or modifying existing ones in C++ source files, use this documentation format in PyMethodDef arrays:
**As of 2025-10-30, McRogueFace uses a macro-based documentation system** (`src/McRFPy_Doc.h`) that ensures consistent, complete docstrings across all Python bindings.
#### Include the Header
```cpp
{"method_name", (PyCFunction)Class::method, METH_VARARGS | METH_KEYWORDS,
"method_name(arg1: type, arg2: type = default) -> return_type\n\n"
"Brief description of what the method does.\n\n"
"Args:\n"
" arg1: Description of first argument\n"
" arg2: Description of second argument (default: value)\n\n"
"Returns:\n"
" Description of return value\n\n"
"Example:\n"
" result = obj.method_name(value1, value2)\n\n"
"Note:\n"
" Any important notes or caveats"},
#include "McRFPy_Doc.h"
```
For properties in PyGetSetDef arrays:
#### Documenting Methods
For methods in PyMethodDef arrays, use `MCRF_METHOD`:
```cpp
{"method_name", (PyCFunction)Class::method, METH_VARARGS,
MCRF_METHOD(ClassName, method_name,
MCRF_SIG("(arg1: type, arg2: type)", "return_type"),
MCRF_DESC("Brief description of what the method does."),
MCRF_ARGS_START
MCRF_ARG("arg1", "Description of first argument")
MCRF_ARG("arg2", "Description of second argument")
MCRF_RETURNS("Description of return value")
MCRF_RAISES("ValueError", "Condition that raises this exception")
MCRF_NOTE("Important notes or caveats")
MCRF_LINK("docs/guide.md", "Related Documentation")
)},
```
#### Documenting Properties
For properties in PyGetSetDef arrays, use `MCRF_PROPERTY`:
```cpp
{"property_name", (getter)getter_func, (setter)setter_func,
"Brief description of the property. "
"Additional details about valid values, side effects, etc.", NULL},
MCRF_PROPERTY(property_name,
"Brief description of the property. "
"Additional details about valid values, side effects, etc."
), NULL},
```
#### Available Macros
- `MCRF_SIG(params, ret)` - Method signature
- `MCRF_DESC(text)` - Description paragraph
- `MCRF_ARGS_START` - Begin arguments section
- `MCRF_ARG(name, desc)` - Individual argument
- `MCRF_RETURNS(text)` - Return value description
- `MCRF_RAISES(exception, condition)` - Exception documentation
- `MCRF_NOTE(text)` - Important notes
- `MCRF_LINK(path, text)` - Reference to external documentation
#### Documentation Prose Guidelines
**Keep C++ docstrings concise** (1-2 sentences per section). For complex topics, use `MCRF_LINK` to reference external guides:
```cpp
MCRF_LINK("docs/animation-guide.md", "Animation System Tutorial")
```
**External documentation** (in `docs/`) can be verbose with examples, tutorials, and design rationale.
### Regenerating Documentation
After modifying C++ inline documentation:
After modifying C++ inline documentation with MCRF_* macros:
1. **Rebuild the project**: `make -j$(nproc)`
2. **Generate stub files** (for IDE support):
2. **Generate documentation** (automatic from compiled module):
```bash
./build/mcrogueface --exec generate_stubs.py
```
3. **Generate dynamic documentation** (recommended):
```bash
./build/mcrogueface --exec generate_dynamic_docs.py
./build/mcrogueface --headless --exec tools/generate_dynamic_docs.py
```
This creates:
- `docs/api_reference_dynamic.html`
- `docs/API_REFERENCE_DYNAMIC.md`
4. **Update hardcoded documentation** (if still using old system):
- `generate_complete_api_docs.py` - Update method dictionaries
- `generate_complete_markdown_docs.py` - Update method dictionaries
3. **Generate stub files** (optional, for IDE support):
```bash
./build/mcrogueface --headless --exec tools/generate_stubs.py
```
Creates `.pyi` stub files for type checking and autocompletion
### Important Notes
- **Single source of truth**: Documentation lives in C++ source files via MCRF_* macros
- **McRogueFace as Python interpreter**: Documentation scripts MUST be run using McRogueFace itself, not system Python
- **Use --exec flag**: `./build/mcrogueface --exec script.py` or `--headless --exec` for CI/automation
- **Dynamic is better**: The new `generate_dynamic_docs.py` extracts documentation directly from compiled module
- **Keep docstrings consistent**: Follow the format above for automatic parsing
- **Use --headless --exec**: For non-interactive documentation generation
- **Link transformation**: `MCRF_LINK` references are transformed to appropriate format (HTML, Markdown, etc.)
- **No manual dictionaries**: The old hardcoded documentation system has been removed
### Documentation Pipeline Architecture
1. **C++ Source** → PyMethodDef/PyGetSetDef arrays with docstrings
2. **Compilation**Docstrings embedded in compiled module
1. **C++ Source**MCRF_* macros in PyMethodDef/PyGetSetDef arrays
2. **Compilation**Macros expand to complete docstrings embedded in module
3. **Introspection** → Scripts use `dir()`, `getattr()`, `__doc__` to extract
4. **Generation** → HTML/Markdown/Stub files created
4. **Generation** → HTML/Markdown/Stub files created with transformed links
5. **No drift**: Impossible for docs and code to disagree - they're the same file!
The documentation is only as good as the C++ inline docstrings!
The macro system ensures complete, consistent documentation across all Python bindings.

View File

@ -108,7 +108,7 @@
<body>
<div class="container">
<h1>McRogueFace API Reference</h1>
<p><em>Generated on 2025-10-30 11:47:05</em></p>
<p><em>Generated on 2025-10-30 12:33:46</em></p>
<p><em>This documentation was dynamically generated from the compiled module.</em></p>
<div class="toc">
@ -424,15 +424,37 @@ Attributes:
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
@ -462,15 +484,37 @@ Attributes:
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
@ -514,7 +558,13 @@ Attributes:
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -523,7 +573,15 @@ Attributes:
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -537,7 +595,15 @@ Attributes:
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -625,15 +691,37 @@ Attributes:
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
@ -743,7 +831,13 @@ Attributes:
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -777,11 +871,27 @@ Attributes:
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
@ -857,15 +967,37 @@ Attributes:
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_boundsGet bounding box as (x, y, width, height)</code></h5>
<h5><code class="method-name">get_boundsget_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">moveMove by relative offset (dx, dy)</code></h5>
<h5><code class="method-name">movemove(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resizeResize to new dimensions (width, height)</code></h5>
<h5><code class="method-name">resizeresize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>