QmlJS: Add semantic highlighting.

Change-Id: If9293244075cff1a52801b50cdbb77248ecd21ea
Reviewed-on: http://codereview.qt.nokia.com/3310
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
Christian Kamm
2011-08-16 09:47:54 +02:00
parent 1b9af316a0
commit c105ae47c1
20 changed files with 865 additions and 141 deletions

View File

@@ -56,6 +56,20 @@ QList<const QmlComponentChain *> QmlComponentChain::instantiatingComponents() co
return m_instantiatingComponents; 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) void QmlComponentChain::addInstantiatingComponent(const QmlComponentChain *component)
{ {
m_instantiatingComponents.append(component); m_instantiatingComponents.append(component);
@@ -188,9 +202,9 @@ static void collectScopes(const QmlComponentChain *chain, QList<const ObjectValu
if (!chain->document()) if (!chain->document())
return; return;
if (ObjectValue *root = chain->document()->bind()->rootObjectValue()) if (const ObjectValue *root = chain->rootObjectScope())
target->append(root); target->append(root);
if (ObjectValue *ids = chain->document()->bind()->idEnvironment()) if (const ObjectValue *ids = chain->idScope())
target->append(ids); target->append(ids);
} }
@@ -294,4 +308,3 @@ void ScopeChain::makeComponentChain(
} }
} }
} }

View File

@@ -58,6 +58,9 @@ public:
Document::Ptr document() const; Document::Ptr document() const;
QList<const QmlComponentChain *> instantiatingComponents() const; QList<const QmlComponentChain *> instantiatingComponents() const;
const ObjectValue *idScope() const;
const ObjectValue *rootObjectScope() const;
// takes ownership // takes ownership
void addInstantiatingComponent(const QmlComponentChain *component); void addInstantiatingComponent(const QmlComponentChain *component);

View File

