From e0a7df2be550498a343b366d8548012a4ccec13c Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 14:13:48 +0200 Subject: [PATCH 1/9] fix(sentence): a number opens a sentence, and a lowercase start can be asked for UAX #29 SB8 keeps a terminator joined to a following word that opens lowercase, which also joined 'stopped. 3 tests'. A number after a terminator and a space now opens a sentence; the abbreviation merge rejoins 'Fig. 3' and 'Eq. 2'. UnicodeSentenceSplitter::with_lowercase_starts(true) opens one at a lowercase word too, for a reader counting claims rather than reflowing prose; formatting keeps SB8. --- src/sentence/unicode.rs | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/sentence/unicode.rs b/src/sentence/unicode.rs index 33c54e41..37278e2e 100644 --- a/src/sentence/unicode.rs +++ b/src/sentence/unicode.rs @@ -120,6 +120,9 @@ pub struct UnicodeSentenceSplitter { lang_multi_pattern: Regex, /// Extra LaTeX command names tokenized like `\verb` before split. extra_verbatim_commands: Vec, + /// Whether a lowercase word after a terminator and a space opens a + /// sentence. UAX #29 SB8 says no; a reader counting claims says yes. + lowercase_starts: bool, } impl UnicodeSentenceSplitter { @@ -160,9 +163,19 @@ impl UnicodeSentenceSplitter { lang_abbrev_pattern, lang_multi_pattern, extra_verbatim_commands: Vec::new(), + lowercase_starts: false, } } + /// Open a sentence at a lowercase word after a terminator and a space + /// (`It stopped. packsetd restarts.`), which UAX #29 SB8 keeps joined. + /// A known abbreviation before the terminator (`e.g. the`) still joins. + /// Off by default: a formatter reflowing prose keeps SB8. + pub fn with_lowercase_starts(mut self, on: bool) -> Self { + self.lowercase_starts = on; + self + } + /// Extra LaTeX command names tokenized like `\verb` before split. pub fn with_verbatim_commands(mut self, cmds: Vec) -> Self { self.extra_verbatim_commands = cmds; @@ -3134,6 +3147,9 @@ impl SentenceSplitter for UnicodeSentenceSplitter { // lowercase. Split same-line `iCloud` starts before abbreviation // merge so `e.g. iCloud` can rejoin. let expanded = split_before_lowercase_proper_nouns(raw_segments.iter().copied()); + // SB8 also keeps `stopped. 3 tests` joined; a number opens a + // sentence, and the abbreviation merge below rejoins `Fig. 3`. + let expanded = split_before_sentence_starts(&expanded, self.lowercase_starts); let refs: Vec<&str> = expanded.iter().map(String::as_str).collect(); let merged = self.refine_segments_from_strs(&refs); let restored = restore_inline_tokens(merged, &placeholders); @@ -3257,6 +3273,45 @@ fn take_lowercase_proper_noun_sentence(seg: &str) -> Option<(String, String)> { Some((head.to_string(), rest.to_string())) } +/// Split each segment after a terminator and whitespace when the next word +/// opens with a digit, or with a lowercase letter when `lowercase` is set. +/// A head with no letter (`1.` opening a list item, `...`) is not a +/// sentence, so the scan moves on to the next terminator. +fn split_before_sentence_starts(segments: &[String], lowercase: bool) -> Vec { + let mut out = Vec::with_capacity(segments.len()); + for seg in segments { + let chars: Vec<(usize, char)> = seg.char_indices().collect(); + let mut from = 0usize; + let mut i = 0usize; + while i < chars.len() { + let (_, c) = chars[i]; + if !matches!(c, '.' | '!' | '?') { + i += 1; + continue; + } + let mut j = i; + while j < chars.len() && matches!(chars[j].1, '.' | '!' | '?') { + j += 1; + } + let mut k = j; + while k < chars.len() && chars[k].1.is_whitespace() { + k += 1; + } + let opens = chars + .get(k) + .is_some_and(|&(_, n)| n.is_ascii_digit() || (lowercase && n.is_lowercase())); + let end = chars.get(j).map_or(seg.len(), |&(at, _)| at); + if k > j && opens && seg[from..end].chars().any(char::is_alphabetic) { + out.push(seg[from..end].to_string()); + from = chars[k].0; + } + i = j.max(i + 1); + } + out.push(seg[from..].to_string()); + } + out.into_iter().filter(|s| !s.trim().is_empty()).collect() +} + fn merge_abbreviation_splits( segments: &[&str], abbrev_re: &Regex, @@ -3847,6 +3902,41 @@ mod tests { ); } + #[test] + fn a_number_opens_a_sentence() { + assert_eq!( + split("It stopped. 3 tests failed."), + vec!["It stopped.", "3 tests failed."] + ); + assert_eq!( + split("See Fig. 3 and Eq. 2 for the rate. Done."), + vec!["See Fig. 3 and Eq. 2 for the rate.", "Done."] + ); + assert_eq!( + split("The tracker has 0.9.3 now. It holds."), + vec!["The tracker has 0.9.3 now.", "It holds."] + ); + } + + #[test] + fn a_lowercase_start_opens_a_sentence_only_when_asked() { + let text = "Restart it. packsetd reloads the pack."; + assert_eq!(split(text), vec![text.to_string()], "SB8 by default"); + let claims = UnicodeSentenceSplitter::new().with_lowercase_starts(true); + assert_eq!( + claims.split(text), + vec!["Restart it.", "packsetd reloads the pack."] + ); + assert_eq!( + claims.split("Use a ring, e.g. the instanton. It converges."), + vec!["Use a ring, e.g. the instanton.", "It converges."] + ); + assert_eq!( + claims.split("1. first item. 2. second item."), + vec!["1. first item.", "2. second item."] + ); + } + #[test] fn simple_sentences() { assert_eq!( From 67b0547487f5945d1809324cb4962a6b638d922e Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 14:51:33 +0200 Subject: [PATCH 2/9] fix(sentence): lowercase academic abbreviations rejoin a number after them Also clears two clippy lints the Rust 1.98 toolchain raises in check.rs and tests/org_incomplete_begin.rs. --- src/abbreviations.rs | 4 +++- src/check.rs | 5 ++--- tests/org_incomplete_begin.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/abbreviations.rs b/src/abbreviations.rs index 5fd2f11b..4da8c48a 100644 --- a/src/abbreviations.rs +++ b/src/abbreviations.rs @@ -4,7 +4,9 @@ pub static EN_ABBREVIATIONS: &[&str] = &[ "Mr", "Mrs", "Ms", "Dr", "Prof", "Sr", "Jr", "St", "Rev", "Gen", "Gov", "Sgt", "Cpl", "Pvt", "Capt", "Lt", "Col", "Maj", "Cmdr", "Adm", // Academic / scientific "Fig", "Figs", "Eq", "Eqs", "Ref", "Refs", "Tab", "Sec", "Ch", "Vol", "No", "Nos", "Ed", "Eds", - "Trans", "Dept", "Thm", "Lem", "Prop", "Def", "Cor", "Rem", "Ex", + "Trans", "Dept", "Thm", "Lem", "Prop", "Def", "Cor", "Rem", + "Ex", // The same, lowercase in running text + "fig", "figs", "eq", "eqs", "ref", "refs", "tab", "sec", "ch", "vol", "nos", "p", // Latin abbreviations "al", "approx", "ca", "cf", "etc", "et", "ibid", "viz", // Common "vs", "misc", "est", "govt", "dept", "univ", "inc", "Inc", "corp", "Corp", "ltd", "Ltd", "Ave", diff --git a/src/check.rs b/src/check.rs index 867084dd..d5ac0da7 100644 --- a/src/check.rs +++ b/src/check.rs @@ -345,9 +345,8 @@ mod tests { #[test] fn long_advisory_needs_clause_boundary() { - let long_with_comma = format!( - "The quick brown fox jumps over the lazy dog, then continues running across a very long meadow without pausing for breath at all today.\n" - ); + let long_with_comma = + "The quick brown fox jumps over the lazy dog, then continues running across a very long meadow without pausing for breath at all today.\n".to_string(); assert!( long_with_comma.trim_end().chars().count() > DEFAULT_LONG_THRESHOLD, "fixture must exceed the default long threshold" diff --git a/tests/org_incomplete_begin.rs b/tests/org_incomplete_begin.rs index e9d26e92..9246fba6 100644 --- a/tests/org_incomplete_begin.rs +++ b/tests/org_incomplete_begin.rs @@ -68,7 +68,7 @@ fn assert_after_next_prose_and_split(input: &str, out: &str, regions: &[Region]) !out.contains("After the env. Next."), "fused trailing prose must not survive, got:\n{out}" ); - assert_eq!(format_text(&out, &org_cfg()).unwrap(), out); + assert_eq!(format_text(out, &org_cfg()).unwrap(), out); let guarded = FormatConfig { format: Format::Org, From ee1f49e9101a6cef5055273f883f0929002078ba Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 14:55:41 +0200 Subject: [PATCH 3/9] style: clear the clippy lints Rust 1.98 raises in tests and the LaTeX parser --- src/parser/latex.rs | 2 +- tests/md_html_type7_close.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser/latex.rs b/src/parser/latex.rs index e41d4930..e49c094c 100644 --- a/src/parser/latex.rs +++ b/src/parser/latex.rs @@ -4740,7 +4740,7 @@ Some text. // Whole-line prefix: leftover scan `from` can sit after another // leftover on the same line. `\lstinline` must stay Prose so // `See \verb|x| here.` is not the only mid-line case. - let after_other = concat!("\\py{print(1)} \\lstinline|print(1)|\n",); + let after_other = "\\py{print(1)} \\lstinline|print(1)|\n"; let after_other_regions = LatexParser::default().parse(after_other); assert!( after_other_regions.iter().any(|r| matches!( diff --git a/tests/md_html_type7_close.rs b/tests/md_html_type7_close.rs index e2d51d42..4dc92097 100644 --- a/tests/md_html_type7_close.rs +++ b/tests/md_html_type7_close.rs @@ -44,7 +44,7 @@ fn closed_span_is_structure_through_close() { Region::Structure(s) if s.contains(" Some(s.as_str()), _ => None, }); - let span = span.expect(&format!("span block must be Structure, got {regions:?}")); + let span = span.unwrap_or_else(|| panic!("span block must be Structure, got {regions:?}")); assert!(span.contains(""), "{span}"); assert!(span.contains("First. Second."), "{span}"); assert!(span.contains(""), "{span}"); @@ -102,7 +102,7 @@ fn type6_closed_div_runs_to_a_blank_line() { Region::Structure(s) if s.contains("
") => Some(s.as_str()), _ => None, }); - let div = div.expect(&format!("div block must be Structure, got {regions:?}")); + let div = div.unwrap_or_else(|| panic!("div block must be Structure, got {regions:?}")); assert!(div.contains("
"), "{div}"); assert!( div.contains("After html. Next."), From db7b015226fb55c8f3fb73f039e464803964379c Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 15:06:27 +0200 Subject: [PATCH 4/9] style: unwrap_or_else with panic! over expect(&format!) in tests, and two needless borrows --- src/parser/latex.rs | 14 ++++++++------ src/parser/markdown.rs | 11 ++++++----- src/parser/pandoc/ast.rs | 7 +++---- tests/md_html_type6_close.rs | 2 +- tests/sentence_delim_props.rs | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/parser/latex.rs b/src/parser/latex.rs index e49c094c..8848e467 100644 --- a/src/parser/latex.rs +++ b/src/parser/latex.rs @@ -6120,7 +6120,8 @@ Some text. Region::Code { body, footer, .. } => Some((body.as_str(), footer.as_str())), _ => None, }); - let (body, footer) = code.expect(&format!("lstlisting must be Code, got: {regions:?}")); + let (body, footer) = + code.unwrap_or_else(|| panic!("lstlisting must be Code, got: {regions:?}")); assert!( body.contains("print(1)"), "listing body must keep source, got body={body:?} regions={regions:?}" @@ -6159,7 +6160,8 @@ Some text. Region::Code { body, footer, .. } => Some((body.as_str(), footer.as_str())), _ => None, }); - let (body, footer) = code.expect(&format!("verbatim must be Code, got: {regions:?}")); + let (body, footer) = + code.unwrap_or_else(|| panic!("verbatim must be Code, got: {regions:?}")); assert!( body.contains("still body"), "inner \\end must not close the outer verbatim; still body stays in the listing, got body={body:?} regions={regions:?}" @@ -6196,7 +6198,8 @@ Some text. Region::Code { body, footer, .. } => Some((body.as_str(), footer.as_str())), _ => None, }); - let (body, footer) = code.expect(&format!("lstlisting must be Code, got: {regions:?}")); + let (body, footer) = + code.unwrap_or_else(|| panic!("lstlisting must be Code, got: {regions:?}")); assert!( body.contains("print(1)"), "listing body must keep source before %, got body={body:?} regions={regions:?}" @@ -7646,9 +7649,8 @@ Some text. Region::Code { body, footer, .. } => Some((body.as_str(), footer.as_str())), _ => None, }); - let (raw_body, raw_footer) = raw_code.expect(&format!( - "lstlisting* must stay Code on %, got: {raw_regions:?}" - )); + let (raw_body, raw_footer) = raw_code + .unwrap_or_else(|| panic!("lstlisting* must stay Code on %, got: {raw_regions:?}")); assert!( raw_body.contains("print(1)"), "lstlisting* raw scan must keep source before %, got body={raw_body:?}" diff --git a/src/parser/markdown.rs b/src/parser/markdown.rs index 34b5a90d..b334b277 100644 --- a/src/parser/markdown.rs +++ b/src/parser/markdown.rs @@ -5740,7 +5740,8 @@ mod tests { Region::Structure(s) if s.contains("\nKeep this. Exactly here.\n\nFinal thing. Last sentence.\n"; - let out = pipe_stdin(input, &["--format", "markdown"]); - let result = String::from_utf8(out.stdout).unwrap(); - assert!(result.contains("Hello world.\nGoodbye world.\n")); - assert!(result.contains("Keep this. Exactly here.\n")); - assert!(result.contains("Final thing.\nLast sentence.")); -} - -#[test] -fn pragma_off_on_plaintext() { - let input = "Hello world. Goodbye world.\nsnapper:off\nKeep this. Exactly here.\nsnapper:on\nFinal thing. Last sentence.\n"; - let out = pipe_stdin(input, &["--format", "plaintext"]); - let result = String::from_utf8(out.stdout).unwrap(); - assert!(result.contains("Hello world.\nGoodbye world.\n")); - assert!(result.contains("Keep this. Exactly here.\n")); - assert!(result.contains("Final thing.\nLast sentence.\n")); -} - -// ---- FEATURE 5: --range ---- - -#[test] -fn range_formats_only_specified_lines() { - let input = "Line one. Stay same.\nLine two. Should split. Into two.\nLine three. Stay same.\n"; - let out = pipe_stdin(input, &["--range", "2:2"]); - let result = String::from_utf8(out.stdout).unwrap(); - // Line 1 unchanged - assert!(result.starts_with("Line one. Stay same.\n")); - // Line 2 split - assert!(result.contains("Line two.\nShould split.\nInto two.\n")); - // Line 3 unchanged - assert!(result.ends_with("Line three. Stay same.\n")); -} - -#[test] -fn range_preserves_outside_lines_exactly() { - let tmp = tempfile::NamedTempFile::new().unwrap(); - fs::write( - tmp.path(), - "Keep. This line.\nReformat. This line. Please.\nKeep. This too.\n", - ) - .unwrap(); - let output = snapper_binary() - .args([ - "--native", - "--format", - "plaintext", - "--range", - "2:2", - &tmp.path().to_string_lossy(), - ]) - .output() - .expect("failed to run"); - let result = String::from_utf8(output.stdout).unwrap(); - assert!(result.starts_with("Keep. This line.\n")); - assert!(result.contains("Reformat.\nThis line.\nPlease.\n")); - assert!(result.ends_with("Keep. This too.\n")); -} - -// ---- FEATURE 6: --output-format json/sarif ---- - -#[test] -fn check_json_output() { - let output = snapper_binary() - .args([ - "--native", - "--check", - "--output-format", - "json", - "--format", - "org", - &fixture_path("sample.org"), - ]) - .output() - .expect("failed to run"); - let stdout = String::from_utf8(output.stdout).unwrap(); - let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("invalid JSON"); - assert!(parsed.is_array()); - let arr = parsed.as_array().unwrap(); - assert_eq!(arr.len(), 1); - assert!(arr[0]["file"].as_str().unwrap().contains("sample.org")); -} - -#[test] -fn check_sarif_output() { - let output = snapper_binary() - .args([ - "--native", - "--check", - "--output-format", - "sarif", - "--format", - "org", - &fixture_path("sample.org"), - ]) - .output() - .expect("failed to run"); - let stdout = String::from_utf8(output.stdout).unwrap(); - let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("invalid SARIF"); - assert_eq!(parsed["version"], "2.1.0"); - let results = &parsed["runs"][0]["results"]; - assert!(results.is_array()); - assert!(!results.as_array().unwrap().is_empty()); -} - -// ---- Golden master: edge cases ---- - -#[test] -fn edge_cases_org() { - let actual = run_format("org", &fixture_path("edge_cases.org")); - let expected = fs::read_to_string(fixture_path("expected_edge_cases.org")).unwrap(); - pretty_assertions::assert_eq!(actual, expected); -} - -#[test] -fn edge_cases_plaintext() { - let actual = run_format("plaintext", &fixture_path("edge_cases.txt")); - let expected = fs::read_to_string(fixture_path("expected_edge_cases.txt")).unwrap(); - pretty_assertions::assert_eq!(actual, expected); -} - -#[test] -fn idempotent_edge_cases_org() { - let first = run_format("org", &fixture_path("edge_cases.org")); - let tmp = tempfile::NamedTempFile::new().unwrap(); - fs::write(tmp.path(), &first).unwrap(); - let second = run_format("org", &tmp.path().to_string_lossy()); - pretty_assertions::assert_eq!(first, second, "edge cases org reflow must be idempotent"); -} - -#[test] -fn idempotent_edge_cases_plaintext() { - let first = run_format("plaintext", &fixture_path("edge_cases.txt")); - let tmp = tempfile::NamedTempFile::new().unwrap(); - fs::write(tmp.path(), &first).unwrap(); - let second = run_format("plaintext", &tmp.path().to_string_lossy()); - pretty_assertions::assert_eq!( - first, - second, - "edge cases plaintext reflow must be idempotent" - ); -} - -// ---- FEATURE 7: snapper init ---- - -#[test] -fn init_dry_run_shows_config() { - let output = snapper_binary() - .args(["init", "--dry-run"]) - .output() - .expect("failed to run"); - let stderr = String::from_utf8(output.stderr).unwrap(); - assert!(stderr.contains(".snapperrc.toml")); - assert!(stderr.contains("format =")); - assert!(stderr.contains("pre-commit")); - assert!(stderr.contains("apheleia")); -} - -#[test] -fn config_lang_applies_to_stdin() { - let dir = tempfile::tempdir().unwrap(); - fs::write(dir.path().join(".snapperrc.toml"), "lang = \"de\"\n").unwrap(); - let out = pipe_stdin_in_dir("z.B. Das ist ein Satz. Noch einer.\n", &[], dir.path()); - assert!(out.status.success()); - let result = String::from_utf8(out.stdout).unwrap(); - assert_eq!(result, "z.B. Das ist ein Satz.\nNoch einer.\n"); -} - -#[test] -fn config_default_format_applies_to_stdin() { - let dir = tempfile::tempdir().unwrap(); - fs::write(dir.path().join(".snapperrc.toml"), "format = \"org\"\n").unwrap(); - let out = pipe_stdin_in_dir("* Heading\nSentence one. Sentence two.\n", &[], dir.path()); - assert!(out.status.success()); - let result = String::from_utf8(out.stdout).unwrap(); - assert!(result.starts_with("* Heading\n")); - assert!(result.contains("Sentence one.\n")); - assert!(result.contains("Sentence two.\n")); -} - -#[test] -fn config_ignore_skips_check_mode() { - let dir = tempfile::tempdir().unwrap(); - fs::write(dir.path().join(".snapperrc.toml"), "ignore = [\"*.md\"]\n").unwrap(); - fs::write(dir.path().join("draft.md"), "Sentence one. Sentence two.\n").unwrap(); - let output = snapper_binary() - .current_dir(dir.path()) - .args(["--native", "--check", "draft.md"]) - .output() - .expect("failed to run snapper"); - assert!(output.status.success()); - assert!(String::from_utf8(output.stderr).unwrap().is_empty()); -} - -#[test] -fn config_per_format_width_applies() { - let dir = tempfile::tempdir().unwrap(); - fs::write( - dir.path().join(".snapperrc.toml"), - "max_width = 80\n[plaintext]\nmax_width = 12\n", - ) - .unwrap(); - fs::write( - dir.path().join("draft.txt"), - "Alpha beta gamma delta epsilon zeta.\n", - ) - .unwrap(); - let output = snapper_binary() - .current_dir(dir.path()) - .args(["--native", "draft.txt"]) - .output() - .expect("failed to run snapper"); - assert!(output.status.success()); - let result = String::from_utf8(output.stdout).unwrap(); - assert_eq!(result, "Alpha beta\ngamma delta\nepsilon\nzeta.\n"); -} - -#[test] -fn config_latex_envs_and_commands_apply() { - let dir = tempfile::tempdir().unwrap(); - fs::write( - dir.path().join(".snapperrc.toml"), - "[latex]\nverbatim_envs = [\"Verbatim\"]\nstructure_envs = [\"algorithm\", \"comment\"]\nverbatim_commands = [\"Verb\"]\n", - ) - .unwrap(); - let input = "\\begin{document}\n\\begin{algorithm}\nFirst step. Second step.\n\\end{algorithm}\nUse \\Verb|a.b! c| here. Next sentence.\n\\end{document}\n"; - let out = pipe_stdin_in_dir(input, &["--format", "latex"], dir.path()); - assert!( - out.status.success(), - "stderr={}", - String::from_utf8_lossy(&out.stderr) - ); - let result = String::from_utf8(out.stdout).unwrap(); - assert!( - result.contains("First step. Second step."), - "algorithm body must not reflow, got:\n{result}" - ); - assert!( - !result.contains("First step.\nSecond step."), - "algorithm must stay one source line, got:\n{result}" - ); - assert!( - result.contains("Use \\Verb|a.b! c| here.\nNext sentence."), - "configured Verb must tokenize like verb, got:\n{result}" - ); -} - -#[test] -fn markdown_numbered_atx_heading_not_split() { - // snapper-25kc / rtrash README regression - let input = - "### 1. `cargo binstall` (preferred binary install)\n\nBody one. Body two more words.\n"; - let out = pipe_stdin(input, &["--format", "markdown"]); - assert!( - out.status.success(), - "stderr={}", - String::from_utf8_lossy(&out.stderr) - ); - let result = String::from_utf8(out.stdout).unwrap(); - assert!( - result.starts_with("### 1. `cargo binstall` (preferred binary install)\n"), - "heading must stay one line, got:\n{result}" - ); - assert!( - !result.contains("### 1.\n`"), - "must not orphan ### 1. before title:\n{result}" - ); -} - -#[test] -fn markdown_setext_heading_not_collapsed() { - let input = "Setext Title With Period. Still Title\n=====================================\n\nBody after. Second body.\n"; - let out = pipe_stdin(input, &["--format", "markdown"]); - assert!( - out.status.success(), - "stderr={}", - String::from_utf8_lossy(&out.stderr) - ); - let result = String::from_utf8(out.stdout).unwrap(); - assert!( - result.starts_with( - "Setext Title With Period. Still Title\n=====================================\n" - ), - "setext title+underline must stay intact, got:\n{result}" - ); - assert!( - !result.contains("Still Title ====="), - "must not glue underline onto title:\n{result}" - ); -} +//! Every integration test in one binary. A file under tests/ built as its +//! own crate links the whole library, so `cargo test --release` would link +//! some 135 optimised binaries at once and can exhaust memory (#504); one +//! binary links the library once. `pandoc_parity_speed` keeps its own +//! process because it sets the pandoc cache variables other tests read. + +#[path = "check_line_diagnostics.rs"] +mod check_line_diagnostics; +#[path = "code_block_format.rs"] +mod code_block_format; +#[path = "code_block_reflow.rs"] +mod code_block_reflow; +#[path = "latex_enumerate_hang.rs"] +mod latex_enumerate_hang; +#[path = "latex_figure_table_captions.rs"] +mod latex_figure_table_captions; +#[path = "latex_ieeeeqnarray_xltabular.rs"] +mod latex_ieeeeqnarray_xltabular; +#[path = "latex_leftover_cmds.rs"] +mod latex_leftover_cmds; +#[path = "latex_more_envs.rs"] +mod latex_more_envs; +#[path = "latex_nicetabular.rs"] +mod latex_nicetabular; +#[path = "latex_non_prose_envs.rs"] +mod latex_non_prose_envs; +#[path = "latex_section_short_title.rs"] +mod latex_section_short_title; +#[path = "latex_tikz_pgf_envs.rs"] +mod latex_tikz_pgf_envs; +#[path = "latex_verbatim_envs.rs"] +mod latex_verbatim_envs; +#[path = "md_atx_heading_indent.rs"] +mod md_atx_heading_indent; +#[path = "md_compact_dl_markers.rs"] +mod md_compact_dl_markers; +#[path = "md_compact_dl_terms.rs"] +mod md_compact_dl_terms; +#[path = "md_definition_lists.rs"] +mod md_definition_lists; +#[path = "md_dollar_period.rs"] +mod md_dollar_period; +#[path = "md_empty_atx_heading.rs"] +mod md_empty_atx_heading; +#[path = "md_empty_list_item.rs"] +mod md_empty_list_item; +#[path = "md_example_list.rs"] +mod md_example_list; +#[path = "md_four_space_nested_list.rs"] +mod md_four_space_nested_list; +#[path = "md_gfm_alerts.rs"] +mod md_gfm_alerts; +#[path = "md_html_comment_indent.rs"] +mod md_html_comment_indent; +#[path = "md_html_type6_close.rs"] +mod md_html_type6_close; +#[path = "md_html_type7_close.rs"] +mod md_html_type7_close; +#[path = "md_light_table_interrupt.rs"] +mod md_light_table_interrupt; +#[path = "md_link_ref_dest_next_line.rs"] +mod md_link_ref_dest_next_line; +#[path = "md_link_ref_footnotes.rs"] +mod md_link_ref_footnotes; +#[path = "md_link_ref_title_next_line.rs"] +mod md_link_ref_title_next_line; +#[path = "md_link_tail_sentence.rs"] +mod md_link_tail_sentence; +#[path = "md_list_hang_code.rs"] +mod md_list_hang_code; +#[path = "md_list_item_lrd.rs"] +mod md_list_item_lrd; +#[path = "md_lrd_no_interrupt.rs"] +mod md_lrd_no_interrupt; +#[path = "md_multiline_setext.rs"] +mod md_multiline_setext; +#[path = "md_ol_opener_list_item.rs"] +mod md_ol_opener_list_item; +#[path = "md_ol_start_setext.rs"] +mod md_ol_start_setext; +#[path = "md_ol_ten_digit.rs"] +mod md_ol_ten_digit; +#[path = "md_pandoc_citation.rs"] +mod md_pandoc_citation; +#[path = "md_pandoc_line_block.rs"] +mod md_pandoc_line_block; +#[path = "md_quoted_definition_lists.rs"] +mod md_quoted_definition_lists; +#[path = "md_quoted_empty_list_item.rs"] +mod md_quoted_empty_list_item; +#[path = "md_quoted_footnote.rs"] +mod md_quoted_footnote; +#[path = "md_quoted_gfm_tables.rs"] +mod md_quoted_gfm_tables; +#[path = "md_quoted_html_blocks.rs"] +mod md_quoted_html_blocks; +#[path = "md_quoted_indented_code.rs"] +mod md_quoted_indented_code; +#[path = "md_quoted_lrd.rs"] +mod md_quoted_lrd; +#[path = "md_ref_link_interior_punct.rs"] +mod md_ref_link_interior_punct; +#[path = "md_shortcut_ref_label.rs"] +mod md_shortcut_ref_label; +#[path = "md_spanning_lrd_title.rs"] +mod md_spanning_lrd_title; +#[path = "md_yaml_front_matter.rs"] +mod md_yaml_front_matter; +#[path = "org_angle_links.rs"] +mod org_angle_links; +#[path = "org_brace_subscript.rs"] +mod org_brace_subscript; +#[path = "org_caption.rs"] +mod org_caption; +#[path = "org_clock_line.rs"] +mod org_clock_line; +#[path = "org_diary_sexp.rs"] +mod org_diary_sexp; +#[path = "org_dollar_space.rs"] +mod org_dollar_space; +#[path = "org_drawer_word_names.rs"] +mod org_drawer_word_names; +#[path = "org_dynamic_block.rs"] +mod org_dynamic_block; +#[path = "org_empty_list_item.rs"] +mod org_empty_list_item; +#[path = "org_export_snippet.rs"] +mod org_export_snippet; +#[path = "org_export_snippet_at.rs"] +mod org_export_snippet_at; +#[path = "org_file_token_punct.rs"] +mod org_file_token_punct; +#[path = "org_footnote_definitions.rs"] +mod org_footnote_definitions; +#[path = "org_hash_comment.rs"] +mod org_hash_comment; +#[path = "org_incomplete_begin.rs"] +mod org_incomplete_begin; +#[path = "org_inline_footnotes.rs"] +mod org_inline_footnotes; +#[path = "org_inline_src.rs"] +mod org_inline_src; +#[path = "org_item_tag.rs"] +mod org_item_tag; +#[path = "org_keyword_re.rs"] +mod org_keyword_re; +#[path = "org_latex_env_eol_closer.rs"] +mod org_latex_env_eol_closer; +#[path = "org_latex_fragment.rs"] +mod org_latex_fragment; +#[path = "org_line_breaks.rs"] +mod org_line_breaks; +#[path = "org_macros.rs"] +mod org_macros; +#[path = "org_planning_case.rs"] +mod org_planning_case; +#[path = "org_radio_targets.rs"] +mod org_radio_targets; +#[path = "org_special_block.rs"] +mod org_special_block; +#[path = "org_table_rule.rs"] +mod org_table_rule; +#[path = "org_timestamps.rs"] +mod org_timestamps; +#[path = "org_unicode_src_subscript.rs"] +mod org_unicode_src_subscript; +#[path = "org_unmatched_display_math.rs"] +mod org_unmatched_display_math; +#[path = "org_unmatched_drawer.rs"] +mod org_unmatched_drawer; +#[path = "org_unmatched_dynamic_block.rs"] +mod org_unmatched_dynamic_block; +#[path = "org_unmatched_opaque.rs"] +mod org_unmatched_opaque; +#[path = "org_verse.rs"] +mod org_verse; +#[path = "org_wrap_created_fn.rs"] +mod org_wrap_created_fn; +#[path = "org_wrap_created_link.rs"] +mod org_wrap_created_link; +#[path = "org_wrap_file_token.rs"] +mod org_wrap_file_token; +#[path = "pandoc_ast_backend.rs"] +mod pandoc_ast_backend; +#[path = "rst_abbrev_list_continuation.rs"] +mod rst_abbrev_list_continuation; +#[path = "rst_anonymous_targets.rs"] +mod rst_anonymous_targets; +#[path = "rst_bibliographic_abstract.rs"] +mod rst_bibliographic_abstract; +#[path = "rst_comment_blank.rs"] +mod rst_comment_blank; +#[path = "rst_compact_listlike_paragraph.rs"] +mod rst_compact_listlike_paragraph; +#[path = "rst_compact_note_directive.rs"] +mod rst_compact_note_directive; +#[path = "rst_container_directives.rs"] +mod rst_container_directives; +#[path = "rst_csv_table_title.rs"] +mod rst_csv_table_title; +#[path = "rst_definition_after_directive.rs"] +mod rst_definition_after_directive; +#[path = "rst_directive_title.rs"] +mod rst_directive_title; +#[path = "rst_doctest_blocks.rs"] +mod rst_doctest_blocks; +#[path = "rst_empty_doctest.rs"] +mod rst_empty_doctest; +#[path = "rst_empty_explicit_comment.rs"] +mod rst_empty_explicit_comment; +#[path = "rst_empty_field_list.rs"] +mod rst_empty_field_list; +#[path = "rst_empty_list_item.rs"] +mod rst_empty_list_item; +#[path = "rst_enumerators.rs"] +mod rst_enumerators; +#[path = "rst_footnotes_citations.rs"] +mod rst_footnotes_citations; +#[path = "rst_header_footer.rs"] +mod rst_header_footer; +#[path = "rst_interior_colon_field.rs"] +mod rst_interior_colon_field; +#[path = "rst_jinja_statements.rs"] +mod rst_jinja_statements; +#[path = "rst_line_block_directive.rs"] +mod rst_line_block_directive; +#[path = "rst_line_blocks.rs"] +mod rst_line_blocks; +#[path = "rst_literal_list_continuation.rs"] +mod rst_literal_list_continuation; +#[path = "rst_meta.rs"] +mod rst_meta; +#[path = "rst_nested_list_continuation.rs"] +mod rst_nested_list_continuation; +#[path = "rst_option_lists.rs"] +mod rst_option_lists; +#[path = "rst_overindented_list_continuation.rs"] +mod rst_overindented_list_continuation; +#[path = "rst_paren_list_continuation.rs"] +mod rst_paren_list_continuation; +#[path = "rst_parsed_literal.rs"] +mod rst_parsed_literal; +#[path = "rst_quoted_list_continuation.rs"] +mod rst_quoted_list_continuation; +#[path = "rst_quoted_literals.rs"] +mod rst_quoted_literals; +#[path = "rst_role_list_continuation.rs"] +mod rst_role_list_continuation; +#[path = "rst_same_line_note.rs"] +mod rst_same_line_note; +#[path = "rst_section_adornment.rs"] +mod rst_section_adornment; +#[path = "rst_simple_table_interior_blank.rs"] +mod rst_simple_table_interior_blank; +#[path = "rst_simple_table_top.rs"] +mod rst_simple_table_top; +#[path = "rst_substitution_refs.rs"] +mod rst_substitution_refs; +#[path = "rst_substitution_replace.rs"] +mod rst_substitution_replace; +#[path = "rst_two_space_bullet_marker.rs"] +mod rst_two_space_bullet_marker; +#[path = "safety_props.rs"] +mod safety_props; +#[path = "safety_splice.rs"] +mod safety_splice; +#[path = "same_line_lowercase_proper_noun.rs"] +mod same_line_lowercase_proper_noun; +#[path = "sentence_delim_props.rs"] +mod sentence_delim_props; +#[path = "snapper_19jf.rs"] +mod snapper_19jf; From eeb13b4e61c10f0b2dc91d2a7b7c74f838824302 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 15:27:37 +0200 Subject: [PATCH 7/9] tst: a nested test runs at the top level, and the clippy lints in the consolidated tests are cleared --- src/parser/pandoc/ast.rs | 2 +- src/sentence/unicode.rs | 14 +-- tests/latex_verbatim_envs.rs | 199 ++++++++++++++++---------------- tests/md_ol_opener_list_item.rs | 8 +- tests/md_ol_ten_digit.rs | 6 +- tests/md_yaml_front_matter.rs | 6 +- 6 files changed, 116 insertions(+), 119 deletions(-) diff --git a/src/parser/pandoc/ast.rs b/src/parser/pandoc/ast.rs index 0fb4103e..aef31f25 100644 --- a/src/parser/pandoc/ast.rs +++ b/src/parser/pandoc/ast.rs @@ -509,7 +509,7 @@ mod tests { .filter(|r| matches!(r, Region::Prose(_))) .collect(); assert!( - prose.len() >= 1, + !prose.is_empty(), "expected ≥1 prose region from Para nodes, got {regions:?}" ); assert!( diff --git a/src/sentence/unicode.rs b/src/sentence/unicode.rs index 37278e2e..50b231ca 100644 --- a/src/sentence/unicode.rs +++ b/src/sentence/unicode.rs @@ -8125,24 +8125,20 @@ mod tests { let spans = atomic_inline_spans(text); let tokens: Vec<&str> = spans.iter().map(|&(s, e)| &text[s..e]).collect(); assert!( - tokens - .iter() - .any(|t| *t == "[the example site](https://ex.com)"), + tokens.contains(&"[the example site](https://ex.com)"), "markdown link: {tokens:?}" ); assert!( - tokens.iter().any(|t| *t == "`some long code`"), + tokens.contains(&"`some long code`"), "inline code: {tokens:?}" ); - assert!(tokens.iter().any(|t| *t == "$E = m$"), "math: {tokens:?}"); + assert!(tokens.contains(&"$E = m$"), "math: {tokens:?}"); assert!( - tokens - .iter() - .any(|t| *t == "[[https://example.com][the example site]]"), + tokens.contains(&"[[https://example.com][the example site]]"), "org link: {tokens:?}" ); assert!( - tokens.iter().any(|t| *t == ""), + tokens.contains(&""), "autolink: {tokens:?}" ); } diff --git a/tests/latex_verbatim_envs.rs b/tests/latex_verbatim_envs.rs index 5270cffd..b4cdc721 100644 --- a/tests/latex_verbatim_envs.rs +++ b/tests/latex_verbatim_envs.rs @@ -3481,107 +3481,108 @@ fn piton_env_and_pipe_cmd_fixture_is_code_and_does_not_reflow() { verb_out.contains("Use \\verb|a.b! c| here.\nNext sentence."), "prose after \\verb must still split, got:\n{verb_out}" ); - /// Ticket fixture (GitHub #398): tools/verbatim.sty `\verbatiminput{file}` - /// stays one atomic command. Following flush `After.` does not join - /// the command line. `After.` / `Next.` still split. lstinputlisting / - /// inputminted unchanged. - #[test] - fn verbatiminput_fixture_does_not_join_following_prose() { - let input = concat!( - "Before. Next.\n", - "\\verbatiminput{foo.py}\n", - "After. Next.\n", - ); - let regions = LatexParser::default().parse(input); - assert!( - regions.iter().any(|r| matches!( - r, - Region::Structure(s) if s.contains(r"\verbatiminput{foo.py}") - )), - "verbatiminput must stay one Structure command, got: {regions:?}" - ); - assert!( - !regions.iter().any(|r| matches!( - r, - Region::Prose(p) if p.contains(r"\verbatiminput{foo.py}") - )), - "verbatiminput must not leak into Prose, got: {regions:?}" - ); - assert!( - regions.iter().any(|r| matches!( - r, - Region::Prose(p) if p.contains("After.") && p.contains("Next.") - )), - "After. / Next. must stay Prose, got: {regions:?}" - ); - let out = format_text(input, &latex_cfg()).unwrap(); - assert!( - out.contains("\\verbatiminput{foo.py}\n"), - "verbatiminput must stay one atomic command, got:\n{out}" - ); - assert!( - !out.contains("\\verbatiminput{foo.py} After."), - "following flush prose must not join the command line, got:\n{out}" - ); - assert!( - out.contains("Before.\nNext."), - "prose before verbatiminput must still split, got:\n{out}" - ); - assert!( - out.contains("After.\nNext."), - "prose after verbatiminput must still split, got:\n{out}" - ); - assert_eq!(format_text(&out, &latex_cfg()).unwrap(), out); +} - let star = concat!( - "Before. Next.\n", - "\\verbatiminput*{foo.py}\n", - "After. Next.\n", - ); - let star_out = format_text(star, &latex_cfg()).unwrap(); - assert!( - star_out.contains("\\verbatiminput*{foo.py}\n"), - "verbatiminput* must stay one atomic command, got:\n{star_out}" - ); - assert!( - !star_out.contains("\\verbatiminput*{foo.py} After."), - "starred verbatiminput must not join following prose, got:\n{star_out}" - ); - assert!( - star_out.contains("After.\nNext."), - "prose after starred verbatiminput must still split, got:\n{star_out}" - ); +/// Ticket fixture (GitHub #398): tools/verbatim.sty `\verbatiminput{file}` +/// stays one atomic command. Following flush `After.` does not join +/// the command line. `After.` / `Next.` still split. lstinputlisting / +/// inputminted unchanged. +#[test] +fn verbatiminput_fixture_does_not_join_following_prose() { + let input = concat!( + "Before. Next.\n", + "\\verbatiminput{foo.py}\n", + "After. Next.\n", + ); + let regions = LatexParser::default().parse(input); + assert!( + regions.iter().any(|r| matches!( + r, + Region::Structure(s) if s.contains(r"\verbatiminput{foo.py}") + )), + "verbatiminput must stay one Structure command, got: {regions:?}" + ); + assert!( + !regions.iter().any(|r| matches!( + r, + Region::Prose(p) if p.contains(r"\verbatiminput{foo.py}") + )), + "verbatiminput must not leak into Prose, got: {regions:?}" + ); + assert!( + regions.iter().any(|r| matches!( + r, + Region::Prose(p) if p.contains("After.") && p.contains("Next.") + )), + "After. / Next. must stay Prose, got: {regions:?}" + ); + let out = format_text(input, &latex_cfg()).unwrap(); + assert!( + out.contains("\\verbatiminput{foo.py}\n"), + "verbatiminput must stay one atomic command, got:\n{out}" + ); + assert!( + !out.contains("\\verbatiminput{foo.py} After."), + "following flush prose must not join the command line, got:\n{out}" + ); + assert!( + out.contains("Before.\nNext."), + "prose before verbatiminput must still split, got:\n{out}" + ); + assert!( + out.contains("After.\nNext."), + "prose after verbatiminput must still split, got:\n{out}" + ); + assert_eq!(format_text(&out, &latex_cfg()).unwrap(), out); - let lst = concat!( - "Before. Next.\n", - "\\lstinputlisting{foo.py}\n", - "After. Next.\n", - ); - let lst_out = format_text(lst, &latex_cfg()).unwrap(); - assert!( - lst_out.contains("\\lstinputlisting{foo.py}\n"), - "lstinputlisting must stay unchanged, got:\n{lst_out}" - ); - assert!( - !lst_out.contains("\\lstinputlisting{foo.py} After."), - "lstinputlisting must not join following prose, got:\n{lst_out}" - ); + let star = concat!( + "Before. Next.\n", + "\\verbatiminput*{foo.py}\n", + "After. Next.\n", + ); + let star_out = format_text(star, &latex_cfg()).unwrap(); + assert!( + star_out.contains("\\verbatiminput*{foo.py}\n"), + "verbatiminput* must stay one atomic command, got:\n{star_out}" + ); + assert!( + !star_out.contains("\\verbatiminput*{foo.py} After."), + "starred verbatiminput must not join following prose, got:\n{star_out}" + ); + assert!( + star_out.contains("After.\nNext."), + "prose after starred verbatiminput must still split, got:\n{star_out}" + ); - let minted = concat!( - "Before. Next.\n", - "\\inputminted{python}{foo.py}\n", - "After. Next.\n", - ); - let minted_out = format_text(minted, &latex_cfg()).unwrap(); - assert!( - minted_out.contains("\\inputminted{python}{foo.py}\n"), - "inputminted must stay unchanged, got:\n{minted_out}" - ); - assert!( - !minted_out.contains("\\inputminted{python}{foo.py} After."), - "inputminted must not join following prose, got:\n{minted_out}" - ); - } + let lst = concat!( + "Before. Next.\n", + "\\lstinputlisting{foo.py}\n", + "After. Next.\n", + ); + let lst_out = format_text(lst, &latex_cfg()).unwrap(); + assert!( + lst_out.contains("\\lstinputlisting{foo.py}\n"), + "lstinputlisting must stay unchanged, got:\n{lst_out}" + ); + assert!( + !lst_out.contains("\\lstinputlisting{foo.py} After."), + "lstinputlisting must not join following prose, got:\n{lst_out}" + ); + + let minted = concat!( + "Before. Next.\n", + "\\inputminted{python}{foo.py}\n", + "After. Next.\n", + ); + let minted_out = format_text(minted, &latex_cfg()).unwrap(); + assert!( + minted_out.contains("\\inputminted{python}{foo.py}\n"), + "inputminted must stay unchanged, got:\n{minted_out}" + ); + assert!( + !minted_out.contains("\\inputminted{python}{foo.py} After."), + "inputminted must not join following prose, got:\n{minted_out}" + ); } /// Ticket fixture (GitHub #400): tcolorbox `\\tcbinputlisting`. @@ -5276,7 +5277,7 @@ fn verb_span_leftover_cmds_fixture_does_not_join_following_prose() { "mid-sentence verb span must stay protected and still split, got:\n{mid_out}" ); - let after_other = concat!("\\py{print(1)} \\lstinline|print(1)|\n",); + let after_other = "\\py{print(1)} \\lstinline|print(1)|\n"; let after_other_regions = LatexParser::default().parse(after_other); assert!( after_other_regions.iter().any(|r| matches!( diff --git a/tests/md_ol_opener_list_item.rs b/tests/md_ol_opener_list_item.rs index 49f0e973..d133d848 100644 --- a/tests/md_ol_opener_list_item.rs +++ b/tests/md_ol_opener_list_item.rs @@ -3,10 +3,10 @@ use snapper_fmt::parser::markdown::MarkdownParser; use snapper_fmt::parser::{FormatParser, Region}; use snapper_fmt::{FormatConfig, format_text}; -/// snapper-9dc1: Markdown `* 0. A.` is one list item. pulldown treats -/// `0. A.` as a nested ordered list. A sembr hang after `0.` is a -/// continuation paragraph, so the HTML oracle vetoes. Keep the opener -/// with the next sentence; do not invent a nested `0. ` Structure. +// snapper-9dc1: Markdown `* 0. A.` is one list item. pulldown treats +// `0. A.` as a nested ordered list. A sembr hang after `0.` is a +// continuation paragraph, so the HTML oracle vetoes. Keep the opener +// with the next sentence; do not invent a nested `0. ` Structure. fn md_cfg() -> FormatConfig { FormatConfig { diff --git a/tests/md_ol_ten_digit.rs b/tests/md_ol_ten_digit.rs index af3561da..c3df4348 100644 --- a/tests/md_ol_ten_digit.rs +++ b/tests/md_ol_ten_digit.rs @@ -3,9 +3,9 @@ use snapper_fmt::parser::markdown::MarkdownParser; use snapper_fmt::parser::{FormatParser, Region}; use snapper_fmt::{FormatConfig, format_text}; -/// snapper-sp7k / GitHub #172: CommonMark 0.31.2 sec 5.2 and pulldown -/// `scan_list_marker_with_indent` cap ordered markers at 9 digits. -/// `1234567890.` is prose, not a list. Oracle stays one `

