Improved the navigation and code completion in the QML editor.

This commit is contained in:
Erik Verbruggen
2009-10-02 10:49:33 +02:00
parent efe336b605
commit c3d1019deb
19 changed files with 798 additions and 348 deletions

View File

@@ -1,3 +1,5 @@
#include <QDebug>
#include "idcollector.h" #include "idcollector.h"
#include "qmljsast_p.h" #include "qmljsast_p.h"
#include "qmljsengine_p.h" #include "qmljsengine_p.h"
@@ -7,36 +9,39 @@ using namespace QmlJS::AST;
using namespace QmlEditor; using namespace QmlEditor;
using namespace QmlEditor::Internal; 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(); _ids.clear();
_currentSymbol = 0;
Node::accept(ast, this); Node::accept(doc->program(), this);
return _ids; return _ids;
} }
bool IdCollector::visit(UiArrayBinding *ast)
{
QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
Node::accept(ast->members, this);
_currentSymbol = oldSymbol;
return false;
}
bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast) bool IdCollector::visit(QmlJS::AST::UiObjectBinding *ast)
{ {
_scopes.push(ast); QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
return true; Node::accept(ast->initializer, this);
_currentSymbol = oldSymbol;
return false;
} }
bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast) bool IdCollector::visit(QmlJS::AST::UiObjectDefinition *ast)
{ {
_scopes.push(ast); QmlSymbolFromFile *oldSymbol = switchSymbol(ast);
return true; Node::accept(ast->initializer, this);
} _currentSymbol = oldSymbol;
return false;
void IdCollector::endVisit(QmlJS::AST::UiObjectBinding *)
{
_scopes.pop();
}
void IdCollector::endVisit(QmlJS::AST::UiObjectDefinition *)
{
_scopes.pop();
} }
bool IdCollector::visit(QmlJS::AST::UiScriptBinding *ast) 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 (!(ast->qualifiedId->next) && ast->qualifiedId->name->asString() == "id")
if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement)) if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement))
if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
addId(i->name->asString(), ast); if (i->name)
addId(i->name->asString(), ast);
return false; 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) void IdCollector::addId(const QString &id, QmlJS::AST::UiScriptBinding *ast)
{ {
if (!_ids.contains(id)) { if (!_ids.contains(id) && _currentSymbol) {
Node *parent = _scopes.top(); QmlSymbolFromFile *symbol = _currentSymbol->findMember(ast);
if (UiObjectBinding *binding = cast<UiObjectBinding*>(parent)) if (QmlIdSymbol *idSymbol = symbol->asIdSymbol())
_ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, binding)); _ids[id] = idSymbol;
else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(parent))
_ids[id] = new QmlIdSymbol(_fileName, ast, QmlSymbolFromFile(_fileName, definition));
else
Q_ASSERT(!"Unknown parent for id");
} }
} }

View File

