diff --git a/src/plugins/duieditor/duicompletionvisitor.cpp b/src/plugins/duieditor/duicompletionvisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2dd407b1f413ca143102ae98faf47deb2f8e5222 --- /dev/null +++ b/src/plugins/duieditor/duicompletionvisitor.cpp @@ -0,0 +1,90 @@ +#include <QtCore/QDebug> + +#include "duicompletionvisitor.h" +#include "qmljsast_p.h" + +using namespace QmlJS; +using namespace QmlJS::AST; + +namespace DuiEditor { +namespace Internal { + +DuiCompletionVisitor::DuiCompletionVisitor() +{ +} + +QSet<QString> 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<ExpressionStatement*>(stmt)) { + if (IdentifierExpression *idExpr = AST::cast<IdentifierExpression *>(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<UiObjectDefinition *>(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 0000000000000000000000000000000000000000..473ed160257979294d19366d4044179c5301c04f --- /dev/null +++ b/src/plugins/duieditor/duicompletionvisitor.h @@ -0,0 +1,43 @@ +#ifndef COMPLETIONVISITOR_H +#define COMPLETIONVISITOR_H + +#include <QtCore/QMap> +#include <QtCore/QSet> +#include <QtCore/QStack> +#include <QtCore/QString> + +#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<QString> 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<QString> m_completions; + quint32 m_pos; + QStack<QmlJS::AST::Node *> m_parentStack; + QMap<QmlJS::AST::Node *, QmlJS::AST::Node *> m_nodeParents; + QMap<QmlJS::AST::Node *, QString> m_objectToId; +}; + +} // namespace Internal +} // namespace DuiEditor + +#endif // COMPLETIONVISITOR_H