File indexing completed on 2025-01-19 04:52:00
0001 /* 0002 Copyright (c) 2020 Christian Mollekopf <mollekopf@kolabsystems.com> 0003 0004 This library is free software; you can redistribute it and/or modify it 0005 under the terms of the GNU Library General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or (at your 0007 option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, but WITHOUT 0010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0012 License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to the 0016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0017 02110-1301, USA. 0018 */ 0019 #include "spellcheckhighlighter.h" 0020 0021 #include <QDebug> 0022 0023 #include "../syntaxhighlighter.h" 0024 0025 SpellcheckHighlighter::SpellcheckHighlighter(QTextDocument *parent) 0026 : QSyntaxHighlighter(parent), 0027 mSpellchecker{new Sonnet::Speller()}, 0028 mLanguageGuesser{new Sonnet::GuessLanguage()} 0029 { 0030 //Danger red from our color scheme 0031 mErrorFormat.setForeground(QColor{"#ed1515"}); 0032 mQuoteFormat.setForeground(QColor{"#7f8c8d"}); 0033 0034 if (!mSpellchecker->isValid()) { 0035 qWarning() << "Spellchecker is invalid"; 0036 } 0037 qDebug() << "Available dictionaries: " << mSpellchecker->availableDictionaries(); 0038 } 0039 0040 void SpellcheckHighlighter::autodetectLanguage(const QString &sentence) 0041 { 0042 const auto lang = mLanguageGuesser->identify(sentence, mSpellchecker->availableLanguages()); 0043 if (lang.isEmpty()) { 0044 return; 0045 } 0046 mSpellchecker->setLanguage(lang); 0047 } 0048 0049 static bool isSpellcheckable(const QStringRef &token) 0050 { 0051 if (token.isNull() || token.isEmpty()) { 0052 return false; 0053 } 0054 if (!token.at(0).isLetter()) { 0055 return false; 0056 } 0057 //TODO ignore urls and uppercase? 0058 return true; 0059 } 0060 0061 void SpellcheckHighlighter::highlightBlock(const QString &text) 0062 { 0063 //Avoid spellchecking quotes 0064 if (text.isEmpty() || text.at(0) == QChar{'>'}) { 0065 setFormat(0, text.length(), mQuoteFormat); 0066 return; 0067 } 0068 for (const auto &sentenceRef : split(QTextBoundaryFinder::Sentence, text)) { 0069 //Avoid spellchecking quotes 0070 if (sentenceRef.isEmpty() || sentenceRef.at(0) == QChar{'>'}) { 0071 continue; 0072 } 0073 0074 const auto sentence = QString::fromRawData(sentenceRef.data(), sentenceRef.length()); 0075 0076 autodetectLanguage(sentence); 0077 0078 const int offset = sentenceRef.position(); 0079 for (const auto &wordRef : split(QTextBoundaryFinder::Word, sentence)) { 0080 //Avoid spellchecking words in progress 0081 //FIXME this will also prevent spellchecking a single word on a line. 0082 if (offset + wordRef.position() + wordRef.length() >= text.length()) { 0083 continue; 0084 } 0085 if (isSpellcheckable(wordRef)) { 0086 const auto word = QString::fromRawData(wordRef.data(), wordRef.length()); 0087 const auto format = mSpellchecker->isMisspelled(word) ? mErrorFormat : QTextCharFormat{}; 0088 setFormat(offset + wordRef.position(), wordRef.length(), format); 0089 } 0090 } 0091 } 0092 }