UNPKG

@dcoffey/espells

Version:

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

103 lines 3.59 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/. */ const END_WORD_SYMBOL = Symbol("$"); /** Classic trie data structure. */ export class Trie { constructor() { /** Entrypoint for the trie. */ Object.defineProperty(this, "head", { enumerable: true, configurable: true, writable: true, value: new Map() }); } /** * Adds a word to the trie, which will point to a value. * * @param word - The word to add. * @param addValue - A callback function for how to add the word to the * trie. It is provided the current value of the word added, if any. * This allows for merging overlapping values. */ add(word, addValue) { const current = this.traverse(word, true); current.set(END_WORD_SYMBOL, !addValue ? word : addValue(current.get(END_WORD_SYMBOL))); } /** Determines if a word is in the trie. */ has(word) { const ret = this.traverse(word); return Boolean(ret.value) && !ret.isSubstring; } /** Returns the segments found when traversing the trie for the given word. */ segments(word) { const ret = this.traverse(word); return !ret.segments.length ? null : ret.segments; } /** Returns the longest/last segment for a word. */ lastSegment(word) { const segments = this.segments(word); return !segments ? null : segments[segments.length - 1]; } /** Removes a word from the trie. */ remove(word) { this.tryDelete(word, -1, this.head); } traverse(word, add = false) { let current = this.head; const segments = []; for (let i = 0; i < word.length; i++) { if (current.has(END_WORD_SYMBOL)) { segments.push(current.get(END_WORD_SYMBOL)); } if (!current.get(word[i])) { if (add) { current.set(word[i], new Map()); } else { return { value: current.get(END_WORD_SYMBOL), word: word.substring(0, i), segments, isSubstring: true }; } } // keep traversing current = current.get(word[i]); } // fully stripped word if (current.has(END_WORD_SYMBOL)) { segments.push(current.get(END_WORD_SYMBOL)); } if (add) { return current; } // found the word return { value: current.get(END_WORD_SYMBOL), word: word, segments, isSubstring: false }; } tryDelete(word = "", index = 0, node = null) { if (index >= word.length) { throw new Error("Bad index to check for deletion."); } if (node === null) { throw new Error(`Bad Node at ${index} for ${word}`); } const currentNode = node; if (index === word.length - 1) { return currentNode.delete(END_WORD_SYMBOL) && node.size === 0; } const newIndex = word[index + 1]; if (this.tryDelete(word, index + 1, node.get(newIndex))) { return currentNode.delete(END_WORD_SYMBOL) && node.size === 0; } return false; } } //# sourceMappingURL=trie.js.map