@@ -442,7 +442,7 @@ bool CheckSymbols::visit(NamespaceAST *ast)
if (! tok.generated()) { if (! tok.generated()) {
unsigned line, column; unsigned line, column;
getTokenStartPosition(ast->identifier_token, &line, &column); getTokenStartPosition(ast->identifier_token, &line, &column);
Use use(line, column, tok.length()); Use use(line, column, tok.length(), SemanticInfo::TypeUse);
addUse(use); addUse(use);
} }
} }
@@ -457,7 +457,7 @@ bool CheckSymbols::visit(UsingDirectiveAST *)
bool CheckSymbols::visit(EnumeratorAST *ast) bool CheckSymbols::visit(EnumeratorAST *ast)
{ {
addUse(ast->identifier_token, Use::Static); addUse(ast->identifier_token, SemanticInfo::StaticUse);
return true; return true;
} }
@@ -469,7 +469,7 @@ bool CheckSymbols::visit(SimpleDeclarationAST *ast)
if (NameAST *declId = declaratorId(ast->declarator_list->value)) { if (NameAST *declId = declaratorId(ast->declarator_list->value)) {
if (Function *funTy = decl->type()->asFunctionType()) { if (Function *funTy = decl->type()->asFunctionType()) {
if (funTy->isVirtual()) { if (funTy->isVirtual()) {
addUse(declId, Use::VirtualMethod); addUse(declId, SemanticInfo::VirtualMethodUse);
} else if (maybeVirtualMethod(decl->name())) { } else if (maybeVirtualMethod(decl->name())) {
addVirtualMethod(_context.lookup(decl->name(), decl->enclosingScope()), declId, funTy->argumentCount()); 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->attribute_list);
accept(ast->name); accept(ast->name);
addUse(ast->name, Use::Type); addUse(ast->name, SemanticInfo::TypeUse);
return false; return false;
} }
@@ -643,7 +643,7 @@ void CheckSymbols::checkName(NameAST *ast, Scope *scope)
if (ast->asDestructorName() != 0) { if (ast->asDestructorName() != 0) {
Class *klass = scope->asClass(); Class *klass = scope->asClass();
if (hasVirtualDestructor(_context.lookupType(klass))) if (hasVirtualDestructor(_context.lookupType(klass)))
addUse(ast, Use::VirtualMethod); addUse(ast, SemanticInfo::VirtualMethodUse);
} else if (maybeType(ast->name) || maybeStatic(ast->name)) { } else if (maybeType(ast->name) || maybeStatic(ast->name)) {
const QList<LookupItem> candidates = _context.lookup(ast->name, scope); const QList<LookupItem> candidates = _context.lookup(ast->name, scope);
addTypeOrStatic(candidates, ast); 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 (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId()) { if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId()) {
if (template_id->template_token) { 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. 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 (binding && ast->unqualified_name) {
if (ast->unqualified_name->asDestructorName() != 0) { if (ast->unqualified_name->asDestructorName() != 0) {
if (hasVirtualDestructor(binding)) if (hasVirtualDestructor(binding))
addUse(ast->unqualified_name, Use::VirtualMethod); addUse(ast->unqualified_name, SemanticInfo::VirtualMethodUse);
} else { } else {
addTypeOrStatic(binding->find(ast->unqualified_name->name), ast->unqualified_name); addTypeOrStatic(binding->find(ast->unqualified_name->name), ast->unqualified_name);
} }
@@ -729,7 +729,7 @@ bool CheckSymbols::visit(QualifiedNameAST *ast)
bool CheckSymbols::visit(TypenameTypeParameterAST *ast) bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
{ {
addUse(ast->name, Use::Type); addUse(ast->name, SemanticInfo::TypeUse);
accept(ast->type_id); accept(ast->type_id);
return false; return false;
} }
@@ -737,7 +737,7 @@ bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
bool CheckSymbols::visit(TemplateTypeParameterAST *ast) bool CheckSymbols::visit(TemplateTypeParameterAST *ast)
{ {
accept(ast->template_parameter_list); accept(ast->template_parameter_list);
addUse(ast->name, Use::Type); addUse(ast->name, SemanticInfo::TypeUse);
accept(ast->type_id); accept(ast->type_id);
return false; return false;
} }
@@ -775,7 +775,7 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast)
declId = q->unqualified_name; declId = q->unqualified_name;
if (fun->isVirtual()) { if (fun->isVirtual()) {
addUse(declId, Use::VirtualMethod); addUse(declId, SemanticInfo::VirtualMethodUse);
} else if (maybeVirtualMethod(fun->name())) { } else if (maybeVirtualMethod(fun->name())) {
addVirtualMethod(_context.lookup(fun->name(), fun->enclosingScope()), declId, fun->argumentCount()); addVirtualMethod(_context.lookup(fun->name(), fun->enclosingScope()), declId, fun->argumentCount());
} }
@@ -798,7 +798,7 @@ bool CheckSymbols::visit(FunctionDefinitionAST *ast)
return false; return false;
} }
void CheckSymbols::addUse(NameAST *ast, Use::Kind kind) void CheckSymbols::addUse(NameAST *ast, UseKind kind)
{ {
if (! ast) if (! ast)
return; return;
@@ -822,7 +822,7 @@ void CheckSymbols::addUse(NameAST *ast, Use::Kind kind)
addUse(startToken, kind); addUse(startToken, kind);
} }
void CheckSymbols::addUse(unsigned tokenIndex, Use::Kind kind) void CheckSymbols::addUse(unsigned tokenIndex, UseKind kind)
{ {
if (! tokenIndex) if (! tokenIndex)
return; return;
@@ -871,7 +871,7 @@ void CheckSymbols::addType(ClassOrNamespace *b, NameAST *ast)
unsigned line, column; unsigned line, column;
getTokenStartPosition(startToken, &line, &column); getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length(); const unsigned length = tok.length();
const Use use(line, column, length, Use::Type); const Use use(line, column, length, SemanticInfo::TypeUse);
addUse(use); addUse(use);
//qDebug() << "added use" << oo(ast->name) << line << column << length; //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); getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length(); const unsigned length = tok.length();
Use::Kind kind = Use::Type; UseKind kind = SemanticInfo::TypeUse;
if (c->enclosingEnum() != 0) if (c->enclosingEnum() != 0)
kind = Use::Static; kind = SemanticInfo::StaticUse;
const Use use(line, column, length, kind); const Use use(line, column, length, kind);
addUse(use); addUse(use);
@@ -951,7 +951,7 @@ void CheckSymbols::addClassMember(const QList<LookupItem> &candidates, NameAST *
getTokenStartPosition(startToken, &line, &column); getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length(); const unsigned length = tok.length();
const Use use(line, column, length, Use::Field); const Use use(line, column, length, SemanticInfo::FieldUse);
addUse(use); addUse(use);
break; break;
} }
@@ -976,7 +976,7 @@ void CheckSymbols::addStatic(const QList<LookupItem> &candidates, NameAST *ast)
getTokenStartPosition(startToken, &line, &column); getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length(); const unsigned length = tok.length();
const Use use(line, column, length, Use::Static); const Use use(line, column, length, SemanticInfo::StaticUse);
addUse(use); addUse(use);
//qDebug() << "added use" << oo(ast->name) << line << column << length; //qDebug() << "added use" << oo(ast->name) << line << column << length;
break; break;
@@ -1015,7 +1015,7 @@ void CheckSymbols::addVirtualMethod(const QList<LookupItem> &candidates, NameAST
getTokenStartPosition(startToken, &line, &column); getTokenStartPosition(startToken, &line, &column);
const unsigned length = tok.length(); const unsigned length = tok.length();
const Use use(line, column, length, Use::VirtualMethod); const Use use(line, column, length, SemanticInfo::VirtualMethodUse);
addUse(use); addUse(use);
break; break;
} }

View File

@@ -55,6 +55,7 @@ public:
virtual ~CheckSymbols(); virtual ~CheckSymbols();
typedef CppEditor::Internal::SemanticInfo::Use Use; typedef CppEditor::Internal::SemanticInfo::Use Use;
typedef CppEditor::Internal::SemanticInfo::UseKind UseKind;
virtual void run(); virtual void run();
@@ -110,8 +111,8 @@ protected:
void checkNamespace(NameAST *name); void checkNamespace(NameAST *name);
void addUse(const Use &use); void addUse(const Use &use);
void addUse(unsigned tokenIndex, Use::Kind kind); void addUse(unsigned tokenIndex, UseKind kind);
void addUse(NameAST *name, Use::Kind kind); void addUse(NameAST *name, UseKind kind);
void addType(ClassOrNamespace *b, NameAST *ast); void addType(ClassOrNamespace *b, NameAST *ast);

View File

@@ -87,6 +87,7 @@
#include <texteditor/tabsettings.h> #include <texteditor/tabsettings.h>
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
#include <texteditor/refactoroverlay.h> #include <texteditor/refactoroverlay.h>
#include <texteditor/semantichighlighter.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h> #include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/basicproposalitem.h> #include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/genericproposal.h> #include <texteditor/codeassist/genericproposal.h>
@@ -461,7 +462,6 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
} }
m_highlightRevision = 0; m_highlightRevision = 0;
m_nextHighlightBlockNumber = 0;
connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int))); connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightSymbolUsages(int,int)));
connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages())); connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishHighlightSymbolUsages()));
@@ -1010,68 +1010,11 @@ void CPPEditorWidget::highlightSymbolUsages(int from, int to)
else if (m_highlighter.isCanceled()) else if (m_highlighter.isCanceled())
return; // aborted return; // aborted
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter()); TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
Q_ASSERT(highlighter); QTC_ASSERT(highlighter, return);
QTextDocument *doc = document();
if (m_nextHighlightBlockNumber >= doc->blockCount()) TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
return; highlighter, m_highlighter, from, to, m_semanticHighlightFormatMap);
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;
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;
}
} }
void CPPEditorWidget::finishHighlightSymbolUsages() void CPPEditorWidget::finishHighlightSymbolUsages()
@@ -1082,20 +1025,11 @@ void CPPEditorWidget::finishHighlightSymbolUsages()
else if (m_highlighter.isCanceled()) else if (m_highlighter.isCanceled())
return; // aborted return; // aborted
CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter()); TextEditor::SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter();
Q_ASSERT(highlighter); QTC_ASSERT(highlighter, return);
QTextDocument *doc = document();
if (m_nextHighlightBlockNumber >= doc->blockCount()) TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
return; highlighter, m_highlighter);
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
while (b.isValid()) {
highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
b = b.next();
++m_nextHighlightBlockNumber;
}
} }
@@ -1777,11 +1711,16 @@ void CPPEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
m_occurrencesUnusedFormat.clearForeground(); m_occurrencesUnusedFormat.clearForeground();
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable")); m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME)); m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
m_typeFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE)); m_semanticHighlightFormatMap[SemanticInfo::TypeUse] =
m_localFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL)); fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TYPE));
m_fieldFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_FIELD)); m_semanticHighlightFormatMap[SemanticInfo::LocalUse] =
m_staticFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_STATIC)); fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LOCAL));
m_virtualMethodFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_VIRTUAL_METHOD)); 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)); 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 // 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); CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
m_highlighter = f; m_highlighter = f;
m_highlightRevision = semanticInfo.revision; m_highlightRevision = semanticInfo.revision;
m_nextHighlightBlockNumber = 0;
m_highlightWatcher.setFuture(m_highlighter); m_highlightWatcher.setFuture(m_highlighter);
} }
} }

