Sonnet

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

KDE's Doxygen guidelines are available online.