mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
storytold/wordcraft#5000: review — a KEEPCASE word is suggested in its dictionary case, never re-cased into a flagged form
`Kmm` with `km/K` suggested `Km`, which the checker flags; `Km` itself got no suggestion. A suggestion whose dictionary form is only correct through a KEEPCASE stem now keeps that case (`km`). Other stems are re-cased as before (`MIXCASX` → `MIXCASE`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
This commit is contained in:
1 parent
9dce709a53
commit
130ac8d404
2 files changed
+25
-2
No files matched your search
@@ -941,6 +941,12 @@ impl AffixDict {
|
||||
self.check_except(w, self.no_suggest | self.substandard)
|
||||
}
|
||||
|
||||
/// Whether `w` is a word only through a `KEEPCASE` stem, so it is correct in this letter case
|
||||
/// alone (`km` from `km/K`, which `Km` and `KM` are not).
|
||||
pub(crate) fn case_locked(&self, w: &str) -> bool {
|
||||
self.keepcase != Mask::EMPTY && self.contains(w) && !self.contains_except(&self.normalize(w), self.keepcase)
|
||||
}
|
||||
|
||||
fn check_except(&self, w: &str, deny: Mask) -> bool {
|
||||
let w = self.normalize(w);
|
||||
if self.contains_except(&w, deny) {
|
||||
|
||||
@@ -297,11 +297,13 @@ pub(crate) fn suggest_in(d: &AffixDict, word: &str, max: usize, t: &Tuning) -> V
|
||||
found.sort_by_key(|(text, (cost, seq))| (*cost, diff_at(text), *seq));
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
for (text, _) in found {
|
||||
let t = match case {
|
||||
let recased = match case {
|
||||
Casing::Initial => capitalize(&text),
|
||||
Casing::Upper if n > 1 => text.to_uppercase(),
|
||||
_ => text,
|
||||
_ => text.clone(),
|
||||
};
|
||||
// A KEEPCASE word only exists in its dictionary case: `Km` would be flagged, so offer `km`.
|
||||
let t = if recased != text && text.split(' ').any(|p| d.case_locked(p)) { text } else { recased };
|
||||
if t != word && !out.contains(&t) {
|
||||
out.push(t);
|
||||
}
|
||||
@@ -521,6 +523,21 @@ mod tests {
|
||||
assert_eq!(weighted_distance(&c(""), &c("ab"), &related, 9), 4);
|
||||
}
|
||||
|
||||
/// A KEEPCASE word is offered in its dictionary case when the word's own case would be flagged
|
||||
/// (`Kmm` → `km`, never `Km`), and a capitalised KEEPCASE word gets its dictionary form.
|
||||
#[test]
|
||||
fn keepcase_suggestions_are_not_recased_into_errors() {
|
||||
let d = AffixDict::from_hunspell("KEEPCASE K\n", "2\nkm/K\npies\n", &[]).unwrap();
|
||||
assert!(!d.check("Km") && !d.check("KM"));
|
||||
for (word, want) in [("Kmm", "km"), ("KMM", "km"), ("Km", "km")] {
|
||||
let got = suggest_in(&d, word, 4, &Tuning::DEFAULT);
|
||||
assert!(got.iter().all(|s| s.split(' ').all(|p| d.check(p))), "{word}: {got:?}");
|
||||
assert_eq!(got.first().map(String::as_str), Some(want), "{word}: {got:?}");
|
||||
}
|
||||
assert_eq!(suggest_in(&d, "Pies", 4, &Tuning::DEFAULT), Vec::<String>::new());
|
||||
assert_eq!(suggest_in(&d, "Piesx", 4, &Tuning::DEFAULT).first().map(String::as_str), Some("Pies"));
|
||||
}
|
||||
|
||||
/// A language's tables come after its dictionary's, a TRY alphabet stands in for a missing TRY
|
||||
/// line, and a split rule decides which missing spaces are offered.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user