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 }