"""Probe code-fence language handling without network or executing fenced content. Run from the repository root with .venv/bin/python. All writes are beside this script. Upstream results.json and the unrelated syntax matrix are left intact. """ import hashlib import importlib.metadata import inspect import json import platform import subprocess from collections import Counter from datetime import datetime, timezone from html.parser import HTMLParser from pathlib import Path import markdown from markdown.extensions.fenced_code import FencedBlockPreprocessor from markdown_it import MarkdownIt from markdown_it.renderer import RendererHTML ROOT = Path(__file__).resolve().parent JS = 'const name = "Ada";\nconsole.log(name);\nconsole.log(name.length);\n' PY = 'name = "Ada"\nprint(name)\nprint(len(name))\n' SESSION = '$ echo "alpha"\nalpha\n$ pwd\n' CASES = [('javascript-upper' if tag == 'JavaScript' else tag or 'unlabelled', tag, JS) for tag in ('javascript', 'js', 'JavaScript', 'javascrpt', '', 'text', 'plaintext', 'none')] CASES += [(tag, tag, PY) for tag in ('python', 'py')] CASES += [(tag, tag, SESSION) for tag in ('bash', 'sh', 'shell', 'console', 'shellsession')] class Structure(HTMLParser): def __init__(self): super().__init__(convert_charrefs=True) self.tags = Counter() self.classes = [] self.code_depth = 0 self.code_text = [] self.token_spans = 0 def handle_starttag(self, tag, attrs): self.tags[tag] += 1 attrs = dict(attrs) if tag == 'code': self.code_depth += 1 if self.code_depth and tag in ('pre', 'code', 'span') and attrs.get('class'): self.classes.append({'element': tag, 'class': attrs['class']}) # Count nonempty class-bearing spans inside code, not line-ID spans. if self.code_depth and tag == 'span' and attrs.get('class'): self.token_spans += 1 def handle_endtag(self, tag): if tag == 'code': self.code_depth -= 1 def handle_data(self, data): if self.code_depth: self.code_text.append(data) def sha(text): return hashlib.sha256(text.encode()).hexdigest() def main(): names = [name.casefold() for name, _, _ in CASES] if len(set(names)) != len(names): raise ValueError('Sample names collide on case-insensitive filesystems') versions = { 'markdown-it-commonmark': importlib.metadata.version('markdown-it-py'), 'python-markdown-fenced': importlib.metadata.version('Markdown'), 'pandoc-gfm': subprocess.check_output(['pandoc', '--version'], text=True).splitlines()[0], } registry = subprocess.check_output(['pandoc', '--list-highlight-languages'], text=True) (ROOT / 'pandoc-highlight-languages.txt').write_text(registry, encoding='utf-8') for name, obj in [('markdown-it-fence-source.txt', RendererHTML.fence), ('python-markdown-fence-source.txt', FencedBlockPreprocessor.run)]: # Preserve installed code verbatim; identify it without publishing local user paths. (ROOT / name).write_text(inspect.getsource(obj), encoding='utf-8') rows = [] for name, tag, body in CASES: source = f'```{tag}\n{body}```\n' stem = f'language-{name}' (ROOT / f'{stem}.md').write_text(source, encoding='utf-8') rendered = { 'markdown-it-commonmark': MarkdownIt('commonmark', {'highlight': None}).render(source), 'python-markdown-fenced': markdown.markdown(source, extensions=['fenced_code']), } proc = subprocess.run(['pandoc', '-f', 'gfm', '-t', 'html', '--syntax-highlighting=pygments'], input=source, text=True, capture_output=True, check=True) rendered['pandoc-gfm'] = proc.stdout for renderer, html in rendered.items(): output = ROOT / f'{stem}--{renderer}.txt' output.write_text(html, encoding='utf-8') parsed = Structure() parsed.feed(html) actual = ''.join(parsed.code_text) # Python-Markdown has a final newline; Pandoc joins line spans without # a trailing newline. Only that terminal LF is ignored, not spaces. preserved = actual.removesuffix('\n') == body.removesuffix('\n') rows.append({ 'sample': name, 'info_string': tag, 'renderer': renderer, 'source': f'lab/renderers/{stem}.md', 'output': f'lab/renderers/{output.name}', 'tags': dict(parsed.tags), 'code_classes': parsed.classes, 'token_spans': parsed.token_spans, 'content_preserved': preserved, 'source_sha256': sha(source), 'output_sha256': sha(html), 'stderr': proc.stderr if renderer == 'pandoc-gfm' else '', }) report = { 'kind': 'markdown-rendering', 'date': datetime.now(timezone.utc).isoformat(), 'python': platform.python_version(), 'versions': versions, 'command': '.venv/bin/python runs/2026-11-01-markovo-markdown-code-block-language-list/lab/renderers/language_probe.py', 'renderer_options': { 'markdown-it-commonmark': "MarkdownIt('commonmark', {'highlight': None}).render(source)", 'python-markdown-fenced': "markdown.markdown(source, extensions=['fenced_code'])", 'pandoc-gfm': 'pandoc -f gfm -t html --syntax-highlighting=pygments', 'pandoc-registry': 'pandoc --list-highlight-languages', }, 'scope': 'Local implementations only; no GitHub.com, VS Code, browser CSS, Prism or highlight.js execution. Fenced content is never executed.', 'definitions': { 'pre_code': 'HTMLParser start-tag counts, reported separately as pre and code', 'token_spans': 'class-bearing span start tags inside code; line-ID spans and anchors excluded; does not measure color, correctness or complete token coverage', 'content_preserved': 'decoded text inside code equals source body after removing at most one terminal LF from each; no other whitespace normalization', 'code_classes': 'class attributes on elements within code, recorded verbatim', }, 'case_count': len(CASES), 'row_count': len(rows), 'results': rows, } for row in rows: for kind in ('source', 'output'): path = ROOT / Path(row[kind]).name if sha(path.read_text(encoding='utf-8')) != row[f'{kind}_sha256']: raise ValueError(f"Evidence changed or was overwritten: {path.name}") (ROOT / 'language-results.json').write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8') lines = ['# Language probe results', '', report['scope'], '', '| Sample | Renderer | pre | code | Token spans | Content preserved |', '| --- | --- | ---: | ---: | ---: | --- |'] for row in rows: lines.append(f"| {row['sample']} | {row['renderer']} | {row['tags'].get('pre', 0)} | {row['tags'].get('code', 0)} | {row['token_spans']} | {row['content_preserved']} |") (ROOT / 'language-results.md').write_text('\n'.join(lines) + '\n', encoding='utf-8') print(json.dumps({'case_count': len(CASES), 'row_count': len(rows), 'content_preserved_rows': sum(row['content_preserved'] for row in rows), 'versions': versions})) if __name__ == '__main__': main()