Skip to content

Commit b046c61

Browse files
committed
Merge remote-tracking branch 'origin/markdown-filter' into cli-group
2 parents 8b67a93 + e67207c commit b046c61

9 files changed

Lines changed: 374 additions & 118 deletions

File tree

in2lambda/filters/Markdown/example.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ $v_0 = 15\,\text{m/s}$. Take $g = 9.8\,\text{m/s}^2$.
55

66
## Time of flight
77

8-
How long does the ball take to reach the ground?
8+
How long does the ball take to reach the ground, using:
9+
10+
- the vertical motion equation
11+
- the given height and gravity
912

1013
## Solution
1114

@@ -15,6 +18,8 @@ $$
1518
h = \frac{1}{2} g t^2 \implies t = \sqrt{\frac{2h}{g}}
1619
$$
1720

21+
---
22+
1823
So $t \approx 2.0\,\text{s}$.
1924

2025
## Horizontal range

in2lambda/filters/Markdown/filter.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def pandoc_filter(
6868
doc: pf.elements.Doc,
6969
set: Set,
7070
parsing_answers: bool,
71-
) -> Optional[pf.Str]:
71+
) -> Optional[pf.Inline]:
7272
"""Turn a ``#``/``##`` markdown document into questions, parts and solutions.
7373
7474
Args:
@@ -87,12 +87,27 @@ def pandoc_filter(
8787

8888
state = _state_for(doc)
8989
is_heading = isinstance(elem, pf.Header)
90-
text = pf.stringify(elem).strip()
90+
is_rule = isinstance(elem, pf.HorizontalRule)
91+
92+
if is_heading:
93+
text = pf.stringify(elem).strip()
94+
elif is_rule:
95+
# HorizontalRule blocks (``---``) stringify to nothing, so they're matched
96+
# separately and kept as literal text: in a Lambda Feedback worked solution
97+
# they mark the boundary between the steps a student clicks through.
98+
text = "---"
99+
else:
100+
# Serialized back to markdown (rather than flattened with pf.stringify) so
101+
# that lists, tables and other markup survive into the question/part/
102+
# solution text verbatim.
103+
text = pf.convert_text(
104+
elem, input_format="panflute", output_format="markdown"
105+
).strip()
91106

92107
if parsing_answers:
93108
if is_heading and elem.level == 1:
94109
set.increment_current_question()
95-
elif not is_heading and text:
110+
elif (is_rule or not is_heading) and text:
96111
set.current_question.add_solution(text)
97112
return None
98113

@@ -108,7 +123,7 @@ def pandoc_filter(
108123
state.part = Part()
109124
set.current_question.parts.append(state.part)
110125
state.target = "part"
111-
elif not is_heading and text:
126+
elif (is_rule or not is_heading) and text:
112127
if state.target == "main":
113128
set.current_question.main_text = text
114129
elif state.target == "part" and state.part is not None:

in2lambda/filters/markdown.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ def image_path(image_name: str, tex_file: str) -> Optional[str]:
118118
def filter(
119119
func: Callable[
120120
[pf.Element, pf.elements.Doc, Set, bool],
121-
Optional[pf.Str],
121+
Optional[pf.Inline],
122122
]
123123
) -> Callable[
124124
[pf.Element, pf.elements.Doc, Set, str, bool],
125-
Optional[pf.Str],
125+
Optional[pf.Inline],
126126
]:
127127
"""Python decorator to make generic LaTeX elements markdown readable.
128128
@@ -139,7 +139,7 @@ def markdown_converter(
139139
set: Set,
140140
tex_file: str,
141141
parsing_answers: bool,
142-
) -> Optional[pf.Str]:
142+
) -> Optional[pf.Inline]:
143143
"""Handles LaTeX elements within the filter, before calling the original function.
144144
145145
N.B. tex_file is required to determine where the relative image directory is.
@@ -163,10 +163,13 @@ def markdown_converter(
163163
expression = latex_to_katex(elem.text)
164164
except Exception:
165165
expression = elem.text
166-
return pf.Str(
167-
f"${expression}$"
168-
if elem.format == "InlineMath"
169-
else f"\n\n$$\n{expression}\n$$\n\n"
166+
return pf.RawInline(
167+
(
168+
f"${expression}$"
169+
if elem.format == "InlineMath"
170+
else f"\n\n$$\n{expression}\n$$\n\n"
171+
),
172+
format="markdown",
170173
)
171174

172175
case pf.Image:
@@ -176,13 +179,13 @@ def markdown_converter(
176179
echo(f"Warning: Couldn't find {elem.url}")
177180
else:
178181
set.current_question.images.append(path)
179-
return pf.Str(f"![pictureTag]({elem.url})")
182+
return pf.RawInline(f"![pictureTag]({elem.url})", format="markdown")
180183

181184
case pf.Strong:
182-
return pf.Str(f"**{pf.stringify(elem)}**")
185+
return pf.RawInline(f"**{pf.stringify(elem)}**", format="markdown")
183186

184187
case pf.Emph:
185-
return pf.Str(f"*{pf.stringify(elem)}*")
188+
return pf.RawInline(f"*{pf.stringify(elem)}*", format="markdown")
186189

187190
# Replace siunitx no-break space with narrow no-break space
188191
# This should be the space between the number and the units

in2lambda/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
def _warn_markdown_issues(text: str, source: str) -> None:
2323
"""Echo a warning for each math-delimiter problem found in a markdown source."""
2424
for problem in check_markdown(text):
25-
click.echo(f"Warning: {source}: {problem.value}")
25+
click.echo(f"Warning: {source}: {problem}")
2626

2727

2828
def docx_to_md(docx_file: str) -> str:

in2lambda/validation/__init__.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,35 @@
66
delimiters - before the markdown is converted.
77
"""
88

9-
from in2lambda.validation.delimiters import MathDelimiterError, math_delimiter_checker
9+
from in2lambda.validation.delimiters import (
10+
MathDelimiterError,
11+
MathDelimiterProblem,
12+
math_delimiter_checker,
13+
)
1014

11-
__all__ = ["MathDelimiterError", "math_delimiter_checker", "check_markdown"]
15+
__all__ = [
16+
"MathDelimiterError",
17+
"MathDelimiterProblem",
18+
"math_delimiter_checker",
19+
"check_markdown",
20+
]
1221

1322

14-
def check_markdown(md_content: str) -> list[MathDelimiterError]:
23+
def check_markdown(md_content: str) -> list[MathDelimiterProblem]:
1524
"""Run every markdown check and return the problems found.
1625
1726
Args:
1827
md_content: The markdown text to validate.
1928
2029
Returns:
21-
A list of :class:`MathDelimiterError` members, one per problem found.
30+
A list of :class:`MathDelimiterProblem`, one per problem found.
2231
An empty list means the markdown passed every check.
2332
2433
Examples:
2534
>>> from in2lambda.validation import check_markdown
2635
>>> check_markdown("Inline $x = y$ is fine.")
2736
[]
2837
>>> check_markdown("Unbalanced $x = y")
29-
[<MathDelimiterError.MISSING_CLOSING_SINGLE_DOLLAR: 'unclosed inline $ ... $'>]
38+
[MathDelimiterProblem(line=1, error=<MathDelimiterError.MISSING_CLOSING_SINGLE_DOLLAR: 'unclosed inline $ ... $'>)]
3039
"""
31-
problems: list[MathDelimiterError] = []
32-
33-
result = math_delimiter_checker(md_content)
34-
if result is not MathDelimiterError.PASSED:
35-
problems.append(result)
36-
37-
return problems
40+
return math_delimiter_checker(md_content)

0 commit comments

Comments
 (0)