diff --git a/src/sentence/unicode.rs b/src/sentence/unicode.rs index d50a9fc..5a785f2 100644 --- a/src/sentence/unicode.rs +++ b/src/sentence/unicode.rs @@ -1571,6 +1571,8 @@ pub(crate) fn leftover_keyval_cs_name(tail: &str) -> Option<&'static str> { "tcbifexternal", "tcbcontinuedraftmode", "tcbinterruptdraftmode", + "tcbstartdraftmode", + "tcbstopdraftmode", "tcbposterset", "tcbfontsize", "tcbEXTERNALIZE", @@ -1767,6 +1769,8 @@ fn leftover_keyval_kind(name: &str) -> VerbKind { | "sarg" | "tcbcontinuedraftmode" | "tcbinterruptdraftmode" + | "tcbstartdraftmode" + | "tcbstopdraftmode" | "PyLTVerbatimEnv" => VerbKind::Listingcont, "setmintedinline" | "usemintedstyle" @@ -3226,7 +3230,11 @@ fn push_segment_preserving_space(dest: &mut String, piece: &str) { }) // `!!a` is one UAX fragment; inventing `!! a` then splitting // `!! a` on the next pass is a wrap/SemBr cycle. - && !dest.ends_with("!!"); + && !dest.ends_with("!!") + // After protect_inline_tokens, `.`` ` is `.` + leftover backtick + // (`dest="…."`, `piece="`\\0PHn\\0"`). Inventing `. `` ` then + // wrapping back is a SemBr cycle (ubuntu CI seed). + && !(dest.ends_with(['.', '!', '?']) && piece.starts_with('`')); if need_space { dest.push(' '); } @@ -3707,6 +3715,15 @@ mod tests { UnicodeSentenceSplitter::new().split(text) } + #[test] + fn period_then_latex_quotes_does_not_invent_space() { + assert_eq!( + split("^{`}`.`` A0`"), + vec!["^{`}`.`` A0`".to_string()], + "must not invent space between . and ``" + ); + } + #[test] fn single_quote_bang_capital_stays_one_sentence() { assert_eq!( diff --git a/tests/latex_leftover_cmds.rs b/tests/latex_leftover_cmds.rs index 503db4c..5a4cf11 100644 --- a/tests/latex_leftover_cmds.rs +++ b/tests/latex_leftover_cmds.rs @@ -378,6 +378,8 @@ fn leftover_constructors_do_not_join() { leftover_cmd_stays_atomic(r"\tcbposterset{colback=red}"); leftover_cmd_stays_atomic(r"\tcbcontinuedraftmode"); leftover_cmd_stays_atomic(r"\tcbinterruptdraftmode"); + leftover_cmd_stays_atomic(r"\tcbstartdraftmode"); + leftover_cmd_stays_atomic(r"\tcbstopdraftmode"); leftover_cmd_stays_atomic(r"\tcbfontsize{2}"); leftover_cmd_stays_atomic(r"\tcbindex"); leftover_cmd_stays_atomic(r"\tcbEXTERNALIZE"); @@ -410,6 +412,8 @@ fn leftover_constructors_do_not_join() { extras_skip_no_brace("cs"); extras_skip_no_brace("OptionalLocalPitonStyle"); extras_skip_no_brace("tcbposterset"); + extras_skip_no_brace("tcbstartdraftmode"); + extras_skip_no_brace("tcbstopdraftmode"); extras_skip_no_brace("tcbfontsize"); extras_skip_no_brace("tcbset"); extras_skip_no_brace("tcbuselibrary"); diff --git a/tests/sentence_delim_props.rs b/tests/sentence_delim_props.rs index 3a1e5c1..6b472e2 100644 --- a/tests/sentence_delim_props.rs +++ b/tests/sentence_delim_props.rs @@ -76,6 +76,18 @@ fn plaintext_latex_quoted_bang_letter_is_span_safe() { } #[test] +#[test] +fn plaintext_period_then_latex_quotes_is_idempotent() { + // ubuntu CI seed: first pass keeps `.`` `; second pass invents `. `` `. + let input = ".^{`}`.`` A0`"; + let out = format_plain(input); + assert_eq!( + format_plain(&out), + out, + "idempotence\n in={input:?}\n out={out:?}" + ); +} + fn plaintext_quoted_bang_letter_is_span_safe() { let input = "\"!!a\""; let out = format_plain(input);