From c3d1019deb5bb4126df0f3bf8fec63ba7fbfaa37 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen <erik.verbruggen@nokia.com> Date: Fri, 2 Oct 2009 10:49:33 +0200 Subject: [PATCH] Improved the navigation and code completion in the QML editor. --- src/plugins/qmleditor/idcollector.cpp | 76 ++-- src/plugins/qmleditor/idcollector.h | 12 +- src/plugins/qmleditor/qmlcodecompletion.cpp | 76 ++-- src/plugins/qmleditor/qmlcodecompletion.h | 6 +- src/plugins/qmleditor/qmldocument.cpp | 25 +- src/plugins/qmleditor/qmldocument.h | 5 + src/plugins/qmleditor/qmleditor.cpp | 22 +- src/plugins/qmleditor/qmleditor.pro | 8 +- src/plugins/qmleditor/qmleditorplugin.cpp | 2 +- .../qmleditor/qmlexpressionundercursor.cpp | 332 +++++++++++++----- .../qmleditor/qmlexpressionundercursor.h | 38 +- src/plugins/qmleditor/qmllookupcontext.cpp | 136 ++++--- src/plugins/qmleditor/qmllookupcontext.h | 13 +- .../qmleditor/qmlresolveexpression.cpp | 88 ++--- src/plugins/qmleditor/qmlresolveexpression.h | 11 +- src/plugins/qmleditor/qmlscope.cpp | 35 ++ src/plugins/qmleditor/qmlscope.h | 40 +++ src/plugins/qmleditor/qmlsymbol.cpp | 134 ++++++- src/plugins/qmleditor/qmlsymbol.h | 65 +++- 19 files changed, 787 insertions(+), 337 deletions(-) create mode 100644 src/plugins/qmleditor/qmlscope.cpp create mode 100644 src/plugins/qmleditor/qmlscope.h diff --git a/src/plugins/qmleditor/idcollector.cpp b/src/plugins/qmleditor/idcollector.cpp index 8677d540fdb..c98019587f3 100644 --- a/src/plugins/qmleditor/idcollector.cpp +++ b/src/plugins/qmleditor/idcollector.cpp @@ -1,3 +1,5 @@ +#include <QDebug> + #include "idcollector.h" #include "qmljsast_p.h" #include "qmljsengine_p.h" @@ -7,36 +9,39 @@ using namespace QmlJS::AST; using namespace QmlEditor; using namespace QmlEditor::Internal; -QMap<QString, QmlIdSymbol*> IdCollector::operator()(const QString &fileName, QmlJS::AST::UiProgram *ast) +QMap<QString, QmlIdSymbol*> IdCollector::operator()(QmlDocument *doc) { - _fileName = fileName; + _doc = doc; _ids.clear(); + _currentSymbol = 0; - Node::accept(ast, this); + Node::accept(doc->program(), this); return _ids; } -bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast) -{ - _scopes.push(ast); - return true; -} - -bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast) +bool IdCollector::visit(UiArrayBinding *ast) { - _scopes.push(ast); - return true; + QmlSymbolFromFile *oldSymbol = switchSymbol(ast); + Node::accept(ast->members, this); + _currentSymbol = oldSymbol; + return false; } -void IdCollector::endVisit(QmlJS::AST::UiObjectBinding *) +bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast) { - _scopes.pop(); + QmlSymbolFromFile *oldSymbol = switchSymbol(ast); + Node::accept(ast->initializer, this); + _currentSymbol = oldSymbol; + return false; } -void IdCollector::endVisit(QmlJS::AST::UiObjectDefinition *) +bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast) { - _scopes.pop(); + QmlSymbolFromFile *oldSymbol = switchSymbol(ast); + Node::accept(ast->initializer, this); + _currentSymbol = oldSymbol; + return false; } bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast) @@ -44,21 +49,40 @@ bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast) if (!(ast->qualifiedId->next) && ast->qualifiedId->name->asString() == "id") if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement)) if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) - addId(i->name->asString(), ast); + if (i->name) + addId(i->name->asString(), ast); return false; } +QmlSymbolFromFile *IdCollector::switchSymbol(QmlJS::AST::UiObjectMember *node) +{ + QmlSymbolFromFile *newSymbol = 0; + + if (_currentSymbol == 0) { + newSymbol = _doc->findSymbol(node); + } else { + newSymbol = _currentSymbol->findMember(node); + } + + QmlSymbolFromFile *oldSymbol = _currentSymbol; + + if (newSymbol) { + _currentSymbol = newSymbol; + } else { + QString filename = _doc->fileName(); + qWarning() << "Scope without symbol @"<<filename<<":"<<node->firstSourceLocation().startLine<<":"<<node->firstSourceLocation().startColumn; + } + + return oldSymbol; +} + void IdCollector::addId(const QString &id, QmlJS::AST::UiScriptBinding *ast) { - if (!_ids.contains(id)) { - Node *parent = _scopes.top(); - - if (UiObjectBinding *binding = cast<UiObjectBinding*>(parent)) - _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, binding)); - else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parent)) - _ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, definition)); - else - Q_ASSERT(!"Unknown parent for id"); + if (!_ids.contains(id) && _currentSymbol) { + QmlSymbolFromFile *symbol = _currentSymbol->findMember(ast); + + if (QmlIdSymbol *idSymbol = symbol->asIdSymbol()) + _ids[id] = idSymbol; } } diff --git a/src/plugins/qmleditor/idcollector.h b/src/plugins/qmleditor/idcollector.h index e4c7e7bf292..8a02b4c355f 100644 --- a/src/plugins/qmleditor/idcollector.h +++ b/src/plugins/qmleditor/idcollector.h @@ -6,6 +6,7 @@ #include <QStack> #include <QString> +#include "qmldocument.h" #include "qmljsastvisitor_p.h" #include "qmlsymbol.h" @@ -15,23 +16,22 @@ namespace Internal { class IdCollector: protected QmlJS::AST::Visitor { public: - QMap<QString, QmlIdSymbol*> operator()(const QString &fileName, QmlJS::AST::UiProgram *ast); + QMap<QString, QmlIdSymbol*> operator()(QmlDocument *doc); protected: + virtual bool visit(QmlJS::AST::UiArrayBinding *ast); 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: + QmlSymbolFromFile *switchSymbol(QmlJS::AST::UiObjectMember *node); void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast); private: - QString _fileName; + QmlDocument *_doc; QMap<QString, QmlIdSymbol*> _ids; - QStack<QmlJS::AST::Node *> _scopes; + QmlSymbolFromFile *_currentSymbol; }; } // namespace Internal diff --git a/src/plugins/qmleditor/qmlcodecompletion.cpp b/src/plugins/qmleditor/qmlcodecompletion.cpp index 9626d213195..53351dda406 100644 --- a/src/plugins/qmleditor/qmlcodecompletion.cpp +++ b/src/plugins/qmleditor/qmlcodecompletion.cpp @@ -1,17 +1,25 @@ -#include "qmlcompletionvisitor.h" #include "qmlcodecompletion.h" #include "qmleditor.h" +#include "qmlmodelmanagerinterface.h" +#include "qmlexpressionundercursor.h" +#include "qmllookupcontext.h" +#include "qmlresolveexpression.h" +#include "qmlsymbol.h" + #include <texteditor/basetexteditor.h> #include <QtDebug> using namespace QmlEditor::Internal; -QmlCodeCompletion::QmlCodeCompletion(QObject *parent) +QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager,QObject *parent) : TextEditor::ICompletionCollector(parent), + m_modelManager(modelManager), m_editor(0), m_startPosition(0), m_caseSensitivity(Qt::CaseSensitive) -{ } +{ + Q_ASSERT(modelManager); +} QmlCodeCompletion::~QmlCodeCompletion() { } @@ -49,26 +57,52 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) m_startPosition = pos; m_completions.clear(); - foreach (const QString &word, edit->keywords()) { - TextEditor::CompletionItem item(this); - item.m_text = word; - m_completions.append(item); - } - - foreach (const QString &word, edit->words()) { - TextEditor::CompletionItem item(this); - item.m_text = word; - m_completions.append(item); - } +// foreach (const QString &word, edit->keywords()) { +// TextEditor::CompletionItem item(this); +// item.m_text = word; +// m_completions.append(item); +// } +// +// foreach (const QString &word, edit->words()) { +// TextEditor::CompletionItem item(this); +// item.m_text = word; +// m_completions.append(item); +// } QmlDocument::Ptr qmlDocument = edit->qmlDocument(); - if (!qmlDocument.isNull()) { - QmlJS::AST::UiProgram *program = qmlDocument->program(); - - if (program) { - QmlCompletionVisitor visitor; - - foreach (const QString &word, visitor(program, m_startPosition)) { + qDebug() << "*** document:" << qmlDocument; + if (qmlDocument.isNull()) + return pos; + + if (!qmlDocument->program()) + qmlDocument = m_modelManager->snapshot().value(qmlDocument->fileName()); + + if (qmlDocument->program()) { + QmlJS::AST::UiProgram *program = qmlDocument->program(); + qDebug() << "*** program:" << program; + + if (program) { + QmlExpressionUnderCursor expressionUnderCursor; + QTextCursor cursor(edit->document()); + cursor.setPosition(pos); + expressionUnderCursor(cursor, qmlDocument); + + QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot()); + QmlResolveExpression resolver(context); + QList<QmlSymbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode()); + + foreach (QmlSymbol *symbol, symbols) { + QString word; + + if (symbol->isIdSymbol()) { + word = symbol->asIdSymbol()->id(); + } else { + word = symbol->name(); + } + + if (word.isEmpty()) + continue; + TextEditor::CompletionItem item(this); item.m_text = word; m_completions.append(item); diff --git a/src/plugins/qmleditor/qmlcodecompletion.h b/src/plugins/qmleditor/qmlcodecompletion.h index fd5e4b1ba62..8ed9aaf05a2 100644 --- a/src/plugins/qmleditor/qmlcodecompletion.h +++ b/src/plugins/qmleditor/qmlcodecompletion.h @@ -8,6 +8,9 @@ class ITextEditable; } namespace QmlEditor { + +class QmlModelManagerInterface; + namespace Internal { class QmlCodeCompletion: public TextEditor::ICompletionCollector @@ -15,7 +18,7 @@ class QmlCodeCompletion: public TextEditor::ICompletionCollector Q_OBJECT public: - QmlCodeCompletion(QObject *parent = 0); + QmlCodeCompletion(QmlModelManagerInterface *modelManager, QObject *parent = 0); virtual ~QmlCodeCompletion(); Qt::CaseSensitivity caseSensitivity() const; @@ -30,6 +33,7 @@ public: virtual void cleanup(); private: + QmlModelManagerInterface *m_modelManager; TextEditor::ITextEditable *m_editor; int m_startPosition; QList<TextEditor::CompletionItem> m_completions; diff --git a/src/plugins/qmleditor/qmldocument.cpp b/src/plugins/qmleditor/qmldocument.cpp index d89e69609f5..d6baba6f821 100644 --- a/src/plugins/qmleditor/qmldocument.cpp +++ b/src/plugins/qmleditor/qmldocument.cpp @@ -61,7 +61,7 @@ QmlDocument::~QmlDocument() if (_pool) delete _pool; - qDeleteAll(_ids.values()); + qDeleteAll(_symbols); } QmlDocument::Ptr QmlDocument::create(const QString &fileName) @@ -109,14 +109,28 @@ bool QmlDocument::parse() _program = parser.ast(); _diagnosticMessages = parser.diagnosticMessages(); - if (_parsedCorrectly && _program) { - Internal::IdCollector collect; - _ids = collect(_fileName, _program); + if (_program) { + for (QmlJS::AST::UiObjectMemberList *iter = _program->members; iter; iter = iter->next) + if (iter->member) + _symbols.append(new QmlSymbolFromFile(_fileName, iter->member)); + + Internal::IdCollector collect; + _ids = collect(this); } return _parsedCorrectly; } +QmlSymbolFromFile *QmlDocument::findSymbol(QmlJS::AST::Node *node) const +{ + foreach (QmlSymbol *symbol, _symbols) + if (symbol->isSymbolFromFile()) + if (symbol->asSymbolFromFile()->node() == node) + return symbol->asSymbolFromFile(); + + return 0; +} + Snapshot::Snapshot() { } @@ -127,6 +141,9 @@ Snapshot::~Snapshot() void Snapshot::insert(const QmlDocument::Ptr &document) { + if (!document || !document->program()) + return; + QMap<QString, QmlDocument::Ptr>::insert(document->fileName(), document); } diff --git a/src/plugins/qmleditor/qmldocument.h b/src/plugins/qmleditor/qmldocument.h index 7639d1b25ac..be6a3658eb7 100644 --- a/src/plugins/qmleditor/qmldocument.h +++ b/src/plugins/qmleditor/qmldocument.h @@ -74,6 +74,10 @@ public: QString path() const { return _path; } QString componentName() const { return _componentName; } + QmlSymbolFromFile *findSymbol(QmlJS::AST::Node *node) const; + QmlSymbol::List symbols() const + { return _symbols; } + private: QmlJS::Engine *_engine; QmlJS::NodePool *_pool; @@ -85,6 +89,7 @@ private: QString _source; bool _parsedCorrectly; IdTable _ids; + QmlSymbol::List _symbols; }; class QMLEDITOR_EXPORT Snapshot: public QMap<QString, QmlDocument::Ptr> diff --git a/src/plugins/qmleditor/qmleditor.cpp b/src/plugins/qmleditor/qmleditor.cpp index 2d68189a7a3..48e3b7c8ce3 100644 --- a/src/plugins/qmleditor/qmleditor.cpp +++ b/src/plugins/qmleditor/qmleditor.cpp @@ -719,12 +719,28 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur if (!doc) return link; + QTextCursor expressionCursor(cursor); + { + // correct the position by moving to the end of an identifier (if we're hovering over one): + const QString txt = cursor.block().text(); + int pos = cursor.position(); + forever { + const QChar ch = characterAt(pos); + + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) + ++pos; + else + break; + } + expressionCursor.setPosition(pos); + } + QmlExpressionUnderCursor expressionUnderCursor; - expressionUnderCursor(cursor, doc->program()); + expressionUnderCursor(expressionCursor, doc); QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot); - QmlResolveExpression resolve(context); - QmlSymbol *symbol = resolve(expressionUnderCursor.expressionNode()); + QmlResolveExpression resolver(context); + QmlSymbol *symbol = resolver.typeOf(expressionUnderCursor.expressionNode()); if (!symbol) return link; diff --git a/src/plugins/qmleditor/qmleditor.pro b/src/plugins/qmleditor/qmleditor.pro index 1399b679e1e..0f222998009 100644 --- a/src/plugins/qmleditor/qmleditor.pro +++ b/src/plugins/qmleditor/qmleditor.pro @@ -17,7 +17,6 @@ HEADERS += qmleditor.h \ qmleditorconstants.h \ qmlhoverhandler.h \ qmldocument.h \ - qmlcompletionvisitor.h \ qmlmodelmanagerinterface.h \ qmleditor_global.h \ qmlmodelmanager.h \ @@ -27,7 +26,8 @@ HEADERS += qmleditor.h \ qmllookupcontext.h \ qmlresolveexpression.h \ qmlsymbol.h \ - qmlfilewizard.h + qmlfilewizard.h \ + qmlscope.h SOURCES += qmleditor.cpp \ qmleditorfactory.cpp \ qmleditorplugin.cpp \ @@ -36,7 +36,6 @@ SOURCES += qmleditor.cpp \ qmlcodecompletion.cpp \ qmlhoverhandler.cpp \ qmldocument.cpp \ - qmlcompletionvisitor.cpp \ qmlmodelmanagerinterface.cpp \ qmlmodelmanager.cpp \ qmlcodeformatter.cpp \ @@ -45,5 +44,6 @@ SOURCES += qmleditor.cpp \ qmllookupcontext.cpp \ qmlresolveexpression.cpp \ qmlsymbol.cpp \ - qmlfilewizard.cpp + qmlfilewizard.cpp \ + qmlscope.cpp RESOURCES += qmleditor.qrc diff --git a/src/plugins/qmleditor/qmleditorplugin.cpp b/src/plugins/qmleditor/qmleditorplugin.cpp index 474268a6946..466fef432cb 100644 --- a/src/plugins/qmleditor/qmleditorplugin.cpp +++ b/src/plugins/qmleditor/qmleditorplugin.cpp @@ -111,7 +111,7 @@ bool QmlEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err | TextEditor::TextEditorActionHandler::UnCollapseAll); m_actionHandler->initializeActions(); - m_completion = new QmlCodeCompletion(); + m_completion = new QmlCodeCompletion(m_modelManager); addAutoReleasedObject(m_completion); addAutoReleasedObject(new QmlHoverHandler()); diff --git a/src/plugins/qmleditor/qmlexpressionundercursor.cpp b/src/plugins/qmleditor/qmlexpressionundercursor.cpp index 133ab2bac6f..d834b2318f7 100644 --- a/src/plugins/qmleditor/qmlexpressionundercursor.cpp +++ b/src/plugins/qmleditor/qmlexpressionundercursor.cpp @@ -1,124 +1,294 @@ +#include <QDebug> + #include "qmljsast_p.h" +#include "qmljsastvisitor_p.h" #include "qmljsengine_p.h" - +#include "qmljslexer_p.h" +#include "qmljsnodepool_p.h" +#include "qmljsparser_p.h" #include "qmlexpressionundercursor.h" -using namespace QmlEditor; -using namespace QmlEditor::Internal; using namespace QmlJS; using namespace QmlJS::AST; +namespace QmlEditor { + namespace Internal { + class PositionCalculator: protected Visitor + { + public: + Node *operator()(Node *ast, int pos) + { + _pos = pos; + _expression = 0; + _offset = -1; + _length = -1; + + Node::accept(ast, this); + + return _expression; + } + + int offset() const + { return _offset; } + + int length() const + { return _length; } + + protected: + bool visit(FieldMemberExpression *ast) + { + if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) { + _expression = ast; + _offset = ast->identifierToken.offset; + _length = ast->identifierToken.length; + } + + return true; + } + + bool visit(IdentifierExpression *ast) + { + if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) { + _expression = ast; + _offset = ast->firstSourceLocation().offset; + _length = ast->lastSourceLocation().end() - _offset; + } + + return false; + } + + bool visit(UiImport * /*ast*/) + { + return false; + } + + bool visit(UiQualifiedId *ast) + { + if (ast->identifierToken.offset <= _pos) { + for (UiQualifiedId *iter = ast; iter; iter = iter->next) { + if (_pos <= iter->identifierToken.end()) { + // found it + _expression = ast; + _offset = ast->identifierToken.offset; + + for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) { + _length = iter2->identifierToken.end() - _offset; + } + + break; + } + } + } + + return false; + } + + private: + quint32 _pos; + Node *_expression; + int _offset; + int _length; + }; + + class ScopeCalculator: protected Visitor + { + public: + QStack<QmlSymbol*> operator()(const QmlDocument::Ptr &doc, int pos) + { + _doc = doc; + _pos = pos; + _scopes.clear(); + _currentSymbol = 0; + Node::accept(doc->program(), this); + return _scopes; + } + + protected: + virtual bool visit(Block *ast) + { + // TODO +// if (_pos > ast->lbraceToken.end() && _pos < ast->rbraceToken.offset) { +// push(ast); +// Node::accept(ast->statements, this); +// } + + return false; + } + + virtual bool visit(UiObjectBinding *ast) + { + if (ast->initializer && ast->initializer->rbraceToken.offset < _pos && _pos <= ast->initializer->lbraceToken.end()) { + push(ast); + Node::accept(ast->initializer, this); + } + + return false; + } + + virtual bool visit(UiObjectDefinition *ast) + { + if (ast->initializer && ast->initializer->rbraceToken.offset < _pos && _pos <= ast->initializer->lbraceToken.end()) { + push(ast); + Node::accept(ast->initializer, this); + } + + return false; + } + + virtual bool visit(UiArrayBinding *ast) + { + if (ast->lbracketToken.offset < _pos && _pos <= ast->rbracketToken.end()) { + push(ast); + Node::accept(ast->members, this); + } + + return false; + } + + private: + void push(Node *node) + { + QmlSymbolFromFile* symbol; + + if (_currentSymbol) { + symbol = _currentSymbol->findMember(node); + } else { + symbol = _doc->findSymbol(node); + } + + if (symbol) { + _currentSymbol = symbol; + + if (!cast<UiArrayBinding*>(node)) + _scopes.push(symbol); + } + } + + private: + QmlDocument::Ptr _doc; + quint32 _pos; + QStack<QmlSymbol*> _scopes; + QmlSymbolFromFile* _currentSymbol; + }; + } +} + +using namespace QmlEditor; +using namespace QmlEditor::Internal; + QmlExpressionUnderCursor::QmlExpressionUnderCursor() : _expressionNode(0), - _pos(0) + _pos(0), _engine(0), _nodePool(0) +{ +} + +QmlExpressionUnderCursor::~QmlExpressionUnderCursor() { + if (_engine) { delete _engine; _engine = 0; } + if (_nodePool) { delete _nodePool; _nodePool = 0; } } void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor, - QmlJS::AST::UiProgram *program) + const QmlDocument::Ptr &doc) { + if (_engine) { delete _engine; _engine = 0; } + if (_nodePool) { delete _nodePool; _nodePool = 0; } + _pos = cursor.position(); _expressionNode = 0; _expressionOffset = -1; _expressionLength = -1; - _scopes.clear(); + _expressionScopes.clear(); - if (program) - program->accept(this); -} - -bool QmlExpressionUnderCursor::visit(QmlJS::AST::Block *ast) -{ - _scopes.push(ast); - - return true; -} + const QTextBlock block = cursor.block(); + parseExpression(block); -void QmlExpressionUnderCursor::endVisit(QmlJS::AST::Block *) -{ - _scopes.pop(); -} - -bool QmlExpressionUnderCursor::visit(QmlJS::AST::FieldMemberExpression *ast) -{ - if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) { - _expressionNode = ast; - _expressionOffset = ast->identifierToken.offset; - _expressionLength = ast->identifierToken.length; - _expressionScopes = _scopes; + if (_expressionOffset != -1) { + ScopeCalculator calculator; + _expressionScopes = calculator(doc, _expressionOffset); } - - return true; } -bool QmlExpressionUnderCursor::visit(QmlJS::AST::IdentifierExpression *ast) +void QmlExpressionUnderCursor::parseExpression(const QTextBlock &block) { - if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) { - _expressionNode = ast; - _expressionOffset = ast->firstSourceLocation().offset; - _expressionLength = ast->lastSourceLocation().end() - _expressionOffset; - _expressionScopes = _scopes; + int textPosition = _pos - block.position(); + const QString blockText = block.text(); + if (textPosition > 0) { + if (blockText.at(textPosition - 1) == QLatin1Char('.')) + --textPosition; + } else { + textPosition = 0; } - return false; -} + const QString text = blockText.left(textPosition); -bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectBinding *ast) -{ - Node::accept(ast->qualifiedId, this); - Node::accept(ast->qualifiedTypeNameId, this); - - _scopes.push(ast); + Node *node = 0; - Node::accept(ast->initializer, this); + if (UiObjectMember *binding = tryBinding(text)) { + qDebug() << "**** binding"; + node = binding; + } else if (Statement *stmt = tryStatement(text)) { + qDebug() << "**** statement"; + node = stmt; + } else if (ExpressionNode *expr = tryExpression(text)) { + qDebug() << "**** expression"; + node = expr; + } else { + qDebug() << "**** none"; + } - return false; + if (node) { + PositionCalculator calculator; + _expressionNode = calculator(node, textPosition); + _expressionOffset = calculator.offset() + block.position(); + _expressionLength = calculator.length(); + } } -void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectBinding *) +ExpressionNode *QmlExpressionUnderCursor::tryExpression(const QString &text) { - _scopes.pop(); + _engine = new Engine(); + _nodePool = new NodePool("", _engine); + Lexer lexer(_engine); + Parser parser(_engine); + lexer.setCode(text, /*line = */ 1); + + if (parser.parseExpression()) + return parser.expression(); + else + return 0; } -bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectDefinition *ast) +Statement *QmlExpressionUnderCursor::tryStatement(const QString &text) { - Node::accept(ast->qualifiedTypeNameId, this); + _engine = new Engine(); + _nodePool = new NodePool("", _engine); + Lexer lexer(_engine); + Parser parser(_engine); + lexer.setCode(text, /*line = */ 1); - _scopes.push(ast); - - Node::accept(ast->initializer, this); - - return false; + if (parser.parseStatement()) + return parser.statement(); + else + return 0; } -void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectDefinition *) +UiObjectMember *QmlExpressionUnderCursor::tryBinding(const QString &text) { - _scopes.pop(); -} + _engine = new Engine(); + _nodePool = new NodePool("", _engine); + Lexer lexer(_engine); + Parser parser(_engine); + lexer.setCode(text, /*line = */ 1); -bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiQualifiedId *ast) -{ - if (ast->identifierToken.offset <= _pos) { - for (UiQualifiedId *iter = ast; iter; iter = iter->next) { - if (_pos <= iter->identifierToken.end()) { - // found it - _expressionNode = ast; - _expressionOffset = ast->identifierToken.offset; - - for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) { - _expressionLength = iter2->identifierToken.end() - _expressionOffset; - } + if (parser.parseUiObjectMember()) { + UiObjectMember *member = parser.uiObjectMember(); - _expressionScopes = _scopes; - break; - } - } + if (cast<UiObjectBinding*>(member) || cast<UiArrayBinding*>(member) || cast<UiScriptBinding*>(member)) + return member; + else + return 0; + } else { + return 0; } - - return false; -} - -bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiImport * /*ast*/) -{ - return false; } diff --git a/src/plugins/qmleditor/qmlexpressionundercursor.h b/src/plugins/qmleditor/qmlexpressionundercursor.h index a891dc8968f..144edde2ced 100644 --- a/src/plugins/qmleditor/qmlexpressionundercursor.h +++ b/src/plugins/qmleditor/qmlexpressionundercursor.h @@ -2,21 +2,30 @@ #define QMLEXPRESSIONUNDERCURSOR_H #include <QStack> +#include <QTextBlock> #include <QTextCursor> -#include "qmljsastvisitor_p.h" +#include "qmldocument.h" +#include "qmljsastfwd_p.h" +#include "qmlsymbol.h" + +namespace QmlJS { + class Engine; + class NodePool; +} namespace QmlEditor { namespace Internal { -class QmlExpressionUnderCursor: protected QmlJS::AST::Visitor +class QmlExpressionUnderCursor { public: QmlExpressionUnderCursor(); + virtual ~QmlExpressionUnderCursor(); - void operator()(const QTextCursor &cursor, QmlJS::AST::UiProgram *program); + void operator()(const QTextCursor &cursor, const QmlDocument::Ptr &doc); - QStack<QmlJS::AST::Node *> expressionScopes() const + QStack<QmlSymbol *> expressionScopes() const { return _expressionScopes; } QmlJS::AST::Node *expressionNode() const @@ -28,26 +37,21 @@ public: int expressionLength() const { return _expressionLength; } -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::UiImport *ast); - virtual bool visit(QmlJS::AST::UiObjectBinding *ast); - virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); - virtual bool visit(QmlJS::AST::UiQualifiedId *ast); +private: + void parseExpression(const QTextBlock &block); - virtual void endVisit(QmlJS::AST::Block *); - virtual void endVisit(QmlJS::AST::UiObjectBinding *); - virtual void endVisit(QmlJS::AST::UiObjectDefinition *); + QmlJS::AST::ExpressionNode *tryExpression(const QString &text); + QmlJS::AST::Statement *tryStatement(const QString &text); + QmlJS::AST::UiObjectMember *tryBinding(const QString &text); private: - QStack<QmlJS::AST::Node *> _scopes; - QStack<QmlJS::AST::Node *> _expressionScopes; + QStack<QmlSymbol *> _expressionScopes; QmlJS::AST::Node *_expressionNode; int _expressionOffset; int _expressionLength; quint32 _pos; + QmlJS::Engine *_engine; + QmlJS::NodePool *_nodePool; }; } // namespace Internal diff --git a/src/plugins/qmleditor/qmllookupcontext.cpp b/src/plugins/qmleditor/qmllookupcontext.cpp index 6e2ca83ea86..abfca64db86 100644 --- a/src/plugins/qmleditor/qmllookupcontext.cpp +++ b/src/plugins/qmleditor/qmllookupcontext.cpp @@ -10,7 +10,7 @@ using namespace QmlEditor::Internal; using namespace QmlJS; using namespace QmlJS::AST; -QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, +QmlLookupContext::QmlLookupContext(const QStack<QmlSymbol *> &scopes, const QmlDocument::Ptr &doc, const Snapshot &snapshot): _scopes(scopes), @@ -19,28 +19,49 @@ QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, { } -QmlLookupContext::~QmlLookupContext() +static inline int findFirstQmlObjectScope(const QStack<QmlSymbol*> &scopes, int startIdx) { - qDeleteAll(_temporarySymbols); + if (startIdx < 0 || startIdx >= scopes.size()) + return -1; + + for (int i = startIdx; i >= 0; --i) { + QmlSymbol *current = scopes.at(i); + + if (current->isSymbolFromFile()) { + Node *node = current->asSymbolFromFile()->node(); + + if (cast<UiObjectBinding*>(node) || cast<UiObjectDefinition*>(node)) { + return i; + } + } + } + + return -1; +} + +static inline QmlSymbol *resolveParent(const QStack<QmlSymbol*> &scopes) +{ + int idx = findFirstQmlObjectScope(scopes, scopes.size() - 1); + if (idx < 1) + return 0; + + idx = findFirstQmlObjectScope(scopes, idx - 1); + + if (idx < 0) + return 0; + else + return scopes.at(idx); } QmlSymbol *QmlLookupContext::resolve(const QString &name) { // look at property definitions - if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName())) - return propertySymbol; + if (!_scopes.isEmpty()) + if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName())) + return propertySymbol; if (name == "parent") { - for (int i = _scopes.size() - 2; i >= 0; --i) { - Node *scope = _scopes.at(i); - - if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope)) - return createSymbol(_doc->fileName(), definition); - else if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope)) - return createSymbol(_doc->fileName(), binding); - } - - return 0; + return resolveParent(_scopes); } // look at the ids. @@ -52,13 +73,6 @@ QmlSymbol *QmlLookupContext::resolve(const QString &name) return 0; } -QmlSymbol *QmlLookupContext::createSymbol(const QString &fileName, QmlJS::AST::UiObjectMember *node) -{ - QmlSymbol *symbol = new QmlSymbolFromFile(fileName, node); - _temporarySymbols.append(symbol); - return symbol; -} - QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName) { // TODO: handle import-as. @@ -88,66 +102,41 @@ QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fil if (importedTypes.contains(name)) { QmlDocument::Ptr importedDoc = importedTypes.value(name); - UiProgram *importedProgram = importedDoc->program(); - if (importedProgram && importedProgram->members && importedProgram->members->member) - return createSymbol(importedDoc->fileName(), importedProgram->members->member); + return importedDoc->symbols().at(0); } } return 0; } -QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, Node *scope, const QString &fileName) +QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName) { + foreach (QmlSymbol *symbol, scope->members()) + if (symbol->name() == name) + return symbol; + UiQualifiedId *typeName = 0; - if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope)) { - if (QmlSymbol *symbol = resolveProperty(name, binding->initializer, fileName)) - return symbol; - else + if (scope->isSymbolFromFile()) { + Node *ast = scope->asSymbolFromFile()->node(); + + if (UiObjectBinding *binding = cast<UiObjectBinding*>(ast)) { typeName = binding->qualifiedTypeNameId; - } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope)) { - if (QmlSymbol *symbol = resolveProperty(name, definition->initializer, fileName)) - return symbol; - else + } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(ast)) { typeName = definition->qualifiedTypeNameId; - } // TODO: extend this to handle (JavaScript) block scopes. + } // TODO: extend this to handle (JavaScript) block scopes. + } if (typeName == 0) return 0; QmlSymbol *typeSymbol = resolveType(toString(typeName), fileName); - if (typeSymbol && typeSymbol->isSymbolFromFile()) { - return resolveProperty(name, typeSymbol->asSymbolFromFile()->node(), typeSymbol->asSymbolFromFile()->fileName()); - } - - return 0; -} - -QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlJS::AST::UiObjectInitializer *initializer, const QString &fileName) -{ - if (!initializer) + if (!typeSymbol) return 0; - for (UiObjectMemberList *iter = initializer->members; iter; iter = iter->next) { - UiObjectMember *member = iter->member; - if (!member) - continue; - - if (UiPublicMember *publicMember = cast<UiPublicMember*>(member)) { - if (name == publicMember->name->asString()) - return createSymbol(fileName, publicMember); - } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) { - if (name == toString(objectBinding->qualifiedId)) - return createSymbol(fileName, objectBinding); - } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) { - if (name == toString(arrayBinding->qualifiedId)) - return createSymbol(fileName, arrayBinding); - } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) { - if (name == toString(scriptBinding->qualifiedId)) - return createSymbol(fileName, scriptBinding); - } - } + if (typeSymbol->isSymbolFromFile()) { + return resolveProperty(name, typeSymbol->asSymbolFromFile(), typeSymbol->asSymbolFromFile()->fileName()); + } // TODO: internal types return 0; } @@ -169,10 +158,17 @@ QString QmlLookupContext::toString(UiQualifiedId *id) return str; } -QList<QmlSymbol*> QmlLookupContext::visibleSymbols(QmlJS::AST::Node * /* scope */) +QList<QmlSymbol*> QmlLookupContext::visibleSymbolsInScope() { - // FIXME - return QList<QmlSymbol*>(); + QList<QmlSymbol*> result; + + if (!_scopes.isEmpty()) { + QmlSymbol *scope = _scopes.top(); + + result.append(scope->members()); + } + + return result; } QList<QmlSymbol*> QmlLookupContext::visibleTypes() @@ -196,11 +192,7 @@ QList<QmlSymbol*> QmlLookupContext::visibleTypes() const QMap<QString, QmlDocument::Ptr> types = _snapshot.componentsDefinedByImportedDocuments(_doc, path); foreach (const QmlDocument::Ptr typeDoc, types) { - UiProgram *typeProgram = typeDoc->program(); - - if (typeProgram && typeProgram->members && typeProgram->members->member) { - result.append(createSymbol(typeDoc->fileName(), typeProgram->members->member)); - } + result.append(typeDoc->symbols().at(0)); } } diff --git a/src/plugins/qmleditor/qmllookupcontext.h b/src/plugins/qmleditor/qmllookupcontext.h index 80635c03937..d18e0ec5ec9 100644 --- a/src/plugins/qmleditor/qmllookupcontext.h +++ b/src/plugins/qmleditor/qmllookupcontext.h @@ -13,10 +13,9 @@ namespace Internal { class QmlLookupContext { public: - QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, + QmlLookupContext(const QStack<QmlSymbol *> &scopes, const QmlDocument::Ptr &doc, const Snapshot &snapshot); - ~QmlLookupContext(); QmlSymbol *resolve(const QString &name); QmlSymbol *resolveType(const QString &name) @@ -27,23 +26,19 @@ public: QmlDocument::Ptr document() const { return _doc; } - QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *scope); + QList<QmlSymbol*> visibleSymbolsInScope(); QList<QmlSymbol*> visibleTypes(); private: - QmlSymbol *createSymbol(const QString &fileName, QmlJS::AST::UiObjectMember *node); - QmlSymbol *resolveType(const QString &name, const QString &fileName); - QmlSymbol *resolveProperty(const QString &name, QmlJS::AST::Node *scope, const QString &fileName); - QmlSymbol *resolveProperty(const QString &name, QmlJS::AST::UiObjectInitializer *initializer, const QString &fileName); + QmlSymbol *resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName); static QString toString(QmlJS::AST::UiQualifiedId *id); private: - QStack<QmlJS::AST::Node *> _scopes; + QStack<QmlSymbol *> _scopes; QmlDocument::Ptr _doc; Snapshot _snapshot; - QList<QmlSymbol*> _temporarySymbols; }; } // namespace Internal diff --git a/src/plugins/qmleditor/qmlresolveexpression.cpp b/src/plugins/qmleditor/qmlresolveexpression.cpp index 89c7090a4d4..1b464147aa7 100644 --- a/src/plugins/qmleditor/qmlresolveexpression.cpp +++ b/src/plugins/qmleditor/qmlresolveexpression.cpp @@ -12,11 +12,6 @@ QmlResolveExpression::QmlResolveExpression(const QmlLookupContext &context) { } -QmlResolveExpression::~QmlResolveExpression() -{ - qDeleteAll(_temporarySymbols); -} - QmlSymbol *QmlResolveExpression::typeOf(Node *node) { QmlSymbol *previousValue = switchValue(0); @@ -25,6 +20,30 @@ QmlSymbol *QmlResolveExpression::typeOf(Node *node) return switchValue(previousValue); } +QList<QmlSymbol*> QmlResolveExpression::visibleSymbols(Node *node) +{ + QList<QmlSymbol*> result; + + QmlSymbol *symbol = typeOf(node); + if (symbol) { + if (symbol->isIdSymbol()) + symbol = symbol->asIdSymbol()->parentNode(); + result.append(symbol->members()); + + // TODO: also add symbols from super-types + } else { + result.append(_context.visibleTypes()); + } + + if (node) { + foreach (QmlIdSymbol *idSymbol, _context.document()->ids().values()) + result.append(idSymbol); + } + + result.append(_context.visibleSymbolsInScope()); + + return result; +} QmlSymbol *QmlResolveExpression::switchValue(QmlSymbol *value) { @@ -58,52 +77,19 @@ bool QmlResolveExpression::visit(FieldMemberExpression *ast) { const QString memberName = ast->name->asString(); - const QmlSymbol *base = typeOf(ast->base); + QmlSymbol *base = typeOf(ast->base); if (!base) return false; if (base->isIdSymbol()) base = base->asIdSymbol()->parentNode(); - UiObjectMemberList *members = 0; - - if (const QmlSymbolFromFile *symbol = base->asSymbolFromFile()) { - Node *node = symbol->node(); - - if (UiObjectBinding *binding = cast<UiObjectBinding*>(node)) { - if (binding->initializer) - members = binding->initializer->members; - } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(node)) { - if (definition->initializer) - members = definition->initializer->members; - } - } + if (!base) + return false; - for (UiObjectMemberList *it = members; it; it = it->next) { - UiObjectMember *member = it->member; - - if (UiPublicMember *publicMember = cast<UiPublicMember *>(member)) { - if (publicMember->name && publicMember->name->asString() == memberName) { - _value = createPropertyDefinitionSymbol(publicMember); - break; // we're done. - } - } else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member)) { - if (matches(objectBinding->qualifiedId, memberName)) { - _value = createSymbolFromFile(objectBinding); - break; // we're done - } - } else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member)) { - if (matches(scriptBinding->qualifiedId, memberName)) { - _value = createSymbolFromFile(scriptBinding); - break; // we're done - } - } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member)) { - if (matches(arrayBinding->qualifiedId, memberName)) { - _value = createSymbolFromFile(arrayBinding); - break; // we're done - } - } - } + foreach (QmlSymbol *memberSymbol, base->members()) + if (memberSymbol->name() == memberName) + _value = memberSymbol; return false; } @@ -114,17 +100,3 @@ bool QmlResolveExpression::visit(QmlJS::AST::UiQualifiedId *ast) return false; } - -QmlPropertyDefinitionSymbol *QmlResolveExpression::createPropertyDefinitionSymbol(QmlJS::AST::UiPublicMember *ast) -{ - QmlPropertyDefinitionSymbol *symbol = new QmlPropertyDefinitionSymbol(_context.document()->fileName(), ast); - _temporarySymbols.append(symbol); - return symbol; -} - -QmlSymbolFromFile *QmlResolveExpression::createSymbolFromFile(QmlJS::AST::UiObjectMember *ast) -{ - QmlSymbolFromFile *symbol = new QmlSymbolFromFile(_context.document()->fileName(), ast); - _temporarySymbols.append(symbol); - return symbol; -} diff --git a/src/plugins/qmleditor/qmlresolveexpression.h b/src/plugins/qmleditor/qmlresolveexpression.h index b756887f4a4..41e44e88531 100644 --- a/src/plugins/qmleditor/qmlresolveexpression.h +++ b/src/plugins/qmleditor/qmlresolveexpression.h @@ -12,28 +12,21 @@ class QmlResolveExpression: protected QmlJS::AST::Visitor { public: QmlResolveExpression(const QmlLookupContext &context); - ~QmlResolveExpression(); - QmlSymbol *operator()(QmlJS::AST::Node *node) - { return typeOf(node); } + QmlSymbol *typeOf(QmlJS::AST::Node *node); + QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *node); protected: using QmlJS::AST::Visitor::visit; - QmlSymbol *typeOf(QmlJS::AST::Node *node); QmlSymbol *switchValue(QmlSymbol *symbol); virtual bool visit(QmlJS::AST::FieldMemberExpression *ast); virtual bool visit(QmlJS::AST::IdentifierExpression *ast); virtual bool visit(QmlJS::AST::UiQualifiedId *ast); -private: - QmlPropertyDefinitionSymbol *createPropertyDefinitionSymbol(QmlJS::AST::UiPublicMember *ast); - QmlSymbolFromFile *createSymbolFromFile(QmlJS::AST::UiObjectMember *ast); - private: QmlLookupContext _context; - QList<QmlSymbol*> _temporarySymbols; QmlSymbol *_value; }; diff --git a/src/plugins/qmleditor/qmlscope.cpp b/src/plugins/qmleditor/qmlscope.cpp new file mode 100644 index 00000000000..8d467d12f73 --- /dev/null +++ b/src/plugins/qmleditor/qmlscope.cpp @@ -0,0 +1,35 @@ +#include "qmlscope.h" + +using namespace QmlEditor; +using namespace QmlEditor::Internal; + +Scope::Scope(Scope *parentScope): + _parentScope(parentScope) +{ +} + +Scope::~Scope() +{ +} + +bool Scope::isJSScope() +{ return asJSScope() != 0; } + +bool Scope::isQmlScope() +{ return asQmlScope() != 0; } + +JSScope *Scope::asJSScope() +{ return 0; } + +QmlScope *Scope::asQmlScope() +{ return 0; } + +QmlScope::QmlScope(QmlScope *parentScope): + Scope(parentScope) +{ +} + +JSScope::JSScope(Scope *parentScope): + Scope(parentScope) +{ +} diff --git a/src/plugins/qmleditor/qmlscope.h b/src/plugins/qmleditor/qmlscope.h new file mode 100644 index 00000000000..a53c1174bb0 --- /dev/null +++ b/src/plugins/qmleditor/qmlscope.h @@ -0,0 +1,40 @@ +#ifndef QMLSCOPE_H +#define QMLSCOPE_H + +namespace QmlEditor { +namespace Internal { + +class QmlScope; +class JSScope; + +class Scope +{ +public: + Scope(Scope *parentScope); + virtual ~Scope(); + + bool isQmlScope(); + bool isJSScope(); + + virtual QmlScope *asQmlScope(); + virtual JSScope *asJSScope(); + +private: + Scope *_parentScope; +}; + +class QmlScope: public Scope +{ +public: + QmlScope(QmlScope *parentScope); +}; + +class JSScope: public Scope +{ + JSScope(Scope *parentScope); +}; + +} // namespace Internal +} // namespace QmlEditor + +#endif // QMLSCOPE_H diff --git a/src/plugins/qmleditor/qmlsymbol.cpp b/src/plugins/qmleditor/qmlsymbol.cpp index 21271fb7099..6eacae86450 100644 --- a/src/plugins/qmleditor/qmlsymbol.cpp +++ b/src/plugins/qmleditor/qmlsymbol.cpp @@ -1,4 +1,5 @@ #include "qmljsast_p.h" +#include "qmljsengine_p.h" #include "qmlsymbol.h" using namespace QmlEditor; @@ -7,41 +8,67 @@ using namespace QmlJS::AST; QmlSymbol::~QmlSymbol() { + qDeleteAll(_members); } -bool QmlSymbol::isBuildInSymbol() const +bool QmlSymbol::isBuildInSymbol() { return asBuildInSymbol() != 0; } -bool QmlSymbol::isSymbolFromFile() const +bool QmlSymbol::isSymbolFromFile() { return asSymbolFromFile() != 0; } -bool QmlSymbol::isIdSymbol() const +bool QmlSymbol::isIdSymbol() { return asIdSymbol() != 0; } -QmlBuildInSymbol const *QmlSymbol::asBuildInSymbol() const +bool QmlSymbol::isPropertyDefinitionSymbol() +{ return asPropertyDefinitionSymbol() != 0; } + +QmlBuildInSymbol *QmlSymbol::asBuildInSymbol() +{ return 0; } + +QmlSymbolFromFile *QmlSymbol::asSymbolFromFile() { return 0; } -QmlSymbolFromFile const *QmlSymbol::asSymbolFromFile() const +QmlIdSymbol *QmlSymbol::asIdSymbol() { return 0; } -QmlIdSymbol const *QmlSymbol::asIdSymbol() const +QmlPropertyDefinitionSymbol *QmlSymbol::asPropertyDefinitionSymbol() { return 0; } +const QmlSymbol::List QmlSymbol::members() +{ return _members; } + QmlBuildInSymbol::~QmlBuildInSymbol() {} -QmlBuildInSymbol const* QmlBuildInSymbol::asBuildInSymbol() const +QmlBuildInSymbol *QmlBuildInSymbol::asBuildInSymbol() { return this; } QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node): _fileName(fileName), _node(node) -{} +{ + if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(_node)) { + if (objectBinding->initializer) + for (UiObjectMemberList *iter = objectBinding->initializer->members; iter; iter = iter->next) + if (iter->member) + todo.append(iter->member); + } else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(_node)) { + if (objectDefinition->initializer) + for (UiObjectMemberList *iter = objectDefinition->initializer->members; iter; iter = iter->next) + if (iter->member) + todo.append(iter->member); + } else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(_node)) { + for (UiArrayMemberList *iter = arrayBinding->members; iter; iter = iter->next) + if (iter->member) + todo.append(iter->member); + } +} QmlSymbolFromFile::~QmlSymbolFromFile() {} -const QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile() const +QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile() { return this; } int QmlSymbolFromFile::line() const @@ -50,7 +77,76 @@ int QmlSymbolFromFile::line() const int QmlSymbolFromFile::column() const { return _node->firstSourceLocation().startColumn; } -QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode): +static inline QString toString(UiQualifiedId *qId) +{ + QString result; + + for (UiQualifiedId *iter = qId; iter; iter = iter->next) { + if (!iter->name) + continue; + + result += iter->name->asString(); + + if (iter->next) + result += '.'; + } + + return result; +} + +const QString QmlSymbolFromFile::name() const +{ + if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(_node)) + return toString(objectBinding->qualifiedId); + else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(_node)) + return toString(scriptBinding->qualifiedId); + else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(_node)) + return toString(arrayBinding->qualifiedId); + else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(_node)) + return toString(objectDefinition->qualifiedTypeNameId); + else + return QString::null; +} + +const QmlSymbol::List QmlSymbolFromFile::members() +{ + if (!todo.isEmpty()) { + foreach (Node *node, todo) { + if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(node)) + _members.append(new QmlSymbolFromFile(fileName(), objectBinding)); + else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(node)) + _members.append(new QmlSymbolFromFile(fileName(), objectDefinition)); + else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(node)) + _members.append(new QmlSymbolFromFile(fileName(), arrayBinding)); + else if (UiPublicMember *publicMember = cast<UiPublicMember*>(node)) + _members.append(new QmlPropertyDefinitionSymbol(fileName(), publicMember)); + else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(node)) { + if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name && scriptBinding->qualifiedId->name->asString() == QLatin1String("id") && !scriptBinding->qualifiedId->next) + _members.append(new QmlIdSymbol(fileName(), scriptBinding, this)); + else + _members.append(new QmlSymbolFromFile(fileName(), scriptBinding)); + } + } + + todo.clear(); + } + + return _members; +} + +QmlSymbolFromFile *QmlSymbolFromFile::findMember(QmlJS::AST::Node *memberNode) +{ + List symbols = members(); + + foreach (QmlSymbol *symbol, symbols) + if (symbol->isSymbolFromFile()) + if (memberNode == symbol->asSymbolFromFile()->node()) + return symbol->asSymbolFromFile(); + + return 0; +} + +QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, QmlSymbolFromFile *parentNode): QmlSymbolFromFile(fileName, idNode), _parentNode(parentNode) {} @@ -58,7 +154,7 @@ QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *i QmlIdSymbol::~QmlIdSymbol() {} -QmlIdSymbol const *QmlIdSymbol::asIdSymbol() const +QmlIdSymbol *QmlIdSymbol::asIdSymbol() { return this; } int QmlIdSymbol::line() const @@ -67,6 +163,16 @@ int QmlIdSymbol::line() const int QmlIdSymbol::column() const { return idNode()->statement->firstSourceLocation().startColumn; } +const QString QmlIdSymbol::id() const +{ + if (ExpressionStatement *e = cast<ExpressionStatement*>(idNode()->statement)) + if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) + if (i->name) + return i->name->asString(); + + return QString(); +} + QmlJS::AST::UiScriptBinding *QmlIdSymbol::idNode() const { return cast<UiScriptBinding*>(node()); } @@ -77,6 +183,9 @@ QmlPropertyDefinitionSymbol::QmlPropertyDefinitionSymbol(const QString &fileName QmlPropertyDefinitionSymbol::~QmlPropertyDefinitionSymbol() {} +QmlPropertyDefinitionSymbol *QmlPropertyDefinitionSymbol::asPropertyDefinitionSymbol() +{ return this; } + int QmlPropertyDefinitionSymbol::line() const { return propertyNode()->identifierToken.startLine; } @@ -85,3 +194,6 @@ int QmlPropertyDefinitionSymbol::column() const QmlJS::AST::UiPublicMember *QmlPropertyDefinitionSymbol::propertyNode() const { return cast<UiPublicMember*>(node()); } + +const QString QmlPropertyDefinitionSymbol::name() const +{ return propertyNode()->name->asString(); } diff --git a/src/plugins/qmleditor/qmlsymbol.h b/src/plugins/qmleditor/qmlsymbol.h index be827faea24..8957db80c37 100644 --- a/src/plugins/qmleditor/qmlsymbol.h +++ b/src/plugins/qmleditor/qmlsymbol.h @@ -1,6 +1,7 @@ #ifndef QMLSYMBOL_H #define QMLSYMBOL_H +#include <QList> #include <QString> #include "qmljsastfwd_p.h" @@ -10,25 +11,44 @@ namespace QmlEditor { class QmlSymbol { public: - virtual ~QmlSymbol() = 0; + typedef QList<QmlSymbol*> List; - bool isBuildInSymbol() const; - bool isSymbolFromFile() const; - bool isIdSymbol() const; +public: + virtual ~QmlSymbol(); + + virtual const QString name() const = 0; + virtual const List members(); + + bool isBuildInSymbol(); + bool isSymbolFromFile(); + bool isIdSymbol(); + bool isPropertyDefinitionSymbol(); + + virtual class QmlBuildInSymbol *asBuildInSymbol(); + virtual class QmlSymbolFromFile *asSymbolFromFile(); + virtual class QmlIdSymbol *asIdSymbol(); + virtual class QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol(); - virtual class QmlBuildInSymbol const *asBuildInSymbol() const; - virtual class QmlSymbolFromFile const *asSymbolFromFile() const; - virtual class QmlIdSymbol const *asIdSymbol() const; +protected: + List _members; }; class QmlBuildInSymbol: public QmlSymbol { public: + QmlBuildInSymbol(const QString &name): _name(name) {} virtual ~QmlBuildInSymbol(); - virtual QmlBuildInSymbol const *asBuildInSymbol() const; + virtual QmlBuildInSymbol *asBuildInSymbol(); + + virtual const QString name() const + { return _name; } + + // TODO: +// virtual const List members(); private: + QString _name; }; class QmlSymbolFromFile: public QmlSymbol @@ -37,7 +57,7 @@ public: QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node); virtual ~QmlSymbolFromFile(); - virtual QmlSymbolFromFile const *asSymbolFromFile() const; + virtual QmlSymbolFromFile *asSymbolFromFile(); QString fileName() const { return _fileName; } @@ -48,30 +68,43 @@ public: QmlJS::AST::UiObjectMember *node() const { return _node; } + virtual const QString name() const; + virtual const List members(); + virtual QmlSymbolFromFile *findMember(QmlJS::AST::Node *memberNode); + +private: + void fillTodo(QmlJS::AST::UiObjectMemberList *members); + private: QString _fileName; QmlJS::AST::UiObjectMember *_node; + QList<QmlJS::AST::Node*> todo; }; class QmlIdSymbol: public QmlSymbolFromFile { public: - QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode); + QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, QmlSymbolFromFile *parentNode); virtual ~QmlIdSymbol(); - QmlIdSymbol const *asIdSymbol() const; + QmlIdSymbol *asIdSymbol(); virtual int line() const; virtual int column() const; - QmlSymbolFromFile const *parentNode() const - { return &_parentNode; } + QmlSymbolFromFile *parentNode() const + { return _parentNode; } + + virtual const QString name() const + { return "id"; } + + virtual const QString id() const; private: QmlJS::AST::UiScriptBinding *idNode() const; private: - QmlSymbolFromFile _parentNode; + QmlSymbolFromFile *_parentNode; }; class QmlPropertyDefinitionSymbol: public QmlSymbolFromFile @@ -80,9 +113,13 @@ public: QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode); virtual ~QmlPropertyDefinitionSymbol(); + QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol(); + virtual int line() const; virtual int column() const; + virtual const QString name() const; + private: QmlJS::AST::UiPublicMember *propertyNode() const; }; -- GitLab