|
12 | 12 | from cadquery.occ_impl.assembly import toJSON
|
13 | 13 | from cadquery.occ_impl.jupyter_tools import DEFAULT_COLOR
|
14 | 14 | from docutils.parsers.rst import directives
|
| 15 | +from sphinx.application import Sphinx |
15 | 16 |
|
16 | 17 | from cadquery.cq_directive import cq_directive_vtk, template_vtk, rendering_code
|
17 | 18 |
|
|
45 | 46 |
|
46 | 47 | """
|
47 | 48 |
|
| 49 | +theapp = None |
| 50 | + |
48 | 51 |
|
49 | 52 | class cadscript_directive(cq_directive_vtk):
|
50 | 53 |
|
@@ -90,7 +93,7 @@ def run(self):
|
90 | 93 | pre_script, script, text = self.get_source_file(content, options.get("steps", None), "text_from_comment" in options)
|
91 | 94 | else:
|
92 | 95 | # else use inline code
|
93 |
| - script = "\n".join(script) |
| 96 | + script = "\n".join(script) |
94 | 97 |
|
95 | 98 | # add the prefix and postfix
|
96 | 99 | plot_code = prefix + "\n" + pre_script + "\n" + script + "\n" + postfix.format(result_var=result_var)
|
@@ -174,9 +177,7 @@ def generate_output(self, plot_code, text, script, options):
|
174 | 177 | lines.append(" <div class=\"cq-top-bottom\"><div class=\"cq-top\">")
|
175 | 178 | lines.append("")
|
176 | 179 |
|
177 |
| - lines.extend(["", "::", ""]) |
178 |
| - lines.extend([" %s" % row.rstrip() for row in script.split("\n")]) |
179 |
| - lines.append("") |
| 180 | + lines.extend(self.generate_code_block(script)) |
180 | 181 |
|
181 | 182 | lines.append(".. raw:: html")
|
182 | 183 | lines.append("")
|
@@ -294,6 +295,74 @@ def render_image(self, assy, options):
|
294 | 295 | ).splitlines()
|
295 | 296 |
|
296 | 297 |
|
| 298 | + def generate_code_block(self, code): |
| 299 | + """ |
| 300 | + Generate a code block with links to the documentation for method calls. |
| 301 | + """ |
| 302 | + highlighter = theapp.builder.highlighter |
| 303 | + |
| 304 | + body_prefixes = ["body", "box", "result", "extr"] |
| 305 | + sketch_prefixes = ["sketch", "s"] |
| 306 | + cadscript_prefixes = ["cad", "cadscript"] |
| 307 | + |
| 308 | + # Regular expression to find method calls like obj.method( |
| 309 | + # in html the string looks like |
| 310 | + # <span class="n">cadscript</span><span class="o">.</span><span class="n">make_box</span><span class="p">( |
| 311 | + |
| 312 | + method_call_pattern = re.compile( |
| 313 | + r'\<span\s+class\=\"n\"\>' # <span class="n"> |
| 314 | + r'\s*(\w+)\s*' # methodName |
| 315 | + r'\<\/span\>\s*' # </span> |
| 316 | + r'\<span\s+class\=\"o\"\>' # <span class="o"> |
| 317 | + r'\s*\.\s*' # . |
| 318 | + r'\<\/span\>\s*' # </span> |
| 319 | + r'\<span\s+class\=\"n\"\>' # <span class="n"> |
| 320 | + r'\s*(\w+)\s*' # methodName |
| 321 | + r'\<\/span\>\s*' # </span> |
| 322 | + r'\<span\s+class\=\"p\"\>' # <span class="p"> |
| 323 | + r'\s*\(' # ( |
| 324 | + ) |
| 325 | + |
| 326 | + # Function to replace method calls with links if they are documented |
| 327 | + # class="n">(\w+)\.(\w+)\((.*?)\)') |
| 328 | + def replace_method_call(match): |
| 329 | + obj_name, method_name = match.groups() |
| 330 | + |
| 331 | + # Determine which document to link based on the prefix |
| 332 | + # todo: could be done by examining env.domaindata['py']['objects'] |
| 333 | + if any(obj_name.startswith(prefix) for prefix in body_prefixes): |
| 334 | + link_target = f'ref_body.html#cadscript.Body.{method_name}' |
| 335 | + elif any(obj_name.startswith(prefix) for prefix in sketch_prefixes): |
| 336 | + link_target = f'ref_sketch.html#cadscript.Sketch.{method_name}' |
| 337 | + elif any(obj_name.startswith(prefix) for prefix in cadscript_prefixes): |
| 338 | + link_target = f'ref_module_functions.html#cadscript.{method_name}' |
| 339 | + else: |
| 340 | + return match.group(0) # No replacement if prefix does not match |
| 341 | + |
| 342 | + link = ( |
| 343 | + f'<span class="n">{obj_name}</span>' |
| 344 | + f'<span class="o">.</span>' |
| 345 | + f'<span class="n"><a class="codelink" href="{link_target}">{method_name}</a></span>' |
| 346 | + f'<span class="p">(' |
| 347 | + ) |
| 348 | + return link |
| 349 | + |
| 350 | + |
| 351 | + # Use Sphinx highlighter to generate HTML |
| 352 | + highlighted_code = highlighter.highlight_block(code, 'python') |
| 353 | + |
| 354 | + # Replace method calls with links |
| 355 | + linked_code = method_call_pattern.sub(replace_method_call, highlighted_code) |
| 356 | + |
| 357 | + lines = [] |
| 358 | + lines.append(".. raw:: html") |
| 359 | + lines.append("") |
| 360 | + lines.extend([" %s" % line.rstrip() for line in linked_code.split("\n")]) |
| 361 | + lines.append("") |
| 362 | + |
| 363 | + return lines |
| 364 | + |
| 365 | + |
297 | 366 |
|
298 | 367 | class cadscript_auto_directive(cadscript_directive):
|
299 | 368 |
|
@@ -355,6 +424,12 @@ def setup(app):
|
355 | 424 | app.add_js_file("vtk.js")
|
356 | 425 | app.add_js_file(None, body=rendering_code)
|
357 | 426 |
|
| 427 | + app.connect("builder-inited", remember_app) |
| 428 | + |
| 429 | + |
| 430 | +def remember_app(app: Sphinx) -> None: |
| 431 | + global theapp # bad hack to remember the app object |
| 432 | + theapp = app |
358 | 433 |
|
359 | 434 |
|
360 | 435 | if __name__ == "__main__":
|
|
0 commit comments