@@ -6,6 +6,7 @@
#include <QStack> #include <QStack>
#include <QString> #include <QString>
#include "qmldocument.h"
#include "qmljsastvisitor_p.h" #include "qmljsastvisitor_p.h"
#include "qmlsymbol.h" #include "qmlsymbol.h"
@@ -15,23 +16,22 @@ namespace Internal {
class IdCollector: protected QmlJS::AST::Visitor class IdCollector: protected QmlJS::AST::Visitor
{ {
public: public:
QMap<QString, QmlIdSymbol*> operator()(const QString &fileName, QmlJS::AST::UiProgram *ast); QMap<QString, QmlIdSymbol*> operator()(QmlDocument *doc);
protected: protected:
virtual bool visit(QmlJS::AST::UiArrayBinding *ast);
virtual bool visit(QmlJS::AST::UiObjectBinding *ast); virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
virtual bool visit(QmlJS::AST::UiObjectDefinition *ast); virtual bool visit(QmlJS::AST::UiObjectDefinition *ast);
virtual bool visit(QmlJS::AST::UiScriptBinding *ast); virtual bool visit(QmlJS::AST::UiScriptBinding *ast);
virtual void endVisit(QmlJS::AST::UiObjectBinding *);
virtual void endVisit(QmlJS::AST::UiObjectDefinition *);
private: private:
QmlSymbolFromFile *switchSymbol(QmlJS::AST::UiObjectMember *node);
void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast); void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast);
private: private:
QString _fileName; QmlDocument *_doc;
QMap<QString, QmlIdSymbol*> _ids; QMap<QString, QmlIdSymbol*> _ids;
QStack<QmlJS::AST::Node *> _scopes; QmlSymbolFromFile *_currentSymbol;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -1,17 +1,25 @@
#include "qmlcompletionvisitor.h"
#include "qmlcodecompletion.h" #include "qmlcodecompletion.h"
#include "qmleditor.h" #include "qmleditor.h"
#include "qmlmodelmanagerinterface.h"
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include "qmlsymbol.h"
#include <texteditor/basetexteditor.h> #include <texteditor/basetexteditor.h>
#include <QtDebug> #include <QtDebug>
using namespace QmlEditor::Internal; using namespace QmlEditor::Internal;
QmlCodeCompletion::QmlCodeCompletion(QObject *parent) QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager,QObject *parent)
: TextEditor::ICompletionCollector(parent), : TextEditor::ICompletionCollector(parent),
m_modelManager(modelManager),
m_editor(0), m_editor(0),
m_startPosition(0), m_startPosition(0),
m_caseSensitivity(Qt::CaseSensitive) m_caseSensitivity(Qt::CaseSensitive)
{ } {
Q_ASSERT(modelManager);
}
QmlCodeCompletion::~QmlCodeCompletion() QmlCodeCompletion::~QmlCodeCompletion()
{ } { }
@@ -49,26 +57,52 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
m_startPosition = pos; m_startPosition = pos;
m_completions.clear(); m_completions.clear();
foreach (const QString &word, edit->keywords()) { // foreach (const QString &word, edit->keywords()) {
TextEditor::CompletionItem item(this); // TextEditor::CompletionItem item(this);
item.m_text = word; // item.m_text = word;
m_completions.append(item); // m_completions.append(item);
} // }
//
foreach (const QString &word, edit->words()) { // foreach (const QString &word, edit->words()) {
TextEditor::CompletionItem item(this); // TextEditor::CompletionItem item(this);
item.m_text = word; // item.m_text = word;
m_completions.append(item); // m_completions.append(item);
} // }
QmlDocument::Ptr qmlDocument = edit->qmlDocument(); QmlDocument::Ptr qmlDocument = edit->qmlDocument();
if (!qmlDocument.isNull()) { qDebug() << "*** document:" << qmlDocument;
QmlJS::AST::UiProgram *program = qmlDocument->program(); if (qmlDocument.isNull())
return pos;
if (program) { if (!qmlDocument->program())
QmlCompletionVisitor visitor; qmlDocument = m_modelManager->snapshot().value(qmlDocument->fileName());
foreach (const QString &word, visitor(program, m_startPosition)) { 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); TextEditor::CompletionItem item(this);
item.m_text = word; item.m_text = word;
m_completions.append(item); m_completions.append(item);

View File

@@ -8,6 +8,9 @@ class ITextEditable;
} }
namespace QmlEditor { namespace QmlEditor {
class QmlModelManagerInterface;
namespace Internal { namespace Internal {
class QmlCodeCompletion: public TextEditor::ICompletionCollector class QmlCodeCompletion: public TextEditor::ICompletionCollector
@@ -15,7 +18,7 @@ class QmlCodeCompletion: public TextEditor::ICompletionCollector
Q_OBJECT Q_OBJECT
public: public:
QmlCodeCompletion(QObject *parent = 0); QmlCodeCompletion(QmlModelManagerInterface *modelManager, QObject *parent = 0);
virtual ~QmlCodeCompletion(); virtual ~QmlCodeCompletion();
Qt::CaseSensitivity caseSensitivity() const; Qt::CaseSensitivity caseSensitivity() const;
@@ -30,6 +33,7 @@ public:
virtual void cleanup(); virtual void cleanup();
private: private:
QmlModelManagerInterface *m_modelManager;
TextEditor::ITextEditable *m_editor; TextEditor::ITextEditable *m_editor;
int m_startPosition; int m_startPosition;
QList<TextEditor::CompletionItem> m_completions; QList<TextEditor::CompletionItem> m_completions;

View File

@@ -61,7 +61,7 @@ QmlDocument::~QmlDocument()
if (_pool) if (_pool)
delete _pool; delete _pool;
qDeleteAll(_ids.values()); qDeleteAll(_symbols);
} }
QmlDocument::Ptr QmlDocument::create(const QString &fileName) QmlDocument::Ptr QmlDocument::create(const QString &fileName)
@@ -109,14 +109,28 @@ bool QmlDocument::parse()
_program = parser.ast(); _program = parser.ast();
_diagnosticMessages = parser.diagnosticMessages(); _diagnosticMessages = parser.diagnosticMessages();
if (_parsedCorrectly && _program) { if (_program) {
Internal::IdCollector collect; for (QmlJS::AST::UiObjectMemberList *iter = _program->members; iter; iter = iter->next)
_ids = collect(_fileName, _program); if (iter->member)
_symbols.append(new QmlSymbolFromFile(_fileName, iter->member));
Internal::IdCollector collect;
_ids = collect(this);
} }
return _parsedCorrectly; 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() Snapshot::Snapshot()
{ {
} }
@@ -127,6 +141,9 @@ Snapshot::~Snapshot()
void Snapshot::insert(const QmlDocument::Ptr &document) void Snapshot::insert(const QmlDocument::Ptr &document)
{ {
if (!document || !document->program())
return;
QMap<QString, QmlDocument::Ptr>::insert(document->fileName(), document); QMap<QString, QmlDocument::Ptr>::insert(document->fileName(), document);
} }

View File

@@ -74,6 +74,10 @@ public:
QString path() const { return _path; } QString path() const { return _path; }
QString componentName() const { return _componentName; } QString componentName() const { return _componentName; }
QmlSymbolFromFile *findSymbol(QmlJS::AST::Node *node) const;
QmlSymbol::List symbols() const
{ return _symbols; }
private: private:
QmlJS::Engine *_engine; QmlJS::Engine *_engine;
QmlJS::NodePool *_pool; QmlJS::NodePool *_pool;
@@ -85,6 +89,7 @@ private:
QString _source; QString _source;
bool _parsedCorrectly; bool _parsedCorrectly;
IdTable _ids; IdTable _ids;
QmlSymbol::List _symbols;
}; };
class QMLEDITOR_EXPORT Snapshot: public QMap<QString, QmlDocument::Ptr> class QMLEDITOR_EXPORT Snapshot: public QMap<QString, QmlDocument::Ptr>

