KTextAddons

plaintextsyntaxspellcheckinghighlighter.cpp
1/*
2 SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "plaintextsyntaxspellcheckinghighlighter.h"
8#include "plaintexteditor.h"
9#include "textcustomeditor_debug.h"
10
11#include <KSyntaxHighlighting/Definition>
12#include <KSyntaxHighlighting/Format>
13#include <KSyntaxHighlighting/State>
14#include <KSyntaxHighlighting/Theme>
15
16#include <vector>
17
18Q_DECLARE_METATYPE(QTextBlock)
19
20using namespace TextCustomEditor;
21
22namespace TextCustomEditor
23{
24struct SpellCheckRange {
25 SpellCheckRange(int o, int l)
26 : offset(o)
27 , length(l)
28 {
29 }
30
31 [[nodiscard]] int end() const
32 {
33 return offset + length;
34 }
35
36 int offset;
37 int length;
38};
39
40QDebug operator<<(QDebug dbg, SpellCheckRange s)
41{
42 dbg << '(' << s.offset << ',' << s.length << ')';
43 return dbg;
44}
45
46class TextBlockUserData : public QTextBlockUserData
47{
48public:
50};
51
52class PlainTextSyntaxSpellCheckingHighlighterPrivate
53{
54public:
55 explicit PlainTextSyntaxSpellCheckingHighlighterPrivate(PlainTextEditor *plainText)
56 : editor(plainText)
57 {
58 }
59
60 PlainTextEditor *const editor;
61 QColor misspelledColor;
62 bool spellCheckingEnabled = false;
63
64 // We can't use QTextBlock user data, Sonnet is occupying that already
65 // and we can't just daisy-chain them, as QTextBlock deletes the previous
66 // one when setting a new one. Instead, we need to store this out-of-band.
68
69 std::vector<SpellCheckRange> spellCheckRanges;
70};
71}
72
73PlainTextSyntaxSpellCheckingHighlighter::PlainTextSyntaxSpellCheckingHighlighter(PlainTextEditor *plainText, const QColor &misspelledColor)
74 : Sonnet::Highlighter(plainText)
75 , d(new PlainTextSyntaxSpellCheckingHighlighterPrivate(plainText))
76{
77 qRegisterMetaType<QTextBlock>();
78 d->misspelledColor = misspelledColor;
79 setAutomatic(false);
80}
81
82PlainTextSyntaxSpellCheckingHighlighter::~PlainTextSyntaxSpellCheckingHighlighter() = default;
83
84void PlainTextSyntaxSpellCheckingHighlighter::toggleSpellHighlighting(bool on)
85{
86 if (on != d->spellCheckingEnabled) {
87 d->spellCheckingEnabled = on;
89 }
90}
91
92void PlainTextSyntaxSpellCheckingHighlighter::setDefinition(const KSyntaxHighlighting::Definition &def)
93{
94 const auto needsRehighlight = definition() != def;
95 AbstractHighlighter::setDefinition(def);
96 if (needsRehighlight) {
98 }
99}
100
102{
103 d->spellCheckRanges.clear();
104
106 if (currentBlock().position() > 0) {
107 const auto prevBlock = currentBlock().previous();
108 state = d->blockState.value(prevBlock.userState());
109 }
110
111 state = highlightLine(text, state);
112 if (d->spellCheckingEnabled && d->editor->isEnabled() && !d->spellCheckRanges.empty()) {
113 Highlighter::highlightBlock(text);
114 }
115
116 if (currentBlockState() <= 0) { // first time we highlight this
117 setCurrentBlockState(d->blockState.size() + 1);
118 d->blockState.insert(currentBlockState(), state);
119 return;
120 }
121
122 if (d->blockState.value(currentBlockState()) == state) {
123 return;
124 }
125 d->blockState.insert(currentBlockState(), state);
126
127 const auto nextBlock = currentBlock().next();
128 if (nextBlock.isValid()) {
130 this,
131 [this, nextBlock] {
132 rehighlightBlock(nextBlock);
133 },
135 }
136}
137
139{
140 Q_UNUSED(start)
141 Q_UNUSED(count)
142}
143
145{
146 setMisspelledColor(d->misspelledColor);
147 for (const auto &range : d->spellCheckRanges) {
148 if (range.offset <= start && range.end() >= start + count) {
149 auto f = format(start);
150 f.setFontUnderline(true);
151 f.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
152 f.setUnderlineColor(d->misspelledColor);
153 setFormat(start, count, f);
154 return;
155 }
156 }
157}
158
159void PlainTextSyntaxSpellCheckingHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format)
160{
161 if (format.spellCheck() && length > 0) {
162 if (d->spellCheckRanges.empty()) {
163 d->spellCheckRanges.emplace_back(offset, length);
164 } else if (d->spellCheckRanges.back().end() + 1 == offset) {
165 d->spellCheckRanges.back().length += length;
166 } else {
167 d->spellCheckRanges.emplace_back(offset, length);
168 }
169 }
170
171 if (format.isDefaultTextStyle(theme()) || length == 0) {
172 return;
173 }
174
176 if (format.hasTextColor(theme())) {
177 tf.setForeground(format.textColor(theme()));
178 }
179 if (format.hasBackgroundColor(theme())) {
180 tf.setBackground(format.backgroundColor(theme()));
181 }
182
183 if (format.isBold(theme())) {
185 }
186 if (format.isItalic(theme())) {
187 tf.setFontItalic(true);
188 }
189 if (format.isUnderline(theme())) {
190 tf.setFontUnderline(true);
191 }
192 if (format.isStrikeThrough(theme())) {
193 tf.setFontStrikeOut(true);
194 }
195
196 QSyntaxHighlighter::setFormat(offset, length, tf);
197}
State highlightLine(QStringView text, const State &state)
void setMisspelledColor(const QColor &color)
The PlainTextEditor class.
void setMisspelled(int start, int count) override
Reimplemented to set the color of the misspelled word to a color defined by setQuoteColor().
void highlightBlock(const QString &text) override
Reimplemented to highlight quote blocks.
void unsetMisspelled(int start, int count) override
Reimplemented, the base version sets the text color to black, which is not what we want.
Q_SCRIPTABLE Q_NOREPLY void start()
KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, const MovingCursor &cursor)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QTextBlock currentBlock() const const
int currentBlockState() const const
QTextCharFormat format(int position) const const
void rehighlightBlock(const QTextBlock &block)
void setCurrentBlockState(int newState)
void setFormat(int start, int count, const QColor &color)
QueuedConnection
QTextBlock next() const const
QTextBlock previous() const const
void setFontItalic(bool italic)
void setFontStrikeOut(bool strikeOut)
void setFontUnderline(bool underline)
void setFontWeight(int weight)
void setBackground(const QBrush &brush)
void setForeground(const QBrush &brush)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 20 2024 11:49:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.