@dcoffey/espells
Version:
Pure JS/TS spellchecker, using Hunspell dictionaries. Based on Spylls.
163 lines • 5.55 kB
JavaScript
/* 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 { iterate } from "iterare";
import { CapType, CONSTANTS as C } from "../constants.js";
import { Word } from "./word.js";
export { Word };
/** Hunspell dictionary data. */
export class Dic {
/**
* @param reader - The {@link Reader} instance to use when parsing.
* @param aff - The {@link Aff} data to use.
*/
constructor(reader, aff) {
/** The full set of {@link Word} entries in the dictionary. */
Object.defineProperty(this, "words", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
/**
* A mapping of stems to {@link Word} entries. A stem may map to multiple
* words, so the actual mapped value may either be a set or a singular
* {@link Word} instance. Most stems won't map to multiple words, so for
* the sake of saving memory a set is only used when it's actually needed.
*/
Object.defineProperty(this, "index", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
/**
* A mapping of stems that weren't lowercase to begin with to their
* {@link Word} instances.
*/
Object.defineProperty(this, "lowercaseIndex", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
this.aff = aff;
this.addDictionary(reader);
}
/**
* Utility for adding a stem + {@link Word} to a index.
*
* @param index - Index to add the stem to.
* @param stem - The stem entrypoint.
* @param word - The mapped {@link Word}.
*/
addToIndex(index, stem, word) {
const curr = index.get(stem);
if (curr) {
if (curr instanceof Set)
curr.add(word);
else
index.set(stem, new Set([curr, word]));
}
else {
index.set(stem, word);
}
}
/**
* Utility for getting a {@link Word} set from an index.
*
* @param index - The index to retrieve the {@link Word} set from.
* @param stem - The stem to search for.
*/
getFromindex(index, stem) {
if (!index.has(stem))
return new Set();
const words = index.get(stem);
// have to clone to prevent mutating the dictionary
return words instanceof Set ? new Set([...words]) : new Set([words]);
}
/**
* Adds additional dictionary data.
*
* @param reader - The {@link Reader} to parse with.
*/
addDictionary(reader) {
do {
if (reader.done)
break;
if (C.DIC_SKIP_REGEX.test(reader.line))
continue;
this.add(new Word(reader.line, this.aff));
} while (reader.next());
}
/**
* Adds a word to the dictionary. Can accept flags.
*
* @param word - The word to add.
*/
add(word) {
const stem = word.stem;
this.words.add(word);
this.addToIndex(this.index, stem, word);
if (this.aff.casing.guess(stem) !== CapType.NO) {
const lowers = this.aff.casing.lower(stem);
lowers.forEach(lowered => this.addToIndex(this.lowercaseIndex, lowered, word));
}
}
/**
* Removes a stem and any of its associated {@link Word} instances from
* the dictionary.
*
* @param stem - The stem to remove.
*/
remove(stem) {
const words = this.homonyms(stem, true);
if (!words)
return;
for (const word of words) {
this.words.delete(word);
}
this.index.delete(stem);
const lowers = this.aff.casing.lower(stem);
lowers.forEach(lowered => this.lowercaseIndex.delete(lowered));
}
/**
* Retrieves all of the homonyms (all associated {@link Word} instances) for a stem.
*
* @param stem - The stem to retrieve with.
* @param ignorecase - If true, the {@link Dic.lowercaseIndex} will be
* searched through as well.
*/
homonyms(stem, ignorecase = false) {
const words = this.getFromindex(this.index, stem);
if (ignorecase) {
const lowers = this.aff.casing.lower(stem);
lowers.forEach(lowered => iterate(this.getFromindex(this.lowercaseIndex, lowered)).forEach(word => words.add(word)));
}
return words;
}
/**
* Determines if the given stem has a {@link Flag} associated with it or not.
*
* @param stem - The stem to check.
* @param flag - The flag to check. Can actually be undefined - which
* will just cause the function to return false.
* @param all - If true, every homonym of the stem must have the flag given.
*/
hasFlag(stem, flag, all = false) {
if (flag === undefined)
return false;
let flagged = false;
for (const word of this.homonyms(stem)) {
const has = word.has(flag);
if (has)
flagged = true;
if (all && !has)
return false;
if (!all && has)
return true;
}
return all && flagged;
}
}
//# sourceMappingURL=index.js.map