View File

@@ -719,12 +719,28 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur
if (!doc) if (!doc)
return link; 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; QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(cursor, doc->program()); expressionUnderCursor(expressionCursor, doc);
QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot); QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot);
QmlResolveExpression resolve(context); QmlResolveExpression resolver(context);
QmlSymbol *symbol = resolve(expressionUnderCursor.expressionNode()); QmlSymbol *symbol = resolver.typeOf(expressionUnderCursor.expressionNode());
if (!symbol) if (!symbol)
return link; return link;

View File

@@ -17,7 +17,6 @@ HEADERS += qmleditor.h \
qmleditorconstants.h \ qmleditorconstants.h \
qmlhoverhandler.h \ qmlhoverhandler.h \
qmldocument.h \ qmldocument.h \
qmlcompletionvisitor.h \
qmlmodelmanagerinterface.h \ qmlmodelmanagerinterface.h \
qmleditor_global.h \ qmleditor_global.h \
qmlmodelmanager.h \ qmlmodelmanager.h \
@@ -27,7 +26,8 @@ HEADERS += qmleditor.h \
qmllookupcontext.h \ qmllookupcontext.h \
qmlresolveexpression.h \ qmlresolveexpression.h \
qmlsymbol.h \ qmlsymbol.h \
qmlfilewizard.h qmlfilewizard.h \
qmlscope.h
SOURCES += qmleditor.cpp \ SOURCES += qmleditor.cpp \
qmleditorfactory.cpp \ qmleditorfactory.cpp \
qmleditorplugin.cpp \ qmleditorplugin.cpp \
@@ -36,7 +36,6 @@ SOURCES += qmleditor.cpp \
qmlcodecompletion.cpp \ qmlcodecompletion.cpp \
qmlhoverhandler.cpp \ qmlhoverhandler.cpp \
qmldocument.cpp \ qmldocument.cpp \
qmlcompletionvisitor.cpp \
qmlmodelmanagerinterface.cpp \ qmlmodelmanagerinterface.cpp \
qmlmodelmanager.cpp \ qmlmodelmanager.cpp \
qmlcodeformatter.cpp \ qmlcodeformatter.cpp \
@@ -45,5 +44,6 @@ SOURCES += qmleditor.cpp \
qmllookupcontext.cpp \ qmllookupcontext.cpp \
qmlresolveexpression.cpp \ qmlresolveexpression.cpp \
qmlsymbol.cpp \ qmlsymbol.cpp \
qmlfilewizard.cpp qmlfilewizard.cpp \
qmlscope.cpp
RESOURCES += qmleditor.qrc RESOURCES += qmleditor.qrc

View File

@@ -111,7 +111,7 @@ bool QmlEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err
| TextEditor::TextEditorActionHandler::UnCollapseAll); | TextEditor::TextEditorActionHandler::UnCollapseAll);
m_actionHandler->initializeActions(); m_actionHandler->initializeActions();
m_completion = new QmlCodeCompletion(); m_completion = new QmlCodeCompletion(m_modelManager);
addAutoReleasedObject(m_completion); addAutoReleasedObject(m_completion);
addAutoReleasedObject(new QmlHoverHandler()); addAutoReleasedObject(new QmlHoverHandler());

View File

