Sonnet

hunspelldict.cpp
1 /*
2  * kspell_hunspelldict.cpp
3  *
4  * SPDX-FileCopyrightText: 2009 Montel Laurent <[email protected]>
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  */
8 
9 #include "hunspelldict.h"
10 
11 #include "config-hunspell.h"
12 #include "hunspelldebug.h"
13 
14 #include <QDir>
15 #include <QFile>
16 #include <QFileInfo>
17 #include <QStandardPaths>
18 #include <QTextCodec>
19 #include <QTextStream>
20 
21 using namespace Sonnet;
22 
23 HunspellDict::HunspellDict(const QString &lang, const std::shared_ptr<Hunspell> &speller)
24  : SpellerPlugin(lang)
25 {
26  if (!speller) {
27  qCWarning(SONNET_HUNSPELL) << "Can't create a client without a speller";
28  return;
29  }
30  m_codec = QTextCodec::codecForName(speller->get_dic_encoding());
31  if (!m_codec) {
32  qCWarning(SONNET_HUNSPELL) << "Failed to find a text codec for name" << speller->get_dic_encoding() << "defaulting to locale text codec";
33  m_codec = QTextCodec::codecForLocale();
34  Q_ASSERT(m_codec);
35  }
36  m_speller = speller;
37 
38  const QString userDic = QDir::home().filePath(QLatin1String(".hunspell_") % lang);
39  QFile userDicFile(userDic);
40  if (userDicFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
41  qCDebug(SONNET_HUNSPELL) << "Load a user dictionary" << userDic;
42  QTextStream userDicIn(&userDicFile);
43  while (!userDicIn.atEnd()) {
44  const QString word = userDicIn.readLine();
45  if (word.isEmpty()) {
46  continue;
47  }
48 
49  if (word.contains(QLatin1Char('/'))) {
50  QStringList wordParts = word.split(QLatin1Char('/'));
51  speller->add_with_affix(toDictEncoding(wordParts.at(0)).constData(), toDictEncoding(wordParts.at(1)).constData());
52  }
53  if (word.at(0) == QLatin1Char('*')) {
54  speller->remove(toDictEncoding(word.mid(1)).constData());
55  } else {
56  speller->add(toDictEncoding(word).constData());
57  }
58  }
59  userDicFile.close();
60  }
61 }
62 
63 std::shared_ptr<Hunspell> HunspellDict::createHunspell(const QString &lang, QString path)
64 {
65  qCDebug(SONNET_HUNSPELL) << "Loading dictionary for" << lang << "from" << path;
66 
67  if (!path.endsWith(QLatin1Char('/'))) {
68  path += QLatin1Char('/');
69  }
70  path += lang;
71  QString dictionary = path + QStringLiteral(".dic");
72  QString aff = path + QStringLiteral(".aff");
73 
74  if (!QFileInfo::exists(dictionary) || !QFileInfo::exists(aff)) {
75  qCWarning(SONNET_HUNSPELL) << "Unable to find dictionary for" << lang << "in path" << path;
76  return nullptr;
77  }
78 
79  std::shared_ptr<Hunspell> speller = std::make_shared<Hunspell>(aff.toLocal8Bit().constData(), dictionary.toLocal8Bit().constData());
80  qCDebug(SONNET_HUNSPELL) << "Created " << speller.get();
81 
82  return speller;
83 }
84 
85 HunspellDict::~HunspellDict()
86 {
87 }
88 
89 QByteArray HunspellDict::toDictEncoding(const QString &word) const
90 {
91  if (m_codec) {
92  return m_codec->fromUnicode(word);
93  }
94  return {};
95 }
96 
97 bool HunspellDict::isCorrect(const QString &word) const
98 {
99  qCDebug(SONNET_HUNSPELL) << " isCorrect :" << word;
100  if (!m_speller) {
101  return false;
102  }
103 
104 #if USE_OLD_HUNSPELL_API
105  int result = m_speller->spell(toDictEncoding(word).constData());
106  qCDebug(SONNET_HUNSPELL) << " result :" << result;
107  return result != 0;
108 #else
109  bool result = m_speller->spell(toDictEncoding(word).toStdString());
110  qCDebug(SONNET_HUNSPELL) << " result :" << result;
111  return result;
112 #endif
113 }
114 
115 QStringList HunspellDict::suggest(const QString &word) const
116 {
117  if (!m_speller) {
118  return QStringList();
119  }
120 
121  QStringList lst;
122 #if USE_OLD_HUNSPELL_API
123  char **selection;
124  int nbWord = m_speller->suggest(&selection, toDictEncoding(word).constData());
125  for (int i = 0; i < nbWord; ++i) {
126  lst << m_codec->toUnicode(selection[i]);
127  }
128  m_speller->free_list(&selection, nbWord);
129 #else
130  const auto suggestions = m_speller->suggest(toDictEncoding(word).toStdString());
131  for_each(suggestions.begin(), suggestions.end(), [this, &lst](const std::string &suggestion) {
132  lst << m_codec->toUnicode(suggestion.c_str());
133  });
134 #endif
135 
136  return lst;
137 }
138 
139 bool HunspellDict::storeReplacement(const QString &bad, const QString &good)
140 {
141  Q_UNUSED(bad);
142  Q_UNUSED(good);
143  if (!m_speller) {
144  return false;
145  }
146  qCDebug(SONNET_HUNSPELL) << "HunspellDict::storeReplacement not implemented";
147  return false;
148 }
149 
150 bool HunspellDict::addToPersonal(const QString &word)
151 {
152  if (!m_speller) {
153  return false;
154  }
155  m_speller->add(toDictEncoding(word).constData());
156  const QString userDic = QDir::home().filePath(QLatin1String(".hunspell_") % language());
157  QFile userDicFile(userDic);
158  if (userDicFile.open(QIODevice::Append | QIODevice::Text)) {
159  QTextStream out(&userDicFile);
160  out << word << '\n';
161  userDicFile.close();
162  return true;
163  }
164 
165  return false;
166 }
167 
168 bool HunspellDict::addToSession(const QString &word)
169 {
170  if (!m_speller) {
171  return false;
172  }
173  int r = m_speller->add(toDictEncoding(word).constData());
174  return r == 0;
175 }
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool exists() const const
QTextCodec * codecForLocale()
QDir home()
bool isEmpty() const const
QTextCodec * codecForName(const QByteArray &name)
const T & at(int i) const const
The sonnet namespace.
const char * constData() const const
QString filePath(const QString &fileName) const const
const QChar at(int position) const const
QByteArray toLocal8Bit() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString mid(int position, int n) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Wed Sep 28 2022 04:06:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.