diff --git a/crates/proof/src/cs/mod.rs b/crates/proof/src/cs/mod.rs index c2ec00c9a..7a910c72d 100644 --- a/crates/proof/src/cs/mod.rs +++ b/crates/proof/src/cs/mod.rs @@ -98,18 +98,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Czech dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -233,4 +245,22 @@ mod tests { let opts = crate::Options { ignore_uppercase: false, ..Default::default() }; assert!(!crate::is_correct_in_with("ZQZAKÁZANÉ", crate::ProofLang::Cs, &opts)); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Czech checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_czech_check() { + test_dict::install(); + let lang = crate::ProofLang::Cs; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŠKULA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŠKULA", lang, &check_caps)); + assert!(crate::is_correct_in_with("ŠKOLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("škula2", lang, &defaults)); + assert!(!crate::is_correct_in_with("škula2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŠKULA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/hr/mod.rs b/crates/proof/src/hr/mod.rs index 23df9e935..1c89b4fac 100644 --- a/crates/proof/src/hr/mod.rs +++ b/crates/proof/src/hr/mod.rs @@ -85,18 +85,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Croatian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -147,4 +159,22 @@ mod tests { fn missing_dictionary_is_silent() { assert!(is_correct_with(&AffixDict::default(), "nepostojećariječ")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Croatian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_croatian_check() { + test_dict::install(); + let lang = crate::ProofLang::Hr; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŠKOLLA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŠKOLLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("ŠKOLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("školla2", lang, &defaults)); + assert!(!crate::is_correct_in_with("školla2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŠKOLLA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/id/mod.rs b/crates/proof/src/id/mod.rs index 5b9be698d..09771e852 100644 --- a/crates/proof/src/id/mod.rs +++ b/crates/proof/src/id/mod.rs @@ -91,18 +91,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Indonesian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -113,7 +125,7 @@ pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { || w.split('-').all(|p| { p.chars().count() <= 1 || CLITICS.iter().any(|c| p.eq_ignore_ascii_case(c)) - || (p.chars().all(|c| !c.is_lowercase()) && p.chars().count() <= 6) + || (opts.ignore_uppercase && p.chars().all(|c| !c.is_lowercase()) && p.chars().count() <= 6) || known(d, p) }); } @@ -224,4 +236,24 @@ mod tests { let opts = crate::Options { ignore_uppercase: false, ..Default::default() }; assert!(!crate::is_correct_in_with("ZQTERLARANG", crate::ProofLang::Id, &opts)); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Indonesian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_indonesian_check() { + test_dict::install(); + let lang = crate::ProofLang::Id; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("RUMHA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("RUMHA", lang, &check_caps)); + assert!(crate::is_correct_in_with("RUMAH", lang, &check_caps)); + assert!(crate::is_correct_in_with("RUMHA-nya", lang, &defaults)); + assert!(!crate::is_correct_in_with("RUMHA-nya", lang, &check_caps), "a part in capitals is checked too"); + assert!(crate::is_correct_in_with("rumha2", lang, &defaults)); + assert!(!crate::is_correct_in_with("rumha2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("RUMHA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/it/mod.rs b/crates/proof/src/it/mod.rs index 772847843..0e11ccaae 100644 --- a/crates/proof/src/it/mod.rs +++ b/crates/proof/src/it/mod.rs @@ -111,18 +111,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Italian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -229,4 +241,22 @@ mod tests { crate::add_word("Zxqitalianaparola"); assert!(is_correct("zxqitalianaparola")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Italian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_italian_check() { + test_dict::install(); + let lang = crate::ProofLang::It; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("LIBRRO", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("LIBRRO", lang, &check_caps)); + assert!(crate::is_correct_in_with("LIBRO", lang, &check_caps)); + assert!(crate::is_correct_in_with("librro2", lang, &defaults)); + assert!(!crate::is_correct_in_with("librro2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("LIBRRO"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/lib.rs b/crates/proof/src/lib.rs index bb0c58c08..8f8d9ae05 100644 --- a/crates/proof/src/lib.rs +++ b/crates/proof/src/lib.rs @@ -774,17 +774,17 @@ pub fn is_correct_in_with(word: &str, lang: ProofLang, opts: &Options) -> bool { let word = &*composed(word); match lang { ProofLang::En | ProofLang::EnGb | ProofLang::EnCa => is_correct_english(word, lang, opts), - ProofLang::Pl => accepted_by_options(word, opts) || pl::is_correct(word), - ProofLang::Hr => accepted_by_options(word, opts) || hr::is_correct(word), - ProofLang::Pt => accepted_by_options(word, opts) || pt::is_correct(word), - ProofLang::It => accepted_by_options(word, opts) || it::is_correct(word), + ProofLang::Pl => accepted_by_options(word, opts) || pl::is_correct_with_options(word, opts), + ProofLang::Hr => accepted_by_options(word, opts) || hr::is_correct_with_options(word, opts), + ProofLang::Pt => accepted_by_options(word, opts) || pt::is_correct_with_options(word, opts), + ProofLang::It => accepted_by_options(word, opts) || it::is_correct_with_options(word, opts), ProofLang::Tr => accepted_by_options(word, opts) || tr::is_correct(word), ProofLang::Ru => accepted_by_options(word, opts) || ru::is_correct(word), - ProofLang::Cs => accepted_by_options(word, opts) || cs::is_correct(word), - ProofLang::Id => accepted_by_options(word, opts) || id::is_correct(word), ProofLang::Es | ProofLang::EsMx | ProofLang::Es419 | ProofLang::EsUy => { accepted_by_options(word, opts) || es::is_correct_with_options(word, lang, opts) } + ProofLang::Cs => accepted_by_options(word, opts) || cs::is_correct_with_options(word, opts), + ProofLang::Id => accepted_by_options(word, opts) || id::is_correct_with_options(word, opts), ProofLang::Bg | ProofLang::De | ProofLang::Other => true, } } diff --git a/crates/proof/src/pl/mod.rs b/crates/proof/src/pl/mod.rs index 5330dd722..76fa455ec 100644 --- a/crates/proof/src/pl/mod.rs +++ b/crates/proof/src/pl/mod.rs @@ -96,18 +96,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Polish dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -209,4 +221,22 @@ mod tests { crate::add_word("Zxqpolskisłowo"); assert!(is_correct("zxqpolskisłowo")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Polish checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_polish_check() { + test_dict::install(); + let lang = crate::ProofLang::Pl; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŻEKA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŻEKA", lang, &check_caps)); + assert!(crate::is_correct_in_with("RZEKA", lang, &check_caps)); + assert!(crate::is_correct_in_with("żeka2", lang, &defaults)); + assert!(!crate::is_correct_in_with("żeka2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŻEKA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/pt/mod.rs b/crates/proof/src/pt/mod.rs index e0f62e52b..3a8b059fc 100644 --- a/crates/proof/src/pt/mod.rs +++ b/crates/proof/src/pt/mod.rs @@ -91,18 +91,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Portuguese dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -217,4 +229,22 @@ mod tests { crate::add_word("Unigranrio-RJ"); assert!(crate::check_spelling_in(text, crate::ProofLang::Pt).is_empty()); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Portuguese checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_portuguese_check() { + test_dict::install(); + let lang = crate::ProofLang::Pt; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("PRAYA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("PRAYA", lang, &check_caps)); + assert!(crate::is_correct_in_with("PRAIA", lang, &check_caps)); + assert!(crate::is_correct_in_with("praya2", lang, &defaults)); + assert!(!crate::is_correct_in_with("praya2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("PRAYA"), "direct checks keep the defaults"); + } }