forked from qt-creator/qt-creator
QMLJS::Delta: Improved the delta
Use a smarter way to compare the AST (based on the diffX algorithm) That way we do not rely anymore on the id property
This commit is contained in:
@@ -39,55 +39,172 @@ using namespace QmlJS;
|
||||
using namespace QmlJS::AST;
|
||||
using namespace QmlJSInspector::Internal;
|
||||
|
||||
UiObjectMember *ScriptBindingParser::parent(UiScriptBinding *script) const
|
||||
{ return _parent.value(script); }
|
||||
|
||||
UiScriptBinding *ScriptBindingParser::id(UiObjectMember *parent) const
|
||||
{ return _id.value(parent); }
|
||||
|
||||
QList<UiScriptBinding *> ScriptBindingParser::ids() const
|
||||
{ return _id.values(); }
|
||||
|
||||
QString ScriptBindingParser::header(UiObjectMember *member) const
|
||||
/*!
|
||||
Build a hash of the parents
|
||||
*/
|
||||
struct BuildParentHash : public Visitor
|
||||
{
|
||||
if (member) {
|
||||
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
|
||||
const int begin = def->firstSourceLocation().begin();
|
||||
const int end = def->initializer->lbraceToken.begin();
|
||||
return doc->source().mid(begin, end - begin);
|
||||
} else if (UiObjectBinding *binding = cast<UiObjectBinding *>(member)) {
|
||||
const int begin = binding->firstSourceLocation().begin();
|
||||
const int end = binding->initializer->lbraceToken.begin();
|
||||
return doc->source().mid(begin, end - begin);
|
||||
}
|
||||
}
|
||||
virtual void postVisit(Node* );
|
||||
virtual bool preVisit(Node* );
|
||||
QHash<UiObjectMember *, UiObjectMember *> parent;
|
||||
private:
|
||||
QList<UiObjectMember *> stack;
|
||||
};
|
||||
|
||||
return QString();
|
||||
bool BuildParentHash::preVisit(Node* ast)
|
||||
{
|
||||
if (ast->uiObjectMemberCast()) {
|
||||
stack.append(ast->uiObjectMemberCast());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ScriptBindingParser::scriptCode(UiScriptBinding *script) const
|
||||
|
||||
void BuildParentHash::postVisit(Node* ast)
|
||||
{
|
||||
if (ast->uiObjectMemberCast()) {
|
||||
stack.removeLast();
|
||||
if (!stack.isEmpty()) {
|
||||
parent.insert(ast->uiObjectMemberCast(), stack.last());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString label(UiObjectMember *member, Document::Ptr doc)
|
||||
{
|
||||
QString str;
|
||||
if(!member)
|
||||
return str;
|
||||
|
||||
if (UiObjectDefinition* foo = cast<UiObjectDefinition *>(member)) {
|
||||
quint32 start = foo->firstSourceLocation().begin();
|
||||
quint32 end = foo->initializer->lbraceToken.begin();
|
||||
str = doc->source().mid(start, end-start);
|
||||
} else if(UiObjectBinding *foo = cast<UiObjectBinding *>(member)) {
|
||||
quint32 start = foo->firstSourceLocation().begin();
|
||||
quint32 end = foo->initializer->lbraceToken.begin();
|
||||
str = doc->source().mid(start, end-start);
|
||||
} else if(cast<UiArrayBinding *>(member)) {
|
||||
//TODO
|
||||
} else {
|
||||
quint32 start = member->firstSourceLocation().begin();
|
||||
quint32 end = member->lastSourceLocation().end();
|
||||
str = doc->source().mid(start, end-start);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
struct FindObjectMemberWithLabel : public Visitor
|
||||
{
|
||||
virtual void endVisit(UiObjectDefinition *ast) ;
|
||||
virtual void endVisit(UiObjectBinding *ast) ;
|
||||
|
||||
QList<UiObjectMember *> found;
|
||||
QString cmp;
|
||||
Document::Ptr doc;
|
||||
};
|
||||
|
||||
void FindObjectMemberWithLabel::endVisit(UiObjectDefinition* ast)
|
||||
{
|
||||
if (label(ast, doc) == cmp)
|
||||
found.append(ast);
|
||||
}
|
||||
void FindObjectMemberWithLabel::endVisit(UiObjectBinding* ast)
|
||||
{
|
||||
if (label(ast, doc) == cmp)
|
||||
found.append(ast);
|
||||
}
|
||||
|
||||
struct Map {
|
||||
typedef UiObjectMember*T;
|
||||
QHash<T, T> way1;
|
||||
QHash<T, T> way2;
|
||||
void insert(T t1, T t2) {
|
||||
way1.insert(t1,t2);
|
||||
way2.insert(t2,t1);
|
||||
}
|
||||
int count() { return way1.count(); }
|
||||
void operator+=(const Map &other) {
|
||||
way1.unite(other.way1);
|
||||
way2.unite(other.way2);
|
||||
}
|
||||
bool contains(T t1, T t2) {
|
||||
return way1.value(t1) == t2;
|
||||
}
|
||||
};
|
||||
|
||||
QList<UiObjectMember *> children(UiObjectMember *ast)
|
||||
{
|
||||
QList<UiObjectMember *> ret;
|
||||
if (UiObjectDefinition* foo = cast<UiObjectDefinition *>(ast)) {
|
||||
UiObjectMemberList* list = foo->initializer->members;
|
||||
while (list) {
|
||||
ret.append(list->member);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Map MatchFragment(UiObjectMember *x, UiObjectMember *y, const Map &M, Document::Ptr doc1, Document::Ptr doc2) {
|
||||
Map M2;
|
||||
if (M.way1.contains(x))
|
||||
return M2;
|
||||
if (M.way2.contains(y))
|
||||
return M2;
|
||||
if(label(x, doc1) != label(y, doc2))
|
||||
return M2;
|
||||
M2.insert(x, y);
|
||||
const QList<UiObjectMember *> list1 = children(x);
|
||||
const QList<UiObjectMember *> list2 = children(y);
|
||||
for (int i = 0; i < qMin(list1.count(), list2.count()); i++) {
|
||||
M2 += MatchFragment(list1[i], list2[i], M, doc1, doc2);
|
||||
}
|
||||
return M2;
|
||||
}
|
||||
|
||||
Map Mapping(Document::Ptr doc1, Document::Ptr doc2)
|
||||
{
|
||||
Map M;
|
||||
QList<UiObjectMember *> todo;
|
||||
todo.append(doc1->qmlProgram()->members->member);
|
||||
while(!todo.isEmpty()) {
|
||||
UiObjectMember *x = todo.takeFirst();
|
||||
todo += children(x);
|
||||
|
||||
if (M.way1.contains(x))
|
||||
continue;
|
||||
|
||||
//If this is too slow, we could use some sort of indexing
|
||||
FindObjectMemberWithLabel v3;
|
||||
v3.cmp = label(x, doc1);
|
||||
v3.doc = doc2;
|
||||
doc2->qmlProgram()->accept(&v3);
|
||||
Map M2;
|
||||
foreach (UiObjectMember *y, v3.found) {
|
||||
if (M.way2.contains(y))
|
||||
continue;
|
||||
Map M3 = MatchFragment(x, y, M, doc1, doc2);
|
||||
if (M3.count() > M2.count())
|
||||
M2 = M3;
|
||||
}
|
||||
M += M2;
|
||||
}
|
||||
return M;
|
||||
}
|
||||
|
||||
|
||||
static QString _scriptCode(UiScriptBinding *script, Document::Ptr doc)
|
||||
{
|
||||
if (script) {
|
||||
const int begin = script->statement->firstSourceLocation().begin();
|
||||
const int end = script->statement->lastSourceLocation().end();
|
||||
return doc->source().mid(begin, end - begin);
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
QString ScriptBindingParser::methodName(UiSourceElement *source) const
|
||||
{
|
||||
if (source) {
|
||||
if (FunctionDeclaration *declaration = cast<FunctionDeclaration*>(source->sourceElement)) {
|
||||
return declaration->name->asString();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString ScriptBindingParser::methodCode(UiSourceElement *source) const
|
||||
static QString _methodCode(UiSourceElement *source, Document::Ptr doc)
|
||||
{
|
||||
if (source) {
|
||||
if (FunctionDeclaration *declaration = cast<FunctionDeclaration*>(source->sourceElement)) {
|
||||
@@ -99,6 +216,132 @@ QString ScriptBindingParser::methodCode(UiSourceElement *source) const
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
static QString _propertyName(UiQualifiedId *id)
|
||||
{
|
||||
QString s;
|
||||
|
||||
for (; id; id = id->next) {
|
||||
if (! id->name)
|
||||
return QString();
|
||||
|
||||
s += id->name->asString();
|
||||
|
||||
if (id->next)
|
||||
s += QLatin1Char('.');
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static QString _methodName(UiSourceElement *source)
|
||||
{
|
||||
if (source) {
|
||||
if (FunctionDeclaration *declaration = cast<FunctionDeclaration*>(source->sourceElement)) {
|
||||
return declaration->name->asString();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Delta::DebugIdMap Delta::operator()(Document::Ptr doc1, Document::Ptr doc2, const DebugIdMap &debugIds)
|
||||
{
|
||||
QHash< UiObjectMember*, QList<QDeclarativeDebugObjectReference > > newDebuggIds;
|
||||
|
||||
Map M = Mapping(doc1, doc2);
|
||||
|
||||
BuildParentHash parents2;
|
||||
doc2->qmlProgram()->accept(&parents2);
|
||||
BuildParentHash parents1;
|
||||
doc1->qmlProgram()->accept(&parents1);
|
||||
|
||||
QList<UiObjectMember *> todo;
|
||||
todo.append(doc2->qmlProgram()->members->member);
|
||||
//UiObjectMemberList *list = 0;
|
||||
while(!todo.isEmpty()) {
|
||||
UiObjectMember *y = todo.takeFirst();
|
||||
todo += children(y);
|
||||
if (!M.way2.contains(y)) {
|
||||
qDebug () << "insert " << label(y, doc2) << " to " << label(parents2.parent.value(y), doc2);
|
||||
continue;
|
||||
}
|
||||
UiObjectMember *x = M.way2[y];
|
||||
|
||||
|
||||
//--8<---------------------------------------------------------------------------------------
|
||||
if (debugIds.contains(x)) {
|
||||
newDebuggIds[y] = debugIds[x];
|
||||
|
||||
UiObjectMember *object = y;
|
||||
UiObjectMember *previousObject = x;
|
||||
|
||||
for (UiObjectMemberList *objectMemberIt = objectMembers(object); objectMemberIt; objectMemberIt = objectMemberIt->next) {
|
||||
if (UiScriptBinding *script = cast<UiScriptBinding *>(objectMemberIt->member)) {
|
||||
for (UiObjectMemberList *previousObjectMemberIt = Delta::objectMembers(previousObject); previousObjectMemberIt; previousObjectMemberIt = previousObjectMemberIt->next) {
|
||||
if (UiScriptBinding *previousScript = cast<UiScriptBinding *>(previousObjectMemberIt->member)) {
|
||||
if (compare(script->qualifiedId, previousScript->qualifiedId)) {
|
||||
const QString scriptCode = _scriptCode(script, doc2);
|
||||
const QString previousScriptCode = _scriptCode(previousScript, doc1);
|
||||
|
||||
if (scriptCode != previousScriptCode) {
|
||||
const QString property = _propertyName(script->qualifiedId);
|
||||
foreach (const QDeclarativeDebugObjectReference &ref, debugIds[x]) {
|
||||
if (ref.debugId() != -1)
|
||||
updateScriptBinding(ref, script, property, scriptCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (UiSourceElement *uiSource = cast<UiSourceElement*>(objectMemberIt->member)) {
|
||||
for (UiObjectMemberList *previousObjectMemberIt = objectMembers(previousObject);
|
||||
previousObjectMemberIt; previousObjectMemberIt = previousObjectMemberIt->next)
|
||||
{
|
||||
if (UiSourceElement *previousSource = cast<UiSourceElement*>(previousObjectMemberIt->member)) {
|
||||
if (compare(uiSource, previousSource))
|
||||
{
|
||||
const QString methodCode = _methodCode(uiSource, doc2);
|
||||
const QString previousMethodCode = _methodCode(previousSource, doc1);
|
||||
|
||||
if (methodCode != previousMethodCode) {
|
||||
const QString methodName = _methodName(uiSource);
|
||||
foreach (const QDeclarativeDebugObjectReference &ref, debugIds[x]) {
|
||||
if (ref.debugId() != -1)
|
||||
updateMethodBody(ref, script, methodName, methodCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--8<--------------------------------------------------------------------------------------------------
|
||||
|
||||
//qDebug() << "match "<< label(x, doc1) << "with parent " << label(parents1.parent.value(x), doc1)
|
||||
// << " to "<< label(y, doc2) << "with parent " << label(parents2.parent.value(y), doc2);
|
||||
if (!M.contains(parents1.parent.value(x),parents2.parent.value(y))) {
|
||||
qDebug () << "move " << label(y, doc2) << " from " << label(parents1.parent.value(x), doc1)
|
||||
<< " to " << label(parents2.parent.value(y), doc2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
todo.append(doc1->qmlProgram()->members->member);
|
||||
while(!todo.isEmpty()) {
|
||||
UiObjectMember *x = todo.takeFirst();
|
||||
todo += children(x);
|
||||
if (!M.way1.contains(x)) {
|
||||
qDebug () << "remove " << label(x, doc1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return newDebuggIds;
|
||||
}
|
||||
|
||||
static bool isLiteralValue(ExpressionNode *expr)
|
||||
{
|
||||
if (cast<NumericLiteral*>(expr))
|
||||
@@ -191,227 +434,6 @@ static QVariant castToLiteral(const QString &expression, UiScriptBinding *script
|
||||
return castedExpression;
|
||||
}
|
||||
|
||||
ScriptBindingParser::ScriptBindingParser(QmlJS::Document::Ptr doc,
|
||||
const QList<QDeclarativeDebugObjectReference> &objectReferences)
|
||||
: doc(doc), objectReferences(objectReferences), m_searchElementOffset(-1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ScriptBindingParser::process()
|
||||
{
|
||||
if (!doc.isNull() && doc->qmlProgram())
|
||||
doc->qmlProgram()->accept(this);
|
||||
}
|
||||
|
||||
QDeclarativeDebugObjectReference ScriptBindingParser::objectReferenceForOffset(unsigned int offset)
|
||||
{
|
||||
m_searchElementOffset = offset;
|
||||
if (!doc.isNull() && doc->qmlProgram())
|
||||
doc->qmlProgram()->accept(this);
|
||||
|
||||
return m_foundObjectReference;
|
||||
}
|
||||
|
||||
QDeclarativeDebugObjectReference ScriptBindingParser::objectReference(const QString &id) const
|
||||
{
|
||||
foreach (const QDeclarativeDebugObjectReference &ref, objectReferences) {
|
||||
if (ref.idString() == id)
|
||||
return ref;
|
||||
}
|
||||
|
||||
return QDeclarativeDebugObjectReference();
|
||||
}
|
||||
|
||||
|
||||
QDeclarativeDebugObjectReference ScriptBindingParser::objectReferenceForPosition(const QUrl &url, int line, int col) const
|
||||
{
|
||||
foreach (const QDeclarativeDebugObjectReference &ref, objectReferences) {
|
||||
if (ref.source().lineNumber() == line
|
||||
&& ref.source().columnNumber() == col
|
||||
&& ref.source().url() == url)
|
||||
{
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
return QDeclarativeDebugObjectReference();
|
||||
}
|
||||
|
||||
bool ScriptBindingParser::visit(UiObjectDefinition *ast)
|
||||
{
|
||||
objectStack.append(ast);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptBindingParser::endVisit(UiObjectDefinition *)
|
||||
{
|
||||
objectStack.removeLast();
|
||||
}
|
||||
|
||||
bool ScriptBindingParser::visit(UiObjectBinding *ast)
|
||||
{
|
||||
objectStack.append(ast);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptBindingParser::endVisit(UiObjectBinding *)
|
||||
{
|
||||
objectStack.removeLast();
|
||||
}
|
||||
|
||||
bool ScriptBindingParser::visit(UiScriptBinding *ast)
|
||||
{
|
||||
scripts.append(ast);
|
||||
_parent[ast] = objectStack.back();
|
||||
|
||||
if (ast->qualifiedId && ast->qualifiedId->name && ! ast->qualifiedId->next) {
|
||||
const QString bindingName = ast->qualifiedId->name->asString();
|
||||
|
||||
if (bindingName == QLatin1String("id")) {
|
||||
_id[objectStack.back()] = ast;
|
||||
|
||||
if (ExpressionStatement *s = cast<ExpressionStatement *>(ast->statement)) {
|
||||
if (IdentifierExpression *id = cast<IdentifierExpression *>(s->expression)) {
|
||||
if (id->name) {
|
||||
_idBindings.insert(ast, objectReference(id->name->asString()));
|
||||
|
||||
if (parent(ast)->firstSourceLocation().offset == m_searchElementOffset)
|
||||
m_foundObjectReference = objectReference(id->name->asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QDeclarativeDebugObjectReference ScriptBindingParser::objectReferenceForScriptBinding(UiScriptBinding *binding) const
|
||||
{
|
||||
return _idBindings.value(binding);
|
||||
}
|
||||
|
||||
// Delta
|
||||
|
||||
static QString propertyName(UiQualifiedId *id)
|
||||
{
|
||||
QString s;
|
||||
|
||||
for (; id; id = id->next) {
|
||||
if (! id->name)
|
||||
return QString();
|
||||
|
||||
s += id->name->asString();
|
||||
|
||||
if (id->next)
|
||||
s += QLatin1Char('.');
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
QDeclarativeDebugObjectReference Delta::objectReferenceForUiObject(const ScriptBindingParser &bindingParser, UiObjectMember *object)
|
||||
{
|
||||
if (UiScriptBinding *idBinding = bindingParser.id(object)) {
|
||||
if (ExpressionStatement *s = cast<ExpressionStatement *>(idBinding->statement)) {
|
||||
if (IdentifierExpression *idExpr = cast<IdentifierExpression *>(s->expression)) {
|
||||
const QString idString = idExpr->name->asString();
|
||||
|
||||
const QList<QDeclarativeDebugObjectReference> refs = ClientProxy::instance()->objectReferences(_url);
|
||||
foreach (const QDeclarativeDebugObjectReference &ref, refs) {
|
||||
if (ref.idString() == idString)
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return QDeclarativeDebugObjectReference();
|
||||
}
|
||||
|
||||
|
||||
void Delta::operator()(Document::Ptr doc, Document::Ptr previousDoc)
|
||||
{
|
||||
_doc = doc;
|
||||
_previousDoc = previousDoc;
|
||||
_changes.clear();
|
||||
|
||||
_url = QUrl::fromLocalFile(doc->fileName());
|
||||
const QList<QDeclarativeDebugObjectReference> references = ClientProxy::instance()->objectReferences(_url);
|
||||
|
||||
ScriptBindingParser bindingParser(doc, references);
|
||||
bindingParser.process();
|
||||
|
||||
ScriptBindingParser previousBindingParser(previousDoc, references);
|
||||
previousBindingParser.process();
|
||||
|
||||
QHash<UiObjectMember *, UiObjectMember *> preservedObjects;
|
||||
|
||||
foreach (UiScriptBinding *id, bindingParser.ids()) {
|
||||
UiObjectMember *parent = bindingParser.parent(id);
|
||||
const QString idCode = bindingParser.scriptCode(id);
|
||||
|
||||
foreach (UiScriptBinding *otherId, previousBindingParser.ids()) {
|
||||
const QString otherIdCode = previousBindingParser.scriptCode(otherId);
|
||||
|
||||
if (idCode == otherIdCode) {
|
||||
preservedObjects.insert(parent, previousBindingParser.parent(otherId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHashIterator<UiObjectMember *, UiObjectMember *> it(preservedObjects);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
|
||||
UiObjectMember *object = it.key();
|
||||
UiObjectMember *previousObject = it.value();
|
||||
|
||||
for (UiObjectMemberList *objectMemberIt = objectMembers(object); objectMemberIt; objectMemberIt = objectMemberIt->next) {
|
||||
if (UiScriptBinding *script = cast<UiScriptBinding *>(objectMemberIt->member)) {
|
||||
for (UiObjectMemberList *previousObjectMemberIt = objectMembers(previousObject); previousObjectMemberIt; previousObjectMemberIt = previousObjectMemberIt->next) {
|
||||
if (UiScriptBinding *previousScript = cast<UiScriptBinding *>(previousObjectMemberIt->member)) {
|
||||
if (compare(script->qualifiedId, previousScript->qualifiedId)) {
|
||||
const QString scriptCode = bindingParser.scriptCode(script);
|
||||
const QString previousScriptCode = previousBindingParser.scriptCode(previousScript);
|
||||
|
||||
if (scriptCode != previousScriptCode) {
|
||||
const QString property = propertyName(script->qualifiedId);
|
||||
|
||||
QDeclarativeDebugObjectReference ref = objectReferenceForUiObject(bindingParser, object);
|
||||
if (ref.debugId() != -1)
|
||||
updateScriptBinding(ref, script, property, scriptCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (UiSourceElement *uiSource = cast<UiSourceElement*>(objectMemberIt->member)) {
|
||||
|
||||
for (UiObjectMemberList *previousObjectMemberIt = objectMembers(previousObject);
|
||||
previousObjectMemberIt; previousObjectMemberIt = previousObjectMemberIt->next)
|
||||
{
|
||||
if (UiSourceElement *previousSource = cast<UiSourceElement*>(previousObjectMemberIt->member)) {
|
||||
if (compare(uiSource, previousSource))
|
||||
{
|
||||
const QString methodCode = bindingParser.methodCode(uiSource);
|
||||
const QString previousMethodCode = previousBindingParser.methodCode(previousSource);
|
||||
|
||||
if (methodCode != previousMethodCode) {
|
||||
const QString methodName = bindingParser.methodName(uiSource);
|
||||
QDeclarativeDebugObjectReference ref = objectReferenceForUiObject(bindingParser, object);
|
||||
if (ref.debugId() != -1)
|
||||
updateMethodBody(ref, script, methodName, methodCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Delta::updateMethodBody(const QDeclarativeDebugObjectReference &objectReference,
|
||||
UiScriptBinding *scriptBinding,
|
||||
const QString &methodName,
|
||||
|
||||
@@ -43,53 +43,6 @@ using namespace QmlJS::AST;
|
||||
namespace QmlJSInspector {
|
||||
namespace Internal {
|
||||
|
||||
class ScriptBindingParser : protected Visitor
|
||||
{
|
||||
public:
|
||||
QmlJS::Document::Ptr doc;
|
||||
QList<UiScriptBinding *> scripts;
|
||||
|
||||
ScriptBindingParser(Document::Ptr doc,
|
||||
const QList<QDeclarativeDebugObjectReference> &objectReferences = QList<QDeclarativeDebugObjectReference>());
|
||||
void process();
|
||||
|
||||
UiObjectMember *parent(UiScriptBinding *script) const;
|
||||
UiScriptBinding *id(UiObjectMember *parent) const;
|
||||
QList<UiScriptBinding *> ids() const;
|
||||
|
||||
QDeclarativeDebugObjectReference objectReferenceForPosition(const QUrl &url, int line, int col) const;
|
||||
|
||||
QDeclarativeDebugObjectReference objectReferenceForScriptBinding(UiScriptBinding *binding) const;
|
||||
|
||||
QDeclarativeDebugObjectReference objectReferenceForOffset(unsigned int offset);
|
||||
|
||||
QString header(UiObjectMember *member) const;
|
||||
|
||||
QString scriptCode(UiScriptBinding *script) const;
|
||||
QString methodName(UiSourceElement *source) const;
|
||||
QString methodCode(UiSourceElement *source) const;
|
||||
|
||||
protected:
|
||||
QDeclarativeDebugObjectReference objectReference(const QString &id) const;
|
||||
|
||||
virtual bool visit(UiObjectDefinition *ast);
|
||||
virtual void endVisit(UiObjectDefinition *);
|
||||
virtual bool visit(UiObjectBinding *ast);
|
||||
virtual void endVisit(UiObjectBinding *);
|
||||
virtual bool visit(UiScriptBinding *ast);
|
||||
|
||||
private:
|
||||
QList<UiObjectMember *> objectStack;
|
||||
QHash<UiScriptBinding *, UiObjectMember *> _parent;
|
||||
QHash<UiObjectMember *, UiScriptBinding *> _id;
|
||||
QHash<UiScriptBinding *, QDeclarativeDebugObjectReference> _idBindings;
|
||||
|
||||
QList<QDeclarativeDebugObjectReference> objectReferences;
|
||||
QDeclarativeDebugObjectReference m_foundObjectReference;
|
||||
unsigned int m_searchElementOffset;
|
||||
};
|
||||
|
||||
|
||||
class Delta
|
||||
{
|
||||
public:
|
||||
@@ -102,14 +55,15 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
void operator()(QmlJS::Document::Ptr doc, QmlJS::Document::Ptr previousDoc);
|
||||
typedef QHash< UiObjectMember*, QList<QDeclarativeDebugObjectReference > > DebugIdMap;
|
||||
DebugIdMap operator()(Document::Ptr doc1, Document::Ptr doc2, const DebugIdMap& debugIds);
|
||||
|
||||
QList<Change> changes() const;
|
||||
|
||||
QmlJS::Document::Ptr document() const;
|
||||
QmlJS::Document::Ptr previousDocument() const;
|
||||
|
||||
private:
|
||||
public:
|
||||
void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
|
||||
QmlJS::AST::UiScriptBinding *scriptBinding,
|
||||
const QString &propertyName,
|
||||
@@ -119,10 +73,9 @@ private:
|
||||
const QString &methodName,
|
||||
const QString &methodBody);
|
||||
|
||||
bool compare(UiSourceElement *source, UiSourceElement *other);
|
||||
bool compare(QmlJS::AST::UiQualifiedId *id, QmlJS::AST::UiQualifiedId *other);
|
||||
QmlJS::AST::UiObjectMemberList *objectMembers(QmlJS::AST::UiObjectMember *object);
|
||||
QDeclarativeDebugObjectReference objectReferenceForUiObject(const ScriptBindingParser &bindingParser, UiObjectMember *object);
|
||||
static bool compare(UiSourceElement *source, UiSourceElement *other);
|
||||
static bool compare(QmlJS::AST::UiQualifiedId *id, QmlJS::AST::UiQualifiedId *other);
|
||||
static QmlJS::AST::UiObjectMemberList *objectMembers(QmlJS::AST::UiObjectMember *object);
|
||||
|
||||
private:
|
||||
QmlJS::Document::Ptr _doc;
|
||||
|
||||
@@ -101,6 +101,11 @@ using namespace QmlJS::AST;
|
||||
using namespace QmlJSInspector::Internal;
|
||||
using namespace Debugger::Internal;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
MaxConnectionAttempts = 50,
|
||||
ConnectionAttemptDefaultInterval = 75,
|
||||
@@ -138,6 +143,7 @@ Inspector::Inspector(QObject *parent)
|
||||
connect(m_clientProxy, SIGNAL(aboutToReloadEngines()), SLOT(aboutToReloadEngines()));
|
||||
connect(m_clientProxy, SIGNAL(enginesChanged()), SLOT(updateEngineList()));
|
||||
connect(m_clientProxy, SIGNAL(aboutToDisconnect()), SLOT(disconnectWidgets()));
|
||||
connect(m_clientProxy, SIGNAL(objectTreeUpdated(QDeclarativeDebugObjectReference)),SLOT(objectTreeUpdated(QDeclarativeDebugObjectReference)));
|
||||
|
||||
connect(Debugger::DebuggerPlugin::instance(),
|
||||
SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
|
||||
@@ -145,6 +151,7 @@ Inspector::Inspector(QObject *parent)
|
||||
connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector()));
|
||||
}
|
||||
|
||||
|
||||
Inspector::~Inspector()
|
||||
{
|
||||
}
|
||||
@@ -562,3 +569,68 @@ bool Inspector::addQuotesForData(const QVariant &value) const
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Associates the UiObjectMember* to their QDeclarativeDebugObjectReference.
|
||||
*/
|
||||
class MapObjectWithDebugReference : public Visitor
|
||||
{
|
||||
public:
|
||||
virtual void endVisit(UiObjectDefinition *ast) ;
|
||||
virtual void endVisit(UiObjectBinding *ast) ;
|
||||
|
||||
QDeclarativeDebugObjectReference root;
|
||||
QString filename;
|
||||
QHash<UiObjectMember *, QList<QDeclarativeDebugObjectReference> > result;
|
||||
private:
|
||||
void processRecursive(const QDeclarativeDebugObjectReference &object, UiObjectMember *ast);
|
||||
};
|
||||
|
||||
void MapObjectWithDebugReference::endVisit(UiObjectDefinition* ast)
|
||||
{
|
||||
if (ast->qualifiedTypeNameId->name->asString().at(0).isUpper())
|
||||
processRecursive(root, ast);
|
||||
}
|
||||
void MapObjectWithDebugReference::endVisit(UiObjectBinding* ast)
|
||||
{
|
||||
if (ast->qualifiedId->name->asString().at(0).isUpper())
|
||||
processRecursive(root, ast);
|
||||
}
|
||||
|
||||
void MapObjectWithDebugReference::processRecursive(const QDeclarativeDebugObjectReference& object, UiObjectMember* ast)
|
||||
{
|
||||
// If this is too slow, it can be speed up by indexing
|
||||
// the QDeclarativeDebugObjectReference by filename/loc in a fist pass
|
||||
|
||||
SourceLocation loc = ast->firstSourceLocation();
|
||||
if (object.source().lineNumber() == int(loc.startLine) && object.source().url().toLocalFile() == filename) {
|
||||
result[ast] += object;
|
||||
}
|
||||
|
||||
foreach (const QDeclarativeDebugObjectReference &it, object.children()) {
|
||||
processRecursive(it, ast);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlJSInspector::Internal::Inspector::objectTreeUpdated(const QDeclarativeDebugObjectReference &ref)
|
||||
{
|
||||
QmlJS::ModelManagerInterface *m = QmlJS::ModelManagerInterface::instance();
|
||||
Snapshot snapshot = m->snapshot();
|
||||
QHash<QString, QHash<UiObjectMember *, QList< QDeclarativeDebugObjectReference> > > allDebugIds;
|
||||
foreach(const Document::Ptr &doc, snapshot) {
|
||||
if (!doc->qmlProgram())
|
||||
continue;
|
||||
MapObjectWithDebugReference visitor;
|
||||
visitor.root = ref;
|
||||
QString filename = doc->fileName();
|
||||
visitor.filename = filename;
|
||||
doc->qmlProgram()->accept(&visitor);
|
||||
allDebugIds[filename] = visitor.result;
|
||||
}
|
||||
|
||||
//FIXME
|
||||
m_textPreview->m_initialTable = allDebugIds;
|
||||
m_textPreview->m_debugIds.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ public:
|
||||
QmlProjectWithCppPlugins
|
||||
};
|
||||
|
||||
QHash< QString, QList< QPair< QmlJS::AST::UiObjectMember*, int > > > m_initialTable;
|
||||
|
||||
public:
|
||||
Inspector(QObject *parent = 0);
|
||||
virtual ~Inspector();
|
||||
@@ -115,6 +117,8 @@ private slots:
|
||||
void disconnectWidgets();
|
||||
void disconnected();
|
||||
|
||||
void objectTreeUpdated(const QDeclarativeDebugObjectReference &ref);
|
||||
|
||||
private:
|
||||
Debugger::DebuggerRunControl *createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
|
||||
const QString &executableFile = QString(),
|
||||
|
||||
@@ -60,7 +60,7 @@ void QmlJSLiveTextPreview::changeSelectedElement(int offset, const QString &word
|
||||
ClientProxy *clientProxy = ClientProxy::instance();
|
||||
QUrl url = QUrl::fromLocalFile(m_currentEditor.data()->file()->fileName());
|
||||
QmlJS::Document::Ptr doc = modelManager()->snapshot().document(m_currentEditor.data()->file()->fileName());
|
||||
ScriptBindingParser info(doc, clientProxy->objectReferences(url));
|
||||
//ScriptBindingParser info(doc, clientProxy->objectReferences(url));
|
||||
|
||||
QDeclarativeDebugObjectReference objectRef;
|
||||
|
||||
@@ -72,9 +72,10 @@ void QmlJSLiveTextPreview::changeSelectedElement(int offset, const QString &word
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME
|
||||
if (objectRef.debugId() == -1 && offset >= 0) {
|
||||
objectRef = info.objectReferenceForOffset(offset);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (objectRef.debugId() != -1)
|
||||
emit selectedItemsChanged(QList<QDeclarativeDebugObjectReference>() << objectRef);
|
||||
@@ -96,20 +97,28 @@ void QmlJSLiveTextPreview::setEditor(Core::IEditor *editor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
|
||||
{
|
||||
{
|
||||
/* FIXME
|
||||
Core::ICore *core = Core::ICore::instance();
|
||||
const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
|
||||
|
||||
if (!core->hasContext(dbgcontext))
|
||||
return;
|
||||
*/
|
||||
|
||||
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()) {
|
||||
|
||||
if (m_debugIds.isEmpty())
|
||||
m_debugIds = m_initialTable.value(doc->fileName());
|
||||
Delta delta;
|
||||
delta(doc, m_previousDoc);
|
||||
m_debugIds = delta(m_previousDoc, doc, m_debugIds);
|
||||
m_previousDoc = doc;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlJSInspector
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Internal {
|
||||
class QmlJSLiveTextPreview : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QmlJSLiveTextPreview(QObject *parent = 0);
|
||||
static QmlJS::ModelManagerInterface *modelManager();
|
||||
@@ -39,6 +40,9 @@ public:
|
||||
void setActiveObject(const QDeclarativeDebugObjectReference &object);
|
||||
void mapObjectToQml(const QDeclarativeDebugObjectReference &object);
|
||||
|
||||
QHash<QString, QHash<QmlJS::AST::UiObjectMember *, QList< QDeclarativeDebugObjectReference> > > m_initialTable;
|
||||
QHash< QmlJS::AST::UiObjectMember*, QList<QDeclarativeDebugObjectReference > > m_debugIds;
|
||||
|
||||
signals:
|
||||
void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user