polytypo 1.6.3 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -0
  3. data/lib/polytypo/data/.not-vendored +11 -0
  4. data/lib/polytypo/data/README.md +16 -11
  5. data/lib/polytypo/data/VERSION +1 -1
  6. data/lib/polytypo/data/fixtures/cs.json +1 -1
  7. data/lib/polytypo/data/fixtures/de-CH.json +1 -1
  8. data/lib/polytypo/data/fixtures/de-DE.json +14 -1
  9. data/lib/polytypo/data/fixtures/el.json +1 -1
  10. data/lib/polytypo/data/fixtures/en-GB.json +1 -1
  11. data/lib/polytypo/data/fixtures/en-US.json +521 -1
  12. data/lib/polytypo/data/fixtures/es.json +1 -1
  13. data/lib/polytypo/data/fixtures/fi.json +1 -1
  14. data/lib/polytypo/data/fixtures/fr-CA.json +1 -1
  15. data/lib/polytypo/data/fixtures/fr.json +68 -1
  16. data/lib/polytypo/data/fixtures/it.json +1 -1
  17. data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
  18. data/lib/polytypo/data/fixtures/nl.json +1 -1
  19. data/lib/polytypo/data/fixtures/pl.json +1 -1
  20. data/lib/polytypo/data/fixtures/pt-BR.json +1 -1
  21. data/lib/polytypo/data/fixtures/pt-PT.json +1 -1
  22. data/lib/polytypo/data/fixtures/ru.json +1 -1
  23. data/lib/polytypo/data/fixtures/sv.json +1 -1
  24. data/lib/polytypo/data/fixtures/tr.json +1 -1
  25. data/lib/polytypo/data/fixtures/uk.json +1 -1
  26. data/lib/polytypo/data/locales/cs.json +56 -37
  27. data/lib/polytypo/data/locales/de-CH.json +43 -34
  28. data/lib/polytypo/data/locales/de-DE.json +47 -32
  29. data/lib/polytypo/data/locales/el.json +51 -1
  30. data/lib/polytypo/data/locales/en-GB.json +56 -4
  31. data/lib/polytypo/data/locales/en-US.json +68 -4
  32. data/lib/polytypo/data/locales/es.json +66 -29
  33. data/lib/polytypo/data/locales/fi.json +77 -7
  34. data/lib/polytypo/data/locales/fr-CA.json +63 -36
  35. data/lib/polytypo/data/locales/fr.json +70 -41
  36. data/lib/polytypo/data/locales/it.json +65 -22
  37. data/lib/polytypo/data/locales/nl.json +61 -29
  38. data/lib/polytypo/data/locales/pl.json +61 -28
  39. data/lib/polytypo/data/locales/pt-BR.json +53 -28
  40. data/lib/polytypo/data/locales/pt-PT.json +55 -28
  41. data/lib/polytypo/data/locales/registry.json +1 -1
  42. data/lib/polytypo/data/locales/ru.json +45 -30
  43. data/lib/polytypo/data/locales/sv.json +63 -4
  44. data/lib/polytypo/data/locales/tr.json +72 -32
  45. data/lib/polytypo/data/locales/uk.json +55 -27
  46. data/lib/polytypo/data/rules/modes.md +430 -11
  47. data/lib/polytypo/data/rules/order.json +1 -1
  48. data/lib/polytypo/data/schema/fixtures.schema.json +12 -1
  49. data/lib/polytypo/engine/pipeline.rb +29 -0
  50. data/lib/polytypo/modes/markdown.rb +256 -31
  51. data/lib/polytypo/modes/runner.rb +33 -3
  52. data/lib/polytypo/version.rb +1 -1
  53. data/lib/polytypo.rb +27 -12
  54. metadata +2 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.3",
2
+ "spec": "1.8.0",
3
3
  "locale": "en-US",
