From f67dd7761e458a4a7c358bb7a2a5f0f86da74474 Mon Sep 17 00:00:00 2001 From: Martin Ball Date: Sun, 1 Nov 2026 21:37:07 +0000 Subject: [PATCH] =?UTF-8?q?CP4.2:=20review=20=E2=80=94=20"Ignore=20words?= =?UTF-8?q?=20in=20UPPERCASE"=20skips=20only=20words=20with=20a=20cased=20?= =?UTF-8?q?letter,=20so=20a=20misspelt=20Hebrew=20or=20Arabic=20word=20is?= =?UTF-8?q?=20marked?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2 --- crates/proof/src/lib.rs | 26 +++++++++++++++++++++++++- crates/proof/src/spelling.rs | 3 ++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/proof/src/lib.rs b/crates/proof/src/lib.rs index 4f0eb8f54..4182348e6 100644 --- a/crates/proof/src/lib.rs +++ b/crates/proof/src/lib.rs @@ -659,7 +659,8 @@ pub(crate) fn accepted_by_options(word: &str, opts: &Options) -> bool { w.chars().count() <= 1 || !w.chars().any(char::is_alphabetic) || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) - || (opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase())) + // Only a word with a cased letter is in capitals: a Hebrew or Thai word has none. + || (opts.ignore_uppercase && w.chars().any(char::is_uppercase) && w.chars().all(|c| !c.is_lowercase())) // The whole word: the languages split a hyphenated word into its parts, and an added // `Unigranrio-RJ` is none of them (#2082). || in_user_dictionary(&w.to_lowercase()) @@ -1650,6 +1651,29 @@ mod tests { let _ = std::fs::remove_dir_all(&root); } + /// Second review of #485: "Ignore words in UPPERCASE" never skips a word with no cased + /// letter, so a misspelt Hebrew or Arabic word is still marked. + #[test] + fn caseless_scripts_are_not_read_as_uppercase() { + let _guard = DICTIONARY_TESTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner); + set_dictionaries_folder(Some(shared_test_folder())); + let root = std::env::temp_dir().join(format!("wc-proof-caseless-packs-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&root); + write_pack(&root, "he", &[("spelling.aff", b"SET UTF-8\n"), ("spelling.dic", "1\nשלום\n".as_bytes())]); + write_pack(&root, "ar", &[("spelling.aff", b"SET UTF-8\n"), ("spelling.dic", "1\nسلام\n".as_bytes())]); + packs::set_packs_dir(Some(root.clone())); + let (he, ar) = (ProofLang::from_tag(Some("he-IL")), ProofLang::from_tag(Some("ar-EG"))); + assert!(Options::default().ignore_uppercase); + assert!(is_correct_in("שלום", he) && !is_correct_in("שלוםם", he)); + assert!(is_correct_in("سلام", ar) && !is_correct_in("سلامم", ar)); + let text = "שלום שלוםם"; + let issues = check_text(text, &[(0..text.len(), he)]); + assert_eq!(issues.iter().map(|i| &text[i.start..i.end]).collect::>(), ["שלוםם"], "{issues:?}"); + packs::set_packs_dir(None); + set_dictionaries_folder(Some(shared_test_folder())); + let _ = std::fs::remove_dir_all(&root); + } + /// Review of #485: German and Bulgarian, which have hyphenation of their own but no /// dictionary slot, are spelled from their Language pack (or a Hunspell dictionary) too. #[test] diff --git a/crates/proof/src/spelling.rs b/crates/proof/src/spelling.rs index f4d65fa47..aaf25956a 100644 --- a/crates/proof/src/spelling.rs +++ b/crates/proof/src/spelling.rs @@ -30,7 +30,8 @@ pub(crate) fn word_to_check_in<'a>(d: &AffixDict, word: &'a str, opts: &Options) if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return None; } - if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + // Only a word with a cased letter can be in capitals: Hebrew, Arabic, Thai… words have none. + if opts.ignore_uppercase && w.chars().any(char::is_uppercase) && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return None; } Some(w)