summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libs/qmljs/qmljsscopechain.cpp19
-rw-r--r--src/libs/qmljs/qmljsscopechain.h3
-rw-r--r--src/plugins/cppeditor/cppchecksymbols.cpp36
-rw-r--r--src/plugins/cppeditor/cppchecksymbols.h5
-rw-r--r--src/plugins/cppeditor/cppeditor.cpp100
-rw-r--r--src/plugins/cppeditor/cppeditor.h7
-rw-r--r--src/plugins/cppeditor/cpplocalsymbols.cpp4
-rw-r--r--src/plugins/cppeditor/cppsemanticinfo.h24
-rw-r--r--src/plugins/qmljseditor/qmljseditor.cpp13
-rw-r--r--src/plugins/qmljseditor/qmljseditor.h5
-rw-r--r--src/plugins/qmljseditor/qmljseditor.pro10
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.cpp406
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.h98
-rw-r--r--src/plugins/texteditor/fontsettingspage.cpp7
-rw-r--r--src/plugins/texteditor/fontsettingspage.h2
-rw-r--r--src/plugins/texteditor/semantichighlighter.cpp138
-rw-r--r--src/plugins/texteditor/semantichighlighter.h91
-rw-r--r--src/plugins/texteditor/texteditor.pro6
-rw-r--r--src/plugins/texteditor/texteditorconstants.h11
-rw-r--r--src/plugins/texteditor/texteditorsettings.cpp21
20 files changed, 865 insertions, 141 deletions
diff --git a/src/libs/qmljs/qmljsscopechain.cpp b/src/libs/qmljs/qmljsscopechain.cpp
index 5f605633aa..724f888c50 100644
--- a/src/libs/qmljs/qmljsscopechain.cpp
+++ b/src/libs/qmljs/qmljsscopechain.cpp
@@ -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(
}
}
}
-
diff --git a/src/libs/qmljs/qmljsscopechain.h b/src/libs/qmljs/qmljsscopechain.h
index e55aeaf290..6eb9e57f84 100644
--- a/src/libs/qmljs/qmljsscopechain.h
+++ b/src/libs/qmljs/qmljsscopechain.h
@@ -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);
diff --git a/src/plugins/cppeditor/cppchecksymbols.cpp b/src/plugins/cppeditor/cppchecksymbols.cpp
index 8d459ef1ec..ee1e0c4540 100644
--- a/src/plugins/cppeditor/cppchecksymbols.cpp
+++ b/src/plugins/cppeditor/cppchecksymbols.cpp
@@ -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;
}
diff --git a/src/plugins/cppeditor/cppchecksymbols.h b/src/plugins/cppeditor/cppchecksymbols.h
index 6857dd127a..f54a7fb6e0 100644
--- a/src/plugins/cppeditor/cppchecksymbols.h
+++ b/src/plugins/cppeditor/cppchecksymbols.h
@@ -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);
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index c32b232004..aa0477da1a 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -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);
}
}
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 55278badf8..1c46fddfbf 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -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;
diff --git a/src/plugins/cppeditor/cpplocalsymbols.cpp b/src/plugins/cppeditor/cpplocalsymbols.cpp
index 28e1547a76..94da221127 100644
--- a/src/plugins/cppeditor/cpplocalsymbols.cpp
+++ b/src/plugins/cppeditor/cpplocalsymbols.cpp
@@ -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;
}
}
diff --git a/src/plugins/cppeditor/cppsemanticinfo.h b/src/plugins/cppeditor/cppsemanticinfo.h
index f4095a8b4e..01d80d30c0 100644
--- a/src/plugins/cppeditor/cppsemanticinfo.h
+++ b/src/plugins/cppeditor/cppsemanticinfo.h
@@ -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;
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index a6c47e9598..68828f2fb2 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -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)
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index 932920fc39..5f119ea7de 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -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
diff --git a/src/plugins/qmljseditor/qmljseditor.pro b/src/plugins/qmljseditor/qmljseditor.pro
index b519de6e7b..89b60b91e5 100644
--- a/src/plugins/qmljseditor/qmljseditor.pro
+++ b/src/plugins/qmljseditor/qmljseditor.pro
@@ -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
index 0000000000..5e76ab2608
--- /dev/null
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
@@ -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
index 0000000000..01c2ac95d8
--- /dev/null
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.h
@@ -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
diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp
index 3facc25498..6393019957 100644
--- a/src/plugins/texteditor/fontsettingspage.cpp
+++ b/src/plugins/texteditor/fontsettingspage.cpp
@@ -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)) {
diff --git a/src/plugins/texteditor/fontsettingspage.h b/src/plugins/texteditor/fontsettingspage.h
index cc9300a6b8..8fddfbae84 100644
--- a/src/plugins/texteditor/fontsettingspage.h
+++ b/src/plugins/texteditor/fontsettingspage.h
@@ -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
index 0000000000..6c6ad62823
--- /dev/null
+++ b/src/plugins/texteditor/semantichighlighter.cpp
@@ -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
index 0000000000..4abb94ff4a
--- /dev/null
+++ b/src/plugins/texteditor/semantichighlighter.h
@@ -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
diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro
index fdd9382874..114107601f 100644
--- a/src/plugins/texteditor/texteditor.pro
+++ b/src/plugins/texteditor/texteditor.pro
@@ -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 \
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index b355235dec..fb06787a1b 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -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";
diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp
index dad9708640..295832d3df 100644
--- a/src/plugins/texteditor/texteditorsettings.cpp
+++ b/src/plugins/texteditor/texteditorsettings.cpp
@@ -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));