View File

@@ -297,12 +297,8 @@ private:
QTextCharFormat m_occurrencesFormat; QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrencesUnusedFormat; QTextCharFormat m_occurrencesUnusedFormat;
QTextCharFormat m_occurrenceRenameFormat; QTextCharFormat m_occurrenceRenameFormat;
QTextCharFormat m_typeFormat; QHash<int, QTextCharFormat> m_semanticHighlightFormatMap;
QTextCharFormat m_localFormat;
QTextCharFormat m_fieldFormat;
QTextCharFormat m_staticFormat;
QTextCharFormat m_keywordFormat; QTextCharFormat m_keywordFormat;
QTextCharFormat m_virtualMethodFormat;
QList<QTextEdit::ExtraSelection> m_renameSelections; QList<QTextEdit::ExtraSelection> m_renameSelections;
int m_currentRenameSelection; int m_currentRenameSelection;
@@ -320,7 +316,6 @@ private:
QFuture<SemanticInfo::Use> m_highlighter; QFuture<SemanticInfo::Use> m_highlighter;
QFutureWatcher<SemanticInfo::Use> m_highlightWatcher; QFutureWatcher<SemanticInfo::Use> m_highlightWatcher;
unsigned m_highlightRevision; // the editor revision that requested the highlight unsigned m_highlightRevision; // the editor revision that requested the highlight
int m_nextHighlightBlockNumber;
QFuture<QList<int> > m_references; QFuture<QList<int> > m_references;
QFutureWatcher<QList<int> > m_referencesWatcher; QFutureWatcher<QList<int> > m_referencesWatcher;

