From 72bb7c5c1119d56b39d6fe12b7492de5f3f236e4 Mon Sep 17 00:00:00 2001
From: mae <qt-info@nokia.com>
Date: Tue, 13 Jul 2010 14:37:31 +0200
Subject: [PATCH] Convert semantic highlighter to use additional formats

instead of extra selections. This requires a recent
version of Qt 4.7, the beta 2 will not do.

Done-with: Roberto Raggi
---
 .../cppeditor/cppcheckundefinedsymbols.h      |  16 +++
 src/plugins/cppeditor/cppeditor.cpp           | 120 +++++++++++-------
 src/plugins/cppeditor/cppeditor.h             |   6 +-
 src/plugins/texteditor/basetexteditor.cpp     |   5 +-
 src/plugins/texteditor/basetexteditor.h       |   1 -
 src/plugins/texteditor/syntaxhighlighter.cpp  |  99 +++++++++++++--
 src/plugins/texteditor/syntaxhighlighter.h    |   3 +
 7 files changed, 185 insertions(+), 65 deletions(-)

diff --git a/src/plugins/cppeditor/cppcheckundefinedsymbols.h b/src/plugins/cppeditor/cppcheckundefinedsymbols.h
index 4dbc0b52f3c..49579c6c6db 100644
--- a/src/plugins/cppeditor/cppcheckundefinedsymbols.h
+++ b/src/plugins/cppeditor/cppcheckundefinedsymbols.h
@@ -56,6 +56,22 @@ public:
     typedef QFuture<Use> Future;
     static Future go(Document::Ptr doc, const LookupContext &context);
 
+    static QMap<int, QVector<Use> > chunks(const QFuture<Use> &future, int from, int to)
+    {
+        QMap<int, QVector<Use> > chunks;
+
+        for (int i = from; i < to; ++i) {
+            const Use use = future.resultAt(i);
+            if (! use.line)
+                continue; // skip it, it's an invalid use.
+
+            const int blockNumber = use.line - 1;
+            chunks[blockNumber].append(use);
+        }
+
+        return chunks;
+    }
+
 protected:
     using ASTVisitor::visit;
     using ASTVisitor::endVisit;
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index c2d42cfc6fb..6be22af5399 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -129,47 +129,6 @@ static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document
     return selections;
 }
 
-static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
-                                                         const QList<SemanticInfo::Use> &msgs,
-                                                         const QTextCharFormat &format)
-{
-    QList<QTextEdit::ExtraSelection> selections;
-
-    QTextBlock currentBlock = document->firstBlock();
-    unsigned currentLine = 1;
-
-    foreach (const SemanticInfo::Use &use, msgs) {
-        QTextCursor cursor(document);
-
-        if (currentLine != use.line) {
-            int delta = use.line - currentLine;
-
-            if (delta >= 0) {
-                while (delta--)
-                    currentBlock = currentBlock.next();
-            } else {
-                currentBlock = document->findBlockByNumber(use.line - 1);
-            }
-
-            currentLine = use.line;
-        }
-
-        const int pos = currentBlock.position() + use.column - 1;
-        if (pos < 0)
-            continue;
-
-        cursor.setPosition(pos);
-        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, use.length);
-
-        QTextEdit::ExtraSelection sel;
-        sel.cursor = cursor;
-        sel.format = format;
-        selections.append(sel);
-    }
-
-    return selections;
-}
-
 namespace {
 
 class OverviewTreeView : public QTreeView
@@ -643,8 +602,10 @@ CPPEditor::CPPEditor(QWidget *parent)
                 this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
     }
 
-    m_highlighteRevision = 0;
-    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(highlightTypeUsages()));
+    m_highlightRevision = 0;
+    m_nextHighlightBlockNumber = 0;
+    connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightTypeUsages(int,int)));
+    connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishTypeUsages()));
 }
 
 CPPEditor::~CPPEditor()
