diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) -set(PIM_VERSION "5.6.40") +set(PIM_VERSION "5.6.41") project(KPimTextEdit VERSION ${PIM_VERSION}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,7 +56,6 @@ tableformatdialog.cpp textutils.cpp inserttablewidget.cpp - syntaxhighlighterbase.cpp texteditorcompleter.cpp inserthtmleditor.cpp slidecontainer.cpp @@ -114,7 +113,6 @@ InsertTableDialog InsertTableWidget SelectSpecialCharDialog - SyntaxHighlighterBase SlideContainer TableCellFormatDialog TableFormatDialog diff --git a/src/syntaxhighlighterbase.h b/src/syntaxhighlighterbase.h deleted file mode 100644 --- a/src/syntaxhighlighterbase.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (c) 2014-2017 Montel Laurent - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef SYNTAXHIGHLIGHTERBASE_H -#define SYNTAXHIGHLIGHTERBASE_H - -#include -#include -#include "kpimtextedit_export.h" - -namespace KPIMTextEdit { -class KPIMTEXTEDIT_EXPORT Rule -{ -public: - Rule() - { - } - - Rule(const QRegularExpression &r, const QTextCharFormat &f) - : pattern(r) - , format(f) - { - } - - QRegularExpression pattern; - QTextCharFormat format; -}; - -class KPIMTEXTEDIT_EXPORT SyntaxHighlighterBase : public QSyntaxHighlighter -{ - Q_OBJECT -public: - explicit SyntaxHighlighterBase(QTextDocument *doc); - ~SyntaxHighlighterBase(); - - void highlightBlock(const QString &text) override; - -protected: - virtual void init() = 0; - QVector m_rules; -}; -} -Q_DECLARE_TYPEINFO(KPIMTextEdit::Rule, Q_MOVABLE_TYPE); - -#endif // SYNTAXHIGHLIGHTERBASE_H diff --git a/src/syntaxhighlighterbase.cpp b/src/syntaxhighlighterbase.cpp deleted file mode 100644 --- a/src/syntaxhighlighterbase.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (c) 2014-2017 Montel Laurent - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "syntaxhighlighterbase.h" -#include -#include "kpimtextedit_debug.h" - -using namespace KPIMTextEdit; -SyntaxHighlighterBase::SyntaxHighlighterBase(QTextDocument *doc) - : QSyntaxHighlighter(doc) -{ -} - -SyntaxHighlighterBase::~SyntaxHighlighterBase() -{ -} - -void SyntaxHighlighterBase::highlightBlock(const QString &text) -{ - for (const Rule &rule : qAsConst(m_rules)) { - const QRegularExpression expression(rule.pattern); - if (!expression.isValid()) { - const QString errorString = expression.errorString(); - qCDebug(KPIMTEXTEDIT_LOG) << "expression is invalid, pattern:" << rule.pattern << " error :" << errorString; - } - QRegularExpressionMatch match = expression.match(text); - - int index = match.capturedStart(); - while (index >= 0 && match.hasMatch()) { - setFormat(index, match.capturedLength(), rule.format); - match = expression.match(text, index + match.capturedLength()); - index = match.capturedStart(); - } - } -} diff --git a/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.h b/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.h --- a/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.h +++ b/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.h @@ -23,26 +23,31 @@ #include "kpimtextedit_export.h" #include -namespace KPIMTextEdit { -class Rule; +#include + +namespace KSyntaxHighlighting { +class Format; } namespace KPIMTextEdit { class PlainTextEditor; -class KPIMTEXTEDIT_EXPORT PlainTextSyntaxSpellCheckingHighlighter : public Sonnet::Highlighter +class PlainTextSyntaxSpellCheckingHighlighterPrivate; + +class KPIMTEXTEDIT_EXPORT PlainTextSyntaxSpellCheckingHighlighter : public Sonnet::Highlighter, public KSyntaxHighlighting::AbstractHighlighter { public: explicit PlainTextSyntaxSpellCheckingHighlighter(PlainTextEditor *plainText, const QColor &misspelledColor = Qt::red); ~PlainTextSyntaxSpellCheckingHighlighter(); void toggleSpellHighlighting(bool on); + void setDefinition(const KSyntaxHighlighting::Definition &def) override; + /** * Reimplemented to highlight quote blocks. */ void highlightBlock(const QString &text) override; - void setSyntaxHighlighterRules(const QVector &rule); protected: /** * Reimplemented, the base version sets the text color to black, which @@ -59,9 +64,8 @@ */ void setMisspelled(int start, int count) override; - virtual bool spellCheckBlock(const QString &text); + void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override; private: - class PlainTextSyntaxSpellCheckingHighlighterPrivate; PlainTextSyntaxSpellCheckingHighlighterPrivate *const d; }; } diff --git a/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.cpp b/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.cpp --- a/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.cpp +++ b/src/texteditor/plaintexteditor/plaintextsyntaxspellcheckinghighlighter.cpp @@ -19,31 +19,76 @@ #include "plaintexteditor.h" #include "plaintextsyntaxspellcheckinghighlighter.h" - -#include "syntaxhighlighterbase.h" #include "kpimtextedit_debug.h" +#include +#include +#include +#include + +#include + +Q_DECLARE_METATYPE(QTextBlock) + using namespace KPIMTextEdit; -class Q_DECL_HIDDEN KPIMTextEdit::PlainTextSyntaxSpellCheckingHighlighter::PlainTextSyntaxSpellCheckingHighlighterPrivate +namespace KPIMTextEdit { +struct SpellCheckRange +{ + SpellCheckRange(int o, int l) + : offset(o) + , length(l) + {} + + int end() const + { + return offset + length; + } + + int offset; + int length; +}; + +QDebug operator<<(QDebug dbg, const SpellCheckRange &s) +{ + dbg << '(' << s.offset << ',' << s.length << ')'; + return dbg; +} + +class TextBlockUserData : public QTextBlockUserData +{ +public: + KSyntaxHighlighting::State state; + QTextBlockUserData *spellData = nullptr; +}; + +class PlainTextSyntaxSpellCheckingHighlighterPrivate { public: PlainTextSyntaxSpellCheckingHighlighterPrivate(PlainTextEditor *plainText) : editor(plainText) , spellCheckingEnabled(false) { } - QVector rules; PlainTextEditor *editor = nullptr; QColor misspelledColor; bool spellCheckingEnabled = false; + + // We can't use QTextBlock user data, Sonnet is occupying that already + // and we can't just daisy-chain them, as QTextBlock deletes the previous + // one when setting a new one. Instead, we need to store this out-of-band. + QHash blockState; + + std::vector spellCheckRanges; }; +} PlainTextSyntaxSpellCheckingHighlighter::PlainTextSyntaxSpellCheckingHighlighter(PlainTextEditor *plainText, const QColor &misspelledColor) : Sonnet::Highlighter(plainText) - , d(new PlainTextSyntaxSpellCheckingHighlighter::PlainTextSyntaxSpellCheckingHighlighterPrivate(plainText)) + , d(new PlainTextSyntaxSpellCheckingHighlighterPrivate(plainText)) { + qRegisterMetaType(); d->misspelledColor = misspelledColor; setAutomatic(false); } @@ -61,38 +106,42 @@ } } -void PlainTextSyntaxSpellCheckingHighlighter::setSyntaxHighlighterRules(const QVector &rule) +void PlainTextSyntaxSpellCheckingHighlighter::setDefinition(const KSyntaxHighlighting::Definition &def) { - d->rules = rule; + const auto needsRehighlight = definition() != def; + AbstractHighlighter::setDefinition(def); + if (needsRehighlight) + rehighlight(); } void PlainTextSyntaxSpellCheckingHighlighter::highlightBlock(const QString &text) { - for (const KPIMTextEdit::Rule &rule : qAsConst(d->rules)) { - const QRegularExpression expression(rule.pattern); - if (!expression.isValid()) { - const QString errorString = expression.errorString(); - qCDebug(KPIMTEXTEDIT_LOG) << "expression is invalid, pattern:" << rule.pattern << " error :" << errorString; - } - - QRegularExpressionMatch match = expression.match(text); + d->spellCheckRanges.clear(); - int index = match.capturedStart(); - while (index >= 0 && match.hasMatch()) { - setFormat(index, match.capturedLength(), rule.format); - match = expression.match(text, index + match.capturedLength()); - index = match.capturedStart(); - } + KSyntaxHighlighting::State state; + if (currentBlock().position() > 0) { + const auto prevBlock = currentBlock().previous(); + state = d->blockState.value(prevBlock.userState()); } - if (d->spellCheckingEnabled && spellCheckBlock(text) && d->editor->isEnabled()) { + + state = highlightLine(text, state); + if (d->spellCheckingEnabled && d->editor->isEnabled() && !d->spellCheckRanges.empty()) { Highlighter::highlightBlock(text); } -} -bool PlainTextSyntaxSpellCheckingHighlighter::spellCheckBlock(const QString &text) -{ - Q_UNUSED(text); - return true; + if (currentBlockState() <= 0) { // first time we highlight this + setCurrentBlockState(d->blockState.size() + 1); + d->blockState.insert(currentBlockState(), state); + return; + } + + if (d->blockState.value(currentBlockState()) == state) + return; + d->blockState.insert(currentBlockState(), state); + + const auto nextBlock = currentBlock().next(); + if (nextBlock.isValid()) + QMetaObject::invokeMethod(this, "rehighlightBlock", Qt::QueuedConnection, Q_ARG(QTextBlock, nextBlock)); } void PlainTextSyntaxSpellCheckingHighlighter::unsetMisspelled(int start, int count) @@ -104,5 +153,46 @@ void PlainTextSyntaxSpellCheckingHighlighter::setMisspelled(int start, int count) { setMisspelledColor(d->misspelledColor); - Sonnet::Highlighter::setMisspelled(start, count); + for (const auto &range : d->spellCheckRanges) { + if (range.offset <= start && range.end() >= start + count) { + auto f = format(start); + f.setFontUnderline(true); + f.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); + f.setUnderlineColor(d->misspelledColor); + setFormat(start, count, f); + return; + } + } +} + +void PlainTextSyntaxSpellCheckingHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) +{ + if (format.spellCheck() && length > 0) { + if (d->spellCheckRanges.empty()) + d->spellCheckRanges.push_back({offset, length}); + else if (d->spellCheckRanges.back().end() + 1 == offset) + d->spellCheckRanges.back().length += length; + else + d->spellCheckRanges.push_back({offset, length}); + } + + if (format.isDefaultTextStyle(theme()) || length == 0) + return; + + QTextCharFormat tf; + if (format.hasTextColor(theme())) + tf.setForeground(format.textColor(theme())); + if (format.hasBackgroundColor(theme())) + tf.setBackground(format.backgroundColor(theme())); + + if (format.isBold(theme())) + tf.setFontWeight(QFont::Bold); + if (format.isItalic(theme())) + tf.setFontItalic(true); + if (format.isUnderline(theme())) + tf.setFontUnderline(true); + if (format.isStrikeThrough(theme())) + tf.setFontStrikeOut(true); + + QSyntaxHighlighter::setFormat(offset, length, tf); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,3 @@ KF5PimTextEdit ) -add_executable(syntaxhighlightertest_demo syntaxhighlightertest_demo.cpp) -target_link_libraries(syntaxhighlightertest_demo KF5PimTextEdit Qt5::Widgets) - diff --git a/tests/syntaxhighlightertest_demo.h b/tests/syntaxhighlightertest_demo.h deleted file mode 100644 --- a/tests/syntaxhighlightertest_demo.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of KDE. - - Copyright (C) 2015-2017 Laurent Montel - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef SYNTAXHIGHLIGHTERTEST_H -#define SYNTAXHIGHLIGHTERTEST_H -#include - -class SyntaxHighlighterTest : public KPIMTextEdit::SyntaxHighlighterBase -{ -public: - explicit SyntaxHighlighterTest(QTextDocument *doc); - ~SyntaxHighlighterTest(); -protected: - void init() override; -}; - -#endif diff --git a/tests/syntaxhighlightertest_demo.cpp b/tests/syntaxhighlightertest_demo.cpp deleted file mode 100644 --- a/tests/syntaxhighlightertest_demo.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of KDE. - - Copyright (C) 2015-2017 Laurent Montel - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "syntaxhighlightertest_demo.h" - -#include -#include - -#include - -SyntaxHighlighterTest::SyntaxHighlighterTest(QTextDocument *doc) - : KPIMTextEdit::SyntaxHighlighterBase(doc) -{ - init(); -} - -SyntaxHighlighterTest::~SyntaxHighlighterTest() -{ -} - -void SyntaxHighlighterTest::init() -{ - QTextCharFormat commentFormat; - commentFormat.setForeground(Qt::darkYellow); - QRegularExpression commentRegex(QStringLiteral("TEST")); - m_rules.append(KPIMTextEdit::Rule(commentRegex, commentFormat)); -} - -int main(int argv, char **argc) -{ - QApplication app(argv, argc); - KPIMTextEdit::PlainTextEditor textEdit; - (void)new SyntaxHighlighterTest(textEdit.document()); - textEdit.show(); - return app.exec(); -}