• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • applications API Reference
  • KDE Home
  • Contact Us
 

Kate

  • kde-4.14
  • applications
  • kate
  • part
  • spellcheck
spellcheck.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries and the Kate part.
2  *
3  * Copyright (C) 2009 by Michel Ludwig <michel.ludwig@kdemail.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB. If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /* If ever threads should be used again, thread communication and
22  * synchronization ought to be done with blocking queued signal connections.
23  */
24 
25 #include "spellcheck.h"
26 
27 #include <QHash>
28 #include <QtAlgorithms>
29 #include <QTimer>
30 
31 #include <kactioncollection.h>
32 #include <ktexteditor/view.h>
33 #include <sonnet/speller.h>
34 
35 #include "katedocument.h"
36 #include "katehighlight.h"
37 
38 KateSpellCheckManager::KateSpellCheckManager(QObject *parent)
39 : QObject(parent)
40 {
41 }
42 
43 KateSpellCheckManager::~KateSpellCheckManager()
44 {
45 }
46 
47 QStringList KateSpellCheckManager::suggestions(const QString& word, const QString& dictionary)
48 {
49  Sonnet::Speller speller;
50  speller.setLanguage(dictionary);
51  return speller.suggest(word);
52 }
53 
54 void KateSpellCheckManager::ignoreWord(const QString& word, const QString& dictionary)
55 {
56  Sonnet::Speller speller;
57  speller.setLanguage(dictionary);
58  speller.addToSession(word);
59 }
60 
61 void KateSpellCheckManager::addToDictionary(const QString& word, const QString& dictionary)
62 {
63  Sonnet::Speller speller;
64  speller.setLanguage(dictionary);
65  speller.addToPersonal(word);
66 }
67 
68 QList<KTextEditor::Range> KateSpellCheckManager::rangeDifference(const KTextEditor::Range& r1,
69  const KTextEditor::Range& r2)
70 {
71  Q_ASSERT(r1.contains(r2));
72  QList<KTextEditor::Range> toReturn;
73  KTextEditor::Range before(r1.start(), r2.start());
74  KTextEditor::Range after(r2.end(), r1.end());
75  if(!before.isEmpty()) {
76  toReturn.push_back(before);
77  }
78  if(!after.isEmpty()) {
79  toReturn.push_back(after);
80  }
81  return toReturn;
82 }
83 
84 namespace {
85  bool lessThanRangeDictionaryPair(const QPair<KTextEditor::Range, QString> &s1,
86  const QPair<KTextEditor::Range, QString> &s2)
87  {
88  return s1.first.end() <= s2.first.start();
89  }
90 }
91 
92 QList<QPair<KTextEditor::Range, QString> > KateSpellCheckManager::spellCheckLanguageRanges(KateDocument *doc,
93  const KTextEditor::Range& range)
94 {
95  QString defaultDict = doc->defaultDictionary();
96  QList<RangeDictionaryPair> toReturn;
97  QList<QPair<KTextEditor::MovingRange*, QString> > dictionaryRanges = doc->dictionaryRanges();
98  if(dictionaryRanges.isEmpty()) {
99  toReturn.push_back(RangeDictionaryPair(range, defaultDict));
100  return toReturn;
101  }
102  QList<KTextEditor::Range> splitQueue;
103  splitQueue.push_back(range);
104  while(!splitQueue.isEmpty()) {
105  bool handled = false;
106  KTextEditor::Range consideredRange = splitQueue.takeFirst();
107  for(QList<QPair<KTextEditor::MovingRange*, QString> >::iterator i = dictionaryRanges.begin();
108  i != dictionaryRanges.end(); ++i) {
109  KTextEditor::Range languageRange = *((*i).first);
110  KTextEditor::Range intersection = languageRange.intersect(consideredRange);
111  if(intersection.isEmpty()) {
112  continue;
113  }
114  toReturn.push_back(RangeDictionaryPair(intersection, (*i).second));
115  splitQueue += rangeDifference(consideredRange, intersection);
116  handled = true;
117  break;
118  }
119  if(!handled) {
120  // 'consideredRange' did not intersect with any dictionary range, so we add it with the default dictionary
121  toReturn.push_back(RangeDictionaryPair(consideredRange, defaultDict));
122  }
123  }
124  // finally, we still have to sort the list
125  qStableSort(toReturn.begin(), toReturn.end(), lessThanRangeDictionaryPair);
126  return toReturn;
127 }
128 
129 QList<QPair<KTextEditor::Range, QString> > KateSpellCheckManager::spellCheckWrtHighlightingRanges(KateDocument *document,
130  const KTextEditor::Range& range,
131  const QString& dictionary,
132  bool singleLine,
133  bool returnSingleRange)
134 {
135  QList<QPair<KTextEditor::Range, QString> > toReturn;
136  if(range.isEmpty()) {
137  return toReturn;
138  }
139 
140  KateHighlighting *highlighting = document->highlight();
141 
142  QList<KTextEditor::Range> rangesToSplit;
143  if(!singleLine || range.onSingleLine()) {
144  rangesToSplit.push_back(range);
145  }
146  else {
147  const int startLine = range.start().line();
148  const int startColumn = range.start().column();
149  const int endLine = range.end().line();
150  const int endColumn = range.end().column();
151  for(int line = startLine; line <= endLine; ++line) {
152  const int start = (line == startLine) ? startColumn : 0;
153  const int end = (line == endLine) ? endColumn : document->lineLength(line);
154  KTextEditor::Range toAdd(line, start, line, end);
155  if(!toAdd.isEmpty()) {
156  rangesToSplit.push_back(toAdd);
157  }
158  }
159  }
160  for(QList<KTextEditor::Range>::iterator i = rangesToSplit.begin(); i != rangesToSplit.end(); ++i) {
161  KTextEditor::Range rangeToSplit = *i;
162  KTextEditor::Cursor begin = KTextEditor::Cursor::invalid();
163  const int startLine = rangeToSplit.start().line();
164  const int startColumn = rangeToSplit.start().column();
165  const int endLine = rangeToSplit.end().line();
166  const int endColumn = rangeToSplit.end().column();
167  bool inSpellCheckArea = false;
168  for(int line = startLine; line <= endLine; ++line) {
169  Kate::TextLine kateTextLine = document->kateTextLine(line);
170  if (!kateTextLine) continue; // bug #303496
171  const int start = (line == startLine) ? startColumn : 0;
172  const int end = (line == endLine) ? endColumn : kateTextLine->length();
173  for(int i = start; i < end;) { // WARNING: 'i' has to be incremented manually!
174  int attr = kateTextLine->attribute(i);
175  const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
176  QString prefixFound = prefixStore.findPrefix(kateTextLine, i);
177  unsigned int attribute = kateTextLine->attribute(i);
178  if(!document->highlight()->attributeRequiresSpellchecking(attribute)
179  && prefixFound.isEmpty()) {
180  if(i == start) {
181  ++i;
182  continue;
183  }
184  else if(inSpellCheckArea) {
185  KTextEditor::Range spellCheckRange(begin, KTextEditor::Cursor(line, i));
186  // work around Qt bug 6498
187  trimRange(document, spellCheckRange);
188  if(!spellCheckRange.isEmpty()) {
189  toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary));
190  if(returnSingleRange) {
191  return toReturn;
192  }
193  }
194  begin = KTextEditor::Cursor::invalid();
195  inSpellCheckArea = false;
196  }
197  }
198  else if(!inSpellCheckArea) {
199  begin = KTextEditor::Cursor(line, i);
200  inSpellCheckArea = true;
201  }
202  if(!prefixFound.isEmpty()) {
203  i += prefixFound.length();
204  }
205  else {
206  ++i;
207  }
208  }
209  }
210  if(inSpellCheckArea) {
211  KTextEditor::Range spellCheckRange(begin, rangeToSplit.end());
212  // work around Qt bug 6498
213  trimRange(document, spellCheckRange);
214  if(!spellCheckRange.isEmpty()) {
215  toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary));
216  if(returnSingleRange) {
217  return toReturn;
218  }
219  }
220  }
221  }
222 
223  return toReturn;
224 }
225 
226 QList<QPair<KTextEditor::Range, QString> > KateSpellCheckManager::spellCheckRanges(KateDocument *doc,
227  const KTextEditor::Range& range,
228  bool singleLine)
229 {
230  QList<RangeDictionaryPair> toReturn;
231  QList<RangeDictionaryPair> languageRangeList = spellCheckLanguageRanges(doc, range);
232  for(QList<RangeDictionaryPair>::iterator i = languageRangeList.begin(); i != languageRangeList.end(); ++i) {
233  const RangeDictionaryPair& p = *i;
234  toReturn += spellCheckWrtHighlightingRanges(doc, p.first, p.second, singleLine);
235  }
236  return toReturn;
237 }
238 
239 void KateSpellCheckManager::replaceCharactersEncodedIfNecessary(const QString& newWord, KateDocument *doc,
240  const KTextEditor::Range& replacementRange)
241 {
242  const QString replacedString = doc->text(replacementRange);
243  int attr = doc->kateTextLine(replacementRange.start().line())->attribute(replacementRange.start().column());
244  int p = doc->highlight()->getEncodedCharactersInsertionPolicy(attr);
245 
246  if((p == KateDocument::EncodeAlways)
247  || (p == KateDocument::EncodeWhenPresent && doc->containsCharacterEncoding(replacementRange))) {
248  doc->replaceText(replacementRange, newWord);
249  doc->replaceCharactersByEncoding(KTextEditor::Range(replacementRange.start(),
250  replacementRange.start() + KTextEditor::Cursor(0, newWord.length())));
251  }
252  else {
253  doc->replaceText(replacementRange, newWord);
254  }
255 }
256 
257 void KateSpellCheckManager::trimRange(KateDocument *doc, KTextEditor::Range &r)
258 {
259  if(r.isEmpty()) {
260  return;
261  }
262  KTextEditor::Cursor cursor = r.start();
263  while(cursor < r.end()) {
264  if(doc->lineLength(cursor.line()) > 0
265  && !doc->character(cursor).isSpace() && doc->character(cursor).category() != QChar::Other_Control) {
266  break;
267  }
268  cursor.setColumn(cursor.column() + 1);
269  if(cursor.column() >= doc->lineLength(cursor.line())) {
270  cursor.setPosition(cursor.line() + 1, 0);
271  }
272  }
273  r.start() = cursor;
274  if(r.isEmpty()) {
275  return;
276  }
277 
278  cursor = r.end();
279  KTextEditor::Cursor prevCursor = cursor;
280  // the range cannot be empty now
281  do {
282  prevCursor = cursor;
283  if(cursor.column() <= 0) {
284  cursor.setPosition(cursor.line() - 1, doc->lineLength(cursor.line() - 1));
285  }
286  else {
287  cursor.setColumn(cursor.column() - 1);
288  }
289  if(cursor.column() < doc->lineLength(cursor.line())
290  && !doc->character(cursor).isSpace() && doc->character(cursor).category() != QChar::Other_Control) {
291  break;
292  }
293  }
294  while(cursor > r.start());
295  r.end() = prevCursor;
296 }
297 
298 #include "spellcheck.moc"
299 
300 // kate: space-indent on; indent-width 2; replace-tabs on;
KateSpellCheckManager::~KateSpellCheckManager
virtual ~KateSpellCheckManager()
Definition: spellcheck.cpp:43
KatePrefixStore
This class can be used to efficiently search for occurrences of strings in a given string...
Definition: prefixstore.h:41
katehighlight.h
KateDocument::replaceCharactersByEncoding
void replaceCharactersByEncoding(const KTextEditor::Range &range)
Definition: katedocument.cpp:5316
QList::push_back
void push_back(const T &value)
KateDocument::highlight
KateHighlighting * highlight() const
Definition: katedocument.cpp:4701
KateSpellCheckManager::rangeDifference
static QList< KTextEditor::Range > rangeDifference(const KTextEditor::Range &r1, const KTextEditor::Range &r2)
'r2' is a subrange of 'r1', which is extracted from 'r1' and the remaining ranges are returned ...
Definition: spellcheck.cpp:68
KateDocument::lineLength
virtual int lineLength(int line) const
Definition: katedocument.cpp:758
katedocument.h
QChar::category
Category category() const
spellcheck.h
KateSpellCheckManager::spellCheckLanguageRanges
QList< QPair< KTextEditor::Range, QString > > spellCheckLanguageRanges(KateDocument *doc, const KTextEditor::Range &range)
Definition: spellcheck.cpp:92
KateHighlighting::getCharacterEncodingsPrefixStore
const KatePrefixStore & getCharacterEncodingsPrefixStore(int attrib) const
Definition: katehighlight.cpp:723
KateDocument::replaceText
virtual bool replaceText(const KTextEditor::Range &range, const QString &s, bool block=false)
Definition: katedocument.cpp:4686
KateHighlighting::attributeRequiresSpellchecking
bool attributeRequiresSpellchecking(int attr)
Definition: katehighlight.cpp:1083
QChar::isSpace
bool isSpace() const
KateHighlighting::getEncodedCharactersInsertionPolicy
int getEncodedCharactersInsertionPolicy(int attrib) const
Definition: katehighlight.cpp:733
KateDocument::character
virtual QChar character(const KTextEditor::Cursor &position) const
Definition: katedocument.cpp:388
QSharedPointer
QObject
KateDocument::dictionaryRanges
QList< QPair< KTextEditor::MovingRange *, QString > > dictionaryRanges() const
Definition: katedocument.cpp:5036
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
KateSpellCheckManager::addToDictionary
void addToDictionary(const QString &word, const QString &dictionary)
Definition: spellcheck.cpp:61
QString
QList
KateSpellCheckManager::suggestions
QStringList suggestions(const QString &word, const QString &dictionary)
Definition: spellcheck.cpp:47
QList::iterator
QStringList
QPair
QList::end
iterator end()
KateDocument
Definition: katedocument.h:74
KateDocument::defaultDictionary
QString defaultDictionary() const
Definition: katedocument.cpp:5031
KateDocument::kateTextLine
Kate::TextLine kateTextLine(uint i)
Definition: katedocument.cpp:4706
KateSpellCheckManager::ignoreWord
void ignoreWord(const QString &word, const QString &dictionary)
Definition: spellcheck.cpp:54
QList::takeFirst
T takeFirst()
KateHighlighting
Definition: katehighlight.h:119
QString::length
int length() const
KateSpellCheckManager::KateSpellCheckManager
KateSpellCheckManager(QObject *parent=NULL)
Definition: spellcheck.cpp:38
KateDocument::text
virtual QString text(const KTextEditor::Range &range, bool blockwise=false) const
Definition: katedocument.cpp:337
KateDocument::containsCharacterEncoding
bool containsCharacterEncoding(const KTextEditor::Range &range)
Definition: katedocument.cpp:5222
KateSpellCheckManager::spellCheckRanges
QList< QPair< KTextEditor::Range, QString > > spellCheckRanges(KateDocument *doc, const KTextEditor::Range &range, bool singleLine=false)
Definition: spellcheck.cpp:226
KateSpellCheckManager::spellCheckWrtHighlightingRanges
QList< QPair< KTextEditor::Range, QString > > spellCheckWrtHighlightingRanges(KateDocument *doc, const KTextEditor::Range &range, const QString &dictionary=QString(), bool singleLine=false, bool returnSingleRange=false)
Definition: spellcheck.cpp:129
KateDocument::EncodeWhenPresent
Definition: katedocument.h:1146
KatePrefixStore::findPrefix
QString findPrefix(const QString &s, int start=0) const
Returns the shortest prefix of the given string that is contained in this prefix store starting at po...
Definition: prefixstore.cpp:120
KateDocument::EncodeAlways
Definition: katedocument.h:1146
QList::begin
iterator begin()
KateSpellCheckManager::replaceCharactersEncodedIfNecessary
void replaceCharactersEncodedIfNecessary(const QString &newWord, KateDocument *doc, const KTextEditor::Range &replacementRange)
Definition: spellcheck.cpp:239
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat May 9 2020 03:56:59 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

applications API Reference

Skip menu "applications API Reference"
  •   kate
  •       kate
  •   KTextEditor
  •   Kate
  • Konsole

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal