UNPKG

@dcoffey/espells

Version:

Pure JS/TS spellchecker, using Hunspell dictionaries. Based on Spylls.

113 lines 4.33 kB
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { CapType, CompoundPos } from "../constants.js"; import { includes, lowercase } from "../util.js"; import { decompose } from "./decompose.js"; import { LKFlags } from "./lk-flags.js"; /** * Yields the allowed {@link AffixForm}s for this word, as in all ways the * word can be split into stems and affixes, with all stems and affixes * being mutually compatible. */ export function* affixForms(word, allowNoSuggest = true, withForbidden = false, flags = new LKFlags()) { const aff = word.aff; const dic = word.dic; for (const form of decompose(word, flags)) { let found = false; const homonyms = dic.homonyms(form.stem); if (homonyms.size) { if (!withForbidden && hasForbidden(word, form, homonyms)) return; found = yield* candidates(word, form, allowNoSuggest, homonyms); } if (word.pos === CompoundPos.BEGIN && aff.FORCEUCASE && word.type === CapType.INIT) { const homonyms = dic.homonyms(lowercase(form.stem), true); found = yield* candidates(word, form, allowNoSuggest, homonyms); } if (found || word.pos !== undefined || word.type !== CapType.ALL) { continue; } if (aff.casing.guess(word.word) === CapType.NO) { yield* candidates(word, form, allowNoSuggest, dic.homonyms(form.stem, true)); } } } function hasForbidden(lkword, form, words) { if (!lkword.aff.FORBIDDENWORD) return false; if (lkword.pos === undefined && !form.hasAffixes) return false; for (const word of words) { if (word.has(lkword.aff.FORBIDDENWORD)) return true; } return false; } /** * Takes an {@link AffixForm} and yields all of the allowed forms of the * entire word form, taking into account {@link Aff} directives and other edge cases. */ function* candidates(word, form, allowNoSuggest = true, homonyms) { let found = false; for (const homonym of homonyms) { const candidate = form.replace({ inDictionary: homonym }); if (validForm(candidate, word, allowNoSuggest)) { found = true; yield candidate; } } return found; } /** * Determines if this form is valid for the {@link LKWord} specified. * * @param word - The word to validate against. * @param allowNoSuggest - If false, words which are in the dictionary, but * are flagged with the `NOSUGGEST` flag (if provided), will not be * considered correct. Defaults to true. */ export function validForm(form, word, allowNoSuggest = true) { if (!form.inDictionary) return false; const aff = word.aff; const rootFlags = form.inDictionary.flags ?? new Set(); const allFlags = form.flags; if (!allowNoSuggest && includes(aff.NOSUGGEST, rootFlags)) return false; if (word.type !== form.inDictionary.capType && includes(aff.KEEPCASE, rootFlags) && !aff.isSharps(form.inDictionary.stem)) { return false; } if (aff.NEEDAFFIX) { if (form.has(aff.NEEDAFFIX)) return false; if (!form.hasAffixes && rootFlags.has(aff.NEEDAFFIX)) return false; } if (form.prefix && !allFlags.has(form.prefix.flag)) return false; if (form.suffix && !allFlags.has(form.suffix.flag)) return false; if (aff.CIRCUMFIX) { const suffixHas = Boolean(form.suffix?.has(aff.CIRCUMFIX)); const prefixHas = Boolean(form.prefix?.has(aff.CIRCUMFIX)); if (suffixHas !== prefixHas) return false; } if (word.pos === undefined) { if (!includes(aff.ONLYINCOMPOUND, allFlags)) return true; return false; } if (includes(aff.COMPOUNDFLAG, allFlags)) return true; // prettier-ignore switch (word.pos) { case CompoundPos.BEGIN: return includes(aff.COMPOUNDBEGIN, allFlags); case CompoundPos.MIDDLE: return includes(aff.COMPOUNDMIDDLE, allFlags); case CompoundPos.END: return includes(aff.COMPOUNDEND, allFlags); } } //# sourceMappingURL=affixes.js.map