OSDN Git Service

QmlJS: Add semantic highlighting.
authorChristian Kamm <christian.d.kamm@nokia.com>
Tue, 16 Aug 2011 07:47:54 +0000 (09:47 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Wed, 24 Aug 2011 09:30:20 +0000 (11:30 +0200)
Change-Id: If9293244075cff1a52801b50cdbb77248ecd21ea
Reviewed-on: http://codereview.qt.nokia.com/3310
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
20 files changed:
src/libs/qmljs/qmljsscopechain.cpp
src/libs/qmljs/qmljsscopechain.h
src/plugins/cppeditor/cppchecksymbols.cpp
src/plugins/cppeditor/cppchecksymbols.h
src/plugins/cppeditor/cppeditor.cpp
src/plugins/cppeditor/cppeditor.h
src/plugins/cppeditor/cpplocalsymbols.cpp
src/plugins/cppeditor/cppsemanticinfo.h
src/plugins/qmljseditor/qmljseditor.cpp
src/plugins/qmljseditor/qmljseditor.h
src/plugins/qmljseditor/qmljseditor.pro
src/plugins/qmljseditor/qmljssemantichighlighter.cpp [new file with mode: 0644]
src/plugins/qmljseditor/qmljssemantichighlighter.h [new file with mode: 0644]
src/plugins/texteditor/fontsettingspage.cpp
src/plugins/texteditor/fontsettingspage.h
src/plugins/texteditor/semantichighlighter.cpp [new file with mode: 0644]
src/plugins/texteditor/semantichighlighter.h [new file with mode: 0644]
src/plugins/texteditor/texteditor.pro
src/plugins/texteditor/texteditorconstants.h
src/plugins/texteditor/texteditorsettings.cpp

index 5f60563..724f888 100644 (file)
@@ -56,6 +56,20 @@ QList<const QmlComponentChain *> QmlComponentChain::instantiatingComponents() co
     return m_instantiatingComponents;
 }
 
+const ObjectValue *QmlComponentChain::idScope() const
+{
+    if (!m_document)
+        return 0;
+    return m_document->bind()->idEnvironment();
+}
+
+const ObjectValue *QmlComponentChain::rootObjectScope() const
+{
+    if (!m_document)
+        return 0;
+    return m_document->bind()->rootObjectValue();
+}
+
 void QmlComponentChain::addInstantiatingComponent(const QmlComponentChain *component)
 {
     m_instantiatingComponents.append(component);
@@ -188,9 +202,9 @@ static void collectScopes(const QmlComponentChain *chain, QList<const ObjectValu
     if (!chain->document())
         return;
 
-    if (ObjectValue *root = chain->document()->bind()->rootObjectValue())
+    if (const ObjectValue *root = chain->rootObjectScope())
         target->append(root);
-    if (ObjectValue *ids = chain->document()->bind()->idEnvironment())
+    if (const ObjectValue *ids = chain->idScope())
         target->append(ids);
 }
 
@@ -294,4 +308,3 @@ void ScopeChain::makeComponentChain(
         }
     }
 }
-
index e55aeaf..6eb9e57 100644 (file)
@@ -58,6 +58,9 @@ public:
     Document::Ptr document() const;
     QList<const QmlComponentChain *> instantiatingComponents() const;
 
+    const ObjectValue *idScope() const;
+    const ObjectValue *rootObjectScope() const;
+
     // takes ownership
     void addInstantiatingComponent(const QmlComponentChain *component);
 
index 8d459ef..ee1e0c4 100644 (file)
@@ -442,7 +442,7 @@ bool CheckSymbols::visit(NamespaceAST *ast)
         if (! tok.generated()) {
             unsigned line, column;
             getTokenStartPosition(ast->identifier_token, &line, &column);
-            Use use(line, column, tok.length());
+            Use use(line, column, tok.length(), SemanticInfo::TypeUse);
             addUse(use);
         }
     }
@@ -457,7 +457,7 @@ bool CheckSymbols::visit(UsingDirectiveAST *)
 
 bool CheckSymbols::visit(EnumeratorAST *ast)
 {
-    addUse(ast->identifier_token, Use::Static);
+    addUse(ast->identifier_token, SemanticInfo::StaticUse);
     return true;
 }
 
