diff --git a/src/plugins/duieditor/duicompletionvisitor.cpp b/src/plugins/duieditor/duicompletionvisitor.cpp new file mode 100644 index 00000000000..2dd407b1f41 --- /dev/null +++ b/src/plugins/duieditor/duicompletionvisitor.cpp @@ -0,0 +1,90 @@ +#include + +#include "duicompletionvisitor.h" +#include "qmljsast_p.h" + +using namespace QmlJS; +using namespace QmlJS::AST; + +namespace DuiEditor { +namespace Internal { + +DuiCompletionVisitor::DuiCompletionVisitor() +{ +} + +QSet DuiCompletionVisitor::operator()(QmlJS::AST::UiProgram *ast, int pos) +{ + m_completions.clear(); + m_pos = (quint32) pos; + + Node::acceptChild(ast, this); + + return m_completions; +} + +bool DuiCompletionVisitor::preVisit(QmlJS::AST::Node *node) +{ + if (!m_parentStack.isEmpty()) + m_nodeParents[node] = m_parentStack.top(); + m_parentStack.push(node); + return true; +} + +static QString toString(Statement *stmt) +{ + if (ExpressionStatement *exprStmt = AST::cast(stmt)) { + if (IdentifierExpression *idExpr = AST::cast(exprStmt->expression)) { + return idExpr->name->asString(); + } + } + + return QString(); +} + +bool DuiCompletionVisitor::visit(UiScriptBinding *ast) +{ + if (!ast) + return false; + + UiObjectDefinition *parentObject = findParentObject(ast); + + if (ast->qualifiedId && ast->qualifiedId->name->asString() == QLatin1String("id")) { + const QString nodeId = toString(ast->statement); + if (!nodeId.isEmpty()) + m_objectToId[parentObject] = nodeId; + } else if (m_objectToId.contains(parentObject)) { + if (ast->qualifiedId && ast->qualifiedId->name) { + const QString parentId = m_objectToId[parentObject]; + m_completions.insert(parentId + "." + ast->qualifiedId->name->asString()); + } + } + + if (ast->firstSourceLocation().begin() >= m_pos && m_pos <= ast->lastSourceLocation().end()) { + UiObjectDefinition *parentsParent = findParentObject(parentObject); + + if (parentsParent) { + m_completions.insert(QLatin1String("parent")); + } + } + + return true; +} + +UiObjectDefinition *DuiCompletionVisitor::findParentObject(Node *node) const +{ + if (!node) + return 0; + + Node *candidate = m_nodeParents[node]; + if (candidate == 0) + return 0; + + if (UiObjectDefinition *parentObject = AST::cast(candidate)) + return parentObject; + else + return findParentObject(candidate); +} + +} // namespace Internal +} // namespace DuiEditor diff --git a/src/plugins/duieditor/duicompletionvisitor.h b/src/plugins/duieditor/duicompletionvisitor.h new file mode 100644 index 00000000000..473ed160257 --- /dev/null +++ b/src/plugins/duieditor/duicompletionvisitor.h @@ -0,0 +1,43 @@ +#ifndef COMPLETIONVISITOR_H +#define COMPLETIONVISITOR_H + +#include +#include +#include +#include + +#include "qmljsastfwd_p.h" +#include "qmljsastvisitor_p.h" +#include "qmljsengine_p.h" + +namespace DuiEditor { +namespace Internal { + +class DuiCompletionVisitor: protected QmlJS::AST::Visitor +{ +public: + DuiCompletionVisitor(); + + QSet operator()(QmlJS::AST::UiProgram *ast, int pos); + +protected: + virtual bool preVisit(QmlJS::AST::Node *node); + virtual void postVisit(QmlJS::AST::Node *) { m_parentStack.pop(); } + + virtual bool visit(QmlJS::AST::UiScriptBinding *ast); + +private: + QmlJS::AST::UiObjectDefinition *findParentObject(QmlJS::AST::Node *node) const; + +private: + QSet m_completions; + quint32 m_pos; + QStack m_parentStack; + QMap m_nodeParents; + QMap m_objectToId; +}; + +} // namespace Internal +} // namespace DuiEditor + +#endif // COMPLETIONVISITOR_H