One region_index/without_index helper in lang.rs serves region(), pack_lang::names and packs::locales_for; tests pin the sr-RS-ijekavsk names and the length cut of sr-Cyrl-RS-ijekavsk.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
Review of the elision fix: the cheap cost went to every apostrophe form the
dictionary accepts, so English possessives (churchs → church's), contractions
(neer → ne'er) and Catalan enclitics (acusit → acusi't) jumped ahead of the
one-letter fix. A missing apostrophe now ranks first only before a dictionary
word of two letters or more (j'adore, l'home, qu'il, dell'anno) and only when
the language elides (new Tuning::elision, off for English). Any other form costs
COST_MISTYPE + 1, after every one-letter fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
Suggestions tried a missing space but never a missing apostrophe, so French
"Jadore" got "Adore, J adore, Jade" and never "J'adore". Round 4 now also
offers a'b after a short first part (at most 7 letters), ranked before every
split. The dictionary's own check decides whether the elision is right, so
French, Italian and Catalan elision (l'acqua, dell'anno) work and a dictionary
without elided forms is unaffected. Suggestions keep the writer's case and the
apostrophe the dictionary's OCONV writes (J’adore). Costs at most one lookup
per split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
The shared suggestion code already edits across the whole lowercased word, so no code change is needed; the test keeps it that way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A step now counts a rule, a condition class, a split's suffix lookup,
16 flags of a set looked through and a group visit, so hostile rules
whose steps were cheap to count but costly to run hit the budget in
time. Retuned: 2^13 steps per candidate, 2^25 per suggest call; the
81 packs' typo sets suggest exactly as at d0c341c8.
Condition character sets are sorted and looked up by binary search.
A stem's sets are worked out once per distinct entry list when its
entries hold 256+ flags (16 kept-apart homonyms of 8,190 flags on
17,576 stems: 55 s load, now 57 ms). A near-word candidate whose
steps ran out counts as a word, so suggest never offers a compound
check() would refuse.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
Every TRY/KEY/typo candidate got a fresh 65,536-rule check, so a hostile
.aff with a 128-letter TRY line took ~20 s per suggest call. Now a
suggest call shares 2^23 affix rules across its simple-word checks, each
capped at 2^11; prefix-rule collection counts against the budget and
mixed-case lookups try at most MAX_HOMONYMS stems. Suggestion lists are
unchanged on the 77 packs and the recall sets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
- simple_word sorts the grouped prefix flags (non-cross then cross flags were unsorted, so
`redo`/`rework` from a no-cross prefix beside cross ones were missed).
- One budget of 65,536 affix rules per simple-word check, shared by every prefix group and
suffix walk; past it the word is taken for no word. Prefixes that each strip their own
text (h2.aff: 1,000 roots) no longer cost a full walk each: 1.7 s → 1.1 ms per two
words, suggest 14.9 s → 65 ms. The 77 fetched spelling packs answer as without a budget
on 6,000 generated words each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
- simple_word groups the prefix rules that leave the same root (circumfix and NEEDAFFIX
alike) and answers them in one stem lookup, one chain walk and one suffix walk with all
their flags (FlagProbe): 1,000 or 16,000 prefixes adding nothing no longer cost one walk
each (h1.aff: 1.6 s → 17 ms per two words; 16k prefixes 22 s → 19 ms).
- Deferred suggestion candidates are not looked up again before their compound check.
- A word in capitals matches a mixed-case stem as a simple word only (`JQUERYKABEL`,
`MSCFOO` wrong, as in the CLI).
- to_bytes writes the flags only the kept-apart homonyms use (NEEDAFFIX beside a compound
flag survives a round trip).
- MAGIC WCAFF2: compact data from before homonyms were kept apart is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
- A prefix with two chained suffixes (hu `meg`+`épít`+`és`+`e`, eo `enjugo`): every rule
cross, the prefix flag from the stem or a continuation class (CLI answers).
- Homonyms with different flags are kept apart (and written apart in the compact form):
a prefix and a suffix, or a compound flag and a suffix, must come from one homonym
(nl `afstopje`, `schuurpoederden` wrong in the CLI). nl now 100% in both comparisons.
- Hungarian syllable rule leaves out a last suffix with no continuation classes;
COMPOUNDROOT counts on the stem only.
- Near-word check: a word listed as an ONLYINCOMPOUND/NEEDAFFIX stem counts only with a
suffix, not a prefix (de `Fahrradverkehr` right).
- A word in capitals may be a mixed-case stem (`KB`/`KBS` from `kB/S`, hu `DNY`, nl `BSC`).
- Mixed-case retry keeps KEEPCASE for the first part; KEEPCASE kept apart from the other
exclusions (NOSUGGEST sharing its flag is no longer cleared); overflow of the homonym
groups counts as dropped.
- Suggestions: candidates are checked as simple words first; the rest get the compound
check cheapest first, those changed where the word stops being parts before others, in
one step budget per call, with piece analyses shared between candidates. The suggestions'
stem scan makes forms in one shared budget (mn).
- affix_check example: `suggest` command; AffixDict::suggest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
- ONLYINCOMPOUND homonyms beside others are kept apart (compound parts only), so their suffixes
make no standalone words (de verfilmungs, sv norges); round-trips as a second stem line.
- CHECKCOMPOUNDTRIPLE also checks a SIMPLIFIEDTRIPLE join (busssen, booob wrong).
- KEEPCASE holds for the first part only; a capitalized stem is a part (FooQux).
- CHECKCOMPOUNDPATTERN `0`: no suffix (a prefix doesn't count): unfoobar wrong.
- COMPOUNDFORBIDFLAG on a prefix: no part anywhere (bardefoo wrong).
- FORCEUCASE: from the stem only; wrong only without any capital (fooQuxbar right).
- Suggestion candidates: 2,048 compound steps each, compounds for the first 128 only.
- A piece's ways are hashed, capped at 256 and counted (with the patterns) against the budget.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
Black-box: every rule pinned to hunspell 1.7.5's answers on synthetic dictionaries and on
the Language packs' own files. Bounded work per word (16 parts, 65,536 steps). Also:
ONLYINCOMPOUND belongs to its homonym like NEEDAFFIX; COMPOUNDRULE reads the Swedish `)k`
and the Mongolian `[..]`, `%?`, `.` as the CLI does; COMPOUNDMORESUFFIXES is refused only
with flag-based compounding (Korean keeps loading).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
Follow-ups from craft-libs#485: up to 256 languages' pack patterns (64 MB files each) were kept
by count only; `hunspell::ordered` checked pack files with `is_file`, which follows links,
while the pack scan refuses them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
Follow-ups from craft-libs#485: `CHECK_FROM` is reset by a drop guard; `remember_missing` drops
the oldest of 256; a word none of whose letters the dictionary's words use (a Cyrillic word in
a Dutch run) is left alone, as Word does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
SCOWL en-AU has 238 words en-GB lacks (billabong, brumby, larrikinism, Australian place
names) and spells program, inquiry, livable, licorice, verandah the Australian way (no
programme, enquiry, liveable, dialog or veranda, as Hunspell en_AU says); -ise/-ize policy and
affix file are the same as en-GB's. craft-i18n already ships language-data/en-au.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2