@@ -469,7 +469,7 @@ bool CheckSymbols::visit(SimpleDeclarationAST *ast)
             if (NameAST *declId = declaratorId(ast->declarator_list->value)) {
                 if (Function *funTy = decl->type()->asFunctionType()) {
                     if (funTy->isVirtual()) {
-                        addUse(declId, Use::VirtualMethod);
+                        addUse(declId, SemanticInfo::VirtualMethodUse);
                     } else if (maybeVirtualMethod(decl->name())) {
                         addVirtualMethod(_context.lookup(decl->name(), decl->enclosingScope()), declId, funTy->argumentCount());
                     }
@@ -490,7 +490,7 @@ bool CheckSymbols::visit(ElaboratedTypeSpecifierAST *ast)
 {
     accept(ast->attribute_list);
     accept(ast->name);
-    addUse(ast->name, Use::Type);
+    addUse(ast->name, SemanticInfo::TypeUse);
     return false;
 }
 
@@ -643,7 +643,7 @@ void CheckSymbols::checkName(NameAST *ast, Scope *scope)
         if (ast->asDestructorName() != 0) {
             Class *klass = scope->asClass();
             if (hasVirtualDestructor(_context.lookupType(klass)))
-                addUse(ast, Use::VirtualMethod);
+                addUse(ast, SemanticInfo::VirtualMethodUse);
         } else if (maybeType(ast->name) || maybeStatic(ast->name)) {
             const QList<LookupItem> candidates = _context.lookup(ast->name, scope);
             addTypeOrStatic(candidates, ast);
@@ -693,7 +693,7 @@ bool CheckSymbols::visit(QualifiedNameAST *ast)
                     if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
                         if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId()) {
                             if (template_id->template_token) {
-                                addUse(template_id, Use::Type);
+                                addUse(template_id, SemanticInfo::TypeUse);
                                 binding = 0; // there's no way we can find a binding.
                             }
 
@@ -714,7 +714,7 @@ bool CheckSymbols::visit(QualifiedNameAST *ast)
         if (binding && ast->unqualified_name) {
             if (ast->unqualified_name->asDestructorName() != 0) {
                 if (hasVirtualDestructor(binding))
-                    addUse(ast->unqualified_name, Use::VirtualMethod);
+                    addUse(ast->unqualified_name, SemanticInfo::VirtualMethodUse);
             } else {
                 addTypeOrStatic(binding->find(ast->unqualified_name->name), ast->unqualified_name);
             }
@@ -729,7 +729,7 @@ bool CheckSymbols::visit(QualifiedNameAST *ast)
 
 bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
 {
-    addUse(ast->name, Use::Type);
+    addUse(ast->name, SemanticInfo::TypeUse);
     accept(ast->type_id);
     return false;
 }
@@ -737,7 +737,7 @@ bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
 bool CheckSymbols::visit(TemplateTypeParameterAST *ast)
 {
     accept(ast->template_parameter_list);
-    addUse(ast->name, Use::Type);
+    addUse(ast->name, SemanticInfo::TypeUse);
     accept(ast->type_id);
     return false;
 }
@@ -775,7 +775,7 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast)
                 declId = q->unqualified_name;
 
             if (fun->isVirtual()) {
-                addUse(declId, Use::VirtualMethod);
+                addUse(declId, SemanticInfo::VirtualMethodUse);
             } else if (maybeVirtualMethod(fun->name())) {
                 addVirtualMethod(_context.lookup(fun->name(), fun->enclosingScope()), declId, fun->argumentCount());
             }
@@ -798,7 +798,7 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast)
     return false;
 }
 
-void CheckSymbols::addUse(NameAST *ast, Use::Kind kind)
+void CheckSymbols::addUse(NameAST *ast, UseKind kind)
 {
     if (! ast)
         return;
@@ -822,7 +822,7 @@ void CheckSymbols::addUse(NameAST *ast, Use::Kind kind)
     addUse(startToken, kind);
 }
 
-void CheckSymbols::addUse(unsigned tokenIndex, Use::Kind kind)
+void CheckSymbols::addUse(unsigned tokenIndex, UseKind kind)
 {
     if (! tokenIndex)
         return;
@@ -871,7 +871,7 @@ void CheckSymbols::addType(ClassOrNamespace *b, NameAST *ast)
     unsigned line, column;
     getTokenStartPosition(startToken, &line, &column);
     const unsigned length = tok.length();
-    const Use use(line, column, length, Use::Type);
+    const Use use(line, column, length, SemanticInfo::TypeUse);
     addUse(use);
     //qDebug() << "added use" << oo(ast->name) << line << column << length;
 }
@@ -913,10 +913,10 @@ void CheckSymbols::addTypeOrStatic(const QList<LookupItem> &candidates, NameAST
             getTokenStartPosition(startToken, &line, &column);
             const unsigned length = tok.length();
 
-            Use::Kind kind = Use::Type;
+            UseKind kind = SemanticInfo::TypeUse;
 
             if (c->enclosingEnum() != 0)
-                kind = Use::Static;
+                kind = SemanticInfo::StaticUse;
 
             const Use use(line, column, length, kind);
             addUse(use);
@@ -951,7 +951,7 @@ void CheckSymbols::addClassMember(const QList<LookupItem> &candidates, NameAST *
         getTokenStartPosition(startToken, &line, &column);
         const unsigned length = tok.length();
 
-        const Use use(line, column, length, Use::Field);
+        const Use use(line, column, length, SemanticInfo::FieldUse);
         addUse(use);
         break;
     }
@@ -976,7 +976,7 @@ void CheckSymbols::addStatic(const QList<LookupItem> &candidates, NameAST *ast)
             getTokenStartPosition(startToken, &line, &column);
             const unsigned length = tok.length();
 
-            const Use use(line, column, length, Use::Static);
+            const Use use(line, column, length, SemanticInfo::StaticUse);
             addUse(use);
             //qDebug() << "added use" << oo(ast->name) << line << column << length;
             break;
@@ -1015,7 +1015,7 @@ void CheckSymbols::addVirtualMethod(const QList<LookupItem> &candidates, NameAST
         getTokenStartPosition(startToken, &line, &column);
         const unsigned length = tok.length();
 
-        const Use use(line, column, length, Use::VirtualMethod);
+        const Use use(line, column, length, SemanticInfo::VirtualMethodUse);
         addUse(use);
         break;
     }
index 6857dd1..f54a7fb 100644 (file)
@@ -55,6 +55,7 @@ public:
     virtual ~CheckSymbols();
 
     typedef CppEditor::Internal::SemanticInfo::Use Use;
+    typedef CppEditor::Internal::SemanticInfo::UseKind UseKind;
 
     virtual void run();
 
@@ -110,8 +111,8 @@ protected:
     void checkNamespace(NameAST *name);
 
     void addUse(const Use &use);
-    void addUse(unsigned tokenIndex, Use::Kind kind);
-    void addUse(NameAST *name, Use::Kind kind);
+    void addUse(unsigned tokenIndex, UseKind kind);
+    void addUse(NameAST *name, UseKind kind);
 
     void addType(ClassOrNamespace *b, NameAST *ast);
 
index c32b232..aa0477d 100644 (file)
@@ -87,6 +87,7 @@
 #include <texteditor/tabsettings.h>
 #include <texteditor/texteditorconstants.h>
 #include <texteditor/refactoroverlay.h>
+#include <texteditor/semantichighlighter.h>
 #include <texteditor/codeassist/basicproposalitemlistmodel.h>
 #include <texteditor/codeassist/basicproposalitem.h>
 #include <texteditor/codeassist/genericproposal.h>
@@ -461,7 +462,6 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
     }
 
     m_highlightRevision = 0;
-    m_nextHighlightBlockNumber = 0;
     connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
     connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
 
@@ -1010,68 +1010,11 @@ void CPPEditorWidget::highlightSymbolUsages(int from, int to)
     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 = CheckSymbols::chunks(m_highlighter, from, to);
-    if (chunks.isEmpty())
-        return;
-
-    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;
+    TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
+    QTC_ASSERT(highlighter, return);
 
-            switch (use.kind) {
-            case SemanticInfo::Use::Type:
-                formatRange.format = m_typeFormat;
-                break;
-
-            case SemanticInfo::Use::Field:
-                formatRange.format = m_fieldFormat;
-                break;
-
-            case SemanticInfo::Use::Local:
-                formatRange.format = m_localFormat;
-                break;
-
-            case SemanticInfo::Use::Static:
-                formatRange.format = m_staticFormat;
-                break;
-
-            case SemanticInfo::Use::VirtualMethod:
-                formatRange.format = m_virtualMethodFormat;
-                break;
-
-            default:
-                continue;
-            }
-
-            formatRange.start = use.column - 1;
-            formatRange.length = use.length;
-            formats.append(formatRange);
-        }
-        highlighter->setExtraAdditionalFormats(b, formats);
-        b = b.next();
-        ++m_nextHighlightBlockNumber;
-    }
+    TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
+                highlighter, m_highlighter, from, to, m_semanticHighlightFormatMap);
 }
 
 void CPPEditorWidget::finishHighlightSymbolUsages()
