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