From 0b2b6f96e9379717d45f02fb946ac090d68b8a4a Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Tue, 22 Sep 2009 17:25:18 +0200 Subject: [PATCH] Initial work on the QML expression evaluator. --- src/plugins/duieditor/duidocument.cpp | 13 +- src/plugins/duieditor/duidocument.h | 40 +++---- src/plugins/duieditor/duieditor.cpp | 40 +++++-- src/plugins/duieditor/duieditor.h | 111 +++++++++--------- .../duieditor/qmlexpressionundercursor.cpp | 10 +- .../duieditor/qmlexpressionundercursor.h | 3 +- src/plugins/duieditor/qmllookupcontext.cpp | 16 +++ src/plugins/duieditor/qmllookupcontext.h | 4 + .../duieditor/resolveqmlexpression.cpp | 58 ++++++++- src/plugins/duieditor/resolveqmlexpression.h | 15 ++- 10 files changed, 210 insertions(+), 100 deletions(-) diff --git a/src/plugins/duieditor/duidocument.cpp b/src/plugins/duieditor/duidocument.cpp index 3441406b207..0fb00d48c50 100644 --- a/src/plugins/duieditor/duidocument.cpp +++ b/src/plugins/duieditor/duidocument.cpp @@ -78,6 +78,11 @@ QList<DiagnosticMessage> DuiDocument::diagnosticMessages() const return _diagnosticMessages; } +QString DuiDocument::source() const +{ + return _source; +} + void DuiDocument::setSource(const QString &source) { _source = source; @@ -129,9 +134,7 @@ DuiDocument::PtrList Snapshot::importedDocuments(const DuiDocument::Ptr &doc, co const QString docPath = doc->path() + '/' + importPath; - for (Iterator i = iterator(); i.hasNext();) { - DuiDocument::Ptr candidate = i.next().value(); - + foreach (DuiDocument::Ptr candidate, *this) { if (candidate == doc) continue; @@ -148,9 +151,7 @@ QMap<QString, DuiDocument::Ptr> Snapshot::componentsDefinedByImportedDocuments(c const QString docPath = doc->path() + '/' + importPath; - for (Iterator i = iterator(); i.hasNext();) { - DuiDocument::Ptr candidate = i.next().value(); - + foreach (DuiDocument::Ptr candidate, *this) { if (candidate == doc) continue; diff --git a/src/plugins/duieditor/duidocument.h b/src/plugins/duieditor/duidocument.h index 3295956012a..86c88d1b622 100644 --- a/src/plugins/duieditor/duidocument.h +++ b/src/plugins/duieditor/duidocument.h @@ -45,23 +45,25 @@ namespace DuiEditor { class DUIEDITOR_EXPORT DuiDocument { public: - typedef QSharedPointer<DuiDocument> Ptr; + 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); + DuiDocument(const QString &fileName); public: - ~DuiDocument(); + ~DuiDocument(); - static DuiDocument::Ptr create(const QString &fileName); + static DuiDocument::Ptr create(const QString &fileName); - QmlJS::AST::UiProgram *program() const; - QList<QmlJS::DiagnosticMessage> diagnosticMessages() const; + QmlJS::AST::UiProgram *program() const; + QList<QmlJS::DiagnosticMessage> diagnosticMessages() const; - void setSource(const QString &source); - bool parse(); + QString source() const; + void setSource(const QString &source); + + bool parse(); bool isParsedCorrectly() const { return _parsedCorrectly; } @@ -73,30 +75,26 @@ public: QString componentName() const { return _componentName; } private: - QmlJS::Engine *_engine; - QmlJS::NodePool *_pool; - QmlJS::AST::UiProgram *_program; - QList<QmlJS::DiagnosticMessage> _diagnosticMessages; - QString _fileName; + QmlJS::Engine *_engine; + QmlJS::NodePool *_pool; + QmlJS::AST::UiProgram *_program; + QList<QmlJS::DiagnosticMessage> _diagnosticMessages; + QString _fileName; QString _path; QString _componentName; - QString _source; + QString _source; bool _parsedCorrectly; IdTable _ids; }; -class DUIEDITOR_EXPORT Snapshot: protected QMap<QString, DuiDocument::Ptr> +class DUIEDITOR_EXPORT Snapshot: public QMap<QString, DuiDocument::Ptr> { public: - Snapshot(); - ~Snapshot(); + Snapshot(); + ~Snapshot(); void insert(const DuiDocument::Ptr &document); - typedef QMapIterator<QString, DuiDocument::Ptr> Iterator; - Iterator iterator() const - { return Iterator(*this); } - DuiDocument::Ptr document(const QString &fileName) const { return value(fileName); } diff --git a/src/plugins/duieditor/duieditor.cpp b/src/plugins/duieditor/duieditor.cpp index 3add88f8de7..a2e12b86716 100644 --- a/src/plugins/duieditor/duieditor.cpp +++ b/src/plugins/duieditor/duieditor.cpp @@ -37,7 +37,9 @@ #include "qmljsastvisitor_p.h" #include "qmljsast_p.h" #include "qmljsengine_p.h" - +#include "qmlexpressionundercursor.h" +#include "qmllookupcontext.h" +#include "resolveqmlexpression.h" #include "rewriter_p.h" #include "idcollector.h" @@ -714,16 +716,32 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur if (!doc) return link; - NavigationTokenFinder finder; - finder(doc, cursor.position(), snapshot); - if (finder.targetFound()) { - link.fileName = finder.fileName(); - link.pos = finder.linkPosition(); - link.length = finder.linkLength(); - - if (resolveTarget) { - link.line = finder.targetLine(); - link.column = finder.targetColumn() - 1; +// NavigationTokenFinder finder; +// finder(doc, cursor.position(), snapshot); +// if (finder.targetFound()) { +// link.fileName = finder.fileName(); +// link.pos = finder.linkPosition(); +// link.length = finder.linkLength(); +// +// if (resolveTarget) { +// link.line = finder.targetLine(); +// link.column = finder.targetColumn() - 1; +// } +// } + + QmlExpressionUnderCursor expressionUnderCursor; + expressionUnderCursor(cursor, doc->program()); + + QmlLookupContext context(expressionUnderCursor.expressionScopes(), + expressionUnderCursor.expressionNode(), + doc, snapshot); + + ResolveQmlExpression resolve(context); + if (QmlLookupContext::Symbol *symbol = resolve(expressionUnderCursor.expressionNode())) { + if (UiObjectMember *member = static_cast<UiObjectMember *>(symbol)) { // ### FIXME: don't use static_cast<> + const int begin = member->firstSourceLocation().begin(); + const int end = member->lastSourceLocation().end(); + qDebug() << doc->source().mid(begin, end - begin); } } diff --git a/src/plugins/duieditor/duieditor.h b/src/plugins/duieditor/duieditor.h index 9e73fae908c..3149bc482bc 100644 --- a/src/plugins/duieditor/duieditor.h +++ b/src/plugins/duieditor/duieditor.h @@ -42,7 +42,7 @@ class QTimer; QT_END_NAMESPACE namespace Core { - class ICore; +class ICore; } namespace DuiEditor { @@ -52,103 +52,102 @@ class DuiModelManagerInterface; namespace Internal { class DuiHighlighter; - class ScriptEditor; class ScriptEditorEditable : public TextEditor::BaseTextEditorEditable { - Q_OBJECT + Q_OBJECT public: - ScriptEditorEditable(ScriptEditor *, const QList<int> &); - QList<int> context() const; + ScriptEditorEditable(ScriptEditor *, const QList<int> &); + QList<int> context() const; - bool duplicateSupported() const { return true; } - Core::IEditor *duplicate(QWidget *parent); - const char *kind() const; - bool isTemporary() const { return false; } + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + const char *kind() const; + bool isTemporary() const { return false; } private: - QList<int> m_context; + QList<int> m_context; }; struct Declaration { - QString text; - int startLine; - int startColumn; - int endLine; - int endColumn; - - Declaration() - : startLine(0), - startColumn(0), - endLine(0), - endColumn(0) - { } + QString text; + int startLine; + int startColumn; + int endLine; + int endColumn; + + Declaration() + : startLine(0), + startColumn(0), + endLine(0), + endColumn(0) + { } }; class ScriptEditor : public TextEditor::BaseTextEditor { - Q_OBJECT + Q_OBJECT public: - typedef QList<int> Context; + typedef QList<int> Context; - ScriptEditor(const Context &context, - QWidget *parent = 0); - ~ScriptEditor(); + ScriptEditor(const Context &context, + QWidget *parent = 0); + ~ScriptEditor(); - QList<Declaration> declarations() const; - QStringList words() const; - QStringList keywords() const; + QList<Declaration> declarations() const; + QStringList words() const; + QStringList keywords() const; - QList<QmlJS::DiagnosticMessage> diagnosticMessages() const - { return m_diagnosticMessages; } + QList<QmlJS::DiagnosticMessage> diagnosticMessages() const + { return m_diagnosticMessages; } - virtual void unCommentSelection(); + virtual void unCommentSelection(); DuiDocument::Ptr duiDocument() const { return m_document; } public slots: - virtual void setFontSettings(const TextEditor::FontSettings &); + virtual void setFontSettings(const TextEditor::FontSettings &); private slots: void onDocumentUpdated(DuiDocument::Ptr doc); - void updateDocument(); - void updateDocumentNow(); - void jumpToMethod(int index); - void updateMethodBoxIndex(); - void updateMethodBoxToolTip(); - void updateFileName(); + void updateDocument(); + void updateDocumentNow(); + void jumpToMethod(int index); + void updateMethodBoxIndex(); + void updateMethodBoxToolTip(); + void updateFileName(); - // refactoring ops - void renameIdUnderCursor(); + // refactoring ops + void renameIdUnderCursor(); protected: - void contextMenuEvent(QContextMenuEvent *e); - TextEditor::BaseTextEditorEditable *createEditableInterface(); - void createToolBar(ScriptEditorEditable *editable); + void contextMenuEvent(QContextMenuEvent *e); + TextEditor::BaseTextEditorEditable *createEditableInterface(); + void createToolBar(ScriptEditorEditable *editable); TextEditor::BaseTextEditor::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true); virtual void reformat(QTextDocument *doc, QTextBlock block); private: - virtual bool isElectricCharacter(const QChar &ch) const; - virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); + virtual bool isElectricCharacter(const QChar &ch) const; + virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); - QString wordUnderCursor() const; + QString wordUnderCursor() const; - const Context m_context; + const Context m_context; - QTimer *m_updateDocumentTimer; - QComboBox *m_methodCombo; - QList<Declaration> m_declarations; - QStringList m_words; - QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap - QList<QmlJS::DiagnosticMessage> m_diagnosticMessages; - DuiDocument::Ptr m_document; + QTimer *m_updateDocumentTimer; + QComboBox *m_methodCombo; + QList<Declaration> m_declarations; + QStringList m_words; + QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap + QList<QmlJS::DiagnosticMessage> m_diagnosticMessages; + DuiDocument::Ptr m_document; DuiModelManagerInterface *m_modelManager; }; diff --git a/src/plugins/duieditor/qmlexpressionundercursor.cpp b/src/plugins/duieditor/qmlexpressionundercursor.cpp index 7b0e0980c5e..18df851ff07 100644 --- a/src/plugins/duieditor/qmlexpressionundercursor.cpp +++ b/src/plugins/duieditor/qmlexpressionundercursor.cpp @@ -9,12 +9,20 @@ using namespace QmlJS; using namespace QmlJS::AST; QmlExpressionUnderCursor::QmlExpressionUnderCursor() + : _expressionNode(0), + _pos(0) { } -void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor) +void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor, + QmlJS::AST::UiProgram *program) { _pos = cursor.position(); + _expressionNode = 0; + _scopes.clear(); + + if (program) + program->accept(this); } bool QmlExpressionUnderCursor::visit(QmlJS::AST::Block *ast) diff --git a/src/plugins/duieditor/qmlexpressionundercursor.h b/src/plugins/duieditor/qmlexpressionundercursor.h index 3280218f93b..37dab184623 100644 --- a/src/plugins/duieditor/qmlexpressionundercursor.h +++ b/src/plugins/duieditor/qmlexpressionundercursor.h @@ -14,7 +14,7 @@ class QmlExpressionUnderCursor: protected QmlJS::AST::Visitor public: QmlExpressionUnderCursor(); - void operator()(const QTextCursor &cursor); + void operator()(const QTextCursor &cursor, QmlJS::AST::UiProgram *program); QStack<QmlJS::AST::Node *> expressionScopes() const { return _expressionScopes; } @@ -36,7 +36,6 @@ private: QStack<QmlJS::AST::Node *> _scopes; QStack<QmlJS::AST::Node *> _expressionScopes; QmlJS::AST::Node *_expressionNode; - quint32 _pos; }; diff --git a/src/plugins/duieditor/qmllookupcontext.cpp b/src/plugins/duieditor/qmllookupcontext.cpp index af9d1961437..e967c342655 100644 --- a/src/plugins/duieditor/qmllookupcontext.cpp +++ b/src/plugins/duieditor/qmllookupcontext.cpp @@ -20,3 +20,19 @@ QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, _snapshot(snapshot) { } + +QmlLookupContext::Symbol *QmlLookupContext::resolve(const QString &name) const +{ + // ### TODO: look at property definitions + + // look at the ids. + foreach (DuiDocument::Ptr doc, _snapshot) { + const DuiDocument::IdTable ids = doc->ids(); + const QPair<SourceLocation, Node *> use = ids.value(name); + + if (Node *node = use.second) + return node; + } + + return 0; +} diff --git a/src/plugins/duieditor/qmllookupcontext.h b/src/plugins/duieditor/qmllookupcontext.h index 0ca732bb617..44f50c2eb01 100644 --- a/src/plugins/duieditor/qmllookupcontext.h +++ b/src/plugins/duieditor/qmllookupcontext.h @@ -17,6 +17,10 @@ public: const DuiDocument::Ptr &doc, const Snapshot &snapshot); + typedef QmlJS::AST::Node Symbol; // ### FIXME: this needs to be a class. + + Symbol *resolve(const QString &name) const; + private: QStack<QmlJS::AST::Node *> _scopes; QmlJS::AST::Node *_expressionNode; diff --git a/src/plugins/duieditor/resolveqmlexpression.cpp b/src/plugins/duieditor/resolveqmlexpression.cpp index 895ffbc55de..38311ac30a3 100644 --- a/src/plugins/duieditor/resolveqmlexpression.cpp +++ b/src/plugins/duieditor/resolveqmlexpression.cpp @@ -1,6 +1,5 @@ #include "qmljsast_p.h" #include "qmljsengine_p.h" - #include "resolveqmlexpression.h" using namespace DuiEditor; @@ -8,6 +7,61 @@ using namespace DuiEditor::Internal; using namespace QmlJS; using namespace QmlJS::AST; -ResolveQmlExpression::ResolveQmlExpression() +ResolveQmlExpression::ResolveQmlExpression(const QmlLookupContext &context) + : _context(context), _value(0) +{ +} + +QmlLookupContext::Symbol *ResolveQmlExpression::typeOf(Node *node) +{ + QmlLookupContext::Symbol *previousValue = switchValue(0); + if (node) + node->accept(this); + return switchValue(previousValue); +} + + +QmlLookupContext::Symbol *ResolveQmlExpression::switchValue(QmlLookupContext::Symbol *value) +{ + QmlLookupContext::Symbol *previousValue = _value; + _value = value; + return previousValue; +} + +bool ResolveQmlExpression::visit(IdentifierExpression *ast) { + const QString name = ast->name->asString(); + _value = _context.resolve(name); + return false; +} + +bool ResolveQmlExpression::visit(FieldMemberExpression *ast) +{ + const QString memberName = ast->name->asString(); + + if (QmlLookupContext::Symbol *base = typeOf(ast->base)) { + UiObjectMemberList *members = 0; + + if (UiObjectBinding *uiObjectBinding = cast<UiObjectBinding *>(base)) { + if (uiObjectBinding->initializer) + members = uiObjectBinding->initializer->members; + } else if (UiObjectDefinition *uiObjectDefinition = cast<UiObjectDefinition *>(base)) { + if (uiObjectDefinition->initializer) + members = uiObjectDefinition->initializer->members; + } + + for (UiObjectMemberList *it = members; it; it = it->next) { + UiObjectMember *member = it->member; + + if (UiPublicMember *publicMember = cast<UiPublicMember *>(member)) { + if (publicMember->name->asString() == memberName) { + _value = publicMember; + break; // we're done. + } + } + } + } + + return false; } + diff --git a/src/plugins/duieditor/resolveqmlexpression.h b/src/plugins/duieditor/resolveqmlexpression.h index 61377f313f0..8cbea1ec29a 100644 --- a/src/plugins/duieditor/resolveqmlexpression.h +++ b/src/plugins/duieditor/resolveqmlexpression.h @@ -2,6 +2,7 @@ #define RESOLVEQMLEXPRESSION_H #include "qmljsastvisitor_p.h" +#include "qmllookupcontext.h" namespace DuiEditor { namespace Internal { @@ -9,11 +10,23 @@ namespace Internal { class ResolveQmlExpression: protected QmlJS::AST::Visitor { public: - ResolveQmlExpression(); + ResolveQmlExpression(const QmlLookupContext &context); + + QmlLookupContext::Symbol *operator()(QmlJS::AST::Node *node) + { return typeOf(node); } protected: + using QmlJS::AST::Visitor::visit; + + QmlLookupContext::Symbol *typeOf(QmlJS::AST::Node *node); + QmlLookupContext::Symbol *switchValue(QmlLookupContext::Symbol *symbol); + + virtual bool visit(QmlJS::AST::IdentifierExpression *ast); + virtual bool visit(QmlJS::AST::FieldMemberExpression *ast); private: + QmlLookupContext _context; + QmlLookupContext::Symbol *_value; }; } // namespace Internal -- GitLab