@@ -1082,20 +1025,11 @@ void CPPEditorWidget::finishHighlightSymbolUsages()
     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;
-
-    QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
+    TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
+    QTC_ASSERT(highlighter, return);
 
-    while (b.isValid()) {
-        highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
-        b = b.next();
-        ++m_nextHighlightBlockNumber;
-    }
+    TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
+                highlighter, m_highlighter);
 }
 
 
@@ -1777,11 +1711,16 @@ void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
     m_occurrencesUnusedFormat.clearForeground();
     m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
     m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
-    m_typeFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
-    m_localFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
-    m_fieldFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD));
-    m_staticFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC));
-    m_virtualMethodFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD));
+    m_semanticHighlightFormatMap[SemanticInfo::TypeUse] =
+            fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
+    m_semanticHighlightFormatMap[SemanticInfo::LocalUse] =
+            fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
+    m_semanticHighlightFormatMap[SemanticInfo::FieldUse] =
+            fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD));
+    m_semanticHighlightFormatMap[SemanticInfo::StaticUse] =
+            fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC));
+    m_semanticHighlightFormatMap[SemanticInfo::VirtualMethodUse] =
+            fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD));
     m_keywordFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_KEYWORD));
 
     // only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
@@ -1921,7 +1860,6 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
                 CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
                 m_highlighter = f;
                 m_highlightRevision = semanticInfo.revision;
-                m_nextHighlightBlockNumber = 0;
                 m_highlightWatcher.setFuture(m_highlighter);
             }
         }
index 55278ba..1c46fdd 100644 (file)
@@ -297,12 +297,8 @@ private:
     QTextCharFormat m_occurrencesFormat;
     QTextCharFormat m_occurrencesUnusedFormat;
     QTextCharFormat m_occurrenceRenameFormat;
-    QTextCharFormat m_typeFormat;
-    QTextCharFormat m_localFormat;
-    QTextCharFormat m_fieldFormat;
-    QTextCharFormat m_staticFormat;
+    QHash<int, QTextCharFormat> m_semanticHighlightFormatMap;
     QTextCharFormat m_keywordFormat;
-    QTextCharFormat m_virtualMethodFormat;
 
     QList<QTextEdit::ExtraSelection> m_renameSelections;
     int m_currentRenameSelection;
@@ -320,7 +316,6 @@ private:
     QFuture<SemanticInfo::Use> m_highlighter;
     QFutureWatcher<SemanticInfo::Use> m_highlightWatcher;
     unsigned m_highlightRevision; // the editor revision that requested the highlight
-    int m_nextHighlightBlockNumber;
 
     QFuture<QList<int> > m_references;
     QFutureWatcher<QList<int> > m_referencesWatcher;
index 28e1547..94da221 100644 (file)
@@ -99,7 +99,7 @@ protected:
                         const Identifier *id = member->identifier();
                         unsigned line, column;
                         getTokenStartPosition(member->sourceLocation(), &line, &column);
-                        localUses[member].append(SemanticInfo::Use(line, column, id->size(), SemanticInfo::Use::Local));
+                        localUses[member].append(SemanticInfo::Use(line, column, id->size(), SemanticInfo::LocalUse));
                     }
                 }
             }
@@ -117,7 +117,7 @@ protected:
                     else if (!member->isGenerated() && (member->sourceLocation() < ast->firstToken() || member->enclosingScope()->isFunction())) {
                         unsigned line, column;
                         getTokenStartPosition(simpleName->identifier_token, &line, &column);
-                        localUses[member].append(SemanticInfo::Use(line, column, id->size(), SemanticInfo::Use::Local));
+                        localUses[member].append(SemanticInfo::Use(line, column, id->size(), SemanticInfo::LocalUse));
                         return false;
                     }
                 }
index f4095a8..01d80d3 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <cplusplus/CppDocument.h>
 #include <cplusplus/LookupContext.h>
