Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn schemes() -> Vec<(&'static str, Vec<(String, String)>)> {
let decimal = (0..6)
.map(|i| {
let lvl = (1..=i + 1).map(|j| format!("%{j}")).collect::<Vec<_>>().join(".");
(lvl, "decimal".to_string())
(format!("{lvl}."), "decimal".to_string()) // trailing dot ("1.", "1.1."): the caption form every corpus contract uses
})
.collect();
let legal = [("%1.", "decimal"), ("(%2)", "lowerLetter"), ("(%3)", "lowerRoman"), ("(%4)", "upperLetter"), ("(%5)", "upperRoman"), ("(%6)", "decimal")]
Expand Down Expand Up @@ -238,7 +238,8 @@ impl Resolver {
let Some((display, full)) = self.headnums.get(tgt) else {
return Err(format!("cross-reference #{tgt} needs a number its target does not have; pass number_headings or use {{ref=text}}"));
};
Ok(if variant == "leaf" { display.clone() } else { full.clone() })
let num = if variant == "leaf" { display.clone() } else { full.clone() };
Ok(num.strip_suffix('.').map_or(num.clone(), str::to_string)) // caption "1.2." cites as "Section 1.2": mid-sentence references drop the trailing dot
}

/// Prefix text before a reference: `override` text, the type's prefix
Expand Down
16 changes: 8 additions & 8 deletions tests/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ def test_refs_and_heading_numbering():
h = mdhtml2html(md2mdhtml(REFS_MD), number_headings='legal')
assert '<span class="heading-number">1.</span> Payment' in h
assert '<span class="heading-number">(a)</span> Late fees' in h
assert '<a href="#sec-pay">Section 1.</a>' in h
assert '<a href="#sec-pay">Section 1</a>' in h
assert '<a href="#sec-late">1.(a)</a>' in h # bare: no prefix word
assert '<a href="#sec-late">Clause 1.(a)</a>' in h # override text
assert 'Sections <a href="#sec-pay">1.</a> and <a href="#sec-late">1.(a)</a>' in h
assert 'Sections <a href="#sec-pay">1</a> and <a href="#sec-late">1.(a)</a>' in h
assert '<a href="#sec-late">Section (a)</a>' in h # leaf
assert '<a href="#sec-late">Late fees</a>' in h # text
assert 'page <a href="#sec-late">1.(a)</a>' in h # page degrades to full
assert 'data-ref' not in h
assert h.warnings == []
d = mdhtml2html(md2mdhtml('# One {#sec-a}\n\n## Two {#sec-b}\n\nSee [@sec-b].'), number_headings='decimal')
assert '<span class="heading-number">1.1</span> Two' in d
assert '<span class="heading-number">1.1.</span> Two' in d
assert '<a href="#sec-b">Section 1.1</a>' in d


def test_ref_errors():
with pytest.raises(ValueError, match='not found'): mdhtml2html(md2mdhtml('See [@sec-x].'))
auto = mdhtml2html(md2mdhtml('# A {#sec-a}\n\nSee [@sec-a].')) # refs trigger auto decimal numbering
assert '<span class="heading-number">1</span> A' in auto and '<a href="#sec-a">Section 1</a>' in auto
assert '<span class="heading-number">1.</span> A' in auto and '<a href="#sec-a">Section 1</a>' in auto
assert 'heading-number' not in mdhtml2html(md2mdhtml('# A {#sec-a}\n\nText.')) # no numeric ref: no numbering
md = '# A {#exh-a}\n\nSee [@exh-a].'
with pytest.raises(ValueError, match='reftypes'): mdhtml2html(md2mdhtml(md), number_headings='legal')
h = mdhtml2html(md2mdhtml(md), number_headings='legal', reftypes=dict(exh=('Exhibit', 'Exhibits')))
assert '<a href="#exh-a">Exhibit 1.</a>' in h
assert '<a href="#exh-a">Exhibit 1</a>' in h
with pytest.raises(ValueError, match='data-ref'):
mdhtml2html('<p id="x">t</p><p><a data-ref="zap" href="#x"></a></p>')

Expand Down Expand Up @@ -239,10 +239,10 @@ def test_md2gfm_refs_and_numbering():
out = md2gfm(REFS_MD, number_headings='legal')
assert '# 1. Payment\n' in out and '## (a) Late fees\n' in out
assert '{#sec-pay}' not in out
assert ('See Section 1., 1.(a), Clause 1.(a), Sections 1. and 1.(a), Section (a),\n'
assert ('See Section 1, 1.(a), Clause 1.(a), Sections 1 and 1.(a), Section (a),\n'
'Late fees, and page 1.(a).') in out
auto = md2gfm('# A {#sec-a}\n\nSee [@sec-a].')
assert '# 1 A\n' in auto and 'See Section 1.' in auto
assert '# 1. A\n' in auto and 'See Section 1.' in auto
dl = md2gfm('# A {#sec-a}\n\nT\n: see [@sec-a].\n')
assert ': see Section 1.' in dl # definition bodies are rewrite regions too
assert md2gfm('# A {#sec-a}\n\nText only.\n') == '# A\n\nText only.\n' # strip only; rest byte-identical
Expand All @@ -253,7 +253,7 @@ def test_md2gfm_nested_containers():
md = ('# Top {#sec-top}\n\n::: box\n\n## Inner {#sec-in}\n\nBody.\n\n:::\n\n'
'> ## Quoted {#sec-q}\n\nSee [@sec-top], [@sec-in], and [-@sec-q]{ref=text}.\n')
out = md2gfm(md)
assert '# 1 Top\n' in out and '## 1.1 Inner\n' in out
assert '# 1. Top\n' in out and '## 1.1. Inner\n' in out
assert '{#sec-in}' not in out
assert 'See Section 1, Section 1.1, and Quoted.' in out
assert '{#sec-q}' in out # marker containers pass through unrewritten
Expand Down