View File

@@ -99,7 +99,7 @@ protected:
const Identifier *id = member->identifier(); const Identifier *id = member->identifier();
unsigned line, column; unsigned line, column;
getTokenStartPosition(member->sourceLocation(), &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())) { else if (!member->isGenerated() && (member->sourceLocation() < ast->firstToken() || member->enclosingScope()->isFunction())) {
unsigned line, column; unsigned line, column;
getTokenStartPosition(simpleName->identifier_token, &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; return false;
} }
} }

View File

@@ -35,6 +35,7 @@
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h> #include <cplusplus/LookupContext.h>
#include <texteditor/semantichighlighter.h>
#include <QtCore/QHash> #include <QtCore/QHash>
namespace CppEditor { namespace CppEditor {
@@ -45,22 +46,13 @@ class CPPEditorWidget;
class SemanticInfo class SemanticInfo
{ {
public: public:
struct Use { typedef TextEditor::SemanticHighlighter::Result Use;
unsigned line; enum UseKind {
unsigned column; TypeUse = 0,
unsigned length; LocalUse,
unsigned kind; FieldUse,
StaticUse,
enum Kind { VirtualMethodUse
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 QHash<CPlusPlus::Symbol *, QList<Use> > LocalUseMap; typedef QHash<CPlusPlus::Symbol *, QList<Use> > LocalUseMap;

View File

@@ -41,6 +41,7 @@
#include "qmljsautocompleter.h" #include "qmljsautocompleter.h"
#include "qmljscompletionassist.h" #include "qmljscompletionassist.h"
#include "qmljsquickfixassist.h" #include "qmljsquickfixassist.h"
#include "qmljssemantichighlighter.h"
#include <qmljs/qmljsbind.h> #include <qmljs/qmljsbind.h>
#include <qmljs/qmljsevaluate.h> #include <qmljs/qmljsevaluate.h>
@@ -658,7 +659,8 @@ QmlJSTextEditorWidget::QmlJSTextEditorWidget(QWidget *parent) :
m_modelManager(0), m_modelManager(0),
m_contextPane(0), m_contextPane(0),
m_updateSelectedElements(false), m_updateSelectedElements(false),
m_findReferences(new FindReferences(this)) m_findReferences(new FindReferences(this)),
m_semanticHighlighter(new SemanticHighlighter(this))
{ {
qRegisterMetaType<QmlJSEditor::SemanticInfo>("QmlJSEditor::SemanticInfo"); 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 // only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
m_occurrencesFormat.clearForeground(); m_occurrencesFormat.clearForeground();
m_occurrenceRenameFormat.clearForeground(); m_occurrenceRenameFormat.clearForeground();
m_semanticHighlighter->updateFontSettings(fs);
} }
@@ -1544,9 +1548,6 @@ void QmlJSTextEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
FindIdDeclarations updateIds; FindIdDeclarations updateIds;
m_semanticInfo.idLocations = updateIds(doc); m_semanticInfo.idLocations = updateIds(doc);
FindDeclarations findDeclarations;
m_semanticInfo.declarations = findDeclarations(doc->ast());
if (m_contextPane) { if (m_contextPane) {
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position()); Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
if (newNode) { if (newNode) {
@@ -1563,6 +1564,10 @@ void QmlJSTextEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo)
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document()); appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document()); appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document());
setExtraSelections(CodeWarningsSelection, selections); 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) void QmlJSTextEditorWidget::onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker)

View File

@@ -70,6 +70,7 @@ namespace Internal {
class QmlOutlineModel; class QmlOutlineModel;
class SemanticInfoUpdater; class SemanticInfoUpdater;
struct SemanticInfoUpdaterSource; struct SemanticInfoUpdaterSource;
class SemanticHighlighter;
} // namespace Internal } // namespace Internal
struct QMLJSEDITOR_EXPORT Declaration struct QMLJSEDITOR_EXPORT Declaration
@@ -132,7 +133,6 @@ public: // attributes
QmlJS::ContextPtr context; QmlJS::ContextPtr context;
QList<Range> ranges; QList<Range> ranges;
QHash<QString, QList<QmlJS::AST::SourceLocation> > idLocations; QHash<QString, QList<QmlJS::AST::SourceLocation> > idLocations;
QList<Declaration> declarations;
// these are in addition to the parser messages in the document // these are in addition to the parser messages in the document
QList<QmlJS::DiagnosticMessage> semanticMessages; QList<QmlJS::DiagnosticMessage> semanticMessages;
@@ -251,6 +251,9 @@ private:
bool m_updateSelectedElements; bool m_updateSelectedElements;
FindReferences *m_findReferences; FindReferences *m_findReferences;
Internal::SemanticHighlighter *m_semanticHighlighter;
friend class Internal::SemanticHighlighter;
}; };
} // namespace QmlJSEditor } // namespace QmlJSEditor

View File

@@ -36,7 +36,8 @@ HEADERS += \
qmljsquickfixassist.h \ qmljsquickfixassist.h \
qmljscompletionassist.h \ qmljscompletionassist.h \
qmljsquickfix.h \ qmljsquickfix.h \
qmljssemanticinfoupdater.h qmljssemanticinfoupdater.h \
qmljssemantichighlighter.h
SOURCES += \ SOURCES += \
qmljseditor.cpp \ qmljseditor.cpp \
@@ -66,7 +67,8 @@ SOURCES += \
qmljsquickfixassist.cpp \ qmljsquickfixassist.cpp \
qmljscompletionassist.cpp \ qmljscompletionassist.cpp \
qmljsquickfix.cpp \ qmljsquickfix.cpp \
qmljssemanticinfoupdater.cpp qmljssemanticinfoupdater.cpp \
qmljssemantichighlighter.cpp
RESOURCES += qmljseditor.qrc RESOURCES += qmljseditor.qrc
OTHER_FILES += QmlJSEditor.mimetypes.xml OTHER_FILES += QmlJSEditor.mimetypes.xml
@@ -74,7 +76,3 @@ OTHER_FILES += QmlJSEditor.mimetypes.xml
FORMS += \ FORMS += \
quicktoolbarsettingspage.ui \ quicktoolbarsettingspage.ui \
qmljscomponentnamedialog.ui qmljscomponentnamedialog.ui

View File

@@ -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));
}