@@ -1,124 +1,294 @@
#include "qmljsast_p.h" #include <QDebug>
#include "qmljsengine_p.h"
#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" #include "qmlexpressionundercursor.h"
using namespace QmlEditor;
using namespace QmlEditor::Internal;
using namespace QmlJS; using namespace QmlJS;
using namespace QmlJS::AST; 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() QmlExpressionUnderCursor::QmlExpressionUnderCursor()
: _expressionNode(0), : _expressionNode(0),
_pos(0) _pos(0), _engine(0), _nodePool(0)
{ {
} }
void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor, QmlExpressionUnderCursor::~QmlExpressionUnderCursor()
QmlJS::AST::UiProgram *program)
{ {
if (_engine) { delete _engine; _engine = 0; }
if (_nodePool) { delete _nodePool; _nodePool = 0; }
}
void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor,
const QmlDocument::Ptr &doc)
{
if (_engine) { delete _engine; _engine = 0; }
if (_nodePool) { delete _nodePool; _nodePool = 0; }
_pos = cursor.position(); _pos = cursor.position();
_expressionNode = 0; _expressionNode = 0;
_expressionOffset = -1; _expressionOffset = -1;
_expressionLength = -1; _expressionLength = -1;
_scopes.clear(); _expressionScopes.clear();
if (program) const QTextBlock block = cursor.block();
program->accept(this); parseExpression(block);
if (_expressionOffset != -1) {
ScopeCalculator calculator;
_expressionScopes = calculator(doc, _expressionOffset);
}
} }
bool QmlExpressionUnderCursor::visit(QmlJS::AST::Block *ast) void QmlExpressionUnderCursor::parseExpression(const QTextBlock &block)
{ {
_scopes.push(ast); int textPosition = _pos - block.position();
const QString blockText = block.text();
return true; if (textPosition > 0) {
} if (blockText.at(textPosition - 1) == QLatin1Char('.'))
--textPosition;
void QmlExpressionUnderCursor::endVisit(QmlJS::AST::Block *) } else {
{ textPosition = 0;
_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;
} }
return true; const QString text = blockText.left(textPosition);
}
bool QmlExpressionUnderCursor::visit(QmlJS::AST::IdentifierExpression *ast) Node *node = 0;
{
if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) { if (UiObjectMember *binding = tryBinding(text)) {
_expressionNode = ast; qDebug() << "**** binding";
_expressionOffset = ast->firstSourceLocation().offset; node = binding;
_expressionLength = ast->lastSourceLocation().end() - _expressionOffset; } else if (Statement *stmt = tryStatement(text)) {
_expressionScopes = _scopes; 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);
bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectBinding *ast) _expressionOffset = calculator.offset() + block.position();
{ _expressionLength = calculator.length();
Node::accept(ast->qualifiedId, this);
Node::accept(ast->qualifiedTypeNameId, this);
_scopes.push(ast);
Node::accept(ast->initializer, this);
return false;
}
void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectBinding *)
{
_scopes.pop();
}
bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectDefinition *ast)
{
Node::accept(ast->qualifiedTypeNameId, this);
_scopes.push(ast);
Node::accept(ast->initializer, this);
return false;
}
void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectDefinition *)
{
_scopes.pop();
}
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;
}
_expressionScopes = _scopes;
break;
}
}
} }
return false;
} }
bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiImport * /*ast*/) ExpressionNode *QmlExpressionUnderCursor::tryExpression(const QString &text)
{ {
return false; _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;
}
Statement *QmlExpressionUnderCursor::tryStatement(const QString &text)
{
_engine = new Engine();
_nodePool = new NodePool("", _engine);
Lexer lexer(_engine);
Parser parser(_engine);
lexer.setCode(text, /*line = */ 1);
if (parser.parseStatement())
return parser.statement();
else
return 0;
}
UiObjectMember *QmlExpressionUnderCursor::tryBinding(const QString &text)
{
_engine = new Engine();
_nodePool = new NodePool("", _engine);
Lexer lexer(_engine);
Parser parser(_engine);
lexer.setCode(text, /*line = */ 1);
if (parser.parseUiObjectMember()) {
UiObjectMember *member = parser.uiObjectMember();
if (cast<UiObjectBinding*>(member) || cast<UiArrayBinding*>(member) || cast<UiScriptBinding*>(member))
return member;
else
return 0;
} else {
return 0;
}
} }

View File

