diff --git a/binding.gyp b/binding.gyp index fea190a..a25b57d 100644 --- a/binding.gyp +++ b/binding.gyp @@ -34,6 +34,16 @@ 'src/spellchecker_hunspell.cc', ], }], + ['OS=="win"', { + 'sources': [ + 'src/spellchecker_win.cc' + ], + }], + ['OS=="linux"', { + 'sources': [ + 'src/spellchecker_linux.cc' + ], + }], ['OS=="mac" and spellchecker_use_hunspell=="false"', { 'sources': [ 'src/spellchecker_mac.mm', diff --git a/lib/spellchecker.js b/lib/spellchecker.js index 4646864..2584274 100644 --- a/lib/spellchecker.js +++ b/lib/spellchecker.js @@ -1,7 +1,30 @@ +var path = require('path'); var bindings = require('../build/Release/spellchecker.node'); -bindings.init(__dirname); + +Spellchecker = bindings.Spellchecker; + +var defaultSpellcheck = null; +var ensureDefaultSpellCheck = function() { + if (defaultSpellcheck) return; + + defaultSpellcheck = new Spellchecker(); + defaultSpellcheck.setDictionary('en_US', path.join(__dirname, '..', 'vendor', 'hunspell_dictionaries')); +}; + +var isMisspelled = function() { + ensureDefaultSpellCheck(); + + return defaultSpellcheck.isMisspelled.apply(defaultSpellcheck, arguments); +}; + +var getCorrectionsForMisspelling = function() { + ensureDefaultSpellCheck(); + + return defaultSpellcheck.getCorrectionsForMisspelling.apply(defaultSpellcheck, arguments); +}; module.exports = { - isMisspelled: bindings.isMisspelled, - getCorrectionsForMisspelling: bindings.getCorrectionsForMisspelling + isMisspelled: isMisspelled, + getCorrectionsForMisspelling: getCorrectionsForMisspelling, + Spellchecker: Spellchecker }; diff --git a/src/main.cc b/src/main.cc index 42c751f..6a9f59a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,49 +1,101 @@ +#include "nan.h" #include "spellchecker.h" -#include "nan.h" +using node::ObjectWrap; +using namespace spellchecker; using namespace v8; namespace { -NAN_METHOD(PlatformInit) { - NanScope(); - spellchecker::Init(*String::Utf8Value(args[0])); - NanReturnUndefined(); -} +class Spellchecker : public ObjectWrap { + SpellcheckerImplementation* impl; -NAN_METHOD(IsMisspelled) { - NanScope(); - if (args.Length() < 1) - return NanThrowError("Bad argument"); + static NAN_METHOD(New) { + NanScope(); + Spellchecker* that = new Spellchecker(); + that->Wrap(args.This()); - std::string word = *String::Utf8Value(args[0]); - NanReturnValue(NanNew(spellchecker::IsMisspelled(word))); -} + NanReturnValue(args.This()); + } + + static NAN_METHOD(SetDictionary) { + NanScope(); + + if (args.Length() < 1) { + return NanThrowError("Bad argument"); + } -NAN_METHOD(GetCorrectionsForMisspelling) { - NanScope(); - if (args.Length() < 1) - return NanThrowError("Bad argument"); + Spellchecker* that = ObjectWrap::Unwrap(args.Holder()); - std::string word = *String::Utf8Value(args[0]); - std::vector corrections = - spellchecker::GetCorrectionsForMisspelling(word); + std::string language = *String::Utf8Value(args[0]); + std::string directory = "."; + if (args.Length() > 1) { + directory = *String::Utf8Value(args[1]); + } - Local result = NanNew(corrections.size()); - for (size_t i = 0; i < corrections.size(); ++i) { - const std::string& word = corrections[i]; - result->Set(i, NanNew(word.data(), word.size())); + that->impl->SetDictionary(language, directory); + NanReturnUndefined(); } - NanReturnValue(result); -} + static NAN_METHOD(IsMisspelled) { + NanScope(); + if (args.Length() < 1) { + return NanThrowError("Bad argument"); + } + + Spellchecker* that = ObjectWrap::Unwrap(args.Holder()); + std::string word = *String::Utf8Value(args[0]); + + NanReturnValue(NanNew(that->impl->IsMisspelled(word))); + } + + static NAN_METHOD(GetCorrectionsForMisspelling) { + NanScope(); + if (args.Length() < 1) { + return NanThrowError("Bad argument"); + } + + Spellchecker* that = ObjectWrap::Unwrap(args.Holder()); + + std::string word = *String::Utf8Value(args[0]); + std::vector corrections = + that->impl->GetCorrectionsForMisspelling(word); + + Local result = NanNew(corrections.size()); + for (size_t i = 0; i < corrections.size(); ++i) { + const std::string& word = corrections[i]; + result->Set(i, NanNew(word.data(), word.size())); + } + + NanReturnValue(result); + } + + Spellchecker() { + impl = SpellcheckerFactory::CreateSpellchecker(); + } + + // actual destructor + virtual ~Spellchecker() { + delete impl; + } + + public: + static void Init(Handle exports) { + Local tpl = NanNew(Spellchecker::New); + + tpl->SetClassName(NanSymbol("Spellchecker")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + NODE_SET_METHOD(tpl->InstanceTemplate(), "setDictionary", Spellchecker::SetDictionary); + NODE_SET_METHOD(tpl->InstanceTemplate(), "getCorrectionsForMisspelling", Spellchecker::GetCorrectionsForMisspelling); + NODE_SET_METHOD(tpl->InstanceTemplate(), "isMisspelled", Spellchecker::IsMisspelled); + + exports->Set(NanNew("Spellchecker"), tpl->GetFunction()); + } +}; -void Init(Handle exports) { - NODE_SET_METHOD(exports, "init", PlatformInit); - NODE_SET_METHOD(exports, "isMisspelled", IsMisspelled); - NODE_SET_METHOD(exports, - "getCorrectionsForMisspelling", - GetCorrectionsForMisspelling); +void Init(Handle exports, Handle module) { + Spellchecker::Init(exports); } } // namespace diff --git a/src/spellchecker.h b/src/spellchecker.h index 8d9a7ec..e8a48ee 100644 --- a/src/spellchecker.h +++ b/src/spellchecker.h @@ -6,14 +6,23 @@ namespace spellchecker { -// Initializes everything. -void Init(const std::string& dirname); +class SpellcheckerImplementation { +public: + virtual void SetDictionary(const std::string& language, const std::string& path) = 0; -// Returns true if the word is misspelled. -bool IsMisspelled(const std::string& word); + // Returns an array containing possible corrections for the word. + virtual std::vector GetCorrectionsForMisspelling(const std::string& word) = 0; -// Returns an array containing possible corrections for the word. -std::vector GetCorrectionsForMisspelling(const std::string& word); + // Returns true if the word is misspelled. + virtual bool IsMisspelled(const std::string& word) = 0; + + virtual ~SpellcheckerImplementation() {} +}; + +class SpellcheckerFactory { +public: + static SpellcheckerImplementation* CreateSpellchecker(); +}; } // namespace spellchecker diff --git a/src/spellchecker_hunspell.cc b/src/spellchecker_hunspell.cc index a6b4cdb..e55a939 100644 --- a/src/spellchecker_hunspell.cc +++ b/src/spellchecker_hunspell.cc @@ -1,38 +1,44 @@ -#include "spellchecker.h" - #include "../vendor/hunspell/src/hunspell/hunspell.hxx" +#include "spellchecker_hunspell.h" + namespace spellchecker { -namespace { +HunspellSpellchecker::HunspellSpellchecker() { + this->hunspell = NULL; +} -Hunspell* g_hunspell = NULL; +HunspellSpellchecker::~HunspellSpellchecker() { + if (!this->hunspell) return; -} // namespace + delete this->hunspell; +} -void Init(const std::string& dirname) { - if (g_hunspell != NULL) - return; +void HunspellSpellchecker::SetDictionary(const std::string& language, const std::string& dirname) { + if (hunspell != NULL) { + delete this->hunspell; + } - std::string affixpath = dirname + "/../vendor/hunspell_dictionaries/en_US.aff"; - std::string dpath = dirname + "/../vendor/hunspell_dictionaries/en_US.dic"; - g_hunspell = new Hunspell(affixpath.c_str(), dpath.c_str()); + std::string affixpath = dirname + "/" + language + ".aff"; + std::string dpath = dirname + "/" + language + ".dic"; + this->hunspell = new Hunspell(affixpath.c_str(), dpath.c_str()); } -bool IsMisspelled(const std::string& word) { - return g_hunspell->spell(word.c_str()) == 0; +bool HunspellSpellchecker::IsMisspelled(const std::string& word) { + return this->hunspell->spell(word.c_str()) == 0; } -std::vector GetCorrectionsForMisspelling(const std::string& word) { +std::vector HunspellSpellchecker::GetCorrectionsForMisspelling(const std::string& word) { std::vector corrections; char** slist; - int size = g_hunspell->suggest(&slist, word.c_str()); + int size = hunspell->suggest(&slist, word.c_str()); corrections.reserve(size); - for (int i = 0; i < size; ++i) + for (int i = 0; i < size; ++i) { corrections.push_back(slist[i]); + } - g_hunspell->free_list(&slist, size); + this->hunspell->free_list(&slist, size); return corrections; } diff --git a/src/spellchecker_hunspell.h b/src/spellchecker_hunspell.h new file mode 100644 index 0000000..7a9a515 --- /dev/null +++ b/src/spellchecker_hunspell.h @@ -0,0 +1,25 @@ +#ifndef SRC_SPELLCHECKER_HUNSPELL_H_ +#define SRC_SPELLCHECKER_HUNSPELL_H_ + +#include "spellchecker.h" + +class Hunspell; + +namespace spellchecker { + +class HunspellSpellchecker : public SpellcheckerImplementation { +public: + HunspellSpellchecker(); + ~HunspellSpellchecker(); + + void SetDictionary(const std::string& language, const std::string& path); + std::vector GetCorrectionsForMisspelling(const std::string& word); + bool IsMisspelled(const std::string& word); + +private: + Hunspell* hunspell; +}; + +} // namespace spellchecker + +#endif // SRC_SPELLCHECKER_MAC_H_ diff --git a/src/spellchecker_linux.cc b/src/spellchecker_linux.cc new file mode 100644 index 0000000..e5ab00b --- /dev/null +++ b/src/spellchecker_linux.cc @@ -0,0 +1,10 @@ +#include "spellchecker.h" +#include "spellchecker_hunspell.h" + +namespace spellchecker { + +SpellcheckerImplementation* SpellcheckerFactory::CreateSpellchecker() { + return new HunspellSpellchecker(); +} + +} // namespace spellchecker diff --git a/src/spellchecker_mac.h b/src/spellchecker_mac.h new file mode 100644 index 0000000..ad92082 --- /dev/null +++ b/src/spellchecker_mac.h @@ -0,0 +1,17 @@ +#ifndef SRC_SPELLCHECKER_MAC_H_ +#define SRC_SPELLCHECKER_MAC_H_ + +#include "spellchecker.h" + +namespace spellchecker { + +class MacSpellchecker : public SpellcheckerImplementation { +public: + void SetDictionary(const std::string& language, const std::string& path); + std::vector GetCorrectionsForMisspelling(const std::string& word); + bool IsMisspelled(const std::string& word); +}; + +} // namespace spellchecker + +#endif // SRC_SPELLCHECKER_MAC_H_ diff --git a/src/spellchecker_mac.mm b/src/spellchecker_mac.mm index 73f4057..1d53fe3 100644 --- a/src/spellchecker_mac.mm +++ b/src/spellchecker_mac.mm @@ -1,14 +1,14 @@ -#include "spellchecker.h" +#include "spellchecker_mac.h" #import #import namespace spellchecker { -void Init(const std::string& dirname) { +void MacSpellchecker::SetDictionary(const std::string& language, const std::string& path) { } -bool IsMisspelled(const std::string& word) { +bool MacSpellchecker::IsMisspelled(const std::string& word) { bool result; @autoreleasepool { @@ -23,7 +23,7 @@ bool IsMisspelled(const std::string& word) { return result; } -std::vector GetCorrectionsForMisspelling(const std::string& word) { +std::vector MacSpellchecker::GetCorrectionsForMisspelling(const std::string& word) { std::vector corrections; @autoreleasepool { @@ -47,4 +47,8 @@ bool IsMisspelled(const std::string& word) { return corrections; } +SpellcheckerImplementation* SpellcheckerFactory::CreateSpellchecker() { + return new MacSpellchecker(); +} + } // namespace spellchecker diff --git a/src/spellchecker_win.cc b/src/spellchecker_win.cc new file mode 100644 index 0000000..e5ab00b --- /dev/null +++ b/src/spellchecker_win.cc @@ -0,0 +1,10 @@ +#include "spellchecker.h" +#include "spellchecker_hunspell.h" + +namespace spellchecker { + +SpellcheckerImplementation* SpellcheckerFactory::CreateSpellchecker() { + return new HunspellSpellchecker(); +} + +} // namespace spellchecker