View File

@@ -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

View File

@@ -239,6 +239,13 @@ FormatDescription::FormatDescription(const QString &id, const QString &displayNa
m_format.setForeground(color); 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 QColor FormatDescription::foreground() const
{ {
if (m_id == QLatin1String(Constants::C_LINE_NUMBER)) { if (m_id == QLatin1String(Constants::C_LINE_NUMBER)) {

View File

@@ -60,6 +60,8 @@ class TEXTEDITOR_EXPORT FormatDescription
public: public:
FormatDescription(const QString &id, const QString &displayName, FormatDescription(const QString &id, const QString &displayName,
const QColor &foreground = Qt::black); const QColor &foreground = Qt::black);
FormatDescription(const QString &id, const QString &displayName,
const Format &format);
QString id() const QString id() const
{ return m_id; } { return m_id; }

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -106,7 +106,8 @@ SOURCES += texteditorplugin.cpp \
tabpreferences.cpp \ tabpreferences.cpp \
icodestylepreferencesfactory.cpp \ icodestylepreferencesfactory.cpp \
tabpreferenceswidget.cpp \ tabpreferenceswidget.cpp \
fallbackselectorwidget.cpp fallbackselectorwidget.cpp \
semantichighlighter.cpp
HEADERS += texteditorplugin.h \ HEADERS += texteditorplugin.h \
textfilewizard.h \ textfilewizard.h \
@@ -217,7 +218,8 @@ HEADERS += texteditorplugin.h \
tabpreferences.h \ tabpreferences.h \
icodestylepreferencesfactory.h \ icodestylepreferencesfactory.h \
tabpreferenceswidget.h \ tabpreferenceswidget.h \
fallbackselectorwidget.h fallbackselectorwidget.h \
semantichighlighter.h
FORMS += \ FORMS += \
displaysettingspage.ui \ displaysettingspage.ui \

View File

@@ -128,6 +128,17 @@ const char * const C_COMMENT = "Comment";
const char * const C_DOXYGEN_COMMENT = "Doxygen.Comment"; const char * const C_DOXYGEN_COMMENT = "Doxygen.Comment";
const char * const C_DOXYGEN_TAG = "Doxygen.Tag"; const char * const C_DOXYGEN_TAG = "Doxygen.Tag";
const char * const C_VISUAL_WHITESPACE = "VisualWhitespace"; 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"; const char * const C_DISABLED_CODE = "DisabledCode";

View File

@@ -146,6 +146,27 @@ TextEditorSettings::TextEditorSettings(QObject *parent)
virtualMethodFormatDescriptor.format().setItalic(true); virtualMethodFormatDescriptor.format().setItalic(true);
formatDescriptions.append(virtualMethodFormatDescriptor); 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_KEYWORD), tr("Keyword"), Qt::darkYellow));
formatDescriptions.append(FormatDescription(QLatin1String(C_OPERATOR), tr("Operator"))); formatDescriptions.append(FormatDescription(QLatin1String(C_OPERATOR), tr("Operator")));
formatDescriptions.append(FormatDescription(QLatin1String(C_PREPROCESSOR), tr("Preprocessor"), Qt::darkBlue)); formatDescriptions.append(FormatDescription(QLatin1String(C_PREPROCESSOR), tr("Preprocessor"), Qt::darkBlue));