`. +// snapper-sp7k / GitHub #172: CommonMark 0.31.2 sec 5.2 and pulldown +// `scan_list_marker_with_indent` cap ordered markers at 9 digits. +// `1234567890.` is prose, not a list. Oracle stays one `

`. fn md_cfg() -> FormatConfig { FormatConfig { diff --git a/tests/md_yaml_front_matter.rs b/tests/md_yaml_front_matter.rs index 551b39e5..008e2079 100644 --- a/tests/md_yaml_front_matter.rs +++ b/tests/md_yaml_front_matter.rs @@ -3,9 +3,9 @@ use snapper_fmt::parser::markdown::MarkdownParser; use snapper_fmt::parser::{FormatParser, Region}; use snapper_fmt::{FormatConfig, format_text}; -/// snapper-4why: pulldown `scan_metadata_block` / `scan_closing_metadata_block`. -/// `---` opens only when the next line is neither blank nor the closer. -/// YAML closer is `---` or `...`. Blank after `---` is a thematic break. +// snapper-4why: pulldown `scan_metadata_block` / `scan_closing_metadata_block`. +// `---` opens only when the next line is neither blank nor the closer. +// YAML closer is `---` or `...`. Blank after `---` is a thematic break. fn md_cfg() -> FormatConfig { FormatConfig { From 9d043bbbe025decba3efb1e7f9164bcb5a43322a Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 19:23:51 +0200 Subject: [PATCH 8/9] tst: the fancyvrb VerbatimInput test is named for what it checks --- tests/latex_verbatim_envs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/latex_verbatim_envs.rs b/tests/latex_verbatim_envs.rs index b4cdc721..373f94fe 100644 --- a/tests/latex_verbatim_envs.rs +++ b/tests/latex_verbatim_envs.rs @@ -3095,7 +3095,7 @@ fn inputminted_fixture_does_not_join_following_prose() { /// optional `[...]` stay atomic. inputminted / lstinputlisting /// unchanged. #[test] -fn verbatiminput_fixture_does_not_join_following_prose() { +fn verbatim_input_fancyvrb_fixture_does_not_join_following_prose() { let input = concat!( "Before. Next.\n", "\\VerbatimInput{foo.py}\n", From 918162ed382a1e2b96ea3a64664e0bd7d0300e81 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 3 Oct 2026 20:07:49 +0200 Subject: [PATCH 9/9] doc: cli reference reflowed where a sentence opens with a number --- docs/orgmode/reference/cli.org | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/orgmode/reference/cli.org b/docs/orgmode/reference/cli.org index 20394535..56167744 100644 --- a/docs/orgmode/reference/cli.org +++ b/docs/orgmode/reference/cli.org @@ -290,7 +290,8 @@ Uses =git show REF:path= to retrieve the old version. :END: Watch files and auto-reformat on save. -Accepts file paths or glob patterns. 200ms debounce to avoid reformatting during rapid saves. +Accepts file paths or glob patterns. +200ms debounce to avoid reformatting during rapid saves. Press Ctrl+C to stop. *** =snapper lsp=