From c105ae47c1eb38ee24d919f2fee151c05ec852e0 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 16 Aug 2011 09:47:54 +0200 Subject: [PATCH] QmlJS: Add semantic highlighting. Change-Id: If9293244075cff1a52801b50cdbb77248ecd21ea Reviewed-on: http://codereview.qt.nokia.com/3310 Reviewed-by: Fawzi Mohamed --- src/libs/qmljs/qmljsscopechain.cpp | 19 +- src/libs/qmljs/qmljsscopechain.h | 3 + src/plugins/cppeditor/cppchecksymbols.cpp | 36 +- src/plugins/cppeditor/cppchecksymbols.h | 5 +- src/plugins/cppeditor/cppeditor.cpp | 100 +---- src/plugins/cppeditor/cppeditor.h | 7 +- src/plugins/cppeditor/cpplocalsymbols.cpp | 4 +- src/plugins/cppeditor/cppsemanticinfo.h | 24 +- src/plugins/qmljseditor/qmljseditor.cpp | 13 +- src/plugins/qmljseditor/qmljseditor.h | 5 +- src/plugins/qmljseditor/qmljseditor.pro | 10 +- .../qmljseditor/qmljssemantichighlighter.cpp | 406 +++++++++++++++++++++ src/plugins/qmljseditor/qmljssemantichighlighter.h | 98 +++++ src/plugins/texteditor/fontsettingspage.cpp | 7 + src/plugins/texteditor/fontsettingspage.h | 2 + src/plugins/texteditor/semantichighlighter.cpp | 138 +++++++ src/plugins/texteditor/semantichighlighter.h | 91 +++++ src/plugins/texteditor/texteditor.pro | 6 +- src/plugins/texteditor/texteditorconstants.h | 11 + src/plugins/texteditor/texteditorsettings.cpp | 21 ++ 20 files changed, 865 insertions(+), 141 deletions(-) create mode 100644 src/plugins/qmljseditor/qmljssemantichighlighter.cpp create mode 100644 src/plugins/qmljseditor/qmljssemantichighlighter.h create mode 100644 src/plugins/texteditor/semantichighlighter.cpp create mode 100644 src/plugins/texteditor/semantichighlighter.h 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 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, QListdocument()) 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 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 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 &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 &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 &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 &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 #include #include +#include #include #include #include @@ -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(baseTextDocument()->syntaxHighlighter()); - Q_ASSERT(highlighter); - QTextDocument *doc = document(); - - if (m_nextHighlightBlockNumber >= doc->blockCount()) - return; - - QMap > chunks = CheckSymbols::chunks(m_highlighter, from, to); - if (chunks.isEmpty()) - return; - - QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber); - - QMapIterator > 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()); - b = b.next(); - ++m_nextHighlightBlockNumber; - } - - QList 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(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()); - 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 m_semanticHighlightFormatMap; QTextCharFormat m_keywordFormat; - QTextCharFormat m_virtualMethodFormat; QList m_renameSelections; int m_currentRenameSelection; @@ -320,7 +316,6 @@ private: QFuture m_highlighter; QFutureWatcher m_highlightWatcher; unsigned m_highlightRevision; // the editor revision that requested the highlight - int m_nextHighlightBlockNumber; QFuture > m_references; QFutureWatcher > 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 #include +#include #include 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 > 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 #include @@ -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"); @@ -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 ranges; QHash > idLocations; - QList declarations; // these are in addition to the parser messages in the document QList 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace QmlJSEditor; +using namespace QmlJSEditor::Internal; +using namespace QmlJS; +using namespace QmlJS::AST; + +namespace { + +template +class PriorityTask : + public QFutureInterface, + public QRunnable +{ +public: + typedef QFuture 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 &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(ast) + || cast(ast) + || cast(ast) + || cast(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(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(ast->statement); + if (!expStmt) + return false; + StringLiteral *strLit = cast(expStmt->expression); + if (!strLit || !strLit->value) + return false; + + m_stateNames += strLit->value->asString(); + + return false; + } +}; + +class CollectionTask : + public PriorityTask, + 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 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(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(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(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 +#include + +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 m_watcher; + QmlJSTextEditorWidget *m_editor; + int m_startRevision; + QHash 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 + +#include +#include + +using namespace TextEditor; +using namespace TextEditor::SemanticHighlighter; + +void TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats( + SyntaxHighlighter *highlighter, + const QFuture &future, + int from, int to, + const QHash &kindToFormat) +{ + QMap > 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 > 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()); + b = b.next(); + ++currentBlockNumber; + } + + QList 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 &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()); + 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 +#include +#include +#include +#include + +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 &future, + int from, int to, + const QHash &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 &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)); -- 2.11.0