bool SpellCheckerCore::isWordUnderCursorMistake(Word& word) const
{
    if(d->currentEditor.isNull() == true) {
        return false;
    }

    unsigned int column = d->currentEditor->currentColumn();
    unsigned int line = d->currentEditor->currentLine();
    QString currentFileName = d->currentEditor->document()->filePath().toString();
    WordList wl;
    wl = d->spellingMistakesModel->mistakesForFile(currentFileName);
    if(wl.isEmpty() == true) {
        return false;
    }
    WordList::ConstIterator iter = wl.constBegin();
    while(iter != wl.constEnd()) {
        const Word& currentWord = iter.value();
        if((currentWord.lineNumber == line)
                && ((currentWord.columnNumber <= column)
                    && (currentWord.columnNumber + currentWord.length) >= column)) {
            word = currentWord;
            return true;
        }
        ++iter;
    }
    return false;
}
bool SpellCheckerCore::getAllOccurrencesOfWord(const Word &word, WordList& words)
{
    if(d->currentEditor.isNull() == true) {
        return false;
    }
    WordList wl;
    QString currentFileName = d->currentEditor->document()->filePath().toString();
    wl = d->spellingMistakesModel->mistakesForFile(currentFileName);
    if(wl.isEmpty() == true) {
        return false;
    }
    WordList::ConstIterator iter = wl.constBegin();
    while(iter != wl.constEnd()) {
        const Word& currentWord = iter.value();
        if(currentWord.text == word.text) {
            words.append(currentWord);
        }
        ++iter;
    }
    return (wl.count() > 0);

}
void SpellCheckProcessor::process(QFutureInterface<WordList> &future)
{
#ifdef BENCH_TIME
    QElapsedTimer timer;
    timer.start();
#endif /* BENCH_TIME */
    WordListConstIter misspelledIter;
    WordListConstIter prevMisspelledIter;
    Word misspelledWord;
    WordList misspelledWords;
    WordList words = d_wordList;
    WordListConstIter wordIter = words.constBegin();
    bool spellingMistake;
    future.setProgressRange(0, words.count() + 1);
    while(wordIter != d_wordList.constEnd()) {
        /* Get the word at the current iterator position.
         * After this is done, move the iterator to the next position and
         * increment the progress value. This is done so that one can call
         * 'continue' anywhere after this in the loop without having to worry
         * about advancing the iterator since this will already be done and
         * correct for the next iteration. */
        misspelledWord = (*wordIter);
        ++wordIter;
        future.setProgressValue(future.progressValue() + 1);
        /* Check if the future was cancelled */
        if(future.isCanceled() == true) {
            return;
        }
        spellingMistake = d_spellChecker->isSpellingMistake(misspelledWord.text);
        /* Check to see if the char after the word is a period. If it is,
         * add the period to the word an see if it passes the checker. */
        if((spellingMistake == true)
                && (misspelledWord.charAfter == QLatin1Char('.'))) {
            /* Recheck the word with the period added */
            spellingMistake = d_spellChecker->isSpellingMistake(misspelledWord.text + QLatin1Char('.'));
        }

        if(spellingMistake == true) {
            /* The word is a spelling mistake, check if the word was a mistake
             * the previous time that this file was processed. If it was the
             * suggestions can be reused without having to get the suggestions
             * through the spell checker since this is slow compared to the rest
             * of the processing. */
            prevMisspelledIter = d_previousMistakes.find(misspelledWord.text);
            if(prevMisspelledIter != d_previousMistakes.constEnd()) {
                misspelledWord.suggestions = (*prevMisspelledIter).suggestions;
                misspelledWords.append(misspelledWord);
                continue;
            }
            /* The word was not in the previous iteration, search for the word
             * in the list of words that were already checked in this iteration
             * and identified as spelling mistakes. If there are words that are
             * repeated and misspelled, this can reduce the time to process
             * the file since the time to get suggestions is rather slow.
             * If there are no repeating mistakes then this might add unneeded
             * overhead. */
            misspelledIter = misspelledWords.find(misspelledWord.text);
            if(misspelledIter != misspelledWords.constEnd()) {
                misspelledWord.suggestions = (*misspelledIter).suggestions;
                /* Add the word to the local list of misspelled words. */
                misspelledWords.append(misspelledWord);
                continue;
            }
            /* At this point the word is a mistake for the first time. It was neither
             * a mistake in the previous pass of the file nor did the word occur previously
             * in this file, use the spell checker to get the suggestions for the word. */
            d_spellChecker->getSuggestionsForWord(misspelledWord.text, misspelledWord.suggestions);
            /* Add the word to the local list of misspelled words. */
            misspelledWords.append(misspelledWord);
        }
    }
#ifdef BENCH_TIME
    qDebug() << "File: " << d_fileName
             << "\n  - time : " << timer.elapsed()
             << "\n  - count: " << misspelledWords.size();
#endif /* BENCH_TIME */
    future.reportResult(misspelledWords);
}
void SpellCheckerCore::addMisspelledWords( const QString& fileName, const WordList& words )
{
  d->spellingMistakesModel->insertSpellingMistakes( fileName, words, d->filesInStartupProject.contains( fileName ) );
  if( d->currentFilePath == fileName ) {
    d->mistakesModel->setCurrentSpellingMistakes( words );
  }

  /* Only apply the underlines to the current file. This is done so that if the
   * whole project is scanned, it does not add selections to pages that might
   * potentially never be opened. This can especially be a problem in large
   * projects.
   */
  if( d->currentFilePath != fileName ) {
    return;
  }
  TextEditor::BaseTextEditor* baseEditor = qobject_cast<TextEditor::BaseTextEditor*>( d->currentEditor );
  if( baseEditor == nullptr ) {
    return;
  }
  TextEditor::TextEditorWidget* editorWidget = baseEditor->editorWidget();
  if( editorWidget == nullptr ) {
    return;
  }
  QTextDocument* document = editorWidget->document();
  if( document == nullptr ) {
    return;
  }
  QList<QTextEdit::ExtraSelection> selections;
  selections.reserve( words.size() );
  const WordList::ConstIterator wordsEnd = words.constEnd();
  for( WordList::ConstIterator wordIter = words.constBegin(); wordIter != wordsEnd; ++wordIter ) {
    const Word& word = wordIter.value();
    /* Get the QTextBlock for the line that the misspelled word is on.
     * The QTextDocument manages lines as blocks (in most cases).
     * The lineNumber of the misspelled word is 1 based (seen in the editor)
     * but the blocks on the QTextDocument are 0 based, thus minus one
     * from the line number to get the correct line.
     * If the block is valid, and the word is not longer than the number of
     * characters in the block (which should normally not be the case)
     * then the cursor is moved to the correct column, and the word is
     * underlined.
     * Again the Columns on the misspelled word is 1 based but
     * the blocks and cursor are 0 based. */
    const QTextBlock& block = document->findBlockByNumber( int32_t( word.lineNumber ) - 1 );
    if( ( block.isValid() == false )
        || ( uint32_t( block.length() ) < ( word.columnNumber - 1 + uint32_t( word.length ) ) ) ) {
      continue;
    }

    QTextCursor cursor( block );
    cursor.setPosition( cursor.position() + int32_t( word.columnNumber ) - 1 );
    cursor.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, word.length );
    /* Get the current format from the cursor, this is to make sure that the text font
     * and color stays the same, we just want to underline the mistake. */
    QTextCharFormat format = cursor.charFormat();
    format.setFontUnderline( true );
    static const QColor underLineColor = QColor( Qt::red );
    format.setUnderlineColor( underLineColor );
    format.setUnderlineStyle( QTextCharFormat::WaveUnderline );
    format.setToolTip( word.suggestions.isEmpty()
                       ? QStringLiteral( "Incorrect spelling" )
                       : QStringLiteral( "Incorrect spelling, did you mean '%1' ?" ).arg( word.suggestions.first() ) );
    QTextEdit::ExtraSelection selection;
    selection.cursor = cursor;
    selection.format = format;
    selections.append( selection );
  }
  editorWidget->setExtraSelections( Core::Id( SpellChecker::Constants::SPELLCHECK_MISTAKE_ID ), selections );

  /* The model updated, check if the word under the cursor is now a mistake
   * and notify the rest of the checker with this information. */
  Word word;
  bool wordIsMisspelled = isWordUnderCursorMistake( word );
  emit wordUnderCursorMistake( wordIsMisspelled, word );
}