@@ -2,21 +2,30 @@
#define QMLEXPRESSIONUNDERCURSOR_H #define QMLEXPRESSIONUNDERCURSOR_H
#include <QStack> #include <QStack>
#include <QTextBlock>
#include <QTextCursor> #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 QmlEditor {
namespace Internal { namespace Internal {
class QmlExpressionUnderCursor: protected QmlJS::AST::Visitor class QmlExpressionUnderCursor
{ {
public: public:
QmlExpressionUnderCursor(); 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; } { return _expressionScopes; }
QmlJS::AST::Node *expressionNode() const QmlJS::AST::Node *expressionNode() const
@@ -28,26 +37,21 @@ public:
int expressionLength() const int expressionLength() const
{ return _expressionLength; } { return _expressionLength; }
protected: private:
virtual bool visit(QmlJS::AST::Block *ast); void parseExpression(const QTextBlock &block);
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);
virtual void endVisit(QmlJS::AST::Block *); QmlJS::AST::ExpressionNode *tryExpression(const QString &text);
virtual void endVisit(QmlJS::AST::UiObjectBinding *); QmlJS::AST::Statement *tryStatement(const QString &text);
virtual void endVisit(QmlJS::AST::UiObjectDefinition *); QmlJS::AST::UiObjectMember *tryBinding(const QString &text);
private: private:
QStack<QmlJS::AST::Node *> _scopes; QStack<QmlSymbol *> _expressionScopes;
QStack<QmlJS::AST::Node *> _expressionScopes;
QmlJS::AST::Node *_expressionNode; QmlJS::AST::Node *_expressionNode;
int _expressionOffset; int _expressionOffset;
int _expressionLength; int _expressionLength;
quint32 _pos; quint32 _pos;
QmlJS::Engine *_engine;
QmlJS::NodePool *_nodePool;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -10,7 +10,7 @@ using namespace QmlEditor::Internal;
using namespace QmlJS; using namespace QmlJS;
using namespace QmlJS::AST; using namespace QmlJS::AST;
QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, QmlLookupContext::QmlLookupContext(const QStack<QmlSymbol *> &scopes,
const QmlDocument::Ptr &doc, const QmlDocument::Ptr &doc,
const Snapshot &snapshot): const Snapshot &snapshot):
_scopes(scopes), _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) QmlSymbol *QmlLookupContext::resolve(const QString &name)
{ {
// look at property definitions // look at property definitions
if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName())) if (!_scopes.isEmpty())
return propertySymbol; if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.top(), _doc->fileName()))
return propertySymbol;
if (name == "parent") { if (name == "parent") {
for (int i = _scopes.size() - 2; i >= 0; --i) { return resolveParent(_scopes);
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;
} }
// look at the ids. // look at the ids.
@@ -52,13 +73,6 @@ QmlSymbol *QmlLookupContext::resolve(const QString &name)
return 0; 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) QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName)
{ {
// TODO: handle import-as. // TODO: handle import-as.
@@ -88,66 +102,41 @@ QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fil
if (importedTypes.contains(name)) { if (importedTypes.contains(name)) {
QmlDocument::Ptr importedDoc = importedTypes.value(name); QmlDocument::Ptr importedDoc = importedTypes.value(name);
UiProgram *importedProgram = importedDoc->program(); return importedDoc->symbols().at(0);
if (importedProgram && importedProgram->members && importedProgram->members->member)
return createSymbol(importedDoc->fileName(), importedProgram->members->member);
} }
} }
return 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; UiQualifiedId *typeName = 0;
if (UiObjectBinding *binding = cast<UiObjectBinding*>(scope)) { if (scope->isSymbolFromFile()) {
if (QmlSymbol *symbol = resolveProperty(name, binding->initializer, fileName)) Node *ast = scope->asSymbolFromFile()->node();
return symbol;
else if (UiObjectBinding *binding = cast<UiObjectBinding*>(ast)) {
typeName = binding->qualifiedTypeNameId; typeName = binding->qualifiedTypeNameId;
} else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(scope)) { } else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(ast)) {
if (QmlSymbol *symbol = resolveProperty(name, definition->initializer, fileName))
return symbol;
else
typeName = definition->qualifiedTypeNameId; typeName = definition->qualifiedTypeNameId;
} // TODO: extend this to handle (JavaScript) block scopes. } // TODO: extend this to handle (JavaScript) block scopes.
}
if (typeName == 0) if (typeName == 0)
return 0; return 0;
QmlSymbol *typeSymbol = resolveType(toString(typeName), fileName); QmlSymbol *typeSymbol = resolveType(toString(typeName), fileName);
if (typeSymbol && typeSymbol->isSymbolFromFile()) { if (!typeSymbol)
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)
return 0; return 0;
for (UiObjectMemberList *iter = initializer->members; iter; iter = iter->next) { if (typeSymbol->isSymbolFromFile()) {
UiObjectMember *member = iter->member; return resolveProperty(name, typeSymbol->asSymbolFromFile(), typeSymbol->asSymbolFromFile()->fileName());
if (!member) } // TODO: internal types
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);
}
}
return 0; return 0;
} }
@@ -169,10 +158,17 @@ QString QmlLookupContext::toString(UiQualifiedId *id)
return str; return str;
} }
QList<QmlSymbol*> QmlLookupContext::visibleSymbols(QmlJS::AST::Node * /* scope */) QList<QmlSymbol*> QmlLookupContext::visibleSymbolsInScope()
{ {
// FIXME QList<QmlSymbol*> result;
return QList<QmlSymbol*>();
if (!_scopes.isEmpty()) {
QmlSymbol *scope = _scopes.top();
result.append(scope->members());
}
return result;
} }
QList<QmlSymbol*> QmlLookupContext::visibleTypes() QList<QmlSymbol*> QmlLookupContext::visibleTypes()
@@ -196,11 +192,7 @@ QList<QmlSymbol*> QmlLookupContext::visibleTypes()
const QMap<QString, QmlDocument::Ptr> types = _snapshot.componentsDefinedByImportedDocuments(_doc, path); const QMap<QString, QmlDocument::Ptr> types = _snapshot.componentsDefinedByImportedDocuments(_doc, path);
foreach (const QmlDocument::Ptr typeDoc, types) { foreach (const QmlDocument::Ptr typeDoc, types) {
UiProgram *typeProgram = typeDoc->program(); result.append(typeDoc->symbols().at(0));
if (typeProgram && typeProgram->members && typeProgram->members->member) {
result.append(createSymbol(typeDoc->fileName(), typeProgram->members->member));
}
} }
} }

View File

