Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Search for globally available dictionaries #69

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/spellchecker.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ var getDictionaryPath = function() {
} catch (error) {
}
return dict;
}
};

module.exports = {
setDictionary: setDictionary,
Expand Down
21 changes: 14 additions & 7 deletions spec/spellchecker-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe "SpellChecker", ->
describe ".getCorrectionsForMisspelling(word)", ->
beforeEach ->
@fixture = new Spellchecker()
@fixture.setDictionary defaultLanguage, dictionaryDirectory
@fixture.setDictionary 'en_US', dictionaryDirectory

it "returns an array of possible corrections", ->
corrections = @fixture.getCorrectionsForMisspelling('worrd')
Expand Down Expand Up @@ -158,17 +158,24 @@ describe "SpellChecker", ->
@fixture.setDictionary defaultLanguage, dictionaryDirectory

it "returns an array of string dictionary names", ->
# NB: getAvailableDictionaries is nop'ped in hunspell and it also doesn't
# work inside Appveyor's CI environment
return if process.platform is 'linux' or process.env.CI or process.env.SPELLCHECKER_PREFER_HUNSPELL

dictionaries = @fixture.getAvailableDictionaries()
dictionaries = @fixture.getAvailableDictionaries dictionaryDirectory
expect(Array.isArray(dictionaries)).toBe true

expect(dictionaries.length).toBeGreaterThan 0
for dictionary in dictionaries.length
expect(typeof dictionary).toBe 'string'
expect(diction.length).toBeGreaterThan 0
expect(dictionary.length).toBeGreaterThan 0

it "returns the right dictionary names when using hunspell on linux", ->
return if not (process.platform is 'linux') and not (process.platform is 'win32' and process.env.SPELLCHECKER_PREFER_HUNSPELL)

dictionaries = @fixture.getAvailableDictionaries dictionaryDirectory
expect(Array.isArray(dictionaries)).toBe true

expect(dictionaries.length).toBeGreaterThan 3
expect(dictionaries).toContain('en_US');
expect(dictionaries).toContain('de_DE_frami');
expect(dictionaries).toContain('fr');

describe ".setDictionary(lang, dictDirectory)", ->
it "sets the spell checker's language, and dictionary directory", ->
Expand Down
2 changes: 1 addition & 1 deletion src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Spellchecker : public Nan::ObjectWrap {

std::string path = ".";
if (info.Length() > 0) {
std::string path = *String::Utf8Value(info[0]);
path = *String::Utf8Value(info[0]);
}

std::vector<std::string> dictionaries =
Expand Down
121 changes: 114 additions & 7 deletions src/spellchecker_hunspell.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
#include <cstdio>
#include <cwctype>
#include <algorithm>
#include <fstream>
#include <sstream>
#include "../vendor/hunspell/src/hunspell/hunspell.hxx"
#include "spellchecker_hunspell.h"

#ifdef WIN32

#include <windows.h>

#define SEARCH_PATHS "C:\\Hunspell\\"
#define DIR_SEPARATOR "\\"
#define PATH_SEPARATOR ';'

#else

// Not windows
#include <sys/types.h>
#include <dirent.h>

#define SEARCH_PATHS \
"/usr/share/hunspell:" \
"/usr/share/myspell:" \
"/usr/share/myspell/dicts:" \
"/Library/Spelling"
#define DIR_SEPARATOR "/"
#define PATH_SEPARATOR ':'

#endif

namespace spellchecker {

HunspellSpellchecker::HunspellSpellchecker() : hunspell(NULL), transcoder(NewTranscoder()) { }
Expand All @@ -29,22 +55,23 @@ bool HunspellSpellchecker::SetDictionary(const std::string& language, const std:
std::string lang = language;
std::replace(lang.begin(), lang.end(), '-', '_');

std::string affixpath = dirname + "/" + lang + ".aff";
std::string dpath = dirname + "/" + lang + ".dic";
std::string search_path = dirname + PATH_SEPARATOR + SEARCH_PATHS;

std::string affixpath = FindDictionary(search_path, lang, ".aff");
std::string dpath = FindDictionary(search_path, lang, ".dic");

// TODO: This code is almost certainly jacked on Win32 for non-ASCII paths
FILE* handle = fopen(dpath.c_str(), "r");
if (!handle) {
if (dpath.compare("") == 0) {
return false;
}
fclose(handle);

hunspell = new Hunspell(affixpath.c_str(), dpath.c_str());
return true;
}

std::vector<std::string> HunspellSpellchecker::GetAvailableDictionaries(const std::string& path) {
return std::vector<std::string>();
std::string search_path = path + PATH_SEPARATOR + SEARCH_PATHS;

return SearchAvailableDictionaries(search_path);
}

bool HunspellSpellchecker::IsMisspelled(const std::string& word) {
Expand Down Expand Up @@ -141,4 +168,84 @@ std::vector<std::string> HunspellSpellchecker::GetCorrectionsForMisspelling(cons
return corrections;
}

std::vector<std::string> HunspellSpellchecker::SearchAvailableDictionaries(const std::string& path) {
std::vector<std::string> my_list;
std::istringstream path_stream(path);

for (std::string search_path; std::getline(path_stream, search_path, PATH_SEPARATOR); ) {
search_path.append(DIR_SEPARATOR);

#ifdef WIN32
search_path.append("*");

WIN32_FIND_DATA search_data;
memset(&search_data, 0, sizeof(WIN32_FIND_DATA));

HANDLE handle = FindFirstFile(search_path.c_str(), &search_data);

while (handle != INVALID_HANDLE_VALUE) {
std::string filename(search_data.cFileName);

if (filename.size() > 4 && filename.compare(filename.size() - 4, 4, ".dic") == 0) {
my_list.push_back(filename.substr(0, filename.size() - 4));
}
else if (filename.size() > 7 && filename.compare(filename.size() - 7, 7, ".dic.hz") == 0) {
my_list.push_back(filename.substr(0, filename.size() - 7));
}

if (FindNextFile(handle, &search_data) == FALSE) {
break;
}
}

FindClose(handle);
#else
DIR* dir = opendir(search_path.c_str());

if (dir) {
struct dirent* de;
while ((de = readdir(dir))) {
std::string filename(de->d_name);

if (filename.size() > 4 && filename.compare(filename.size() - 4, 4, ".dic") == 0) {
my_list.push_back(filename.substr(0, filename.size() - 4));
}
else if (filename.size() > 7 && filename.compare(filename.size() - 7, 7, ".dic.hz") == 0) {
my_list.push_back(filename.substr(0, filename.size() - 7));
}
}

closedir(dir);
}
#endif
}

return my_list;
}

std::string HunspellSpellchecker::FindDictionary(const std::string& path, const std::string& language, const std::string& extension) {
std::istringstream path_stream(path);

for (std::string file_path; std::getline(path_stream, file_path, PATH_SEPARATOR); ) {
file_path.append(DIR_SEPARATOR);
file_path.append(language);
file_path.append(extension);

std::ifstream f;
f.open(file_path, std::ios_base::in);
if (f.is_open()) {
return file_path;
}

file_path.append(".hz");

f.open(file_path, std::ios_base::in);
if (f.is_open()) {
return file_path;
}
}

return "";
}

} // namespace spellchecker
3 changes: 3 additions & 0 deletions src/spellchecker_hunspell.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class HunspellSpellchecker : public SpellcheckerImplementation {
private:
Hunspell* hunspell;
Transcoder *transcoder;

std::vector<std::string> SearchAvailableDictionaries(const std::string& path);
std::string FindDictionary(const std::string& path, const std::string& language, const std::string& extension);
};

} // namespace spellchecker
Expand Down