Fortsetzungszeilen werden wieder mit genau einem Leerzeichen zur ursprünglichen Codezeile zusammengefügt. Im Python-Beispielbuch sind das 195 Stellen; danach lassen sich 247 statt 203 von 255 Python-Blöcken parsen. Abschaltbar mit --keep-wrap-markers; der Bericht enthält wrap_lines_joined pro Block und einen Hinweis pro Kapitel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
5 KiB
Python
120 lines
5 KiB
Python
import re
|
|
|
|
from conftest import Book, Doc
|
|
|
|
PY = """def read_book(path):
|
|
if path:
|
|
return path.read_text(encoding="utf-8") # <utf-8>
|
|
\treturn None"""
|
|
|
|
|
|
def fenced_blocks(text: str) -> list[tuple[str, str]]:
|
|
return re.findall(r"^(`{3,})([\w+-]*)\n(.*?)\n\1$", text, re.M | re.S)
|
|
|
|
|
|
def test_python_indentation_and_special_chars(convert):
|
|
body = f'<h1>Code</h1><pre><code class="language-python">{PY}</code></pre>'
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
blocks = fenced_blocks(run.chapter("001"))
|
|
assert len(blocks) == 1
|
|
fence, lang, code = blocks[0]
|
|
assert lang == "python"
|
|
assert code == PY.replace("<", "<").replace(">", ">")
|
|
assert "<" not in code
|
|
|
|
|
|
def test_shell_code_with_pipes_backslashes_and_dollar(convert):
|
|
shell = "$ cat datei.txt | grep -v '^#' \\\n | sort && echo \"$HOME\" > out.txt"
|
|
body = f"<h1>Shell</h1><pre>{shell}</pre>"
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(_, lang, code), = fenced_blocks(run.chapter("001"))
|
|
assert code == "$ cat datei.txt | grep -v '^#' \\\n | sort && echo \"$HOME\" > out.txt"
|
|
assert lang == "" # no reliable language signal -> no label
|
|
|
|
|
|
def test_backticks_in_code_get_longer_fence(convert):
|
|
body = "<h1>T</h1><pre>echo ```nested``` and `x`</pre>"
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(fence, _, code), = fenced_blocks(run.chapter("001"))
|
|
assert fence == "````"
|
|
assert code == "echo ```nested``` and `x`"
|
|
|
|
|
|
def test_br_lines_and_highlight_spans_in_pre(convert):
|
|
body = ('<h1>C</h1><div class="listing"><pre><span class="kw">#include</span> <stdio.h><br/><br/>'
|
|
'int main(void) {<br/> printf("Hallo\\n");<br/>} </pre></div>')
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(_, lang, code), = fenced_blocks(run.chapter("001"))
|
|
assert lang == "c"
|
|
assert code == '#include <stdio.h>\n\nint main(void) {\n printf("Hallo\\n");\n} '
|
|
|
|
|
|
def test_listing_div_with_span_pre(convert):
|
|
body = ('<h1>K</h1><div class="divUnProgramlisting"><span class="pre"><strong>def</strong> f(x):\n'
|
|
' <strong>return</strong> 2 * x\n\n<strong>def</strong> g(x):\n <strong>return</strong> x</span></div>')
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(_, lang, code), = fenced_blocks(run.chapter("001"))
|
|
assert lang == "python"
|
|
assert code == "def f(x):\n return 2 * x\n\ndef g(x):\n return x"
|
|
|
|
|
|
def test_line_numbers_are_removed(convert):
|
|
body = '<h1>L</h1><pre><span class="lineno">1</span>a = 1<br/><span class="lineno">2</span>b = 2</pre>'
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(_, _, code), = fenced_blocks(run.chapter("001"))
|
|
assert code == "a = 1\nb = 2"
|
|
|
|
|
|
def test_inline_code_variants(convert):
|
|
body = ('<h1>I</h1><p>Nutze <code>a*b_c</code>, <span class="tt">x | y</span> und <code>`tick`</code> '
|
|
'sowie <kbd>Strg</kbd>.</p>')
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
text = run.chapter("001")
|
|
assert "`a*b_c`" in text
|
|
assert "`x | y`" in text
|
|
assert "`` `tick` ``" in text
|
|
assert "<kbd>Strg</kbd>" in text
|
|
|
|
|
|
def test_prose_is_not_forced_into_code(convert):
|
|
body = "<h1>P</h1><p>Ein normaler Absatz mit Leerzeichen.</p>"
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
assert "```" not in run.chapter("001")
|
|
assert "Ein normaler Absatz mit Leerzeichen." in run.chapter("001")
|
|
|
|
|
|
def test_unlabeled_code_is_reported(convert):
|
|
run = convert(Book(docs=[Doc("c.xhtml", "<h1>U</h1><pre>foo bar</pre>")]))
|
|
assert run.report["code_blocks"]["languages"] == {"(keine)": 1}
|
|
|
|
|
|
def test_code_language_option_fills_only_unlabeled_blocks(convert):
|
|
body = "<h1>C</h1><pre>x = compute()</pre><pre>#include <stdio.h>\nint main(void) { return 0; }</pre>"
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]), "--code-language", "py")
|
|
langs = [lang for _, lang, _ in fenced_blocks(run.chapter("001"))]
|
|
assert langs == ["python", "c"]
|
|
by = {item["language"]: item["detected_by"] for item in run.report["code_blocks"]["items"]}
|
|
assert by == {"python": "option", "c": "content"}
|
|
|
|
|
|
def test_code_language_option_rejects_unknown_values(convert):
|
|
run = convert(Book(docs=[Doc("c.xhtml", "<h1>C</h1><pre>x</pre>")]), "--code-language", "quatsch")
|
|
assert run.rc == 1
|
|
assert "quatsch" in run.stderr
|
|
|
|
|
|
def test_print_wrap_markers_are_joined(convert):
|
|
body = ("<h1>W</h1><pre> return (s * (s -\n ↪ distance(a, b)) *\n s)\n"
|
|
"x = ['a', 'b',\n↪ 'c']</pre>")
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]))
|
|
(_, _, code), = fenced_blocks(run.chapter("001"))
|
|
assert code == " return (s * (s - distance(a, b)) *\n s)\nx = ['a', 'b', 'c']"
|
|
item = run.report["code_blocks"]["items"][0]
|
|
assert item["wrap_lines_joined"] == 2
|
|
|
|
|
|
def test_print_wrap_markers_can_be_kept(convert):
|
|
body = "<h1>W</h1><pre>a = (1,\n↪ 2)</pre>"
|
|
run = convert(Book(docs=[Doc("c.xhtml", body)]), "--keep-wrap-markers")
|
|
(_, _, code), = fenced_blocks(run.chapter("001"))
|
|
assert code == "a = (1,\n↪ 2)"
|