Commit 8162816
fix(pampa): canonicalize em/en dashes and ellipsis on both read and write (#290)
Smart dashes were broken in two ways (repro: a doc mixing unspaced `---`
and Unicode em dashes):
1. READ — `---`/`--` only converted to em/en dashes as a standalone,
whitespace-delimited token; mid-word runs (`un---spaced`) stayed literal.
2. WRITE — a Unicode em/en dash was emitted verbatim instead of being
canonicalized back to ASCII `---`/`--`, so source kept stray Unicode.
Read side: new `apply_smart_typography` scans each prose-str node for dash
runs (greedy 3=em while ≥3 remain, trailing 2=en, lone hyphen literal —
Pandoc's default `dash` rule), dot runs (`...`→…), and apostrophes. Applied
per node (the four sites that called `apply_smart_quotes`), NOT post-merge:
escaped hyphens arrive as isolated `\-` nodes, so per-node conversion keeps
`a\-\-b` literal. Dropped the whole-token `as_smart_str` branch and the now
dead `apply_smart_quotes`.
Reader escapes: `process_backslash_escapes` now strips a backslash before
em dash / en dash / ellipsis, so the writer's `\—` round-trips.
Write side: `escape_markdown` now canonicalizes Unicode smart chars to their
ASCII spelling UNescaped (reader re-converts) while backslash-escaping
genuinely-literal hyphen runs (≥2) and dot runs (≥3) so they survive the
reader's smart pass. Operating on the original text is what distinguishes a
canonicalized em dash (`—`→bare `---`) from literal hyphens (`--`→`\-\-`) —
fixing a latent round-trip bug the read change exposed (`a\-\-b` → `a–b`).
Thematic-break hazard: a line that would canonicalize to only dashes (≥3)
re-parses as a HorizontalRule. `write_paragraph`/`write_plain` now split into
line segments and, for an all-dash line (`line_is_dash_only_hazard`), escape
each dash (`\—`, `\-\-\-`) so the leading backslash blocks thematic-break
recognition while the AST round-trips.
Tests: 22 unit tests (read + write helpers), a `smart-typography` native AST
snapshot (mid-word convert, escaped-stays-literal, code untouched), and five
`qmd-json-qmd/dashes_*.qmd` idempotency fixtures. Full workspace green
(10065 passed); `cargo xtask verify --skip-hub-build` passes incl. the
WASM rebuild.
Snapshot changes (2 modified, 1 added):
- native/012.snap, native/014.snap: intended mid-token conversion
(`1--30`→`1–30`, `Hello---maybe---world...`→`Hello—maybe—world…`,
`wait---really---did`→`wait—really—did`); regeneration also corrected a
stale `source:` path in both.
- native/smart-typography.snap: new fixture locking the read-side behavior.
Strand: bd-k2h1x7bu
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent d4073f8 commit 8162816
15 files changed
Lines changed: 818 additions & 73 deletions
File tree
- claude-notes/plans
- crates/pampa
- snapshots/native
- src
- pandoc
- treesitter_utils
- writers
- tests
- roundtrip_tests/qmd-json-qmd
- snapshots/native
Lines changed: 381 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
563 | 563 | | |
564 | 564 | | |
565 | 565 | | |
566 | | - | |
| 566 | + | |
567 | 567 | | |
568 | 568 | | |
569 | 569 | | |
| |||
737 | 737 | | |
738 | 738 | | |
739 | 739 | | |
740 | | - | |
| 740 | + | |
741 | 741 | | |
742 | 742 | | |
743 | 743 | | |
| |||
771 | 771 | | |
772 | 772 | | |
773 | 773 | | |
774 | | - | |
| 774 | + | |
775 | 775 | | |
776 | 776 | | |
777 | 777 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
| 62 | + | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1676 | 1676 | | |
1677 | 1677 | | |
1678 | 1678 | | |
1679 | | - | |
1680 | | - | |
1681 | | - | |
1682 | | - | |
1683 | | - | |
1684 | | - | |
1685 | | - | |
1686 | | - | |
1687 | | - | |
1688 | | - | |
1689 | | - | |
1690 | | - | |
1691 | | - | |
1692 | | - | |
| 1679 | + | |
| 1680 | + | |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
| 1684 | + | |
1693 | 1685 | | |
1694 | 1686 | | |
1695 | 1687 | | |
| |||
1702 | 1694 | | |
1703 | 1695 | | |
1704 | 1696 | | |
1705 | | - | |
| 1697 | + | |
1706 | 1698 | | |
1707 | 1699 | | |
1708 | 1700 | | |
| |||
Lines changed: 162 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
149 | 210 | | |
150 | 211 | | |
151 | 212 | | |
| |||
156 | 217 | | |
157 | 218 | | |
158 | 219 | | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
159 | 227 | | |
160 | 228 | | |
161 | 229 | | |
| |||
166 | 234 | | |
167 | 235 | | |
168 | 236 | | |
169 | | - | |
170 | | - | |
171 | | - | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
172 | 240 | | |
173 | 241 | | |
174 | 242 | | |
| |||
230 | 298 | | |
231 | 299 | | |
232 | 300 | | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
233 | 307 | | |
234 | 308 | | |
235 | 309 | | |
| |||
503 | 577 | | |
504 | 578 | | |
505 | 579 | | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
0 commit comments