+#include <texteditor/semantichighlighter.h>
 #include <QtCore/QHash>
 
 namespace CppEditor {
@@ -45,22 +46,13 @@ class CPPEditorWidget;
 class SemanticInfo
 {
 public:
-    struct Use {
-        unsigned line;
-        unsigned column;
-        unsigned length;
-        unsigned kind;
-
-        enum Kind {
-            Type = 0,
-            Local,
-            Field,
-            Static,
-            VirtualMethod
-        };
-
-        Use(unsigned line = 0, unsigned column = 0, unsigned length = 0, unsigned kind = Type)
-            : line(line), column(column), length(length), kind(kind) {}
+    typedef TextEditor::SemanticHighlighter::Result Use;
+    enum UseKind {
+        TypeUse = 0,
+        LocalUse,
+        FieldUse,
+        StaticUse,
+        VirtualMethodUse
     };
 
     typedef QHash<CPlusPlus::Symbol *, QList<Use> > LocalUseMap;
index a6c47e9..68828f2 100644 (file)
@@ -41,6 +41,7 @@
 #include "qmljsautocompleter.h"
 #include "qmljscompletionassist.h"
 #include "qmljsquickfixassist.h"
+#include "qmljssemantichighlighter.h"
 
 #include <qmljs/qmljsbind.h>
 #include <qmljs/qmljsevaluate.h>
@@ -658,7 +659,8 @@ QmlJSTextEditorWidget::QmlJSTextEditorWidget(QWidget *parent) :
     m_modelManager(0),
     m_contextPane(0),
     m_updateSelectedElements(false),
-    m_findReferences(new FindReferences(this))
+    m_findReferences(new FindReferences(this)),
+    m_semanticHighlighter(new SemanticHighlighter(this))
 {
     qRegisterMetaType<QmlJSEditor::SemanticInfo>("QmlJSEditor::SemanticInfo");
 
@@ -1218,6 +1220,8 @@ void QmlJSTextEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
     // only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
     m_occurrencesFormat.clearForeground();
     m_occurrenceRenameFormat.clearForeground();
+
+    m_semanticHighlighter->updateFontSettings(fs);
 }
 
 
@@ -1544,9 +1548,6 @@ void QmlJSTextEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
     FindIdDeclarations updateIds;
     m_semanticInfo.idLocations = updateIds(doc);
 
-    FindDeclarations findDeclarations;
-    m_semanticInfo.declarations = findDeclarations(doc->ast());
-
     if (m_contextPane) {
         Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
         if (newNode) {
@@ -1563,6 +1564,10 @@ void QmlJSTextEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
     appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
     appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document());
     setExtraSelections(CodeWarningsSelection, selections);
+
+    Core::EditorManager *editorManager = Core::EditorManager::instance();
+    if (editorManager->currentEditor() == editor())
+        m_semanticHighlighter->rerun(m_semanticInfo.scopeChain());
 }
 
 void QmlJSTextEditorWidget::onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker)
index 932920f..5f119ea 100644 (file)
@@ -70,6 +70,7 @@ namespace Internal {
 class QmlOutlineModel;
 class SemanticInfoUpdater;
 struct SemanticInfoUpdaterSource;
+class SemanticHighlighter;
 } // namespace Internal
 
 struct QMLJSEDITOR_EXPORT Declaration
@@ -132,7 +133,6 @@ public: // attributes
     QmlJS::ContextPtr context;
     QList<Range> ranges;
     QHash<QString, QList<QmlJS::AST::SourceLocation> > idLocations;
-    QList<Declaration> declarations;
 
     // these are in addition to the parser messages in the document
     QList<QmlJS::DiagnosticMessage> semanticMessages;
@@ -251,6 +251,9 @@ private:
     bool m_updateSelectedElements;
 
     FindReferences *m_findReferences;
+    Internal::SemanticHighlighter *m_semanticHighlighter;
+
+    friend class Internal::SemanticHighlighter;
 };
 
 } // namespace QmlJSEditor
index b519de6..89b60b9 100644 (file)
@@ -36,7 +36,8 @@ HEADERS += \
     qmljsquickfixassist.h \
     qmljscompletionassist.h \
     qmljsquickfix.h \
-    qmljssemanticinfoupdater.h
+    qmljssemanticinfoupdater.h \
+    qmljssemantichighlighter.h
 
 SOURCES += \
     qmljseditor.cpp \
@@ -66,7 +67,8 @@ SOURCES += \
     qmljsquickfixassist.cpp \
     qmljscompletionassist.cpp \
     qmljsquickfix.cpp \
-    qmljssemanticinfoupdater.cpp
+    qmljssemanticinfoupdater.cpp \
+    qmljssemantichighlighter.cpp
 
 RESOURCES += qmljseditor.qrc
 OTHER_FILES += QmlJSEditor.mimetypes.xml
@@ -74,7 +76,3 @@ OTHER_FILES += QmlJSEditor.mimetypes.xml
 FORMS += \
     quicktoolbarsettingspage.ui \
     qmljscomponentnamedialog.ui
