diff --git a/src/plugins/duieditor/duidocument.cpp b/src/plugins/duieditor/duidocument.cpp index 9d13d4745cdca77fa51cdfa0196a9b2337ce3711..02680b08f5b43098a42faa10e474dce65b063adb 100644 --- a/src/plugins/duieditor/duidocument.cpp +++ b/src/plugins/duieditor/duidocument.cpp @@ -27,6 +27,7 @@ ** **************************************************************************/ +#include "idcollector.h" #include "duidocument.h" #include "qmljsast_p.h" #include "qmljslexer_p.h" @@ -44,12 +45,21 @@ DuiDocument::DuiDocument(const QString &fileName) , _fileName(fileName) , _parsedCorrectly(false) { + const int slashIdx = fileName.lastIndexOf('/'); + if (slashIdx != -1) + _path = fileName.left(slashIdx); + + if (fileName.toLower().endsWith(".qml")) + _componentName = fileName.mid(slashIdx + 1, fileName.size() - (slashIdx + 1) - 4); } DuiDocument::~DuiDocument() { - delete _engine; - delete _pool; + if (_engine) + delete _engine; + + if (_pool) + delete _pool; } DuiDocument::Ptr DuiDocument::create(const QString &fileName) @@ -81,6 +91,7 @@ bool DuiDocument::parse() _engine = new Engine(); _pool = new NodePool(_fileName, _engine); + _ids.clear(); Lexer lexer(_engine); Parser parser(_engine); @@ -90,6 +101,12 @@ bool DuiDocument::parse() _parsedCorrectly = parser.parse(); _program = parser.ast(); _diagnosticMessages = parser.diagnosticMessages(); + + if (_parsedCorrectly && _program) { + Internal::IdCollector collect; + _ids = collect(_program); + } + return _parsedCorrectly; } @@ -100,3 +117,46 @@ Snapshot::Snapshot() Snapshot::~Snapshot() { } + +void Snapshot::insert(const DuiDocument::Ptr &document) +{ + QMap<QString, DuiDocument::Ptr>::insert(document->fileName(), document); +} + +DuiDocument::PtrList Snapshot::importedDocuments(const DuiDocument::Ptr &doc, const QString &importPath) +{ + DuiDocument::PtrList result; + + const QString docPath = doc->path() + '/' + importPath; + + for (Iterator i = iterator(); i.hasNext();) { + DuiDocument::Ptr candidate = i.next().value(); + + if (candidate == doc) + continue; + + if (candidate->path() == doc->path() || candidate->path() == docPath) + result.append(candidate); + } + + return result; +} + +QMap<QString, DuiDocument::Ptr> Snapshot::componentsDefinedByImportedDocuments(const DuiDocument::Ptr &doc, const QString &importPath) +{ + QMap<QString, DuiDocument::Ptr> result; + + const QString docPath = doc->path() + '/' + importPath; + + for (Iterator i = iterator(); i.hasNext();) { + DuiDocument::Ptr candidate = i.next().value(); + + if (candidate == doc) + continue; + + if (candidate->path() == doc->path() || candidate->path() == docPath) + result.insert(candidate->componentName(), candidate); + } + + return result; +} diff --git a/src/plugins/duieditor/duidocument.h b/src/plugins/duieditor/duidocument.h index f1ad2a8471d6bc130b025dffa61f1903e6677dd9..dd99c97ab2df751d4ed80607394e0237590c6a0a 100644 --- a/src/plugins/duieditor/duidocument.h +++ b/src/plugins/duieditor/duidocument.h @@ -29,8 +29,10 @@ #ifndef DUIDOCUMENT_H #define DUIDOCUMENT_H -#include <QtCore/QSharedPointer> +#include <QtCore/QList> #include <QtCore/QMap> +#include <QtCore/QPair> +#include <QtCore/QSharedPointer> #include <QtCore/QString> #include "duieditor_global.h" @@ -44,6 +46,8 @@ class DUIEDITOR_EXPORT DuiDocument { public: typedef QSharedPointer<DuiDocument> Ptr; + typedef QList<DuiDocument::Ptr> PtrList; + typedef QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > IdTable; protected: DuiDocument(const QString &fileName); @@ -62,7 +66,11 @@ public: bool isParsedCorrectly() const { return _parsedCorrectly; } + IdTable ids() const { return _ids; } + QString fileName() const { return _fileName; } + QString path() const { return _path; } + QString componentName() const { return _componentName; } private: QmlJS::Engine *_engine; @@ -70,8 +78,11 @@ private: QmlJS::AST::UiProgram *_program; QList<QmlJS::DiagnosticMessage> _diagnosticMessages; QString _fileName; + QString _path; + QString _componentName; QString _source; bool _parsedCorrectly; + IdTable _ids; }; class DUIEDITOR_EXPORT Snapshot: protected QMap<QString, DuiDocument::Ptr> @@ -80,8 +91,7 @@ public: Snapshot(); ~Snapshot(); - void insert(const DuiDocument::Ptr &document) - { QMap<QString, DuiDocument::Ptr>::insert(document->fileName(), document); } + void insert(const DuiDocument::Ptr &document); typedef QMapIterator<QString, DuiDocument::Ptr> Iterator; Iterator iterator() const @@ -89,6 +99,9 @@ public: DuiDocument::Ptr document(const QString &fileName) const { return value(fileName); } + + DuiDocument::PtrList importedDocuments(const DuiDocument::Ptr &doc, const QString &importPath); + QMap<QString, DuiDocument::Ptr> componentsDefinedByImportedDocuments(const DuiDocument::Ptr &doc, const QString &importPath); }; } // emd of namespace DuiEditor diff --git a/src/plugins/duieditor/duieditor.cpp b/src/plugins/duieditor/duieditor.cpp index e3820817879edc0ca3103247eff76a243bc61744..3add88f8de76bacfcc5479faf873211fa926ac26 100644 --- a/src/plugins/duieditor/duieditor.cpp +++ b/src/plugins/duieditor/duieditor.cpp @@ -714,11 +714,10 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur if (!doc) return link; - QMap<QString, QmlJS::AST::SourceLocation> idPositions = IdCollector()(doc->program()); - NavigationTokenFinder finder; - if (finder(doc->program(), cursor.position(), resolveTarget, idPositions)) { - link.fileName = file()->fileName(); + finder(doc, cursor.position(), snapshot); + if (finder.targetFound()) { + link.fileName = finder.fileName(); link.pos = finder.linkPosition(); link.length = finder.linkLength(); diff --git a/src/plugins/duieditor/duimodelmanager.cpp b/src/plugins/duieditor/duimodelmanager.cpp index 6af277514a4c0931f258756c31738d10148d617e..5645c8cc424e5fed5bccabf8285c3fe26fcde968 100644 --- a/src/plugins/duieditor/duimodelmanager.cpp +++ b/src/plugins/duieditor/duimodelmanager.cpp @@ -69,34 +69,36 @@ void DuiModelManager::updateSourceFiles(const QStringList &files) QFuture<void> DuiModelManager::refreshSourceFiles(const QStringList &sourceFiles) { - if (! sourceFiles.isEmpty()) { - const QMap<QString, QString> workingCopy = buildWorkingCopyList(); + if (sourceFiles.isEmpty()) { + return QFuture<void>(); + } - QFuture<void> result = QtConcurrent::run(&DuiModelManager::parse, - workingCopy, sourceFiles, - this); + const QMap<QString, QString> workingCopy = buildWorkingCopyList(); - if (m_synchronizer.futures().size() > 10) { - QList<QFuture<void> > futures = m_synchronizer.futures(); + QFuture<void> result = QtConcurrent::run(&DuiModelManager::parse, + workingCopy, sourceFiles, + this); - m_synchronizer.clearFutures(); + if (m_synchronizer.futures().size() > 10) { + QList<QFuture<void> > futures = m_synchronizer.futures(); - foreach (QFuture<void> future, futures) { - if (! (future.isFinished() || future.isCanceled())) - m_synchronizer.addFuture(future); - } + m_synchronizer.clearFutures(); + + foreach (QFuture<void> future, futures) { + if (! (future.isFinished() || future.isCanceled())) + m_synchronizer.addFuture(future); } + } - m_synchronizer.addFuture(result); + m_synchronizer.addFuture(result); - if (sourceFiles.count() > 1) { - m_core->progressManager()->addTask(result, tr("Indexing"), - DuiEditor::Constants::TASK_INDEX, - Core::ProgressManager::CloseOnSuccess); - } - return result; + if (sourceFiles.count() > 1) { + m_core->progressManager()->addTask(result, tr("Indexing"), + DuiEditor::Constants::TASK_INDEX, + Core::ProgressManager::CloseOnSuccess); } - return QFuture<void>(); + + return result; } QMap<QString, QString> DuiModelManager::buildWorkingCopyList() diff --git a/src/plugins/duieditor/idcollector.cpp b/src/plugins/duieditor/idcollector.cpp index 3597a752d0b5855588e7ec3f63cfcf8d4bd0c1d5..97536aa7193fcbbea3aa8d34526df9972ad313cb 100644 --- a/src/plugins/duieditor/idcollector.cpp +++ b/src/plugins/duieditor/idcollector.cpp @@ -6,13 +6,35 @@ using namespace QmlJS; using namespace QmlJS::AST; using namespace DuiEditor::Internal; -QMap<QString, QmlJS::AST::SourceLocation> IdCollector::operator()(QmlJS::AST::UiProgram *ast) +QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > IdCollector::operator()(QmlJS::AST::UiProgram *ast) { - _idLocations.clear(); + _ids.clear(); Node::accept(ast, this); - return _idLocations; + return _ids; +} + +bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast) +{ + _scopes.push(ast); + return true; +} + +bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast) +{ + _scopes.push(ast); + return true; +} + +void IdCollector::endVisit(QmlJS::AST::UiObjectBinding *) +{ + _scopes.pop(); +} + +void IdCollector::endVisit(QmlJS::AST::UiObjectDefinition *) +{ + _scopes.pop(); } bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast) @@ -29,6 +51,6 @@ void IdCollector::addId(QmlJS::NameId* id, const QmlJS::AST::SourceLocation &idL { const QString idString = id->asString(); - if (!_idLocations.contains(idString)) - _idLocations[idString] = idLocation; + if (!_ids.contains(idString)) + _ids[idString] = qMakePair(idLocation, _scopes.top()); } diff --git a/src/plugins/duieditor/idcollector.h b/src/plugins/duieditor/idcollector.h index ac10b9c1e47b9e37130ae1c3cc8486032e5a32af..91e2a1fed86bde082d0ccad59fc1788aa6bdaeda 100644 --- a/src/plugins/duieditor/idcollector.h +++ b/src/plugins/duieditor/idcollector.h @@ -2,6 +2,8 @@ #define IDCOLLECTOR_H #include <QMap> +#include <QPair> +#include <QStack> #include <QString> #include "qmljsastvisitor_p.h" @@ -14,16 +16,22 @@ namespace Internal { class IdCollector: protected QmlJS::AST::Visitor { public: - QMap<QString, QmlJS::AST::SourceLocation> operator()(QmlJS::AST::UiProgram *ast); + QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > operator()(QmlJS::AST::UiProgram *ast); protected: + virtual bool visit(QmlJS::AST::UiObjectBinding *ast); + virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); virtual bool visit(QmlJS::AST::UiScriptBinding *ast); + virtual void endVisit(QmlJS::AST::UiObjectBinding *); + virtual void endVisit(QmlJS::AST::UiObjectDefinition *); + private: void addId(QmlJS::NameId* id, const QmlJS::AST::SourceLocation &idLocation); private: - QMap<QString, QmlJS::AST::SourceLocation> _idLocations; + QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > _ids; + QStack<QmlJS::AST::Node *> _scopes; }; } // namespace Internal diff --git a/src/plugins/duieditor/navigationtokenfinder.cpp b/src/plugins/duieditor/navigationtokenfinder.cpp index ea007c95349d699bfcec425c085ae678b4cf1907..0dda0f8e372d76ecba896e875664e09b912a33ee 100644 --- a/src/plugins/duieditor/navigationtokenfinder.cpp +++ b/src/plugins/duieditor/navigationtokenfinder.cpp @@ -5,23 +5,60 @@ using namespace QmlJS; using namespace QmlJS::AST; +using namespace DuiEditor; using namespace DuiEditor::Internal; -bool NavigationTokenFinder::operator()(QmlJS::AST::UiProgram *ast, int position, bool resolveTarget, const QMap<QString, QmlJS::AST::SourceLocation> &idPositions) +void NavigationTokenFinder::operator()(const DuiDocument::Ptr &doc, int position, const Snapshot &snapshot) { - _resolveTarget = resolveTarget; + _doc = doc; + _snapshot = snapshot; _pos = position; - _idPositions = idPositions; _scopes.clear(); _linkPosition = -1; + _fileName.clear(); _targetLine = -1; - Node::accept(ast, this); + Node::accept(doc->program(), this); +} + +static QStringList buildQualifiedId(QmlJS::AST::FieldMemberExpression *expr) +{ + QStringList qId; + + if (FieldMemberExpression *baseExpr = cast<FieldMemberExpression*>(expr->base)) { + qId = buildQualifiedId(baseExpr); + } else if (IdentifierExpression *idExpr = cast<IdentifierExpression*>(expr->base)) { + qId.append(idExpr->name->asString()); + } else { + return qId; + } - if (resolveTarget) - return targetFound(); - else - return linkFound(); + qId.append(expr->name->asString()); + return qId; +} + +bool NavigationTokenFinder::visit(QmlJS::AST::FieldMemberExpression *ast) +{ + if (linkFound()) + return false; + + if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) { + if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) { + // found it: + _linkPosition = ast->identifierToken.offset; + _linkLength = ast->identifierToken.end() - _linkPosition; + + const QStringList qualifiedId(buildQualifiedId(ast)); + if (!qualifiedId.isEmpty()) + findDeclaration(qualifiedId); + } else { + Node::accept(ast->base, this); + } + + return false; + } + + return true; } bool NavigationTokenFinder::visit(QmlJS::AST::IdentifierExpression *ast) @@ -33,8 +70,7 @@ bool NavigationTokenFinder::visit(QmlJS::AST::IdentifierExpression *ast) _linkPosition = ast->identifierToken.offset; _linkLength = ast->identifierToken.length; - if (Node *node = findDeclarationInScopesOrIds(ast->name)) - rememberStartPosition(node); + findDeclaration(QStringList() << ast->name->asString()); } return false; @@ -50,6 +86,11 @@ bool NavigationTokenFinder::visit(QmlJS::AST::UiArrayBinding *ast) return false; } +bool NavigationTokenFinder::visit(QmlJS::AST::UiImportList *) +{ + return false; +} + bool NavigationTokenFinder::visit(QmlJS::AST::UiPublicMember *ast) { if (linkFound()) @@ -64,10 +105,7 @@ bool NavigationTokenFinder::visit(QmlJS::AST::Block *ast) { _scopes.push(ast); - if (linkFound()) - return false; - - return true; + return !linkFound(); } void NavigationTokenFinder::endVisit(QmlJS::AST::Block *) @@ -79,11 +117,10 @@ bool NavigationTokenFinder::visit(QmlJS::AST::UiObjectBinding *ast) { _scopes.push(ast); - if (linkFound()) - return false; - - Node::accept(ast->qualifiedTypeNameId, this); - Node::accept(ast->initializer, this); + if (!linkFound()) { + checkType(ast->qualifiedTypeNameId); + Node::accept(ast->initializer, this); + } return false; } @@ -97,10 +134,12 @@ bool NavigationTokenFinder::visit(QmlJS::AST::UiObjectDefinition *ast) { _scopes.push(ast); - if (linkFound()) - return false; + if (!linkFound()) { + checkType(ast->qualifiedTypeNameId); + Node::accept(ast->initializer, this); + } - return true; + return false; } void NavigationTokenFinder::endVisit(QmlJS::AST::UiObjectDefinition *) @@ -108,36 +147,34 @@ void NavigationTokenFinder::endVisit(QmlJS::AST::UiObjectDefinition *) _scopes.pop(); } -bool NavigationTokenFinder::visit(QmlJS::AST::UiQualifiedId *ast) +void NavigationTokenFinder::checkType(QmlJS::AST::UiQualifiedId *ast) { - if (linkFound()) - return false; - if (ast->identifierToken.offset <= _pos) { for (UiQualifiedId *iter = ast; iter; iter = iter->next) { if (_pos <= iter->identifierToken.end()) { _linkPosition = ast->identifierToken.offset; - for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) + QStringList names; + for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) { _linkLength = iter2->identifierToken.end() - _linkPosition; + names.append(iter2->name->asString()); + } + findTypeDeclaration(names); - if (Node *node = findDeclarationInScopesOrIds(ast)) - rememberStartPosition(node); - - return false; + return; } } } - - return false; } bool NavigationTokenFinder::visit(QmlJS::AST::UiScriptBinding *ast) { - if (linkFound()) - return false; - - Node::accept(ast->statement, this); + if (!linkFound()) { + if (ast->qualifiedId && !ast->qualifiedId->next && ast->qualifiedId->name && ast->qualifiedId->name->asString() == "id") + return false; + else + Node::accept(ast->statement, this); + } return false; } @@ -147,134 +184,172 @@ bool NavigationTokenFinder::visit(QmlJS::AST::UiSourceElement * /*ast*/) return false; } -void NavigationTokenFinder::rememberStartPosition(QmlJS::AST::Node *node) +bool NavigationTokenFinder::findInJS(const QStringList &qualifiedId, QmlJS::AST::Block *block) { - if (UiObjectMember *om = dynamic_cast<UiObjectMember*>(node)) { - _targetLine = om->firstSourceLocation().startLine; - _targetColumn = om->firstSourceLocation().startColumn; - } else if (VariableDeclaration *vd = cast<VariableDeclaration*>(node)) { - _targetLine = vd->identifierToken.startLine; - _targetColumn = vd->identifierToken.startColumn; - } else { -// qWarning() << "Found declaration of unknown type as a navigation target"; - } -} + if (qualifiedId.size() > 1) + return false; // we can only find "simple" JavaScript variables this way -void NavigationTokenFinder::rememberStartPosition(const QmlJS::AST::SourceLocation &location) -{ - _targetLine = location.startLine; - _targetColumn = location.startColumn; -} + const QString id = qualifiedId[0]; -static QmlJS::AST::Node *findDeclaration(const QString &nameId, QmlJS::AST::UiObjectMember *m) -{ - if (UiPublicMember *p = cast<UiPublicMember*>(m)) { - if (p->name->asString() == nameId) - return p; - } else if (UiObjectBinding *o = cast<UiObjectBinding*>(m)) { - if (!(o->qualifiedId->next) && o->qualifiedId->name->asString() == nameId) - return o; - } else if (UiArrayBinding *a = cast<UiArrayBinding*>(m)) { - if (!(a->qualifiedId->next) && a->qualifiedId->name->asString() == nameId) - return a; + for (StatementList *iter = block->statements; iter; iter = iter->next) { + Statement *stmt = iter->statement; + + if (VariableStatement *varStmt = cast<VariableStatement*>(stmt)) { + for (VariableDeclarationList *varIter = varStmt->declarations; varIter; varIter = varIter->next) { + if (varIter->declaration && varIter->declaration->name && varIter->declaration->name->asString() == id) { + rememberLocation(varIter->declaration->identifierToken); + return true; + } + } + } } - return 0; + return false; } -static QmlJS::AST::Node *findDeclaration(const QString &nameId, QmlJS::AST::UiObjectMemberList *l) +static bool matches(UiQualifiedId *candidate, const QStringList &wanted) { - for (UiObjectMemberList *iter = l; iter; iter = iter->next) - if (Node *n = findDeclaration(nameId, iter->member)) - return n; + UiQualifiedId *iter = candidate; + for (int i = 0; i < wanted.size(); ++i) { + if (!iter) + return false; + + if (iter->name->asString() != wanted[i]) + return false; + + iter = iter->next; + } - return 0; + return !iter; } -static QmlJS::AST::Node *findDeclaration(const QString &nameId, QmlJS::AST::Statement *s) +bool NavigationTokenFinder::findProperty(const QStringList &qualifiedId, QmlJS::AST::UiQualifiedId *typeId, QmlJS::AST::UiObjectMemberList *ast, int scopeLevel) { - if (VariableStatement *v = cast<VariableStatement*>(s)) { - for (VariableDeclarationList *l = v->declarations; l; l = l->next) { - if (l->declaration->name->asString() == nameId) - return l->declaration; + // 1. try the "overridden" properties: + for (UiObjectMemberList *iter = ast; iter; iter = iter->next) { + UiObjectMember *member = iter->member; + + if (UiPublicMember *publicMember = cast<UiPublicMember*>(member)) { + if (publicMember->name && qualifiedId.size() == 1 && publicMember->name->asString() == qualifiedId.first()) { + rememberLocation(publicMember->identifierToken); + return true; + } + } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) { + if (matches(objectBinding->qualifiedId, qualifiedId)) { + rememberLocation(objectBinding->qualifiedId->identifierToken); + return true; + } + } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) { + if (matches(scriptBinding->qualifiedId, qualifiedId)) { + rememberLocation(scriptBinding->qualifiedId->identifierToken); + return true; + } + } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) { + if (matches(arrayBinding->qualifiedId, qualifiedId)) { + rememberLocation(arrayBinding->qualifiedId->identifierToken); + return true; + } } } - return 0; -} + // 2. if the property is "parent", go one scope level up: + if (qualifiedId[0] == "parent"){ + if (scopeLevel <= 0) + return false; + + int newScopeLevel = scopeLevel - 1; + Node *parentScope = _scopes[newScopeLevel]; + + QStringList newQualifiedId = qualifiedId; + newQualifiedId.removeFirst(); + if (UiObjectBinding *binding = cast<UiObjectBinding*>(parentScope)) { + if (newQualifiedId.isEmpty()) { + rememberLocation(binding->qualifiedTypeNameId->identifierToken); + return true; + } else { + return findProperty(newQualifiedId, binding->qualifiedTypeNameId, binding->initializer->members, newScopeLevel); + } + } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parentScope)) { + if (newQualifiedId.isEmpty()) { + rememberLocation(definition->qualifiedTypeNameId->identifierToken); + return true; + } else { + return findProperty(newQualifiedId, definition->qualifiedTypeNameId, definition->initializer->members, newScopeLevel); + } + } else { + return false; + } + } -static QmlJS::AST::Node *findDeclaration(const QString &nameId, QmlJS::AST::StatementList *l) -{ - for (StatementList *iter = l; iter; iter = iter->next) - if (Node *n = findDeclaration(nameId, iter->statement)) - return n; + // 3. if the type is a custom type, search properties there: + { + // TODO + } - return 0; + // all failed, so: + return false; } -static QmlJS::AST::Node *findDeclarationAsDirectChild(const QString &nameId, QmlJS::AST::Node *node) +void NavigationTokenFinder::findAsId(const QStringList &qualifiedId) { - if (UiObjectBinding *binding = cast<UiObjectBinding*>(node)) { - return findDeclaration(nameId, binding->initializer->members); - } else if (UiObjectDefinition *def = cast<UiObjectDefinition*>(node)) { - return findDeclaration(nameId, def->initializer->members); - } else if (Block *block = cast<Block *>(node)) { - return findDeclaration(nameId, block->statements); - } else { - return 0; + const DuiDocument::IdTable ids = _doc->ids(); + + if (ids.contains(qualifiedId.first())) { + QPair<SourceLocation, Node*> idInfo = ids[qualifiedId.first()]; + if (qualifiedId.size() == 1) { + rememberLocation(idInfo.first); + } else if (qualifiedId.size() == 2) { + Node *parent = idInfo.second; + + if (UiObjectBinding *binding = cast<UiObjectBinding*>(parent)) { + findProperty(QStringList() << qualifiedId[1], binding->qualifiedTypeNameId, binding->initializer->members, -1); + } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parent)) { + findProperty(QStringList() << qualifiedId[1], definition->qualifiedTypeNameId, definition->initializer->members, -1); + } + } } } -static QmlJS::AST::Node *findDeclarationInNode(QmlJS::AST::UiQualifiedId *qualifiedId, QmlJS::AST::Node *node) +void NavigationTokenFinder::findDeclaration(const QStringList &qualifiedId, int scopeLevel) { - if (!qualifiedId || !node) - return node; - else - return findDeclarationInNode(qualifiedId->next, findDeclarationAsDirectChild(qualifiedId->name->asString(), node)); + Node *scope = _scopes[scopeLevel]; + + if (Block *block = cast<Block*>(scope)) { + if (!findInJS(qualifiedId, block)) // continue searching the parent scope: + findDeclaration(qualifiedId, scopeLevel - 1); + } else if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope)) { + if (findProperty(qualifiedId, binding->qualifiedTypeNameId, binding->initializer->members, scopeLevel)) { + return; + } else { + findAsId(qualifiedId); + } + } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope)) { + if (findProperty(qualifiedId, definition->qualifiedTypeNameId, definition->initializer->members, scopeLevel)) { + return; + } else { + findAsId(qualifiedId); + } + } else { + Q_ASSERT(!"Unknown scope type"); + } } -QmlJS::AST::Node *NavigationTokenFinder::findDeclarationInScopesOrIds(QmlJS::NameId *nameId) +void NavigationTokenFinder::findDeclaration(const QStringList &id) { - if (!_resolveTarget) - return 0; - - const QString nameAsString = nameId->asString(); - if (nameAsString == "parent" && _scopes.size() >= 2) - return _scopes.at(_scopes.size() - 2); - - foreach (QmlJS::AST::Node *scope, _scopes) { - Node *result = findDeclarationAsDirectChild(nameAsString, scope); - - if (result) - return result; - } - - if (_idPositions.contains(nameAsString)) { - rememberStartPosition(_idPositions[nameAsString]); - } + if (id.isEmpty()) + return; - return 0; + findDeclaration(id, _scopes.size() - 1); } -QmlJS::AST::Node *NavigationTokenFinder::findDeclarationInScopesOrIds(QmlJS::AST::UiQualifiedId *qualifiedId) +void NavigationTokenFinder::findTypeDeclaration(const QStringList &id) { - if (!_resolveTarget) - return 0; - - const QString nameAsString = qualifiedId->name->asString(); - if (nameAsString == "parent" && _scopes.size() >= 2) - return findDeclarationInNode(qualifiedId->next, _scopes.at(_scopes.size() - 2)); - - foreach (QmlJS::AST::Node *scope, _scopes) { - Node *result = findDeclarationAsDirectChild(nameAsString, scope); - - if (result) - return findDeclarationInNode(qualifiedId->next, result); - } - - if (_idPositions.contains(nameAsString)) { - rememberStartPosition(_idPositions[nameAsString]); - } + // TODO +} - return 0; +void NavigationTokenFinder::rememberLocation(const QmlJS::AST::SourceLocation &loc) +{ + _fileName = _doc->fileName(); + _targetLine = loc.startLine; + _targetColumn = loc.startColumn; } diff --git a/src/plugins/duieditor/navigationtokenfinder.h b/src/plugins/duieditor/navigationtokenfinder.h index bcd8c73b46b7c3f0b24d02f2632550a070ab55e1..c05b4026148e94faa03cb5cbe648683099baefb7 100644 --- a/src/plugins/duieditor/navigationtokenfinder.h +++ b/src/plugins/duieditor/navigationtokenfinder.h @@ -4,7 +4,9 @@ #include <QMap> #include <QStack> #include <QString> +#include <QStringList> +#include "duidocument.h" #include "qmljsastvisitor_p.h" namespace QmlJS { @@ -17,24 +19,26 @@ namespace Internal { class NavigationTokenFinder: protected QmlJS::AST::Visitor { public: - bool operator()(QmlJS::AST::UiProgram *ast, int position, bool resolveTarget, const QMap<QString, QmlJS::AST::SourceLocation> &idPositions); + void operator()(const DuiDocument::Ptr &doc, int position, const Snapshot &snapshot); bool targetFound() const { return _targetLine != -1; } bool linkFound() const { return _linkPosition != -1; } int linkPosition() const { return _linkPosition; } int linkLength() const { return _linkLength; } + QString fileName() const { return _fileName; } int targetLine() const { return _targetLine; } int targetColumn() const { return _targetColumn; } protected: virtual bool visit(QmlJS::AST::Block *ast); + virtual bool visit(QmlJS::AST::FieldMemberExpression *ast); virtual bool visit(QmlJS::AST::IdentifierExpression *ast); virtual bool visit(QmlJS::AST::UiArrayBinding *ast); + virtual bool visit(QmlJS::AST::UiImportList *); virtual bool visit(QmlJS::AST::UiPublicMember *ast); virtual bool visit(QmlJS::AST::UiObjectBinding *ast); virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); - virtual bool visit(QmlJS::AST::UiQualifiedId *ast); virtual bool visit(QmlJS::AST::UiScriptBinding *ast); virtual bool visit(QmlJS::AST::UiSourceElement *ast); @@ -43,18 +47,22 @@ protected: virtual void endVisit(QmlJS::AST::UiObjectDefinition *); private: - QmlJS::AST::Node *findDeclarationInScopesOrIds(QmlJS::AST::UiQualifiedId *ast); - QmlJS::AST::Node *findDeclarationInScopesOrIds(QmlJS::NameId *id); - - void rememberStartPosition(QmlJS::AST::Node *node); - void rememberStartPosition(const QmlJS::AST::SourceLocation &location); + void checkType(QmlJS::AST::UiQualifiedId *ast); + bool findInJS(const QStringList &qualifiedId, QmlJS::AST::Block *block); + bool findProperty(const QStringList &qualifiedId, QmlJS::AST::UiQualifiedId *typeId, QmlJS::AST::UiObjectMemberList *ast, int scopeLevel); + void findAsId(const QStringList &qualifiedId); + void findDeclaration(const QStringList &qualifiedId, int scopeLevel); + void findDeclaration(const QStringList &id); + void findTypeDeclaration(const QStringList &id); + void rememberLocation(const QmlJS::AST::SourceLocation &loc); private: - bool _resolveTarget; quint32 _pos; - QMap<QString, QmlJS::AST::SourceLocation> _idPositions; + DuiDocument::Ptr _doc; + Snapshot _snapshot; int _linkPosition; int _linkLength; + QString _fileName; int _targetLine; int _targetColumn; QStack<QmlJS::AST::Node*> _scopes;