Skip to content

Commit 07eb6c2

Browse files
committed
Merge branch 'doc_improvements' into dev
2 parents 7b9728a + 4f84d6d commit 07eb6c2

8 files changed

+155
-56
lines changed

dist/cadscript-0.5.3-py3-none-any.whl

38.1 KB
Binary file not shown.

dist/cadscript-0.5.3.tar.gz

32 KB
Binary file not shown.

docs/ext/cadscript_directives.py

+103-24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from cadquery.occ_impl.assembly import toJSON
1313
from cadquery.occ_impl.jupyter_tools import DEFAULT_COLOR
1414
from docutils.parsers.rst import directives
15+
from sphinx.application import Sphinx
1516

1617
from cadquery.cq_directive import cq_directive_vtk, template_vtk, rendering_code
1718

@@ -45,6 +46,8 @@
4546
4647
"""
4748

49+
theapp = None
50+
4851

4952
class cadscript_directive(cq_directive_vtk):
5053

@@ -90,7 +93,7 @@ def run(self):
9093
pre_script, script, text = self.get_source_file(content, options.get("steps", None), "text_from_comment" in options)
9194
else:
9295
# else use inline code
93-
script = "\n".join(script)
96+
script = "\n".join(script)
9497

9598
# add the prefix and postfix
9699
plot_code = prefix + "\n" + pre_script + "\n" + script + "\n" + postfix.format(result_var=result_var)
@@ -106,7 +109,7 @@ def run(self):
106109
def get_source_file(self, content, steps, text_from_comment):
107110

108111
content = re.sub(r'^cadscript.show\(.*$', '', content, flags=re.MULTILINE) # remove show() calls
109-
text = ""
112+
text = []
110113
pre_script = []
111114
content = content + "\n" # fix problem with last line
112115
ellipsis = False
@@ -134,13 +137,15 @@ def get_source_file(self, content, steps, text_from_comment):
134137
if text_from_comment:
135138
# extract the text from the comment
136139
newscript = []
137-
last_comment = ""
140+
last_comment = []
138141
for part in script:
139-
last_comment = ""
142+
last_comment = []
140143
new_part = ""
141144
for line in part.split("\n")[:-1]: # skip last empty item with [:-1]
142-
if line.startswith("#"):
143-
last_comment += line[1:].strip() + " "
145+
if line.startswith("# "):
146+
last_comment.append(line[2:])
147+
elif line.startswith("#"):
148+
last_comment.append(line[1:])
144149
else:
145150
new_part += line + "\n"
146151
newscript.append(new_part)
@@ -160,23 +165,24 @@ def generate_output(self, plot_code, text, script, options):
160165
lines = []
161166

162167
if len(text):
163-
lines.extend(["", text, ""])
164-
165-
if "side-by-side" in options:
166-
lines.append(".. raw:: html")
167168
lines.append("")
168-
lines.append(" <div class=\"side-by-side\"><div class=\"leftside\">")
169+
lines.extend(text)
169170
lines.append("")
170171

171-
lines.extend(["", "::", ""])
172-
lines.extend([" %s" % row.rstrip() for row in script.split("\n")])
172+
lines.append(".. raw:: html")
173173
lines.append("")
174-
175174
if "side-by-side" in options:
176-
lines.append(".. raw:: html")
177-
lines.append("")
178-
lines.append(" </div>")
179-
lines.append("")
175+
lines.append(" <div class=\"cq-side-by-side\"><div class=\"leftside\">")
176+
else:
177+
lines.append(" <div class=\"cq-top-bottom\"><div class=\"cq-top\">")
178+
lines.append("")
179+
180+
lines.extend(self.generate_code_block(script))
181+
182+
lines.append(".. raw:: html")
183+
lines.append("")
184+
lines.append(" </div>")
185+
lines.append("")
180186

181187
try:
182188
result = cqgi.parse(plot_code).build()
@@ -210,11 +216,10 @@ def generate_output(self, plot_code, text, script, options):
210216
traceback.print_exc()
211217
assy = Assembly(Compound.makeText("CQGI error", 10, 5))
212218

213-
if "side-by-side" in options:
214-
lines.append("")
215-
lines.append(".. raw:: html")
216-
lines.append("")
217-
lines.append(" </div>")
219+
lines.append("")
220+
lines.append(".. raw:: html")
221+
lines.append("")
222+
lines.append(" </div>")
218223
lines.append("")
219224

220225
return lines
@@ -290,6 +295,74 @@ def render_image(self, assy, options):
290295
).splitlines()
291296

292297

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+
293366

294367
class cadscript_auto_directive(cadscript_directive):
295368

@@ -351,6 +424,12 @@ def setup(app):
351424
app.add_js_file("vtk.js")
352425
app.add_js_file(None, body=rendering_code)
353426

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
354433

355434

356435
if __name__ == "__main__":
@@ -367,5 +446,5 @@ def setup(app):
367446
setup(app)
368447
except Exception:
369448
pass
370-
pre_script, script, text = c.get_source_file(open(c.get_file('./examples/bracket.py')).read(), "2-14", True)
449+
pre_script, script, text = c.get_source_file(open(c.get_file('./examples/bracket.py')).read(), "2", True)
371450
pre_script, script, text = c.get_source_file(open(c.get_file('./examples/getting_started.py')).read(), None, None)

docs/source/_static/cadscript.css

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* cq render right of code */
2+
13
div.leftside {
24
width: 43%;
35
padding: 0px 3px 0px 0px;
@@ -9,11 +11,33 @@ div.rightside {
911
/* float: right; */
1012
}
1113

12-
div.side-by-side {
14+
div.cq-side-by-side {
1315
padding: 1px;
1416
border: 1px solid #ccc;
1517
}
1618

19+
/* cq render after code */
20+
21+
22+
div.cq-top > div.highlight { /* code block */
23+
margin-bottom: 0;
24+
border-left: 5px solid #ccc;
25+
}
26+
27+
div.cq-top-bottom > div.cq { /* render */
28+
padding-top: 5px;
29+
padding-left: 10px;
30+
margin-bottom: 20px;
31+
border-left: 5px solid #ccc;
32+
}
33+
34+
/* better table formatting */
1735
table.docutils td p {
1836
text-wrap: wrap;
37+
}
38+
39+
/* links in code */
40+
a.codelink, a.codelink:visited {
41+
color: #0000FF;
42+
text-decoration: underline #C0C0FF;
1943
}

docs/source/conf.py

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
'ext.cadscript_directives',
2828
]
2929

30+
3031
autodoc_default_options = {
3132
'member-order': 'bysource',
3233
# 'special-members': '__init__',
@@ -48,3 +49,18 @@
4849
# relative to this directory. They are copied after the builtin static files,
4950
# so a file named "default.css" will overwrite the builtin "default.css".
5051
html_static_path = ["_static"]
52+
53+
def skip_member(app, what, name, obj, skip, options):
54+
# List of menbers (methods, classes, ...) to exclude
55+
exclude = ["CqMode"]
56+
57+
# Exclude if the name is in the exclude list
58+
if name in exclude:
59+
return True
60+
# Exclude if starts with an underscore
61+
if name.startswith("_"):
62+
return True
63+
return skip
64+
65+
def setup(app):
66+
app.connect("autodoc-skip-member", skip_member)

docs/source/doc_sketch_examples.rst

-15
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ Rectangles
4141

4242

4343

44-
See also
45-
46-
.. hlist::
47-
48-
* :py:meth:`cadscript.make_box`
49-
* :py:meth:`Sketch.add_rect`
50-
51-
52-
5344
Circles
5445
-------
5546

@@ -74,10 +65,4 @@ Circles
7465
sketch.add_circle(d=40, pos=(20, 10))
7566

7667

77-
See also
78-
79-
.. hlist::
80-
81-
* :py:meth:`cadscript.make_box`
82-
* :py:meth:`Sketch.add_circle`
8368

docs/source/intro_getting_started.rst

-12
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,6 @@ Finally, we place the sketch on the top face of our object, rotate it 45 degrees
5252
.. cadscript::
5353
:source: ../examples/getting_started.py
5454

55-
.. topic:: Api References
56-
57-
.. hlist::
58-
59-
* :py:meth:`cadscript.make_box`
60-
* :py:meth:`Body.fillet`
61-
* :py:meth:`Body.chamfer`
62-
* :py:meth:`cadscript.make_sketch`
63-
* :py:meth:`Sketch.add_circle`
64-
* :py:meth:`Sketch.add_rect`
65-
* :py:meth:`Body.cut_extrude`
66-
6755

6856
Second Example
6957
--------------

examples/bracket.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# This file is part of Cadscript
33
# SPDX-License-Identifier: Apache-2.0
44

5+
# example script also used to generate documentation
6+
57
# this is used by the documentation generation
68
# DOCSTEP: 2,sketch1
79
# DOCSTEP: ...3,sketch1
@@ -21,7 +23,13 @@
2123
import cadscript
2224

2325
# STEP 2
24-
# Let's start with a sketch.
26+
# In this example we build a complex object by
27+
#
28+
# - creating two sketches
29+
# - extruding them
30+
# - intersecting the two extrusions
31+
#
32+
# Let's start with the first sketch.
2533
# We add a rectangle with width 70 and height 30, centered at the origin.
2634
sketch1 = cadscript.make_sketch()
2735
sketch1.add_rect(70, 30)
@@ -53,7 +61,7 @@
5361
sketch1.cut_circle(d=16, pos=(50, 0))
5462

5563
# STEP 8
56-
# Make a new sketch, add a rectangle and a circle.
64+
# now we create the second sketch. We start with a rectangle and a circle.
5765
sketch2 = cadscript.make_sketch()
5866
sketch2.add_rect(70, 5, center=False)
5967
sketch2.add_circle(d=26, pos=(0, 10))
@@ -92,5 +100,4 @@
92100

93101
# STEP 14 dummy step to show whole script in documentation
94102
# The complete script looks like this:
95-
96-
cadscript.show(result)
103+
cadscript.show(result)

0 commit comments

Comments
 (0)