-
-
-
-
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
new file mode 100644 (file)
index 0000000..5e76ab2
--- /dev/null
@@ -0,0 +1,406 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "qmljssemantichighlighter.h"
+
+#include "qmljseditor.h"
+
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsscopechain.h>
+#include <qmljs/qmljsscopebuilder.h>
+#include <qmljs/qmljsevaluate.h>
+#include <qmljs/qmljscontext.h>
+#include <qmljs/qmljsbind.h>
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/parser/qmljsastvisitor_p.h>
+#include <texteditor/syntaxhighlighter.h>
+#include <texteditor/basetextdocument.h>
+#include <texteditor/texteditorconstants.h>
+#include <texteditor/fontsettings.h>
+#include <utils/qtcassert.h>
+
+#include <QtCore/QThreadPool>
+#include <QtCore/QFutureInterface>
+#include <QtCore/QRunnable>
+
+using namespace QmlJSEditor;
+using namespace QmlJSEditor::Internal;
+using namespace QmlJS;
+using namespace QmlJS::AST;
+
+namespace {
+
+template <typename T>
+class PriorityTask :
+        public QFutureInterface<T>,
+        public QRunnable
+{
+public:
+    typedef QFuture<T> Future;
+
+    Future start(QThread::Priority priority)
+    {
+        this->setRunnable(this);
+        this->reportStarted();
+        Future future = this->future();
+        QThreadPool::globalInstance()->start(this, priority);
+        return future;
+    }
+};
+
+static bool isIdScope(const ObjectValue *scope, const QList<const QmlComponentChain *> &chain)
+{
+    foreach (const QmlComponentChain *c, chain) {
+        if (c->idScope() == scope)
+            return true;
+        if (isIdScope(scope, c->instantiatingComponents()))
+            return true;
+    }
+    return false;
+}
+
+class CollectStateNames : protected Visitor
+{
+    QStringList m_stateNames;
+    bool m_inStateType;
+    ScopeChain m_scopeChain;
+    const QmlObjectValue *m_statePrototype;
+
+public:
+    CollectStateNames(const ScopeChain &scopeChain)
+        : m_scopeChain(scopeChain)
+    {
+        m_statePrototype = scopeChain.context()->valueOwner()->cppQmlTypes().typeByCppName(QLatin1String("QDeclarativeState"));
+    }
+
+    QStringList operator()(Node *ast)
+    {
+        m_stateNames.clear();
+        if (!m_statePrototype)
+            return m_stateNames;
+        m_inStateType = false;
+        accept(ast);
+        return m_stateNames;
+    }
+
+protected:
+    void accept(Node *ast)
+    {
+        if (ast)
+            ast->accept(this);
+    }
+
+    bool preVisit(Node *ast)
+    {
+        return ast->uiObjectMemberCast()
+                || cast<UiProgram *>(ast)
+                || cast<UiObjectInitializer *>(ast)
+                || cast<UiObjectMemberList *>(ast)
+                || cast<UiArrayMemberList *>(ast);
+    }
+
+    bool hasStatePrototype(Node *ast)
+    {
+        Bind *bind = m_scopeChain.document()->bind();
+        const ObjectValue *v = bind->findQmlObject(ast);
+        if (!v)
+            return false;
+        PrototypeIterator it(v, m_scopeChain.context());
+        while (it.hasNext()) {
+            const ObjectValue *proto = it.next();
+            const QmlObjectValue *qmlProto = dynamic_cast<const QmlObjectValue *>(proto);
+            if (!qmlProto)
+                continue;
+            if (qmlProto->metaObject() == m_statePrototype->metaObject())
+                return true;
+        }
+        return false;
+    }
+
+    bool visit(UiObjectDefinition *ast)
+    {
+        const bool old = m_inStateType;
+        m_inStateType = hasStatePrototype(ast);
+        accept(ast->initializer);
+        m_inStateType = old;
+        return false;
+    }
+
+    bool visit(UiObjectBinding *ast)
+    {
+        const bool old = m_inStateType;
+        m_inStateType = hasStatePrototype(ast);
+        accept(ast->initializer);
+        m_inStateType = old;
+        return false;
+    }
+
+    bool visit(UiScriptBinding *ast)
+    {
+        if (!m_inStateType)
+            return false;
+        if (!ast->qualifiedId || ! ast->qualifiedId->name || ast->qualifiedId->next)
+            return false;
+        if (ast->qualifiedId->name->asString() != QLatin1String("name"))
+            return false;
+
+        ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
+        if (!expStmt)
+            return false;
+        StringLiteral *strLit = cast<StringLiteral *>(expStmt->expression);
+        if (!strLit || !strLit->value)
+            return false;
+
+        m_stateNames += strLit->value->asString();
+
+        return false;
+    }
+};
+
+class CollectionTask :
+        public PriorityTask<SemanticHighlighter::Use>,
+        protected Visitor
+{
+public:
+    CollectionTask(const ScopeChain &scopeChain)
+        : m_scopeChain(scopeChain)
+        , m_scopeBuilder(&m_scopeChain)
+    {}
+
+protected:
+    void accept(Node *ast)
+    {
+        if (ast)
+            ast->accept(this);
+    }
+
+    void scopedAccept(Node *ast, Node *child)
+    {
+        m_scopeBuilder.push(ast);
+        accept(child);
+        m_scopeBuilder.pop();
+    }
+
+    void processName(NameId *name, SourceLocation location)
+    {
+        if (!name)
+            return;
+
+        const QString nameStr = name->asString();
+        const ObjectValue *scope = 0;
+        const Value *value = m_scopeChain.lookup(nameStr, &scope);
+        if (!value || !scope)
+            return;
+
+        SemanticHighlighter::Use use = SemanticHighlighter::makeUse(location);
+        if (QSharedPointer<const QmlComponentChain> chain = m_scopeChain.qmlComponentChain()) {
+            if (scope == chain->idScope()) {
+                use.kind = SemanticHighlighter::LocalIdType;
+            } else if (isIdScope(scope, chain->instantiatingComponents())) {
+                use.kind = SemanticHighlighter::ExternalIdType;
+            } else if (scope == chain->rootObjectScope()) {
+                use.kind = SemanticHighlighter::RootObjectPropertyType;
+            }
+        }
+
+        if (m_scopeChain.qmlTypes() == scope) {
+            use.kind = SemanticHighlighter::QmlTypeType;
+        } else if (m_scopeChain.qmlScopeObjects().contains(scope)) {
+            use.kind = SemanticHighlighter::ScopeObjectPropertyType;
+        } else if (m_scopeChain.jsScopes().contains(scope)) {
+            use.kind = SemanticHighlighter::JsScopeType;
+        } else if (m_scopeChain.jsImports() == scope) {
+            use.kind = SemanticHighlighter::JsImportType;
+        } else if (m_scopeChain.globalScope() == scope) {
+            use.kind = SemanticHighlighter::JsGlobalType;
+        }
+
+        // eliminated other possibilities, should potentially be a real check if this yields false-positives
+        if (use.kind == SemanticHighlighter::UnknownType) {
+            use.kind = SemanticHighlighter::ExternalObjectPropertyType;
+        }
+
+        reportResult(use);
+    }
+
+    bool visit(UiObjectDefinition *ast)
+    {
+        scopedAccept(ast, ast->initializer);
+        return false;
+    }
+
+    bool visit(UiObjectBinding *ast)
+    {
+        scopedAccept(ast, ast->initializer);
+        return false;
+    }
+
+    bool visit(UiScriptBinding *ast)
+    {
+        scopedAccept(ast, ast->statement);
+        return false;
+    }
+
+    bool visit(UiPublicMember *ast)
+    {
+        scopedAccept(ast, ast->statement);
+        return false;
+    }
+
+    bool visit(FunctionExpression *ast)
+    {
+        processName(ast->name, ast->identifierToken);
+        scopedAccept(ast, ast->body);
+        return false;
+    }
+
+    bool visit(FunctionDeclaration *ast)
+    {
+        return visit(static_cast<FunctionExpression *>(ast));
+    }
+
+    bool visit(VariableDeclaration *ast)
+    {
+        processName(ast->name, ast->identifierToken);
+        return true;
+    }
+
+    bool visit(IdentifierExpression *ast)
+    {
+        processName(ast->name, ast->identifierToken);
+        return false;
+    }
+
+    bool visit(StringLiteral *ast)
+    {
+        if (!ast->value)
+            return false;
+
+        const QString value = ast->value->asString();
+        if (m_stateNames.contains(value)) {
+            SemanticHighlighter::Use use = SemanticHighlighter::makeUse(
+                        ast->literalToken, SemanticHighlighter::LocalStateNameType);
+            reportResult(use);
+        }
+
+        return false;
+    }
+
+private:
+    void run()
+    {
+        Node *root = m_scopeChain.document()->ast();
+        m_stateNames = CollectStateNames(m_scopeChain)(root);
+        accept(root);
+        reportFinished();
+    }
+
+    ScopeChain m_scopeChain;
+    ScopeBuilder m_scopeBuilder;
+    QStringList m_stateNames;
+};
+
+} // anonymous namespace
+
+SemanticHighlighter::SemanticHighlighter(QmlJSTextEditorWidget *editor)
+    : QObject(editor)
+    , m_editor(editor)
+    , m_startRevision(0)
+{
+    connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)),
+            this, SLOT(applyResults(int,int)));
+    connect(&m_watcher, SIGNAL(finished()),
+            this, SLOT(finished()));
+}
+
+void SemanticHighlighter::rerun(const ScopeChain &scopeChain)
+{
+    m_watcher.cancel();
+
+    // this does not simply use QtConcurrentRun because we want a low-priority future
+    // the thread pool deletes the task when it is done
+    CollectionTask::Future f = (new CollectionTask(scopeChain))->start(QThread::LowestPriority);
+    m_startRevision = m_editor->editorRevision();
+    m_watcher.setFuture(f);
+}
+
+void SemanticHighlighter::applyResults(int from, int to)
+{
+    if (m_watcher.isCanceled())
+        return;
+    if (m_startRevision != m_editor->editorRevision())
+        return;
+
+    TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument();
+    QTC_ASSERT(baseTextDocument, return);
+    TextEditor::SyntaxHighlighter *highlighter = qobject_cast<TextEditor::SyntaxHighlighter *>(baseTextDocument->syntaxHighlighter());
+    QTC_ASSERT(highlighter, return);
+
+    TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
+                highlighter, m_watcher.future(), from, to, m_formats);
+}
+
+void SemanticHighlighter::finished()
+{
+    if (m_watcher.isCanceled())
+        return;
+    if (m_startRevision != m_editor->editorRevision())
+        return;
+
+    TextEditor::BaseTextDocument *baseTextDocument = m_editor->baseTextDocument();
+    QTC_ASSERT(baseTextDocument, return);
+    TextEditor::SyntaxHighlighter *highlighter = qobject_cast<TextEditor::SyntaxHighlighter *>(baseTextDocument->syntaxHighlighter());
+    QTC_ASSERT(highlighter, return);
+
+    TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
+                highlighter, m_watcher.future());
+}
+
+SemanticHighlighter::Use SemanticHighlighter::makeUse(const SourceLocation &location, SemanticHighlighter::UseType type)
+{
+    return Use(location.startLine, location.startColumn, location.length, type);
+}
+
+void SemanticHighlighter::updateFontSettings(const TextEditor::FontSettings &fontSettings)
+{
+    m_formats[LocalIdType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_LOCAL_ID));
+    m_formats[ExternalIdType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_EXTERNAL_ID));
+    m_formats[QmlTypeType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_TYPE_ID));
+    m_formats[RootObjectPropertyType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_ROOT_OBJECT_PROPERTY));
+    m_formats[ScopeObjectPropertyType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_SCOPE_OBJECT_PROPERTY));
+    m_formats[ExternalObjectPropertyType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_EXTERNAL_OBJECT_PROPERTY));
+    m_formats[JsScopeType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_JS_SCOPE_VAR));
+    m_formats[JsImportType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_JS_IMPORT_VAR));
+    m_formats[JsGlobalType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_JS_GLOBAL_VAR));
+    m_formats[LocalStateNameType] = fontSettings.toTextCharFormat(QLatin1String(TextEditor::Constants::C_QML_STATE_NAME));
+}
+
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.h b/src/plugins/qmljseditor/qmljssemantichighlighter.h
new file mode 100644 (file)
index 0000000..01c2ac9
--- /dev/null
@@ -0,0 +1,98 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLJSSEMANTICHIGHLIGHTER_H
+#define QMLJSSEMANTICHIGHLIGHTER_H
+
+#include <texteditor/semantichighlighter.h>
+#include <QtCore/QFutureWatcher>
+
+namespace QmlJS {
+class ScopeChain;
+namespace AST {
+class SourceLocation;
+}
+}
+
+namespace TextEditor {
+class FontSettings;
+}
+
+namespace QmlJSEditor {
+
+class QmlJSTextEditorWidget;
+
+namespace Internal {
+
+class SemanticHighlighter : public QObject
+{
+    Q_OBJECT
+public:
+    enum UseType
+    {
+        UnknownType,
+        LocalIdType, // ids in the same file
+        ExternalIdType, // ids from instantiating files
+        QmlTypeType, // qml types
+        RootObjectPropertyType, // property in root object
+        ScopeObjectPropertyType, // property in scope object
+        ExternalObjectPropertyType, // property in root object of instantiating file
+        JsScopeType, // var or function in local js scope
+        JsImportType, // name of js import
+        JsGlobalType, // in global scope
+        LocalStateNameType // name of a state in the current file
+    };
+
+    typedef TextEditor::SemanticHighlighter::Result Use;
+    static Use makeUse(const QmlJS::AST::SourceLocation &location, UseType type = UnknownType);
+
+    SemanticHighlighter(QmlJSTextEditorWidget *editor);
+
+    void rerun(const QmlJS::ScopeChain &scopeChain);
+
+    void updateFontSettings(const TextEditor::FontSettings &fontSettings);
+
+private slots:
+    void applyResults(int from, int to);
+    void finished();
+
+private:
+    QFutureWatcher<Use>  m_watcher;
+    QmlJSTextEditorWidget *m_editor;
+    int m_startRevision;
+    QHash<int, QTextCharFormat> m_formats;
+};
+
+} // namespace Internal
+} // namespace QmlJSEditor
+
+#endif // QMLJSSEMANTICHIGHLIGHTER_H
index 3facc25..6393019 100644 (file)
@@ -239,6 +239,13 @@ FormatDescription::FormatDescription(const QString &id, const QString &displayNa
     m_format.setForeground(color);
 }
 