4
4
  "cases": [
5
5
  {
@@ -1903,6 +1903,148 @@
1903
1903
  "out": "---\ntitle: “Unclosed”\n\nBody “quotes” here.\n",
1904
1904
  "note": "modes.md §3.7.3 skips a frontmatter block; an opening delimiter with no closing one is not a block. The leading --- is then an ordinary thematic break and everything after it is prose, so the metadata-looking line converts like any other paragraph. Pinned so the skip cannot be widened into \"anything after a leading ---\"."
1905
1905
  },
1906
+ {
1907
+ "id": "en-us-markdown-commonmark-frontmatter-keys",
1908
+ "rule": "quotes",
1909
+ "mode": "markdown",
1910
+ "dialect": "commonmark",
1911
+ "frontmatterKeys": [
1912
+ "title"
1913
+ ],
1914
+ "in": "---\ntitle: \"He said 'hi' twice\"\nslug: \"he-said-hi\"\n---\n\nBody has \"quotes\" here.\n",
1915
+ "out": "---\ntitle: \"He said “hi” twice\"\nslug: \"he-said-hi\"\n---\n\nBody has “quotes” here.\n",
1916
+ "note": "modes.md §3.7.4: the option names title, so that scalar is processable and everything else in the block — the unlisted slug, both delimiters, every key — is not. Compare en-us-markdown-commonmark-frontmatter, the same shape with the option absent."
1917
+ },
1918
+ {
1919
+ "id": "en-us-markdown-commonmark-frontmatter-keys-empty",
1920
+ "rule": "quotes",
1921
+ "mode": "markdown",
1922
+ "dialect": "commonmark",
1923
+ "frontmatterKeys": [],
1924
+ "in": "---\ntitle: \"He said 'hi' twice\"\n---\n\nBody has \"quotes\" here.\n",
1925
+ "out": "---\ntitle: \"He said 'hi' twice\"\n---\n\nBody has “quotes” here.\n",
1926
+ "note": "modes.md §3.7.4: an empty list is legal and yields no spans, as it does for keys in yaml mode. Process nothing is a choice, not an error, and the output is identical to the option being absent."
1927
+ },
1928
+ {
1929
+ "id": "en-us-markdown-commonmark-frontmatter-keys-unlisted-only",
1930
+ "rule": "dashes",
1931
+ "mode": "markdown",
1932
+ "dialect": "commonmark",
1933
+ "frontmatterKeys": [
1934
+ "title"
1935
+ ],
1936
+ "in": "---\ndescription: \"Fast - reliable\"\n---\n\nBody - here.\n",
1937
+ "out": "---\ndescription: \"Fast - reliable\"\n---\n\nBody—here.\n",
1938
+ "note": "modes.md §3.7.4: a block containing none of the listed keys is left exactly as the skip left it. The same dash converts in the body, so the case pins that the block was not processed rather than that the rule declined."
1939
+ },
1940
+ {
1941
+ "id": "en-us-markdown-commonmark-frontmatter-keys-toml",
1942
+ "rule": "quotes",
1943
+ "mode": "markdown",
1944
+ "dialect": "commonmark",
1945
+ "frontmatterKeys": [
1946
+ "title"
1947
+ ],
1948
+ "in": "+++\ntitle = \"He said 'hi'\"\n+++\n\nBody has \"quotes\" here.\n",
1949
+ "out": "+++\ntitle = \"He said 'hi'\"\n+++\n\nBody has “quotes” here.\n",
1950
+ "note": "modes.md §3.7.4: a TOML block yields no spans with the option given. TOML quoting is a second grammar the scan does not claim (§7.13), so +++ keeps §3.7.3's skip-whole treatment whatever the caller lists."
1951
+ },
1952
+ {
1953
+ "id": "en-us-markdown-commonmark-frontmatter-keys-unterminated",
1954
+ "rule": "quotes",
1955
+ "mode": "markdown",
1956
+ "dialect": "commonmark",
1957
+ "frontmatterKeys": [
1958
+ "title"
1959
+ ],
1960
+ "in": "---\ntitle: \"Unclosed\"\n\nBody \"quotes\" here.\n",
1961
+ "out": "---\ntitle: “Unclosed”\n\nBody “quotes” here.\n",
1962
+ "note": "modes.md §3.7.4: an opening delimiter with no closing one is not a block, so the option has nothing to name and adds no spans. The line converts anyway — as ordinary prose, per en-us-markdown-commonmark-frontmatter-unterminated — which is why the option must not be credited with it."
1963
+ },
1964
+ {
1965
+ "id": "en-us-markdown-commonmark-frontmatter-keys-escape-bail",
1966
+ "rule": "quotes",
1967
+ "mode": "markdown",
1968
+ "dialect": "commonmark",
1969
+ "frontmatterKeys": [
1970
+ "title"
1971
+ ],
1972
+ "in": "---\ntitle: \"She said \\\"hi\\\" to me\"\n---\n\nBody has \"quotes\" here.\n",
1973
+ "out": "---\ntitle: \"She said \\\"hi\\\" to me\"\n---\n\nBody has “quotes” here.\n",
1974
+ "note": "modes.md §3.7.4 inherits every bail of §3.8.6: a double-quoted scalar whose content contains U+005C yields no spans, because the source spells the content with more characters than it has. The listed key is not enough on its own."
1975
+ },
1976
+ {
1977
+ "id": "en-us-markdown-commonmark-frontmatter-keys-single-quote-bail",
1978
+ "rule": "apostrophe",
1979
+ "mode": "markdown",
1980
+ "dialect": "commonmark",
1981
+ "frontmatterKeys": [
1982
+ "title"
1983
+ ],
1984
+ "in": "---\ntitle: 'It''s a test'\n---\n\nBody it's here.\n",
1985
+ "out": "---\ntitle: 'It''s a test'\n---\n\nBody it’s here.\n",
1986
+ "note": "modes.md §3.7.4, and the cost §7.13 measures: a single-quoted scalar containing two consecutive U+0027 yields no spans, and an apostrophe in a single-quoted scalar is written exactly that way. Half the prose values of the §3.7.4.1 corpus bail here when re-emitted in this style."
1987
+ },
1988
+ {
1989
+ "id": "en-us-markdown-commonmark-frontmatter-keys-separate-unit",
1990
+ "rule": "quotes",
1991
+ "mode": "markdown",
1992
+ "dialect": "commonmark",
1993
+ "frontmatterKeys": [
1994
+ "title"
1995
+ ],
1996
+ "in": "---\ntitle: He said \"hello\n---\n\nworld\" she said\n",
1997
+ "out": "---\ntitle: He said \"hello\n---\n\nworld\" she said\n",
1998
+ "note": "modes.md §3.7.4: the block is its own text unit, so the unbalanced mark in title cannot pair with the one in the body. This case discriminates: run as one unit the pair converts — measured, text mode over the same characters gives “hello … world” — and under two units both marks stay straight. A runtime that concatenates the block with the body passes every other frontmatter case and fails this one."
1999
+ },
2000
+ {
2001
+ "id": "en-us-markdown-commonmark-frontmatter-keys-nested",
2002
+ "rule": "quotes",
2003
+ "mode": "markdown",
2004
+ "dialect": "commonmark",
2005
+ "frontmatterKeys": [
2006
+ "title"
2007
+ ],
2008
+ "in": "---\nseo:\n title: He said \"hi\"\nslug: \"x\"\n---\n\nBody \"quotes\" here.\n",
2009
+ "out": "---\nseo:\n title: He said “hi”\nslug: \"x\"\n---\n\nBody “quotes” here.\n",
2010
+ "note": "modes.md §3.7.4 with §3.8.2's matching: a listed key is processable at any depth and by bare name only, so a nested title converts while the sibling slug does not. §7.12's accepted cost is the same one — a caller cannot say seo.title and not title."
2011
+ },
2012
+ {
2013
+ "id": "en-us-markdown-commonmark-frontmatter-keys-block-scalar",
2014
+ "rule": "dashes",
2015
+ "mode": "markdown",
2016
+ "dialect": "commonmark",
2017
+ "frontmatterKeys": [
2018
+ "summary"
2019
+ ],
2020
+ "in": "---\nsummary: |\n One line - here.\n Another - line.\n---\n\nBody - here.\n",
2021
+ "out": "---\nsummary: |\n One line—here.\n Another—line.\n---\n\nBody—here.\n",
2022
+ "note": "modes.md §3.7.4 reuses §3.8.5: each non-blank content line of a listed block scalar is one span, the header and the indentation stay outside every span, and the gap between two content lines is a −2 marker."
2023
+ },
2024
+ {
2025
+ "id": "en-us-markdown-commonmark-frontmatter-keys-crlf",
2026
+ "rule": "quotes",
2027
+ "mode": "markdown",
2028
+ "dialect": "commonmark",
2029
+ "frontmatterKeys": [
2030
+ "title"
2031
+ ],
2032
+ "in": "---\r\ntitle: \"He said 'hi'\"\r\n---\r\n\r\nBody has \"quotes\" here.\r\n",
2033
+ "out": "---\r\ntitle: \"He said “hi”\"\r\n---\r\n\r\nBody has “quotes” here.\r\n",
2034
+ "note": "modes.md §3.7.4: a U+000D before the terminator belongs to the terminator, so a CRLF document gives the same block content as the same bytes with LF and the delimiters stay outside every span."
2035
+ },
2036
+ {
2037
+ "id": "en-us-markdown-mdx-frontmatter-keys",
2038
+ "rule": "quotes",
2039
+ "mode": "markdown",
2040
+ "dialect": "mdx",
2041
+ "frontmatterKeys": [
2042
+ "title"
2043
+ ],
2044
+ "in": "---\ntitle: \"He said 'hi'\"\n---\n\n<Callout>Body \"quotes\" here</Callout>\n",
2045
+ "out": "---\ntitle: \"He said “hi”\"\n---\n\n<Callout>Body “quotes” here</Callout>\n",
2046
+ "note": "modes.md §3.7.4 applies to both dialects: YAML frontmatter is the same construct in mdx as in commonmark, and §3.7.3 lists it once for both."
2047
+ },
1906
2048
  {
1907
2049
  "id": "en-us-ranges-closed-up-currency",
1908
2050
  "rule": "ranges",
@@ -2570,6 +2712,384 @@
2570
2712
  "in": "(c)'. and (c)'s",
2571
2713
  "out": "©'. and ©’s",
2572
2714
  "note": "symbols.md §5's I₆ discharge, deletion half (spec 1.5.0): §3.2 step 6 deletes the U+0029 that case 2a reads. Both directions in one case. The first mark does not convert — its right neighbour is U+002E, not ALNUM — and after the replacement its left neighbour is the emitted sign, which is in no class, so it does not convert on a second pass either. The second mark converts here, one rule before symbols shortens the span, and is U+2019 by the time symbols runs, which its own S2 guard accepts. Through spec 1.4.0 the second mark stayed straight."
2715
+ },
2716
+ {
2717
+ "id": "en-us-yaml-plain-apostrophe",
2718
+ "rule": "apostrophe",
2719
+ "mode": "yaml",
2720
+ "keys": [
2721
+ "description"
2722
+ ],
2723
+ "in": "description: it's one of theirs\n",
2724
+ "out": "description: it’s one of theirs\n",
2725
+ "note": "modes.md §3.8.6 and §7.11: an apostrophe is ordinary content in a plain scalar — nothing about it is syntactic, so the scan claims the value and `apostrophe` curls it. The style matrix's first cell: the single-quoted bail is about how the source spells the character, not about the character."
2726
+ },
2727
+ {
2728
+ "id": "en-us-yaml-double-quoted-apostrophe",
2729
+ "rule": "apostrophe",
2730
+ "mode": "yaml",
2731
+ "keys": [
2732
+ "description"
2733
+ ],
2734
+ "in": "description: \"it's one of theirs\"\n",
2735
+ "out": "description: \"it’s one of theirs\"\n",
2736
+ "note": "modes.md §3.8.6: inside a double-quoted scalar an apostrophe needs no escape, so the content and the source are the same characters and the scan claims it. Compare en-us-yaml-single-quoted-escape-untouched, the same sentence in the one style that has to double the mark."
2737
+ },
2738
+ {
2739
+ "id": "en-us-yaml-block-scalar-apostrophe",
2740
+ "rule": "apostrophe",
2741
+ "mode": "yaml",
2742
+ "keys": [
2743
+ "description"
2744
+ ],
2745
+ "in": "description: |-\n it's one of theirs\n",
2746
+ "out": "description: |-\n it’s one of theirs\n",
2747
+ "note": "modes.md §3.8.5: a block scalar has no quoting and therefore no escaping, so an apostrophe in it is content like any other. Together with the plain and double-quoted cases this pins that only one of the four styles declines, which is what makes §7.13's measured cost a property of that style rather than of the mark."
2748
+ },
2749
+ {
2750
+ "id": "en-us-markdown-commonmark-frontmatter-keys-plain-apostrophe",
2751
+ "rule": "apostrophe",
2752
+ "mode": "markdown",
2753
+ "dialect": "commonmark",
2754
+ "frontmatterKeys": [
2755
+ "title"
2756
+ ],
2757
+ "in": "---\ntitle: it's one of theirs\n---\n\nBody it's here.\n",
2758
+ "out": "---\ntitle: it’s one of theirs\n---\n\nBody it’s here.\n",
2759
+ "note": "modes.md §3.7.4 inherits §3.8.6 whole, so the style matrix holds inside a frontmatter block too: a plain scalar's apostrophe converts, while the single-quoted form does not (en-us-markdown-commonmark-frontmatter-keys-single-quote-bail)."
2760
+ },
2761
+ {
2762
+ "id": "en-us-markdown-commonmark-frontmatter-opener-trailing-space",
2763
+ "rule": "quotes",
2764
+ "mode": "markdown",
2765
+ "dialect": "commonmark",
2766
+ "in": "--- \ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2767
+ "out": "--- \ntitle: \"Une note\"\n---\n\nBody has “quotes” here.\n",
2768
+ "note": "modes.md §3.7.3a step 2: the rest of the opening delimiter line may be U+0020 and U+0009 and nothing else, so a fence with trailing whitespace still opens a block. Measured before it was specified: two runtimes typeset the metadata here and two skipped it, and the wider reading wins because a miss is invisible where typeset metadata is damage."
2769
+ },
2770
+ {
2771
+ "id": "en-us-markdown-commonmark-frontmatter-closer-trailing-space",
2772
+ "rule": "quotes",
2773
+ "mode": "markdown",
2774
+ "dialect": "commonmark",
2775
+ "in": "---\ntitle: \"Une note\"\n--- \n\nBody has \"quotes\" here.\n",
2776
+ "out": "---\ntitle: \"Une note\"\n--- \n\nBody has “quotes” here.\n",
2777
+ "note": "modes.md §3.7.3a step 3: the same rule for the closing delimiter line. Whitespace an editor left behind does not turn a metadata block into prose."
2778
+ },
2779
+ {
2780
+ "id": "en-us-markdown-commonmark-frontmatter-opener-trailing-tab",
2781
+ "rule": "quotes",
2782
+ "mode": "markdown",
2783
+ "dialect": "commonmark",
2784
+ "in": "---\t\ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2785
+ "out": "---\t\ntitle: \"Une note\"\n---\n\nBody has “quotes” here.\n",
2786
+ "note": "modes.md §3.7.3a step 2 names U+0009 as well as U+0020, because a tab is what a fence picks up from an editor that indents with tabs, and leaving it out would have made the rule depend on which whitespace character the author cannot see."
2787
+ },
2788
+ {
2789
+ "id": "en-us-markdown-commonmark-frontmatter-dots-closer",
2790
+ "rule": "quotes",
2791
+ "mode": "markdown",
2792
+ "dialect": "commonmark",
2793
+ "in": "---\ntitle: \"Une note\"\n...\n\nBody has \"quotes\" here.\n",
2794
+ "out": "---\ntitle: “Une note”\n…\n\nBody has “quotes” here.\n",
2795
+ "note": "modes.md §3.7.3a step 3: `...` closes a YAML document but does not close a frontmatter block, so there is no block and the metadata line is prose. All four runtimes already agree; the fixture is what keeps them agreeing."
2796
+ },
2797
+ {
2798
+ "id": "en-us-markdown-commonmark-frontmatter-after-blank-line",
2799
+ "rule": "quotes",
2800
+ "mode": "markdown",
2801
+ "dialect": "commonmark",
2802
+ "in": "\n---\ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2803
+ "out": "\n---\ntitle: “Une note”\n---\n\nBody has “quotes” here.\n",
2804
+ "note": "modes.md §3.7.3a step 1: the document must begin with the delimiter. One blank line before it and the construct is a thematic break, which is what all four runtimes already do."
2805
+ },
2806
+ {
2807
+ "id": "en-us-markdown-commonmark-frontmatter-opener-with-text",
2808
+ "rule": "quotes",
2809
+ "mode": "markdown",
2810
+ "dialect": "commonmark",
2811
+ "in": "--- yaml\ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2812
+ "out": "--- yaml\ntitle: “Une note”\n---\n\nBody has “quotes” here.\n",
2813
+ "note": "modes.md §3.7.3a step 2: anything on the opening line that is not U+0020 or U+0009 means there is no block. The wider reading widens the whitespace, not the syntax."
2814
+ },
2815
+ {
2816
+ "id": "en-us-markdown-commonmark-frontmatter-closer-indented",
2817
+ "rule": "quotes",
2818
+ "mode": "markdown",
2819
+ "dialect": "commonmark",
2820
+ "in": "---\ntitle: \"Une note\"\n ---\n\nBody has \"quotes\" here.\n",
2821
+ "out": "---\ntitle: “Une note”\n ---\n\nBody has “quotes” here.\n",
2822
+ "note": "modes.md §3.7.3a step 3: a closer is a line, not something searched for inside one, so one space of indentation disqualifies it and the document has no block. Without that clause the step reads as \"a line containing the delimiter\" and two implementations of it disagree."
2823
+ },
2824
+ {
2825
+ "id": "en-us-markdown-commonmark-frontmatter-opener-indented",
2826
+ "rule": "quotes",
2827
+ "mode": "markdown",
2828
+ "dialect": "commonmark",
2829
+ "in": " ---\ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2830
+ "out": " ---\ntitle: “Une note”\n---\n\nBody has “quotes” here.\n",
2831
+ "note": "modes.md §3.7.3a step 1: the document must begin with the delimiter, so one space before it is enough to make the construct a thematic break instead."
2832
+ },
2833
+ {
2834
+ "id": "en-us-markdown-commonmark-frontmatter-four-dashes",
2835
+ "rule": "quotes",
2836
+ "mode": "markdown",
2837
+ "dialect": "commonmark",
2838
+ "in": "----\ntitle: \"Une note\"\n----\n\nBody has \"quotes\" here.\n",
2839
+ "out": "----\ntitle: “Une note”\n----\n\nBody has “quotes” here.\n",
2840
+ "note": "modes.md §3.7.3a step 2: the rest of the delimiter line may be U+0020 and U+0009, and a fourth dash is neither. The widened rule widens the whitespace, not the delimiter."
2841
+ },
2842
+ {
2843
+ "id": "en-us-markdown-commonmark-frontmatter-mixed-delimiters",
2844
+ "rule": "quotes",
2845
+ "mode": "markdown",
2846
+ "dialect": "commonmark",
2847
+ "in": "---\ntitle: \"Une note\"\n+++\n\nBody has \"quotes\" here.\n",
2848
+ "out": "---\ntitle: “Une note”\n+++\n\nBody has “quotes” here.\n",
2849
+ "note": "modes.md §3.7.3a step 3: the closer is the same delimiter as the opener, so a `+++` line does not close a `---` block and the document has none."
2850
+ },
2851
+ {
2852
+ "id": "en-us-markdown-commonmark-frontmatter-empty-block",
2853
+ "rule": "quotes",
2854
+ "mode": "markdown",
2855
+ "dialect": "commonmark",
2856
+ "in": "---\n---\n\nBody has \"quotes\" here.\n",
2857
+ "out": "---\n---\n\nBody has “quotes” here.\n",
2858
+ "note": "modes.md §3.7.3a: an opener immediately followed by a closer is a block with no content — the second line is the first later line that is the delimiter — so there is nothing to skip and nothing for `frontmatterKeys` to name, and the body converts either way."
2859
+ },
2860
+ {
2861
+ "id": "en-us-markdown-commonmark-frontmatter-byte-order-mark",
2862
+ "rule": "quotes",
2863
+ "mode": "markdown",
2864
+ "dialect": "commonmark",
2865
+ "in": "---\ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2866
+ "out": "---\ntitle: \"Une note\"\n---\n\nBody has “quotes” here.\n",
2867
+ "note": "modes.md §3.7.3a step 1: a single leading U+FEFF is stepped over before the scan begins, so a file some editors write with a byte-order mark still has its metadata skipped. Measured on 1.7.0, two runtimes read the mark as content and typeset the block — the same split, on the same damaging side, as the trailing space."
2868
+ },
2869
+ {
2870
+ "id": "en-us-markdown-commonmark-frontmatter-byte-order-mark-trailing-space",
2871
+ "rule": "quotes",
2872
+ "mode": "markdown",
2873
+ "dialect": "commonmark",
2874
+ "in": "--- \ntitle: \"Une note\"\n---\n\nBody has \"quotes\" here.\n",
2875
+ "out": "--- \ntitle: \"Une note\"\n---\n\nBody has “quotes” here.\n",
2876
+ "note": "modes.md §3.7.3a steps 1 and 2 composed: the two clauses that were each a two-against-two divergence, in one document, which is the file a Windows editor produces without being asked."
2877
+ },
2878
+ {
2879
+ "id": "en-us-markdown-commonmark-frontmatter-masks-the-parser",
2880
+ "rule": "quotes",
2881
+ "mode": "markdown",
2882
+ "dialect": "commonmark",
2883
+ "in": "---\nx: |\n ```\n---\n\n```\ncode \"q\" here\n```\n\nBody \"q\" here.\n",
2884
+ "out": "---\nx: |\n ```\n---\n\n```\ncode \"q\" here\n```\n\nBody “q” here.\n",
2885
+ "note": "modes.md §3.7.3a: the parser is handed the block masked out, so a fence inside a metadata value cannot pair with the body's own fence. Without masking the two code blocks swap roles — the body's code is typeset and its prose is skipped — which no span-level check can see, because the damage is in what the parser concluded."
2886
+ },
2887
+ {
2888
+ "id": "en-us-markdown-commonmark-frontmatter-masks-the-parser-trailing-space",
2889
+ "rule": "quotes",
2890
+ "mode": "markdown",
2891
+ "dialect": "commonmark",
2892
+ "in": "--- \nx: |\n ```\n---\n\n```\ncode \"q\" here\n```\n\nBody \"q\" here.\n",
2893
+ "out": "--- \nx: |\n ```\n---\n\n```\ncode \"q\" here\n```\n\nBody “q” here.\n",
2894
+ "note": "modes.md §3.7.3a: the same document with a trailing space on the fence, which is where the widened locator and the masking rule compose — a runtime that gets either one wrong typesets the code block."
2895
+ },
2896
+ {
2897
+ "id": "en-us-markdown-commonmark-frontmatter-crlf-trailing-space",
2898
+ "rule": "quotes",
2899
+ "mode": "markdown",
2900
+ "dialect": "commonmark",
2901
+ "in": "--- \r\ntitle: \"Une note\"\r\n---\r\n\r\nBody has \"quotes\" here.\r\n",
2902
+ "out": "--- \r\ntitle: \"Une note\"\r\n---\r\n\r\nBody has “quotes” here.\r\n",
2903
+ "note": "modes.md §3.7.3a steps 2 and 5 composed: a runtime that splits lines on U+000A alone sees `--- \\r`, decides U+000D is not whitespace it admits, and loses the block. That is the most ordinary Windows file there is."
2904
+ },
2905
+ {
2906
+ "id": "en-us-markdown-commonmark-frontmatter-crlf-closer-trailing-space",
2907
+ "rule": "quotes",
2908
+ "mode": "markdown",
2909
+ "dialect": "commonmark",
2910
+ "in": "---\r\ntitle: \"Une note\"\r\n--- \r\n\r\nBody has \"quotes\" here.\r\n",
2911
+ "out": "---\r\ntitle: \"Une note\"\r\n--- \r\n\r\nBody has “quotes” here.\r\n",
2912
+ "note": "modes.md §3.7.3a steps 3 and 5: the same composition on the closing delimiter."
2913
+ },
2914
+ {
2915
+ "id": "en-us-markdown-commonmark-frontmatter-keys-trailing-space-opener",
2916
+ "rule": "quotes",
2917
+ "mode": "markdown",
2918
+ "dialect": "commonmark",
2919
+ "frontmatterKeys": [
2920
+ "title"
2921
+ ],
2922
+ "in": "--- \ntitle: He said \"hello\" once\nslug: \"he-said-hello\"\n---\n\nBody \"quotes\" here.\n",
2923
+ "out": "--- \ntitle: He said “hello” once\nslug: \"he-said-hello\"\n---\n\nBody “quotes” here.\n",
2924
+ "note": "modes.md §3.7.4 with §3.7.3a: every other locator case takes the skip path, where a wrong extent only costs prose. Here it costs an offset — a port computing the content start as \"four code points in\" is right for every bare fence and wrong for this one, and the spans it emits land on the wrong characters."
2925
+ },
2926
+ {
2927
+ "id": "en-us-markdown-commonmark-frontmatter-toml-trailing-space",
2928
+ "rule": "quotes",
2929
+ "mode": "markdown",
2930
+ "dialect": "commonmark",
2931
+ "in": "+++ \ntitle = \"Une note\"\n+++\n\nBody has \"quotes\" here.\n",
2932
+ "out": "+++ \ntitle = \"Une note\"\n+++\n\nBody has “quotes” here.\n",
2933
+ "note": "modes.md §3.7.3a: the widening is about whitespace, not about the delimiter, so it applies to a TOML block exactly as to a YAML one — the block is skipped whole either way, which is what §7.13 records."
2934
+ },
2935
+ {
2936
+ "id": "en-us-markdown-commonmark-frontmatter-closer-trailing-tab",
2937
+ "rule": "quotes",
2938
+ "mode": "markdown",
2939
+ "dialect": "commonmark",
2940
+ "in": "---\ntitle: \"Une note\"\n---\t\n\nBody has \"quotes\" here.\n",
2941
+ "out": "---\ntitle: \"Une note\"\n---\t\n\nBody has “quotes” here.\n",
2942
+ "note": "modes.md §3.7.3a step 3 names U+0009 as well as U+0020 for the closing line, and the opener case alone would not catch a port that admitted tabs in one place only."
2943
+ },
2944
+ {
2945
+ "id": "en-us-markdown-commonmark-frontmatter-only-an-opener",
2946
+ "rule": "quotes",
2947
+ "mode": "markdown",
2948
+ "dialect": "commonmark",
2949
+ "in": "--- \n",
2950
+ "out": "--- \n",
2951
+ "note": "modes.md §3.7.3a step 4 with nothing after the fence: there is no later line, so there is no block, and a one-line document comes back byte for byte. The degenerate case a scan written as \"find the next delimiter\" gets wrong by running off the end."
2952
+ },
2953
+ {
2954
+ "id": "en-us-markdown-mdx-frontmatter-trailing-space",
2955
+ "rule": "quotes",
2956
+ "mode": "markdown",
2957
+ "dialect": "mdx",
2958
+ "in": "--- \ntitle: \"Une note\"\n---\n\n<Callout>Body \"quotes\" here</Callout>\n",
2959
+ "out": "--- \ntitle: \"Une note\"\n---\n\n<Callout>Body “quotes” here</Callout>\n",
2960
+ "note": "modes.md §3.7.3a is dialect-independent: the locator is a scan over the source and knows nothing about JSX. Pinned in `mdx` because the claim spans both dialects and every other locator case is `commonmark`."
2961
+ },
2962
+ {
2963
+ "id": "en-us-markdown-commonmark-frontmatter-final-cr-no-newline",
2964
+ "rule": "quotes",
2965
+ "mode": "markdown",
2966
+ "dialect": "commonmark",
2967
+ "in": "---\r\ntitle: \"Une note\"\r\n---\r",
2968
+ "out": "---\r\ntitle: \"Une note\"\r\n---\r",
2969
+ "note": "modes.md §3.7.3a step 5: a file whose last line is the closing fence followed by U+000D and no U+000A still closes its block — end of input ends a line, and the U+000D belongs to the terminator. A literal reading of §3.8.4's LF-only model loses the block here and hands the metadata to the rules, which is why step 5 states the Markdown line model instead of borrowing the YAML one."
2970
+ },
2971
+ {
2972
+ "id": "en-us-markdown-commonmark-frontmatter-lone-cr-line-endings",
2973
+ "rule": "quotes",
2974
+ "mode": "markdown",
2975
+ "dialect": "commonmark",
2976
+ "in": "---\rtitle: \"Une note\"\r---\r\rBody has \"quotes\" here.\r",
2977
+ "out": "---\rtitle: \"Une note\"\r---\r\rBody has “quotes” here.\r",
2978
+ "note": "modes.md §3.7.3a step 5: lone U+000D line endings are line endings in CommonMark, so a document written with them has a frontmatter block like any other. The content inside it is still read with §3.8.4's LF-only model, because that half is YAML — the two line models are different on purpose and the note in step 5 says so."
2979
+ },
2980
+ {
2981
+ "id": "en-us-markdown-commonmark-frontmatter-masks-an-unclosed-construct",
2982
+ "rule": "quotes",
2983
+ "mode": "markdown",
2984
+ "dialect": "commonmark",
2985
+ "in": "---\nx: |\n ```\n---\n\nBody \"q\" here.\n",
2986
+ "out": "---\nx: |\n ```\n---\n\nBody “q” here.\n",
2987
+ "note": "modes.md §3.7.3a: the shorter mask witness, and the sharper one — a construct merely OPENED inside a metadata value, with nothing in the body to pair with. Unmasked, the lone fence swallows the rest of the document into a code block and the body converts nothing at all. Measured on 1.7.0, that is what one runtime does."
2988
+ },
2989
+ {
2990
+ "id": "en-us-markdown-commonmark-frontmatter-keys-lone-cr-is-inert",
2991
+ "rule": "quotes",
2992
+ "mode": "markdown",
2993
+ "dialect": "commonmark",
2994
+ "frontmatterKeys": [
2995
+ "title"
2996
+ ],
2997
+ "in": "---\rtitle: a \"b\"\rslug: \"x\"\r---\r\rBody \"c\" here.\r",
2998
+ "out": "---\rtitle: a \"b\"\rslug: \"x\"\r---\r\rBody “c” here.\r",
2999
+ "note": "modes.md §3.7.3a step 5 and §7.13: the block is found by CommonMark's line model, and the content is then read by §3.8.4's LF-only one, which sees a two-line block as a single line and yields no span. So the option is inert here — nothing machine-read is typeset, the block is still skipped, and the body converts. Pinned because it is a silence, and a silence nobody pinned is a silence nobody notices."
3000
+ },
3001
+ {
3002
+ "id": "en-us-markdown-commonmark-frontmatter-keys-lone-cr-one-key",
3003
+ "rule": "quotes",
3004
+ "mode": "markdown",
3005
+ "dialect": "commonmark",
3006
+ "frontmatterKeys": [
3007
+ "title"
3008
+ ],
3009
+ "in": "---\rtitle: a \"b\"\r---\r\rBody \"c\" here.\r",
3010
+ "out": "---\rtitle: a \"b\"\r---\r\rBody “c” here.\r",
3011
+ "note": "modes.md §3.7.4: a block whose content carries a U+000D not followed by U+000A yields no spans, however few lines it has. An earlier draft let a one-line block through and declined a two-line one, which made the rule depend on a line count rather than on the character; the two-line case is en-us-markdown-commonmark-frontmatter-keys-lone-cr-is-inert and this is the other side of it."
3012
+ },
3013
+ {
3014
+ "id": "en-us-markdown-commonmark-lone-cr-no-block",
3015
+ "rule": "ellipsis",
3016
+ "mode": "markdown",
3017
+ "dialect": "commonmark",
3018
+ "in": "Wait for it... he said \"hi\"\rand then \"left\".\r",
3019
+ "out": "Wait for it… he said “hi”\rand then “left”.\r",
3020
+ "note": "modes.md §3.7.3a step 5 makes a lone U+000D a line ending the locator understands, so a document written that way reaches the body walk like any other. Pinned because the failure mode is not an error but silent offset corruption: one runtime reported byte columns for such a document and another raised POLYTYPO_MALFORMED_INPUT, a code §3.7.2 reserves for mdx."
3021
+ },
3022
+ {
3023
+ "id": "en-us-markdown-commonmark-byte-order-mark-no-block",
3024
+ "rule": "ellipsis",
3025
+ "mode": "markdown",
3026
+ "dialect": "commonmark",
3027
+ "in": "Wait for it... he said \"hi\"\n",
3028
+ "out": "Wait for it… he said “hi”\n",
3029
+ "note": "modes.md §3.7.3a step 1 steps over a leading U+FEFF, and a document carrying a mark but no block must convert exactly as the same document without one. Measured across the four runtimes that implement the mode: three convert and one returns the document unchanged, so this pins the majority and names the fourth as the defect."
3030
+ },
3031
+ {
3032
+ "id": "en-us-markdown-commonmark-frontmatter-keys-astral-in-the-block",
3033
+ "rule": "apostrophe",
3034
+ "mode": "markdown",
3035
+ "dialect": "commonmark",
3036
+ "frontmatterKeys": [
3037
+ "title"
3038
+ ],
3039
+ "in": "---\ntitle: café 😀 it's ok\n---\n\nBody \"q\" here...\n",
3040
+ "out": "---\ntitle: café 😀 it’s ok\n---\n\nBody “q” here…\n",
3041
+ "note": "modes.md §3.7.3a's mask is one U+0020 per index unit, not per code point: a runtime that indexes bytes and masks an astral character to a single space shortens its input by three and shifts every body offset after it. The trailing ellipsis is what a shift eats first, so this case fails loudly rather than subtly."
3042
+ },
3043
+ {
3044
+ "id": "en-us-markdown-commonmark-frontmatter-keys-lone-cr-costs-one-line",
3045
+ "rule": "quotes",
3046
+ "mode": "markdown",
3047
+ "dialect": "commonmark",
3048
+ "frontmatterKeys": [
3049
+ "title",
3050
+ "slug"
3051
+ ],
3052
+ "in": "---\ntitle: \"a\rb\"\nslug: c \"d\"\n---\n\nBody \"q\" here.\n",
3053
+ "out": "---\ntitle: \"a\rb\"\nslug: c “d”\n---\n\nBody “q” here.\n",
3054
+ "note": "modes.md §3.7.4's U+000D bail is per line, not per block: `title`'s value carries a stray carriage return and is declined, `slug`'s line converts, and the body converts. The other side of the lone-U+000D document cases, which lose everything because such a document is one §3.8.4 line. Implementers: the per-line test has to run to the next line's start rather than to the line's end, or a block whose only line ends in U+000D looks clean — that is the one reading under which both this case and -keys-lone-cr-one-key pass."
3055
+ },
3056
+ {
3057
+ "id": "en-us-markdown-commonmark-frontmatter-keys-lone-cr-ends-the-content",
3058
+ "rule": "quotes",
3059
+ "mode": "markdown",
3060
+ "dialect": "commonmark",
3061
+ "frontmatterKeys": [
3062
+ "title",
3063
+ "slug"
3064
+ ],
3065
+ "in": "---\ntitle: a \"b\"\nslug: c \"d\"\n\r---\n\nBody \"q\" here.\n",
3066
+ "out": "---\ntitle: a “b”\nslug: c \"d\"\n\r---\n\nBody “q” here.\n",
3067
+ "note": "modes.md §3.7.4: the content's last line is a lone U+000D, and `title` converts while `slug` does not. What this case discriminates is an implementation that substitutes the U+000D for another declining character: that converts both keys. What it does NOT discriminate is the per-line filter itself — measured in three ports, `slug` is already silent before the filter runs, because the trailing line is blank and joins `slug`'s value run, which §3.8.6's continuation bail declines. The cases that exercise the filter are -keys-lone-cr-one-key and -keys-lone-cr-costs-one-line."
3068
+ },
3069
+ {
3070
+ "id": "en-us-markdown-commonmark-frontmatter-keys-lone-cr-in-a-value-run",
3071
+ "rule": "dashes",
3072
+ "mode": "markdown",
3073
+ "dialect": "commonmark",
3074
+ "frontmatterKeys": [
3075
+ "summary",
3076
+ "title"
3077
+ ],
3078
+ "in": "---\nsummary: a\rb\n title: c -- d\n---\n\nBody \"q\" here.\n",
3079
+ "out": "---\nsummary: a\rb\n title: c -- d\n---\n\nBody “q” here.\n",
3080
+ "note": "modes.md §3.7.4: the decline drops the spans the content scan produced for that line and does not alter the content the scan was given. Here the U+000D sits on a listed key whose value run continues onto a more-indented line, so the scan yields nothing for the block at all — `title` is inside `summary`'s value run, not a key of its own. Substituting the U+000D for U+0009 instead converts `c -- d` and is the divergence this case exists to fail."
3081
+ },
3082
+ {
3083
+ "id": "en-us-markdown-commonmark-frontmatter-byte-order-mark-and-lone-cr",
3084
+ "rule": "quotes",
3085
+ "mode": "markdown",
3086
+ "dialect": "commonmark",
3087
+ "frontmatterKeys": [
3088
+ "title"
3089
+ ],
3090
+ "in": "---\rtitle: a \"b\"\r---\r\rBody \"q\".\r",
3091
+ "out": "---\rtitle: a \"b\"\r---\r\rBody “q”.\r",
3092
+ "note": "modes.md §3.7.3a steps 1 and 5 composed: a byte-order mark in front of a document whose lines end with lone U+000D. Each clause is pinned alone and their combination is where one runtime lost the body entirely — found by an adversarial pass over a finished port, not by any fixture then in the suite. The block is located and skipped, its content is declined for the U+000D, and the body converts."
2573
3093
  }
2574
3094
  ]
2575
3095
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.3",
2
+ "spec": "1.8.0",
3
3
  "locale": "es",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.3",
2
+ "spec": "1.8.0",
3
3
  "locale": "fi",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.3",
2
+ "spec": "1.8.0",
3
3
  "locale": "fr-CA",
4
4
  "cases": [
5
5
  {