From 25048c8b9c4ca80c302125e09ae327b6d2edccdb Mon Sep 17 00:00:00 2001 From: ripdog Date: Mon, 12 Oct 2026 22:29:57 +1300 Subject: [PATCH] M7: ignore ellipses in sentence capitalization proofing (#742) Co-authored-by: Brandon Thomas Co-authored-by: Claude Opus 5.5 --- crates/proof/src/lib.rs | 30 ++++++++++++++++++++++++++++-- docs/typing-parity.md | 3 ++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/proof/src/lib.rs b/crates/proof/src/lib.rs index 925ab7e..71a0e4c 100644 --- a/crates/proof/src/lib.rs +++ b/crates/proof/src/lib.rs @@ -539,8 +539,12 @@ pub fn check_grammar(text: &str) -> Vec { }); } let tail = text.get(*b..).unwrap_or(""); - let next_non_space = tail.trim_start_matches(['"', '”', ')', '\'']).chars().next(); - after_end = matches!(next_non_space, Some('.' | '!' | '?')) && tail.trim_start_matches(['.', '!', '?', '"', '”', ')']).starts_with(' '); + let punctuation = tail.trim_start_matches(['"', '”', ')', '\'']); + // Two or more dots are an ellipsis, not a full stop ending a sentence. + // A Unicode ellipsis (U+2026) does not match sentence-ending punctuation either. + after_end = matches!(punctuation.chars().next(), Some('.' | '!' | '?')) + && !punctuation.starts_with("..") + && punctuation.trim_start_matches(['.', '!', '?', '"', '”', ')']).starts_with(' '); } v.sort_by_key(|i| i.start); v @@ -655,6 +659,28 @@ mod tests { assert!(check_grammar("A university is an hour away.").is_empty(), "{:?}", check_grammar("A university is an hour away.")); } + #[test] + fn ellipses_do_not_start_sentences() { + for text in [ + "I saw... a figure right outside.", + "I saw… a figure right outside.", + "I saw.... a figure right outside.", + "I saw... \"a figure\" right outside.", + "I saw… “a figure” right outside.", + ] { + let capitals: Vec<_> = + check_grammar(text).into_iter().filter(|issue| issue.message == "Capitalize the first word of a sentence").collect(); + assert!(capitals.is_empty(), "{text}: {capitals:?}"); + } + + for text in ["I saw. a figure.", "I saw! a figure.", "I saw? a figure.", "I saw.\" a figure."] { + let capitals: Vec<_> = + check_grammar(text).into_iter().filter(|issue| issue.message == "Capitalize the first word of a sentence").collect(); + assert_eq!(capitals.len(), 1, "{text}: {capitals:?}"); + assert_eq!(capitals[0].suggestions, ["A"], "{text}"); + } + } + #[test] fn user_dictionary_works() { assert!(!is_correct("zxqwordcrafty")); diff --git a/docs/typing-parity.md b/docs/typing-parity.md index 41f37dc..5d67de2 100644 --- a/docs/typing-parity.md +++ b/docs/typing-parity.md @@ -1,6 +1,6 @@ # Typing parity with Word -> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-11 · **Change:** trivial (complex-script caret and Backspace, #604; Move Paragraph Up/Down, Promote/Demote, Go Back and Alt+X keys, #343; previously trivial: outline keys in Outline view, #493) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 +> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-11 · **Change:** trivial (grammar proofing: ellipses are not sentence boundaries; previously minor: pasted paragraph breaks tracked under Track Changes, kept as text in lists, #417, #422) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 What Word does when you type, observed black-box (same keystrokes typed into Word and WordCraft, results read back paragraph by paragraph) and pinned by tests in @@ -135,6 +135,7 @@ Word. | Date | Change | Summary | |---|---|---| +| 2026-10-11 | trivial | Grammar proofing (not AutoCorrect) no longer suggests capitalising a sentence after `...` or `…` (`proof::tests::ellipses_do_not_start_sentences`) | | 2026-10-11 | minor | AutoFormat tables from `+---+`, arrow bullets, smileys inside words, undo after a border line (#546) | | 2026-10-11 | trivial | Outline view keys: expand/collapse, Show Level, moving and promoting with collapsed content (#493) | | 2026-10-11 | minor | Editing options: typing replaces selection, overtype and the Insert key (#485) |