@@ -1139,8 +1100,9 @@ void CPPEditor::updateOutlineToolTip()
 
 void CPPEditor::updateUses()
 {
+    if (editorRevision() != m_highlightRevision)
+        m_highlighter.cancel();
     m_updateUsesTimer->start();
-    m_highlighter.cancel();
 }
 
 void CPPEditor::updateUsesNow()
@@ -1151,18 +1113,76 @@ void CPPEditor::updateUsesNow()
     semanticRehighlight();
 }
 
-void CPPEditor::highlightTypeUsages()
+void CPPEditor::highlightTypeUsages(int from, int to)
+{
+    if (editorRevision() != m_highlightRevision)
+        return; // outdated
+
+    else if (m_highlighter.isCanceled())
+        return; // aborted
+
+    CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
+    Q_ASSERT(highlighter);
+    QTextDocument *doc = document();
+
+    if (m_nextHighlightBlockNumber >= doc->blockCount())
+        return;
+
+    QMap<int, QVector<SemanticInfo::Use> > chunks = CheckUndefinedSymbols::chunks(m_highlighter, from, to);
+    Q_ASSERT(!chunks.isEmpty());
+    QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
+
+    QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
+    while (b.isValid() && it.hasNext()) {
+        it.next();
+        const int blockNumber = it.key();
+        Q_ASSERT(blockNumber < doc->blockCount());
+
+        while (m_nextHighlightBlockNumber < blockNumber) {
+            highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+            b = b.next();
+            ++m_nextHighlightBlockNumber;
+        }
+
+        QList<QTextLayout::FormatRange> formats;
+        foreach (const SemanticInfo::Use &use, it.value()) {
+            QTextLayout::FormatRange formatRange;
+            formatRange.format = m_typeFormat;
+            formatRange.start = use.column - 1;
+            formatRange.length = use.length;
+            formats.append(formatRange);
+        }
+        highlighter->setExtraAdditionalFormats(b, formats);
+        b = b.next();
+        ++m_nextHighlightBlockNumber;
+    }
+}
+
+void CPPEditor::finishTypeUsages()
 {
-    if (editorRevision() != m_highlighteRevision)
+    if (editorRevision() != m_highlightRevision)
         return; // outdated
 
     else if (m_highlighter.isCanceled())
         return; // aborted
 
-    const QList<SemanticInfo::Use> typeUsages = m_highlighter.results();
-    setExtraSelections(TypeSelection, createSelections(document(), typeUsages, m_typeFormat));
+    CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
+    Q_ASSERT(highlighter);
+    QTextDocument *doc = document();
+
+    if (m_nextHighlightBlockNumber >= doc->blockCount())
+        return;
+
+    QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
+
+    while (b.isValid()) {
+        highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+        b = b.next();
+        ++m_nextHighlightBlockNumber;
+    }
 }
 
+
 void CPPEditor::switchDeclarationDefinition()
 {
     if (! m_modelManager)
@@ -1959,7 +1979,8 @@ void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
             LookupContext context(semanticInfo.doc, semanticInfo.snapshot);
             CheckUndefinedSymbols::Future f = CheckUndefinedSymbols::go(semanticInfo.doc, context);
             m_highlighter = f;
-            m_highlighteRevision = semanticInfo.revision;
+            m_highlightRevision = semanticInfo.revision;
+            m_nextHighlightBlockNumber = 0;
             m_highlightWatcher.setFuture(m_highlighter);
         }
 
@@ -1970,6 +1991,7 @@ void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
 #endif
     }
 
+
     setExtraSelections(UnusedSymbolSelection, unusedSelections);
 
     if (! m_renameSelections.isEmpty())
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index fe7cff471cc..738a9775866 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -233,7 +233,8 @@ private Q_SLOTS:
 
     void semanticRehighlight();
     void updateSemanticInfo(const CppEditor::Internal::SemanticInfo &semanticInfo);
-    void highlightTypeUsages();
+    void highlightTypeUsages(int from, int to);
+    void finishTypeUsages();
 
     void performQuickFix(int index);
 
@@ -300,7 +301,8 @@ private:
 
     QFuture<SemanticInfo::Use> m_highlighter;
     QFutureWatcher<SemanticInfo::Use> m_highlightWatcher;