+FormatDescription::FormatDescription(const QString &id, const QString &displayName, const Format &format) :
+    m_id(id),
+    m_displayName(displayName),
+    m_format(format)
+{
+}
+
 QColor FormatDescription::foreground() const
 {
     if (m_id == QLatin1String(Constants::C_LINE_NUMBER)) {
index cc9300a..8fddfba 100644 (file)
@@ -60,6 +60,8 @@ class TEXTEDITOR_EXPORT FormatDescription
 public:
     FormatDescription(const QString &id, const QString &displayName,
                       const QColor &foreground = Qt::black);
+    FormatDescription(const QString &id, const QString &displayName,
+                      const Format &format);
 
     QString id() const
     { return m_id; }
diff --git a/src/plugins/texteditor/semantichighlighter.cpp b/src/plugins/texteditor/semantichighlighter.cpp
new file mode 100644 (file)
index 0000000..6c6ad62
--- /dev/null
@@ -0,0 +1,138 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "semantichighlighter.h"
+
+#include "syntaxhighlighter.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtGui/QTextDocument>
+#include <QtGui/QTextBlock>
+
+using namespace TextEditor;
+using namespace TextEditor::SemanticHighlighter;
+
+void TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
+        SyntaxHighlighter *highlighter,
+        const QFuture<Result> &future,
+        int from, int to,
+        const QHash<int, QTextCharFormat> &kindToFormat)
+{
+    QMap<int, QVector<Result> > blockNumberToResults;
+    for (int i = from; i < to; ++i) {
+        const Result &result = future.resultAt(i);
+        if (!result.line)
+            continue;
+        blockNumberToResults[result.line - 1].append(result);
+    }
+    if (blockNumberToResults.isEmpty())
+        return;
+
+    const int firstResultBlockNumber = blockNumberToResults.constBegin().key();
+
+    // blocks between currentBlockNumber and the last block with results will
+    // be cleaned of additional extra formats if they have no results
+    int currentBlockNumber = 0;
+    for (int i = from - 1; i >= 0; --i) {
+        const Result &result = future.resultAt(i);
+        if (!result.line)
+            continue;
+        const int blockNumber = result.line - 1;
+        if (blockNumber < firstResultBlockNumber) {
+            // stop! found where last format stopped
+            currentBlockNumber = blockNumber + 1;
+            break;
+        } else {
+            // add previous results for the same line to avoid undoing their formats
+            blockNumberToResults[blockNumber].append(result);
+        }
+    }
+
+    QTextDocument *doc = highlighter->document();
+    QTC_ASSERT(currentBlockNumber < doc->blockCount(), return);
+    QTextBlock b = doc->findBlockByNumber(currentBlockNumber);
+
+    QMapIterator<int, QVector<Result> > it(blockNumberToResults);
+    while (b.isValid() && it.hasNext()) {
+        it.next();
+        const int blockNumber = it.key();
+        QTC_ASSERT(blockNumber < doc->blockCount(), return);
+
+        // clear formats of blocks until blockNumber
+        while (currentBlockNumber < blockNumber) {
+            highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+            b = b.next();
+            ++currentBlockNumber;
+        }
+
+        QList<QTextLayout::FormatRange> formats;
+        foreach (const Result &result, it.value()) {
+            QTextLayout::FormatRange formatRange;
+
+            formatRange.format = kindToFormat.value(result.kind);
+            if (!formatRange.format.isValid())
+                continue;
+
+            formatRange.start = result.column - 1;
+            formatRange.length = result.length;
+            formats.append(formatRange);
+        }
+        highlighter->setExtraAdditionalFormats(b, formats);
+        b = b.next();
+        ++currentBlockNumber;
+    }
+}
+
+void TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
+        SyntaxHighlighter *highlighter,
+        const QFuture<Result> &future)
+{
+    // find block number of last result
+    int lastBlockNumber = 0;
+    for (int i = future.resultCount() - 1; i >= 0; --i) {
+        const Result &result = future.resultAt(i);
+        if (result.line) {
+            lastBlockNumber = result.line - 1;
+            break;
+        }
+    }
+
+    QTextDocument *doc = highlighter->document();
+    QTC_ASSERT(lastBlockNumber + 1 < doc->blockCount(), return);
+    QTextBlock b = doc->findBlockByNumber(lastBlockNumber + 1);
+
+    while (b.isValid()) {
+        highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+        b = b.next();
+    }
+}
diff --git a/src/plugins/texteditor/semantichighlighter.h b/src/plugins/texteditor/semantichighlighter.h
new file mode 100644 (file)
index 0000000..4abb94f
--- /dev/null
@@ -0,0 +1,91 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef TEXTEDITOR_SEMANTICHIGHLIGHTER_H
+#define TEXTEDITOR_SEMANTICHIGHLIGHTER_H
+
+#include "texteditor_global.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QVector>
+#include <QtCore/QFuture>
+#include <QtGui/QTextCharFormat>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
+namespace TextEditor {
+
+class SyntaxHighlighter;
+
+namespace SemanticHighlighter {
+
+class TEXTEDITOR_EXPORT Result {
+public:
+    unsigned line; // 1-based
+    unsigned column; // 1-based
+    unsigned length;
+    int kind;
+
+    Result()
+        : line(0), column(0), length(0), kind(-1) {}
+    Result(unsigned line, unsigned column, unsigned length, int kind)
+        : line(line), column(column), length(length), kind(kind) {}
+};
+
+// Applies the future results [from, to) and applies the extra formats
+// indicated by Result::kind and kindToFormat to the correct location using
+// SyntaxHighlighter::setExtraAdditionalFormats.
+// It is incremental in the sense that it clears the extra additional formats
+// from all lines that have no results between the (from-1).line result and
+// the (to-1).line result.
+// Requires that results of the Future are ordered by line.
+void TEXTEDITOR_EXPORT incrementalApplyExtraAdditionalFormats(
+        SyntaxHighlighter *highlighter,
+        const QFuture<Result> &future,
+        int from, int to,
+        const QHash<int, QTextCharFormat> &kindToFormat);
+
+// Cleans the extra additional formats after the last result of the Future
+// until the end of the document.
+// Requires that results of the Future are ordered by line.
+void TEXTEDITOR_EXPORT clearExtraAdditionalFormatsUntilEnd(
+        SyntaxHighlighter *highlighter,
+        const QFuture<Result> &future);
+
+
+} // namespace SemanticHighlighter
+} // namespace TextEditor
+
+#endif // TEXTEDITOR_SEMANTICHIGHLIGHTER_H
index fdd9382..1141076 100644 (file)
@@ -106,7 +106,8 @@ SOURCES += texteditorplugin.cpp \
     tabpreferences.cpp \
     icodestylepreferencesfactory.cpp \
     tabpreferenceswidget.cpp \
