File indexing completed on 2024-05-12 15:49:37
0001 /* 0002 * kspell_hunspelldict.cpp 0003 * 0004 * SPDX-FileCopyrightText: 2009 Montel Laurent <montel@kde.org> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "hunspelldict.h" 0010 0011 #include "config-hunspell.h" 0012 #include "hunspelldebug.h" 0013 0014 #include <QDir> 0015 #include <QFile> 0016 #include <QFileInfo> 0017 #include <QStandardPaths> 0018 #include <QTextCodec> 0019 #include <QTextStream> 0020 0021 using namespace Sonnet; 0022 0023 HunspellDict::HunspellDict(const QString &lang, const std::shared_ptr<Hunspell> &speller) 0024 : SpellerPlugin(lang) 0025 { 0026 if (!speller) { 0027 qCWarning(SONNET_HUNSPELL) << "Can't create a client without a speller"; 0028 return; 0029 } 0030 m_codec = QTextCodec::codecForName(speller->get_dic_encoding()); 0031 if (!m_codec) { 0032 qCWarning(SONNET_HUNSPELL) << "Failed to find a text codec for name" << speller->get_dic_encoding() << "defaulting to locale text codec"; 0033 m_codec = QTextCodec::codecForLocale(); 0034 Q_ASSERT(m_codec); 0035 } 0036 m_speller = speller; 0037 0038 const QString userDic = QDir::home().filePath(QLatin1String(".hunspell_") % lang); 0039 QFile userDicFile(userDic); 0040 if (userDicFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 0041 qCDebug(SONNET_HUNSPELL) << "Load a user dictionary" << userDic; 0042 QTextStream userDicIn(&userDicFile); 0043 while (!userDicIn.atEnd()) { 0044 const QString word = userDicIn.readLine(); 0045 if (word.isEmpty()) { 0046 continue; 0047 } 0048 0049 if (word.contains(QLatin1Char('/'))) { 0050 QStringList wordParts = word.split(QLatin1Char('/')); 0051 speller->add_with_affix(toDictEncoding(wordParts.at(0)).constData(), toDictEncoding(wordParts.at(1)).constData()); 0052 } 0053 if (word.at(0) == QLatin1Char('*')) { 0054 speller->remove(toDictEncoding(word.mid(1)).constData()); 0055 } else { 0056 speller->add(toDictEncoding(word).constData()); 0057 } 0058 } 0059 userDicFile.close(); 0060 } 0061 } 0062 0063 std::shared_ptr<Hunspell> HunspellDict::createHunspell(const QString &lang, QString path) 0064 { 0065 qCDebug(SONNET_HUNSPELL) << "Loading dictionary for" << lang << "from" << path; 0066 0067 if (!path.endsWith(QLatin1Char('/'))) { 0068 path += QLatin1Char('/'); 0069 } 0070 path += lang; 0071 QString dictionary = path + QStringLiteral(".dic"); 0072 QString aff = path + QStringLiteral(".aff"); 0073 0074 if (!QFileInfo::exists(dictionary) || !QFileInfo::exists(aff)) { 0075 qCWarning(SONNET_HUNSPELL) << "Unable to find dictionary for" << lang << "in path" << path; 0076 return nullptr; 0077 } 0078 0079 std::shared_ptr<Hunspell> speller = std::make_shared<Hunspell>(aff.toLocal8Bit().constData(), dictionary.toLocal8Bit().constData()); 0080 qCDebug(SONNET_HUNSPELL) << "Created " << speller.get(); 0081 0082 return speller; 0083 } 0084 0085 HunspellDict::~HunspellDict() 0086 { 0087 } 0088 0089 QByteArray HunspellDict::toDictEncoding(const QString &word) const 0090 { 0091 if (m_codec) { 0092 return m_codec->fromUnicode(word); 0093 } 0094 return {}; 0095 } 0096 0097 bool HunspellDict::isCorrect(const QString &word) const 0098 { 0099 qCDebug(SONNET_HUNSPELL) << " isCorrect :" << word; 0100 if (!m_speller) { 0101 return false; 0102 } 0103 0104 #if USE_OLD_HUNSPELL_API 0105 int result = m_speller->spell(toDictEncoding(word).constData()); 0106 qCDebug(SONNET_HUNSPELL) << " result :" << result; 0107 return result != 0; 0108 #else 0109 bool result = m_speller->spell(toDictEncoding(word).toStdString()); 0110 qCDebug(SONNET_HUNSPELL) << " result :" << result; 0111 return result; 0112 #endif 0113 } 0114 0115 QStringList HunspellDict::suggest(const QString &word) const 0116 { 0117 if (!m_speller) { 0118 return QStringList(); 0119 } 0120 0121 QStringList lst; 0122 #if USE_OLD_HUNSPELL_API 0123 char **selection; 0124 int nbWord = m_speller->suggest(&selection, toDictEncoding(word).constData()); 0125 for (int i = 0; i < nbWord; ++i) { 0126 lst << m_codec->toUnicode(selection[i]); 0127 } 0128 m_speller->free_list(&selection, nbWord); 0129 #else 0130 const auto suggestions = m_speller->suggest(toDictEncoding(word).toStdString()); 0131 for_each(suggestions.begin(), suggestions.end(), [this, &lst](const std::string &suggestion) { 0132 lst << m_codec->toUnicode(suggestion.c_str()); 0133 }); 0134 #endif 0135 0136 return lst; 0137 } 0138 0139 bool HunspellDict::storeReplacement(const QString &bad, const QString &good) 0140 { 0141 Q_UNUSED(bad); 0142 Q_UNUSED(good); 0143 if (!m_speller) { 0144 return false; 0145 } 0146 qCDebug(SONNET_HUNSPELL) << "HunspellDict::storeReplacement not implemented"; 0147 return false; 0148 } 0149 0150 bool HunspellDict::addToPersonal(const QString &word) 0151 { 0152 if (!m_speller) { 0153 return false; 0154 } 0155 m_speller->add(toDictEncoding(word).constData()); 0156 const QString userDic = QDir::home().filePath(QLatin1String(".hunspell_") % language()); 0157 QFile userDicFile(userDic); 0158 if (userDicFile.open(QIODevice::Append | QIODevice::Text)) { 0159 QTextStream out(&userDicFile); 0160 out << word << '\n'; 0161 userDicFile.close(); 0162 return true; 0163 } 0164 0165 return false; 0166 } 0167 0168 bool HunspellDict::addToSession(const QString &word) 0169 { 0170 if (!m_speller) { 0171 return false; 0172 } 0173 int r = m_speller->add(toDictEncoding(word).constData()); 0174 return r == 0; 0175 }