-    unsigned m_highlighteRevision; // the editor revision that requested the highlight
+    unsigned m_highlightRevision; // the editor revision that requested the highlight
+    int m_nextHighlightBlockNumber;
 };
 
 
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index 3283b7b77d7..37f3ebcea02 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -4063,10 +4063,8 @@ int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
         while (block.isValid() && ts.onlySpace(block.text()))
             block = block.next();
         if (block.isValid()
-                && ts.indentationColumn(block.text()) > indentation) {
-            qDebug() << "indentation check failed" << indentation << ts.indentationColumn(block.next().text());
+                && ts.indentationColumn(block.text()) > indentation)
             return 0;
-        }
     }
 
     int pos = cursor.position();
@@ -4664,7 +4662,6 @@ void BaseTextEditor::maybeClearSomeExtraSelections(const QTextCursor &cursor)
     if (cursor.selectionEnd() - cursor.selectionStart() < smallSelectionSize)
         return;
 
-    d->m_extraSelections[TypeSelection].clear();
     d->m_extraSelections[UndefinedSymbolSelection].clear();
     d->m_extraSelections[ObjCSelection].clear();
     d->m_extraSelections[CodeWarningsSelection].clear();
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index 8a0cb0499ca..450c3350730 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -349,7 +349,6 @@ public:
         FakeVimSelection,
         OtherSelection,
         SnippetPlaceholderSelection,
-        TypeSelection,
         ObjCSelection,
         NExtraSelectionKinds
     };
diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp
index 59113d99f44..a2245f79231 100644
--- a/src/plugins/texteditor/syntaxhighlighter.cpp
+++ b/src/plugins/texteditor/syntaxhighlighter.cpp
@@ -65,7 +65,7 @@ public:
 
     void _q_reformatBlocks(int from, int charsRemoved, int charsAdded);
     void reformatBlocks(int from, int charsRemoved, int charsAdded);
-    void reformatBlock(const QTextBlock &block);
+    void reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded);
 
     inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) {
         inReformatBlocks = true;
@@ -84,14 +84,26 @@ public:
         q_func()->rehighlight();
     }
 
-    void applyFormatChanges();
+    void applyFormatChanges(int from, int charsRemoved, int charsAdded);
     QVector<QTextCharFormat> formatChanges;
     QTextBlock currentBlock;
     bool rehighlightPending;
     bool inReformatBlocks;
 };
 
-void SyntaxHighlighterPrivate::applyFormatChanges()
+static bool adjustRange(QTextLayout::FormatRange &range, int from, int charsRemoved, int charsAdded) {
+
+    if (range.start >= from) {
+        range.start += charsAdded - charsRemoved;
+        return true;
+    } else if (range.start + range.length > from) {
+        range.length += charsAdded - charsRemoved;
+        return true;
+    }
+    return false;
+}
+
+void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded)
 {
     bool formatsChanged = false;
 
@@ -101,11 +113,17 @@ void SyntaxHighlighterPrivate::applyFormatChanges()
 
     const int preeditAreaStart = layout->preeditAreaPosition();
     const int preeditAreaLength = layout->preeditAreaText().length();
+    bool doAdjustRange = currentBlock.contains(from);
 
     if (preeditAreaLength != 0) {
         QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
         while (it != ranges.end()) {
-            if (it->start >= preeditAreaStart
+            if (it->format.property(QTextFormat::UserProperty).toBool()) {
+                if (doAdjustRange)
+                    formatsChanged = adjustRange(*it, from - currentBlock.position(), charsRemoved, charsAdded)
+                            || formatsChanged;
+                ++it;
+            } else  if (it->start >= preeditAreaStart
                 && it->start + it->length <= preeditAreaStart + preeditAreaLength) {
                 ++it;
             } else {
@@ -114,8 +132,18 @@ void SyntaxHighlighterPrivate::applyFormatChanges()
             }
         }
     } else if (!ranges.isEmpty()) {
-        ranges.clear();
-        formatsChanged = true;
+        QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
+        while (it != ranges.end()) {
+            if (it->format.property(QTextFormat::UserProperty).toBool()) {
+                if (doAdjustRange)
+                    formatsChanged = adjustRange(*it, from - currentBlock.position(), charsRemoved, charsAdded)
+                            || formatsChanged;
+                ++it;
+            } else {
+                it = ranges.erase(it);
+                formatsChanged = true;
+            }
+        }
     }
 
     QTextCharFormat emptyFormat;
@@ -201,7 +229,7 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
     while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
         const int stateBeforeHighlight = block.userState();
 
-        reformatBlock(block);
+        reformatBlock(block, from, charsRemoved, charsAdded);
 
         forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
 
@@ -211,7 +239,7 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
     formatChanges.clear();
 }
 
-void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
+void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded)
 {
     Q_Q(SyntaxHighlighter);
 
@@ -221,7 +249,7 @@ void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
 
     formatChanges.fill(QTextCharFormat(), block.length() - 1);
     q->highlightBlock(block.text());
-    applyFormatChanges();
+    applyFormatChanges(from, charsRemoved, charsAdded);
 
     currentBlock = QTextBlock();
 }
