Commit ea6623ea authored by Erik Verbruggen's avatar Erik Verbruggen
Browse files

Switched over to using the new LookupContext for navigation and symbol

resolving.
parent e9bf505c
......@@ -60,6 +60,8 @@ DuiDocument::~DuiDocument()
if (_pool)
delete _pool;
qDeleteAll(_ids.values());
}
DuiDocument::Ptr DuiDocument::create(const QString &fileName)
......@@ -109,7 +111,7 @@ bool DuiDocument::parse()
if (_parsedCorrectly && _program) {
Internal::IdCollector collect;
_ids = collect(_program);
_ids = collect(_fileName, _program);
}
return _parsedCorrectly;
......
......@@ -36,9 +36,9 @@
#include <QtCore/QString>
#include "duieditor_global.h"
#include "qmljsengine_p.h"
#include "qmljsastfwd_p.h"
#include "qmlsymbol.h"
namespace DuiEditor {
......@@ -47,7 +47,7 @@ class DUIEDITOR_EXPORT DuiDocument
public:
typedef QSharedPointer<DuiDocument> Ptr;
typedef QList<DuiDocument::Ptr> PtrList;
typedef QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > IdTable;
typedef QMap<QString, QmlIdSymbol*> IdTable;
protected:
DuiDocument(const QString &fileName);
......
......@@ -39,11 +39,10 @@
#include "qmljsengine_p.h"
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "resolveqmlexpression.h"
#include "qmlresolveexpression.h"
#include "rewriter_p.h"
#include "idcollector.h"
#include "navigationtokenfinder.h"
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
......@@ -57,7 +56,6 @@
#include <utils/uncommentselection.h>
#include <QtCore/QTimer>
#include <QtCore/QtDebug>
#include <QtGui/QMenu>
#include <QtGui/QComboBox>
......@@ -704,7 +702,7 @@ void ScriptEditor::createToolBar(ScriptEditorEditable *editable)
toolBar->insertWidget(actions.first(), m_methodCombo);
}
TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cursor, bool resolveTarget)
TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/)
{
Link link;
......@@ -716,34 +714,25 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur
if (!doc)
return link;
NavigationTokenFinder finder;
finder(doc, cursor.position(), snapshot);
if (finder.targetFound()) {
link.fileName = finder.fileName();
link.pos = finder.linkPosition();
link.length = finder.linkLength();
QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(cursor, doc->program());
if (resolveTarget) {
link.line = finder.targetLine();
link.column = finder.targetColumn() - 1;
}
}
QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot);
QmlResolveExpression resolve(context);
QmlSymbol *symbol = resolve(expressionUnderCursor.expressionNode());
qDebug() << "*** Searching for node" << expressionUnderCursor.expressionNode() << "returned symbol" << symbol;
// QmlExpressionUnderCursor expressionUnderCursor;
// expressionUnderCursor(cursor, doc->program());
//
// QmlLookupContext context(expressionUnderCursor.expressionScopes(),
// expressionUnderCursor.expressionNode(),
// doc, snapshot);
//
// ResolveQmlExpression resolve(context);
// if (QmlLookupContext::Symbol *symbol = resolve(expressionUnderCursor.expressionNode())) {
// if (UiObjectMember *member = static_cast<UiObjectMember *>(symbol)) { // ### FIXME: don't use static_cast<>
// const int begin = member->firstSourceLocation().begin();
// const int end = member->lastSourceLocation().end();
// qDebug() << doc->source().mid(begin, end - begin);
// }
// }
if (!symbol)
return link;
if (const QmlSymbolFromFile *target = symbol->asSymbolFromFile()) {
link.pos = expressionUnderCursor.expressionOffset();
link.length = expressionUnderCursor.expressionLength();
link.fileName = target->fileName();
link.line = target->line();
link.column = target->column();
}
return link;
}
......
......@@ -22,11 +22,11 @@ HEADERS += duieditor.h \
duieditor_global.h \
duimodelmanager.h \
duicodeformatter.h \
navigationtokenfinder.h \
idcollector.h \
qmlexpressionundercursor.h \
qmllookupcontext.h \
resolveqmlexpression.h
qmlresolveexpression.h \
qmlsymbol.h
SOURCES += duieditor.cpp \
duieditorfactory.cpp \
duieditorplugin.cpp \
......@@ -39,9 +39,9 @@ SOURCES += duieditor.cpp \
duimodelmanagerinterface.cpp \
duimodelmanager.cpp \
duicodeformatter.cpp \
navigationtokenfinder.cpp \
idcollector.cpp \
qmlexpressionundercursor.cpp \
qmllookupcontext.cpp \
resolveqmlexpression.cpp
qmlresolveexpression.cpp \
qmlsymbol.cpp
RESOURCES += duieditor.qrc
......@@ -4,10 +4,12 @@
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace DuiEditor;
using namespace DuiEditor::Internal;
QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > IdCollector::operator()(QmlJS::AST::UiProgram *ast)
QMap<QString, QmlIdSymbol*> IdCollector::operator()(const QString &fileName, QmlJS::AST::UiProgram *ast)
{
_fileName = fileName;
_ids.clear();
Node::accept(ast, this);
......@@ -42,15 +44,21 @@ 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, i->identifierToken);
addId(i->name->asString(), ast);
return false;
}
void IdCollector::addId(QmlJS::NameId* id, const QmlJS::AST::SourceLocation &idLocation)
void IdCollector::addId(const QString &id, QmlJS::AST::UiScriptBinding *ast)
{
const QString idString = id->asString();
if (!_ids.contains(id)) {
Node *parent = _scopes.top();
if (!_ids.contains(idString))
_ids[idString] = qMakePair(idLocation, _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");
}
}
......@@ -7,8 +7,7 @@
#include <QString>
#include "qmljsastvisitor_p.h"
namespace QmlJS { class NameId; }
#include "qmlsymbol.h"
namespace DuiEditor {
namespace Internal {
......@@ -16,7 +15,7 @@ namespace Internal {
class IdCollector: protected QmlJS::AST::Visitor
{
public:
QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > operator()(QmlJS::AST::UiProgram *ast);
QMap<QString, QmlIdSymbol*> operator()(const QString &fileName, QmlJS::AST::UiProgram *ast);
protected:
virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
......@@ -27,10 +26,11 @@ protected:
virtual void endVisit(QmlJS::AST::UiObjectDefinition *);
private:
void addId(QmlJS::NameId* id, const QmlJS::AST::SourceLocation &idLocation);
void addId(const QString &id, QmlJS::AST::UiScriptBinding *ast);
private:
QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > _ids;
QString _fileName;
QMap<QString, QmlIdSymbol*> _ids;
QStack<QmlJS::AST::Node *> _scopes;
};
......
#ifndef NAVIGATIONTOKENFINDER_H
#define NAVIGATIONTOKENFINDER_H
#include <QMap>
#include <QStack>
#include <QString>
#include <QStringList>
#include "duidocument.h"
#include "qmljsastvisitor_p.h"
namespace QmlJS {
class NameId;
} // namespace QmlJS
namespace DuiEditor {
namespace Internal {
class NavigationTokenFinder: protected QmlJS::AST::Visitor
{
public:
void operator()(const DuiDocument::Ptr &doc, int position, const Snapshot &snapshot);
bool targetFound() const { return _targetLine != -1; }
bool linkFound() const { return _linkPosition != -1; }
int linkPosition() const { return _linkPosition; }
int linkLength() const { return _linkLength; }
QString fileName() const { return _fileName; }
int targetLine() const { return _targetLine; }
int targetColumn() const { return _targetColumn; }
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::UiArrayBinding *ast);
virtual bool visit(QmlJS::AST::UiImportList *);
virtual bool visit(QmlJS::AST::UiPublicMember *ast);
virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
virtual bool visit(QmlJS::AST::UiObjectDefinition *ast);
virtual bool visit(QmlJS::AST::UiScriptBinding *ast);
virtual bool visit(QmlJS::AST::UiSourceElement *ast);
virtual void endVisit(QmlJS::AST::Block *);
virtual void endVisit(QmlJS::AST::UiObjectBinding *);
virtual void endVisit(QmlJS::AST::UiObjectDefinition *);
private:
void checkType(QmlJS::AST::UiQualifiedId *ast);
bool findInJS(const QStringList &qualifiedId, QmlJS::AST::Block *block);
bool findProperty(const QStringList &qualifiedId, QmlJS::AST::UiQualifiedId *typeId, QmlJS::AST::UiObjectMemberList *ast, int scopeLevel);
void findAsId(const QStringList &qualifiedId);
void findDeclaration(const QStringList &qualifiedId, int scopeLevel);
void findDeclaration(const QStringList &id);
void findTypeDeclaration(const QStringList &id);
void rememberLocation(const QmlJS::AST::SourceLocation &loc);
DuiDocument::Ptr findCustomType(const QStringList& qualifiedId) const;
private:
quint32 _pos;
DuiDocument::Ptr _doc;
Snapshot _snapshot;
int _linkPosition;
int _linkLength;
QString _fileName;
int _targetLine;
int _targetColumn;
QStack<QmlJS::AST::Node*> _scopes;
};
} // namespace Internal
} // namespace DuiEditor
#endif // NAVIGATIONTOKENFINDER_H
......@@ -19,6 +19,8 @@ void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor,
{
_pos = cursor.position();
_expressionNode = 0;
_expressionOffset = -1;
_expressionLength = -1;
_scopes.clear();
if (program)
......@@ -37,6 +39,30 @@ 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;
}
return true;
}
bool QmlExpressionUnderCursor::visit(QmlJS::AST::IdentifierExpression *ast)
{
if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) {
_expressionNode = ast;
_expressionOffset = ast->firstSourceLocation().offset;
_expressionLength = ast->lastSourceLocation().end() - _expressionOffset;
_expressionScopes = _scopes;
}
return false;
}
bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiObjectBinding *ast)
{
_scopes.push(ast);
......@@ -60,13 +86,3 @@ void QmlExpressionUnderCursor::endVisit(QmlJS::AST::UiObjectDefinition *)
{
_scopes.pop();
}
bool QmlExpressionUnderCursor::visit(QmlJS::AST::UiScriptBinding *ast)
{
if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) {
_expressionNode = ast;
_expressionScopes = _scopes;
}
return false;
}
......@@ -22,11 +22,18 @@ public:
QmlJS::AST::Node *expressionNode() const
{ return _expressionNode; }
int expressionOffset() const
{ return _expressionOffset; }
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::UiObjectBinding *ast);
virtual bool visit(QmlJS::AST::UiObjectDefinition *ast);
virtual bool visit(QmlJS::AST::UiScriptBinding *ast);
virtual void endVisit(QmlJS::AST::Block *);
virtual void endVisit(QmlJS::AST::UiObjectBinding *);
......@@ -36,6 +43,8 @@ private:
QStack<QmlJS::AST::Node *> _scopes;
QStack<QmlJS::AST::Node *> _expressionScopes;
QmlJS::AST::Node *_expressionNode;
int _expressionOffset;
int _expressionLength;
quint32 _pos;
};
......
......@@ -3,7 +3,7 @@
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "resolveqmlexpression.h"
#include "qmlresolveexpression.h"
using namespace DuiEditor;
using namespace DuiEditor::Internal;
......@@ -11,28 +11,23 @@ using namespace QmlJS;
using namespace QmlJS::AST;
QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
QmlJS::AST::Node *expressionNode,
const DuiDocument::Ptr &doc,
const Snapshot &snapshot):
_scopes(scopes),
_expressionNode(expressionNode),
_doc(doc),
_snapshot(snapshot)
{
}
QmlLookupContext::Symbol *QmlLookupContext::resolve(const QString &name) const
QmlSymbol *QmlLookupContext::resolve(const QString &name) const
{
// ### TODO: look at property definitions
// look at the ids.
foreach (DuiDocument::Ptr doc, _snapshot) {
const DuiDocument::IdTable ids = doc->ids();
const QPair<SourceLocation, Node *> use = ids.value(name);
const DuiDocument::IdTable ids = _doc->ids();
if (Node *node = use.second)
return node;
}
return 0;
if (ids.contains(name))
return ids[name];
else
return 0;
}
......@@ -5,6 +5,7 @@
#include "duidocument.h"
#include "qmljsastvisitor_p.h"
#include "qmlsymbol.h"
namespace DuiEditor {
namespace Internal {
......@@ -13,17 +14,16 @@ class QmlLookupContext
{
public:
QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
QmlJS::AST::Node *expressionNode,
const DuiDocument::Ptr &doc,
const Snapshot &snapshot);
typedef QmlJS::AST::Node Symbol; // ### FIXME: this needs to be a class.
QmlSymbol *resolve(const QString &name) const;
Symbol *resolve(const QString &name) const;
DuiDocument::Ptr document() const
{ return _doc; }
private:
QStack<QmlJS::AST::Node *> _scopes;
QmlJS::AST::Node *_expressionNode;
DuiDocument::Ptr _doc;
Snapshot _snapshot;
};
......
#include "qmljsast_p.h"
#include "qmljsengine_p.h"
#include "qmlresolveexpression.h"
using namespace DuiEditor;
using namespace DuiEditor::Internal;
using namespace QmlJS;
using namespace QmlJS::AST;
QmlResolveExpression::QmlResolveExpression(const QmlLookupContext &context)
: _context(context), _value(0)
{
}
QmlResolveExpression::~QmlResolveExpression()
{
qDeleteAll(_temporarySymbols);
}
QmlSymbol *QmlResolveExpression::typeOf(Node *node)
{
QmlSymbol *previousValue = switchValue(0);
if (node)
node->accept(this);
return switchValue(previousValue);
}
QmlSymbol *QmlResolveExpression::switchValue(QmlSymbol *value)
{
QmlSymbol *previousValue = _value;
_value = value;
return previousValue;
}
bool QmlResolveExpression::visit(IdentifierExpression *ast)
{
const QString name = ast->name->asString();
_value = _context.resolve(name);
return false;
}
static inline bool matches(UiQualifiedId *candidate, const QString &wanted)
{
if (!candidate)
return false;
if (!(candidate->name))
return false;
if (candidate->next)
return false; // TODO: verify this!
return wanted == candidate->name->asString();
}
bool QmlResolveExpression::visit(FieldMemberExpression *ast)
{
const QString memberName = ast->name->asString();
if (QmlSymbol *base = typeOf(ast->base)) {
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 = binding->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;
}
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;
}
#ifndef RESOLVEQMLEXPRESSION_H
#define RESOLVEQMLEXPRESSION_H
#ifndef QMLRESOLVEEXPRESSION_H
#define QMLRESOLVEEXPRESSION_H
#include "qmljsastvisitor_p.h"
#include "qmllookupcontext.h"
#include "qmlsymbol.h"
namespace DuiEditor {
namespace Internal {
class ResolveQmlExpression: protected QmlJS::AST::Visitor
class QmlResolveExpression: protected QmlJS::AST::Visitor
{
public:
ResolveQmlExpression(const QmlLookupContext &context);
QmlResolveExpression(const QmlLookupContext &context);
~QmlResolveExpression();
QmlLookupContext::Symbol *operator()(QmlJS::AST::Node *node)
QmlSymbol *operator()(QmlJS::AST::Node *node)