-    fallbackselectorwidget.cpp
+    fallbackselectorwidget.cpp \
+    semantichighlighter.cpp
 
 HEADERS += texteditorplugin.h \
     textfilewizard.h \
@@ -217,7 +218,8 @@ HEADERS += texteditorplugin.h \
     tabpreferences.h \
     icodestylepreferencesfactory.h \
     tabpreferenceswidget.h \
-    fallbackselectorwidget.h
+    fallbackselectorwidget.h \
+    semantichighlighter.h
 
 FORMS += \
     displaysettingspage.ui \
index b355235..fb06787 100644 (file)
@@ -128,6 +128,17 @@ const char * const C_COMMENT             = "Comment";
 const char * const C_DOXYGEN_COMMENT     = "Doxygen.Comment";
 const char * const C_DOXYGEN_TAG         = "Doxygen.Tag";
 const char * const C_VISUAL_WHITESPACE   = "VisualWhitespace";
+const char * const C_QML_LOCAL_ID        = "QmlLocalId";
+const char * const C_QML_EXTERNAL_ID     = "QmlExternalId";
+const char * const C_QML_TYPE_ID         = "QmlTypeId";
+const char * const C_QML_ROOT_OBJECT_PROPERTY     = "QmlRootObjectProperty";
+const char * const C_QML_SCOPE_OBJECT_PROPERTY    = "QmlScopeObjectProperty";
+const char * const C_QML_EXTERNAL_OBJECT_PROPERTY = "QmlExternalObjectProperty";
+const char * const C_JS_SCOPE_VAR        = "JsScopeVar";
+const char * const C_JS_IMPORT_VAR       = "JsImportVar";
+const char * const C_JS_GLOBAL_VAR       = "JsGlobalVar";
+const char * const C_QML_STATE_NAME      = "QmlStateName";
+
 
 const char * const C_DISABLED_CODE       = "DisabledCode";
 