@@ -13,10 +13,9 @@ namespace Internal {
class QmlLookupContext class QmlLookupContext
{ {
public: public:
QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes, QmlLookupContext(const QStack<QmlSymbol *> &scopes,
const QmlDocument::Ptr &doc, const QmlDocument::Ptr &doc,
const Snapshot &snapshot); const Snapshot &snapshot);
~QmlLookupContext();
QmlSymbol *resolve(const QString &name); QmlSymbol *resolve(const QString &name);
QmlSymbol *resolveType(const QString &name) QmlSymbol *resolveType(const QString &name)
@@ -27,23 +26,19 @@ public:
QmlDocument::Ptr document() const QmlDocument::Ptr document() const
{ return _doc; } { return _doc; }
QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *scope); QList<QmlSymbol*> visibleSymbolsInScope();
QList<QmlSymbol*> visibleTypes(); QList<QmlSymbol*> visibleTypes();
private: private:
QmlSymbol *createSymbol(const QString &fileName, QmlJS::AST::UiObjectMember *node);
QmlSymbol *resolveType(const QString &name, const QString &fileName); 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, QmlSymbol *scope, const QString &fileName);
QmlSymbol *resolveProperty(const QString &name, QmlJS::AST::UiObjectInitializer *initializer, const QString &fileName);
static QString toString(QmlJS::AST::UiQualifiedId *id); static QString toString(QmlJS::AST::UiQualifiedId *id);
private: private:
QStack<QmlJS::AST::Node *> _scopes; QStack<QmlSymbol *> _scopes;
QmlDocument::Ptr _doc; QmlDocument::Ptr _doc;
Snapshot _snapshot; Snapshot _snapshot;
QList<QmlSymbol*> _temporarySymbols;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -12,11 +12,6 @@ QmlResolveExpression::QmlResolveExpression(const QmlLookupContext &context)
{ {
} }
QmlResolveExpression::~QmlResolveExpression()
{
qDeleteAll(_temporarySymbols);
}
QmlSymbol *QmlResolveExpression::typeOf(Node *node) QmlSymbol *QmlResolveExpression::typeOf(Node *node)
{ {
QmlSymbol *previousValue = switchValue(0); QmlSymbol *previousValue = switchValue(0);
@@ -25,6 +20,30 @@ QmlSymbol *QmlResolveExpression::typeOf(Node *node)
return switchValue(previousValue); 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) QmlSymbol *QmlResolveExpression::switchValue(QmlSymbol *value)
{ {
@@ -58,52 +77,19 @@ bool QmlResolveExpression::visit(FieldMemberExpression *ast)
{ {
const QString memberName = ast->name->asString(); const QString memberName = ast->name->asString();
const QmlSymbol *base = typeOf(ast->base); QmlSymbol *base = typeOf(ast->base);
if (!base) if (!base)
return false; return false;
if (base->isIdSymbol()) if (base->isIdSymbol())
base = base->asIdSymbol()->parentNode(); base = base->asIdSymbol()->parentNode();
UiObjectMemberList *members = 0; if (!base)
return false;
if (const QmlSymbolFromFile *symbol = base->asSymbolFromFile()) { foreach (QmlSymbol *memberSymbol, base->members())
Node *node = symbol->node(); if (memberSymbol->name() == memberName)
_value = memberSymbol;
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;
}
}
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
}
}
}
return false; return false;
} }
@@ -114,17 +100,3 @@ bool QmlResolveExpression::visit(QmlJS::AST::UiQualifiedId *ast)
return false; 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;
}

View File

@@ -12,28 +12,21 @@ class QmlResolveExpression: protected QmlJS::AST::Visitor
{ {
public: public:
QmlResolveExpression(const QmlLookupContext &context); QmlResolveExpression(const QmlLookupContext &context);
~QmlResolveExpression();
QmlSymbol *operator()(QmlJS::AST::Node *node) QmlSymbol *typeOf(QmlJS::AST::Node *node);
{ return typeOf(node); } QList<QmlSymbol*> visibleSymbols(QmlJS::AST::Node *node);
protected: protected:
using QmlJS::AST::Visitor::visit; using QmlJS::AST::Visitor::visit;
QmlSymbol *typeOf(QmlJS::AST::Node *node);
QmlSymbol *switchValue(QmlSymbol *symbol); QmlSymbol *switchValue(QmlSymbol *symbol);
virtual bool visit(QmlJS::AST::FieldMemberExpression *ast); virtual bool visit(QmlJS::AST::FieldMemberExpression *ast);
virtual bool visit(QmlJS::AST::IdentifierExpression *ast); virtual bool visit(QmlJS::AST::IdentifierExpression *ast);
virtual bool visit(QmlJS::AST::UiQualifiedId *ast); virtual bool visit(QmlJS::AST::UiQualifiedId *ast);
private:
QmlPropertyDefinitionSymbol *createPropertyDefinitionSymbol(QmlJS::AST::UiPublicMember *ast);
QmlSymbolFromFile *createSymbolFromFile(QmlJS::AST::UiObjectMember *ast);
private: private:
QmlLookupContext _context; QmlLookupContext _context;
QList<QmlSymbol*> _temporarySymbols;
QmlSymbol *_value; QmlSymbol *_value;
}; };