@@ -659,4 +687,57 @@ QTextBlock SyntaxHighlighter::currentBlock() const
     return d->currentBlock;
 }
 
+void SyntaxHighlighter::setExtraAdditionalFormats(const QTextBlock& block,
+                                                  const QList<QTextLayout::FormatRange> &formats)
+{
+
+//    qDebug() << "setAdditionalFormats() on block" << block.blockNumber();
+//    for (int i = 0; i < overrides.count(); ++i)
+//        qDebug() << "   from " << overrides.at(i).start << "length"
+//                 << overrides.at(i).length
+//                 << "color:" << overrides.at(i).format.foreground().color();
+    Q_D(SyntaxHighlighter);
+
+    if (block.layout() == 0)
+        return;
+
+    QList<QTextLayout::FormatRange> all = block.layout()->additionalFormats();
+
+    bool modified = false;
+
+    int skip = 0;
+
+    QList<QTextLayout::FormatRange>::Iterator it = all.begin();
+    while (it != all.end()) {
+        if (it->format.property(QTextFormat::UserProperty).toBool()) {
+            if (skip < formats.size()
+                    && it->start == formats.at(skip).start
+                    && it->length == formats.at(skip).length) {
+                ++skip;
+                ++it;
+            } else {
+                it = all.erase(it);
+                modified = true;
+            }
+        } else {
+            ++it;
+        }
+    }
+
+    if (!modified && skip == formats.length())
+        return; // skip'em all
+
+    for (int i = skip; i < formats.length(); ++i) {
+        QTextLayout::FormatRange range = formats.at(i);
+        range.format.setProperty(QTextFormat::UserProperty, true);
+        all.append(range);
+    }
+
+    bool wasInReformatBlocks = d->inReformatBlocks;
+    d->inReformatBlocks = true;
+    block.layout()->setAdditionalFormats(all);
+    document()->markContentsDirty(block.position(), block.length()-1);
+    d->inReformatBlocks = wasInReformatBlocks;
+}
+
 #include "moc_syntaxhighlighter.cpp"
diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h
index aadbfb46d28..5b6722b5a34 100644
--- a/src/plugins/texteditor/syntaxhighlighter.h
+++ b/src/plugins/texteditor/syntaxhighlighter.h
@@ -46,6 +46,7 @@
 #include <QtCore/qglobal.h>
 #include <QtCore/qobject.h>
 #include <QtGui/qtextobject.h>
+#include <QtGui/QTextLayout>
 
 QT_BEGIN_NAMESPACE
 class QTextDocument;
@@ -74,6 +75,8 @@ public:
     void setDocument(QTextDocument *doc);
     QTextDocument *document() const;
 
+    void setExtraAdditionalFormats(const QTextBlock& block, const QList<QTextLayout::FormatRange> &formats);
+
 public Q_SLOTS:
     void rehighlight();
     void rehighlightBlock(const QTextBlock &block);
-- 
GitLab