index dad9708..295832d 100644 (file)
@@ -146,6 +146,27 @@ TextEditorSettings::TextEditorSettings(QObject *parent)
     virtualMethodFormatDescriptor.format().setItalic(true);
     formatDescriptions.append(virtualMethodFormatDescriptor);
 
+    Format qmlLocalNameFormat;
+    qmlLocalNameFormat.setItalic(true);
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_LOCAL_ID), tr("QML Local Id"), qmlLocalNameFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_ROOT_OBJECT_PROPERTY), tr("QML Root Object Property"), qmlLocalNameFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_SCOPE_OBJECT_PROPERTY), tr("QML Scope Object Property"), qmlLocalNameFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_STATE_NAME), tr("QML State Name"), qmlLocalNameFormat));
+
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_TYPE_ID), tr("QML Type Name"), Qt::darkMagenta));
+
+    Format qmlExternalNameFormat = qmlLocalNameFormat;
+    qmlExternalNameFormat.setForeground(Qt::darkBlue);
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_EXTERNAL_ID), tr("QML External Id"), qmlExternalNameFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_QML_EXTERNAL_OBJECT_PROPERTY), tr("QML External Object Property"), qmlExternalNameFormat));
+
+    Format jsFormat;
+    jsFormat.setForeground(QColor(Qt::darkCyan).darker());
+    jsFormat.setItalic(true);
+    formatDescriptions.append(FormatDescription(QLatin1String(C_JS_SCOPE_VAR), tr("JavaScript Scope Var"), jsFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_JS_IMPORT_VAR), tr("JavaScript Import"), jsFormat));
+    formatDescriptions.append(FormatDescription(QLatin1String(C_JS_GLOBAL_VAR), tr("JavaScript Global Var"), jsFormat));
+
     formatDescriptions.append(FormatDescription(QLatin1String(C_KEYWORD), tr("Keyword"), Qt::darkYellow));
     formatDescriptions.append(FormatDescription(QLatin1String(C_OPERATOR), tr("Operator")));
     formatDescriptions.append(FormatDescription(QLatin1String(C_PREPROCESSOR), tr("Preprocessor"), Qt::darkBlue));