View File

@@ -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)
{
}

View File

@@ -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

View File

@@ -1,4 +1,5 @@
#include "qmljsast_p.h" #include "qmljsast_p.h"
#include "qmljsengine_p.h"
#include "qmlsymbol.h" #include "qmlsymbol.h"
using namespace QmlEditor; using namespace QmlEditor;
@@ -7,41 +8,67 @@ using namespace QmlJS::AST;
QmlSymbol::~QmlSymbol() QmlSymbol::~QmlSymbol()
{ {
qDeleteAll(_members);
} }
bool QmlSymbol::isBuildInSymbol() const bool QmlSymbol::isBuildInSymbol()
{ return asBuildInSymbol() != 0; } { return asBuildInSymbol() != 0; }
bool QmlSymbol::isSymbolFromFile() const bool QmlSymbol::isSymbolFromFile()
{ return asSymbolFromFile() != 0; } { return asSymbolFromFile() != 0; }
bool QmlSymbol::isIdSymbol() const bool QmlSymbol::isIdSymbol()
{ return asIdSymbol() != 0; } { return asIdSymbol() != 0; }
QmlBuildInSymbol const *QmlSymbol::asBuildInSymbol() const bool QmlSymbol::isPropertyDefinitionSymbol()
{ return asPropertyDefinitionSymbol() != 0; }
QmlBuildInSymbol *QmlSymbol::asBuildInSymbol()
{ return 0; } { return 0; }
QmlSymbolFromFile const *QmlSymbol::asSymbolFromFile() const QmlSymbolFromFile *QmlSymbol::asSymbolFromFile()
{ return 0; } { return 0; }
QmlIdSymbol const *QmlSymbol::asIdSymbol() const QmlIdSymbol *QmlSymbol::asIdSymbol()
{ return 0; } { return 0; }
QmlPropertyDefinitionSymbol *QmlSymbol::asPropertyDefinitionSymbol()
{ return 0; }
const QmlSymbol::List QmlSymbol::members()
{ return _members; }
QmlBuildInSymbol::~QmlBuildInSymbol() QmlBuildInSymbol::~QmlBuildInSymbol()
{} {}
QmlBuildInSymbol const* QmlBuildInSymbol::asBuildInSymbol() const QmlBuildInSymbol *QmlBuildInSymbol::asBuildInSymbol()
{ return this; } { return this; }
QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node): QmlSymbolFromFile::QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node):
_fileName(fileName), _fileName(fileName),
_node(node) _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() QmlSymbolFromFile::~QmlSymbolFromFile()
{} {}
const QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile() const QmlSymbolFromFile *QmlSymbolFromFile::asSymbolFromFile()
{ return this; } { return this; }
int QmlSymbolFromFile::line() const int QmlSymbolFromFile::line() const
@@ -50,7 +77,76 @@ int QmlSymbolFromFile::line() const
int QmlSymbolFromFile::column() const int QmlSymbolFromFile::column() const
{ return _node->firstSourceLocation().startColumn; } { 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), QmlSymbolFromFile(fileName, idNode),
_parentNode(parentNode) _parentNode(parentNode)
{} {}
@@ -58,7 +154,7 @@ QmlIdSymbol::QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *i
QmlIdSymbol::~QmlIdSymbol() QmlIdSymbol::~QmlIdSymbol()
{} {}
QmlIdSymbol const *QmlIdSymbol::asIdSymbol() const QmlIdSymbol *QmlIdSymbol::asIdSymbol()
{ return this; } { return this; }
int QmlIdSymbol::line() const int QmlIdSymbol::line() const
@@ -67,6 +163,16 @@ int QmlIdSymbol::line() const
int QmlIdSymbol::column() const int QmlIdSymbol::column() const
{ return idNode()->statement->firstSourceLocation().startColumn; } { 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 QmlJS::AST::UiScriptBinding *QmlIdSymbol::idNode() const
{ return cast<UiScriptBinding*>(node()); } { return cast<UiScriptBinding*>(node()); }
@@ -77,6 +183,9 @@ QmlPropertyDefinitionSymbol::QmlPropertyDefinitionSymbol(const QString &fileName
QmlPropertyDefinitionSymbol::~QmlPropertyDefinitionSymbol() QmlPropertyDefinitionSymbol::~QmlPropertyDefinitionSymbol()
{} {}
QmlPropertyDefinitionSymbol *QmlPropertyDefinitionSymbol::asPropertyDefinitionSymbol()
{ return this; }
int QmlPropertyDefinitionSymbol::line() const int QmlPropertyDefinitionSymbol::line() const
{ return propertyNode()->identifierToken.startLine; } { return propertyNode()->identifierToken.startLine; }
@@ -85,3 +194,6 @@ int QmlPropertyDefinitionSymbol::column() const
QmlJS::AST::UiPublicMember *QmlPropertyDefinitionSymbol::propertyNode() const QmlJS::AST::UiPublicMember *QmlPropertyDefinitionSymbol::propertyNode() const
{ return cast<UiPublicMember*>(node()); } { return cast<UiPublicMember*>(node()); }
const QString QmlPropertyDefinitionSymbol::name() const
{ return propertyNode()->name->asString(); }

View File

@@ -1,6 +1,7 @@
#ifndef QMLSYMBOL_H #ifndef QMLSYMBOL_H
#define QMLSYMBOL_H #define QMLSYMBOL_H
#include <QList>
#include <QString> #include <QString>
#include "qmljsastfwd_p.h" #include "qmljsastfwd_p.h"
@@ -10,25 +11,44 @@ namespace QmlEditor {
class QmlSymbol class QmlSymbol
{ {
public: public:
virtual ~QmlSymbol() = 0; typedef QList<QmlSymbol*> List;
bool isBuildInSymbol() const; public:
bool isSymbolFromFile() const; virtual ~QmlSymbol();
bool isIdSymbol() const;
virtual class QmlBuildInSymbol const *asBuildInSymbol() const; virtual const QString name() const = 0;
virtual class QmlSymbolFromFile const *asSymbolFromFile() const; virtual const List members();
virtual class QmlIdSymbol const *asIdSymbol() const;
bool isBuildInSymbol();
bool isSymbolFromFile();
bool isIdSymbol();
bool isPropertyDefinitionSymbol();
virtual class QmlBuildInSymbol *asBuildInSymbol();
virtual class QmlSymbolFromFile *asSymbolFromFile();
virtual class QmlIdSymbol *asIdSymbol();
virtual class QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol();
protected:
List _members;
}; };
class QmlBuildInSymbol: public QmlSymbol class QmlBuildInSymbol: public QmlSymbol
{ {
public: public:
QmlBuildInSymbol(const QString &name): _name(name) {}
virtual ~QmlBuildInSymbol(); virtual ~QmlBuildInSymbol();
virtual QmlBuildInSymbol const *asBuildInSymbol() const; virtual QmlBuildInSymbol *asBuildInSymbol();
virtual const QString name() const
{ return _name; }
// TODO:
// virtual const List members();
private: private:
QString _name;
}; };
class QmlSymbolFromFile: public QmlSymbol class QmlSymbolFromFile: public QmlSymbol
@@ -37,7 +57,7 @@ public:
QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node); QmlSymbolFromFile(const QString &fileName, QmlJS::AST::UiObjectMember *node);
virtual ~QmlSymbolFromFile(); virtual ~QmlSymbolFromFile();
virtual QmlSymbolFromFile const *asSymbolFromFile() const; virtual QmlSymbolFromFile *asSymbolFromFile();
QString fileName() const QString fileName() const
{ return _fileName; } { return _fileName; }
@@ -48,30 +68,43 @@ public:
QmlJS::AST::UiObjectMember *node() const QmlJS::AST::UiObjectMember *node() const
{ return _node; } { 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: private:
QString _fileName; QString _fileName;
QmlJS::AST::UiObjectMember *_node; QmlJS::AST::UiObjectMember *_node;
QList<QmlJS::AST::Node*> todo;
}; };
class QmlIdSymbol: public QmlSymbolFromFile class QmlIdSymbol: public QmlSymbolFromFile
{ {
public: public:
QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, const QmlSymbolFromFile &parentNode); QmlIdSymbol(const QString &fileName, QmlJS::AST::UiScriptBinding *idNode, QmlSymbolFromFile *parentNode);
virtual ~QmlIdSymbol(); virtual ~QmlIdSymbol();
QmlIdSymbol const *asIdSymbol() const; QmlIdSymbol *asIdSymbol();
virtual int line() const; virtual int line() const;
virtual int column() const; virtual int column() const;
QmlSymbolFromFile const *parentNode() const QmlSymbolFromFile *parentNode() const
{ return &_parentNode; } { return _parentNode; }
virtual const QString name() const
{ return "id"; }
virtual const QString id() const;
private: private:
QmlJS::AST::UiScriptBinding *idNode() const; QmlJS::AST::UiScriptBinding *idNode() const;
private: private:
QmlSymbolFromFile _parentNode; QmlSymbolFromFile *_parentNode;
}; };
class QmlPropertyDefinitionSymbol: public QmlSymbolFromFile class QmlPropertyDefinitionSymbol: public QmlSymbolFromFile
@@ -80,9 +113,13 @@ public:
QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode); QmlPropertyDefinitionSymbol(const QString &fileName, QmlJS::AST::UiPublicMember *propertyNode);
virtual ~QmlPropertyDefinitionSymbol(); virtual ~QmlPropertyDefinitionSymbol();
QmlPropertyDefinitionSymbol *asPropertyDefinitionSymbol();
virtual int line() const; virtual int line() const;
virtual int column() const; virtual int column() const;
virtual const QString name() const;
private: private:
QmlJS::AST::UiPublicMember *propertyNode() const; QmlJS::AST::UiPublicMember *propertyNode() const;
}; };