Skip to content

Commit de2f989

Browse files
committed
✨ NEW: hide specific directives and hide all solutions
1 parent f37722f commit de2f989

File tree

2 files changed

+53
-86
lines changed

2 files changed

+53
-86
lines changed

sphinx_exercise/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ def process(self, doctree: nodes.document, docname: str) -> None:
340340

341341
def setup(app: Sphinx) -> Dict[str, Any]:
342342

343+
app.add_config_value("hide_solutions", False, "env")
344+
343345
app.add_css_file("exercise.css")
344346
app.connect("build-finished", copy_asset_files)
345347
app.connect("config-inited", init_numfig)

sphinx_exercise/directive.py

Lines changed: 51 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,48 @@
1919
logger = logging.getLogger(__name__)
2020

2121

22-
class ExerciseDirective(SphinxDirective):
23-
""" A custom Sphinx Directive """
22+
class CustomDirective(SphinxDirective):
23+
""" A custom Sphinx directive """
2424

25-
name = "exercise"
26-
has_content = True
27-
required_arguments = 0
28-
optional_arguments = 1
29-
final_argument_whitespace = True
30-
option_spec = {
31-
"label": directives.unchanged_required,
32-
"class": directives.class_option,
33-
"nonumber": directives.flag,
34-
}
25+
name = ""
3526

3627
def run(self) -> List[Node]:
28+
if self.name == "solution" and self.env.app.config.hide_solutions:
29+
return []
30+
3731
serial_no = self.env.new_serialno()
3832

3933
if not hasattr(self.env, "exercise_list"):
4034
self.env.exercise_list = {}
4135

42-
# Take care of class option
4336
classes, class_name = [self.name], self.options.get("class", "")
4437
if class_name:
4538
classes.extend(class_name)
4639

47-
title_text, _ = "", ""
48-
if "nonumber" in self.options:
49-
title_text = f"{self.name.title()} "
40+
title_text, title = "", ""
41+
if self.name == "exercise":
42+
if "nonumber" in self.options:
43+
title_text = f"{self.name.title()} "
5044

51-
if self.arguments != []:
52-
title_text += f"({self.arguments[0]})"
53-
_ += self.arguments[0]
45+
if self.arguments != []:
46+
title_text += f"({self.arguments[0]})"
47+
title += self.arguments[0]
48+
else:
49+
title_text = f"{self.name.title()} to "
50+
target_label = self.arguments[0]
5451

5552
textnodes, messages = self.state.inline_text(title_text, self.lineno)
5653

5754
section = nodes.section(ids=[f"{self.name}-content"])
5855
self.state.nested_parse(self.content, self.content_offset, section)
5956

60-
if "nonumber" in self.options:
61-
node = unenumerable_node()
57+
if self.name == "exercise":
58+
if "nonumber" in self.options:
59+
node = unenumerable_node()
60+
else:
61+
node = enumerable_node()
6262
else:
63-
node = enumerable_node()
63+
node = linked_node()
6464

6565
node += nodes.title(title_text, "", *textnodes)
6666
node += section
@@ -88,21 +88,46 @@ def run(self) -> List[Node]:
8888
node["ids"].append(label)
8989
node["label"] = label
9090
node["docname"] = self.env.docname
91+
node["hidden"] = True if "hidden" in self.options else False
9192
node.document = self.state.document
9293

94+
if self.name == "solution":
95+
node["target_label"] = target_label
96+
9397
self.add_name(node)
9498

9599
self.env.exercise_list[label] = {
96100
"type": self.name,
97101
"docname": self.env.docname,
98102
"node": node,
99-
"title": _,
103+
"title": title,
104+
"hidden": node.get("hidden", bool),
100105
}
106+
107+
if node.get("hidden", bool):
108+
return []
109+
101110
return [node]
102111

103112

104-
class SolutionDirective(SphinxDirective):
105-
""" A custom Sphinx Directive """
113+
class ExerciseDirective(CustomDirective):
114+
""" A custom exercise directive """
115+
116+
name = "exercise"
117+
has_content = True
118+
required_arguments = 0
119+
optional_arguments = 1
120+
final_argument_whitespace = True
121+
option_spec = {
122+
"label": directives.unchanged_required,
123+
"class": directives.class_option,
124+
"nonumber": directives.flag,
125+
"hidden": directives.flag,
126+
}
127+
128+
129+
class SolutionDirective(CustomDirective):
130+
""" A custom solution directive """
106131

107132
name = "solution"
108133
has_content = True
@@ -112,65 +137,5 @@ class SolutionDirective(SphinxDirective):
112137
option_spec = {
113138
"label": directives.unchanged_required,
114139
"class": directives.class_option,
140+
"hidden": directives.flag,
115141
}
116-
title_text = f"{name.title()} to "
117-
118-
def run(self):
119-
serial_no = self.env.new_serialno()
120-
121-
if not hasattr(self.env, "exercise_list"):
122-
self.env.exercise_list = {}
123-
124-
# Take care of class option
125-
classes, class_name = [self.name], self.options.get("class", [])
126-
if class_name:
127-
classes.extend(class_name)
128-
129-
target_label = self.arguments[0]
130-
131-
textnodes, messages = self.state.inline_text(self.title_text, self.lineno)
132-
133-
section = nodes.section(ids=[f"{self.name}-content"])
134-
self.state.nested_parse(self.content, self.content_offset, section)
135-
136-
node = linked_node()
137-
node.document = self.state.document
138-
node += nodes.title(self.title_text, "", *textnodes)
139-
node += section
140-
141-
label = self.options.get("label", "")
142-
if label:
143-
self.options["noindex"] = False
144-
else:
145-
self.options["noindex"] = True
146-
label = f"{self.env.docname}-{self.name}-{serial_no}"
147-
148-
# Duplicate label warning
149-
if not label == "" and label in self.env.exercise_list.keys():
150-
docpath = self.env.doc2path(self.env.docname)
151-
path = docpath[: docpath.rfind(".")]
152-
other_path = self.env.doc2path(self.env.exercise_list[label]["docname"])
153-
msg = f"duplicate label: {label}; other instance in {other_path}"
154-
logger.warning(msg, location=path, color="red")
155-
return []
156-
157-
self.options["name"] = label
158-
159-
# Set node attributes
160-
node["classes"].extend(classes)
161-
node["ids"].append(label)
162-
node["label"] = label
163-
node["docname"] = self.env.docname
164-
node["target_label"] = target_label
165-
node.document = self.state.document
166-
167-
self.add_name(node)
168-
169-
self.env.exercise_list[label] = {
170-
"type": self.name,
171-
"docname": self.env.docname,
172-
"node": node,
173-
"title": "",
174-
}
175-
176-
return [node]

0 commit comments

Comments
 (0)