mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-31 05:56:55 -06:00
Merge pull request #1473
Spell check: don't check words longer than 100 characters (fixes #1175)
This commit is contained in:
@@ -356,7 +356,14 @@ impl Dictionary {
|
||||
}
|
||||
|
||||
/// Is `word` spelt correctly? Capitalised and upper-case forms of a lower-case word pass.
|
||||
/// Words longer than 100 characters are not checked (they pass).
|
||||
pub fn check(&self, word: &str) -> bool {
|
||||
// Compound splitting tries every split point, so checking costs O(n²) in the word's
|
||||
// length: a 100,000-character "word" took seconds per check under a compounding
|
||||
// dictionary, on every edit (#1175).
|
||||
if word.chars().nth(MAX_CHECK_CHARS).is_some() {
|
||||
return true;
|
||||
}
|
||||
if word.contains('\u{2019}') {
|
||||
return self.check(&word.replace('\u{2019}', "'"));
|
||||
}
|
||||
@@ -553,6 +560,9 @@ pub fn find_in(dirs: &[PathBuf], lang: &str) -> Option<(PathBuf, PathBuf)> {
|
||||
/// Longest word `Dictionary::suggest` generates corrections for.
|
||||
const MAX_SUGGEST_CHARS: usize = 64;
|
||||
|
||||
/// Longest word `Dictionary::check` checks, as Hunspell's word length limit.
|
||||
const MAX_CHECK_CHARS: usize = 100;
|
||||
|
||||
type Cache = Mutex<HashMap<String, Option<Arc<Dictionary>>>>;
|
||||
|
||||
fn cache() -> &'static Cache {
|
||||
@@ -843,6 +853,20 @@ SFX D Y 1\nSFX D 0 ed/S .\n";
|
||||
assert!(d.suggest("workingslides", 5).contains(&"working slides".to_string()));
|
||||
}
|
||||
|
||||
/// #1175: compound splitting is quadratic in the word's length, so a word longer than
|
||||
/// `MAX_CHECK_CHARS` is not checked; shorter ones still are.
|
||||
#[test]
|
||||
fn a_word_longer_than_the_check_limit_is_not_checked() {
|
||||
let d = Dictionary::parse(b"COMPOUNDFLAG X\nCOMPOUNDMIN 3\n", b"2\nfoot/X\nball/X\n".as_slice());
|
||||
assert!(d.check("footballfoot"), "a compound is checked");
|
||||
let under: String = "footb".repeat(MAX_CHECK_CHARS / 5);
|
||||
assert_eq!(under.chars().count(), MAX_CHECK_CHARS);
|
||||
assert!(!d.check(&under), "a word at the limit is still checked");
|
||||
// Unchecked, not seconds of compound splitting.
|
||||
assert!(d.check(&"footb".repeat(20_000)));
|
||||
assert!(d.check(&format!("{under}x")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compounds_join_flagged_parts() {
|
||||
let aff = "COMPOUNDFLAG X\nCOMPOUNDMIN 3\nONLYINCOMPOUND O\nSFX S Y 1\nSFX S 0 en .\n";
|
||||
|
||||
Reference in New Issue
Block a user