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 QmlJS::AST;
|
||||||
using namespace QmlJSInspector::Internal;
|
using namespace QmlJSInspector::Internal;
|
||||||
|
|
||||||
UiObjectMember *ScriptBindingParser::parent(UiScriptBinding *script) const
|
/*!
|
||||||
{ return _parent.value(script); }
|
Build a hash of the parents
|
||||||
|
*/
|
||||||
UiScriptBinding *ScriptBindingParser::id(UiObjectMember *parent) const
|
struct BuildParentHash : public Visitor
|
||||||
{ return _id.value(parent); }
|
|
||||||
|
|
||||||
QList<UiScriptBinding *> ScriptBindingParser::ids() const
|
|
||||||
{ return _id.values(); }
|
|
||||||
|
|
||||||
QString ScriptBindingParser::header(UiObjectMember *member) const
|
|
||||||
{
|
{
|
||||||
if (member) {
|
virtual void postVisit(Node* );
|
||||||
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
|
virtual bool preVisit(Node* );
|
||||||
const int begin = def->firstSourceLocation().begin();
|
QHash<UiObjectMember *, UiObjectMember *> parent;
|
||||||
const int end = def->initializer->lbraceToken.begin();
|
private:
|
||||||
return doc->source().mid(begin, end - begin);
|
QList<UiObjectMember *> stack;
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
if (script) {
|
||||||
const int begin = script->statement->firstSourceLocation().begin();
|
const int begin = script->statement->firstSourceLocation().begin();
|
||||||
const int end = script->statement->lastSourceLocation().end();
|
const int end = script->statement->lastSourceLocation().end();
|
||||||
return doc->source().mid(begin, end - begin);
|
return doc->source().mid(begin, end - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString _methodCode(UiSourceElement *source, Document::Ptr doc)
|
||||||
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
|
|
||||||
{
|
{
|
||||||
if (source) {
|
if (source) {
|
||||||
if (FunctionDeclaration *declaration = cast<FunctionDeclaration*>(source->sourceElement)) {
|
if (FunctionDeclaration *declaration = cast<FunctionDeclaration*>(source->sourceElement)) {
|
||||||
@@ -99,6 +216,132 @@ QString ScriptBindingParser::methodCode(UiSourceElement *source) const
|
|||||||
return QString();
|
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)
|
static bool isLiteralValue(ExpressionNode *expr)
|
||||||
{
|
{
|
||||||
if (cast<NumericLiteral*>(expr))
|
if (cast<NumericLiteral*>(expr))
|
||||||
@@ -191,227 +434,6 @@ static QVariant castToLiteral(const QString &expression, UiScriptBinding *script
|
|||||||
return castedExpression;
|
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,
|
void Delta::updateMethodBody(const QDeclarativeDebugObjectReference &objectReference,
|
||||||
UiScriptBinding *scriptBinding,
|
UiScriptBinding *scriptBinding,
|
||||||
const QString &methodName,
|
const QString &methodName,
|
||||||
|
|||||||
@@ -43,53 +43,6 @@ using namespace QmlJS::AST;
|
|||||||
namespace QmlJSInspector {
|
namespace QmlJSInspector {
|
||||||
namespace Internal {
|
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
|
class Delta
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -102,14 +55,15 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
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;
|
QList<Change> changes() const;
|
||||||
|
|
||||||
QmlJS::Document::Ptr document() const;
|
QmlJS::Document::Ptr document() const;
|
||||||
QmlJS::Document::Ptr previousDocument() const;
|
QmlJS::Document::Ptr previousDocument() const;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
|
void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
|
||||||
QmlJS::AST::UiScriptBinding *scriptBinding,
|
QmlJS::AST::UiScriptBinding *scriptBinding,
|
||||||
const QString &propertyName,
|
const QString &propertyName,
|
||||||
@@ -119,10 +73,9 @@ private:
|
|||||||
const QString &methodName,
|
const QString &methodName,
|
||||||
const QString &methodBody);
|
const QString &methodBody);
|
||||||
|
|
||||||
bool compare(UiSourceElement *source, UiSourceElement *other);
|
static bool compare(UiSourceElement *source, UiSourceElement *other);
|
||||||
bool compare(QmlJS::AST::UiQualifiedId *id, QmlJS::AST::UiQualifiedId *other);
|
static bool compare(QmlJS::AST::UiQualifiedId *id, QmlJS::AST::UiQualifiedId *other);
|
||||||
QmlJS::AST::UiObjectMemberList *objectMembers(QmlJS::AST::UiObjectMember *object);
|
static QmlJS::AST::UiObjectMemberList *objectMembers(QmlJS::AST::UiObjectMember *object);
|
||||||
QDeclarativeDebugObjectReference objectReferenceForUiObject(const ScriptBindingParser &bindingParser, UiObjectMember *object);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QmlJS::Document::Ptr _doc;
|
QmlJS::Document::Ptr _doc;
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ using namespace QmlJS::AST;
|
|||||||
using namespace QmlJSInspector::Internal;
|
using namespace QmlJSInspector::Internal;
|
||||||
using namespace Debugger::Internal;
|
using namespace Debugger::Internal;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MaxConnectionAttempts = 50,
|
MaxConnectionAttempts = 50,
|
||||||
ConnectionAttemptDefaultInterval = 75,
|
ConnectionAttemptDefaultInterval = 75,
|
||||||
@@ -138,6 +143,7 @@ Inspector::Inspector(QObject *parent)
|
|||||||
connect(m_clientProxy, SIGNAL(aboutToReloadEngines()), SLOT(aboutToReloadEngines()));
|
connect(m_clientProxy, SIGNAL(aboutToReloadEngines()), SLOT(aboutToReloadEngines()));
|
||||||
connect(m_clientProxy, SIGNAL(enginesChanged()), SLOT(updateEngineList()));
|
connect(m_clientProxy, SIGNAL(enginesChanged()), SLOT(updateEngineList()));
|
||||||
connect(m_clientProxy, SIGNAL(aboutToDisconnect()), SLOT(disconnectWidgets()));
|
connect(m_clientProxy, SIGNAL(aboutToDisconnect()), SLOT(disconnectWidgets()));
|
||||||
|
connect(m_clientProxy, SIGNAL(objectTreeUpdated(QDeclarativeDebugObjectReference)),SLOT(objectTreeUpdated(QDeclarativeDebugObjectReference)));
|
||||||
|
|
||||||
connect(Debugger::DebuggerPlugin::instance(),
|
connect(Debugger::DebuggerPlugin::instance(),
|
||||||
SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
|
SIGNAL(stateChanged(int)), this, SLOT(debuggerStateChanged(int)));
|
||||||
@@ -145,6 +151,7 @@ Inspector::Inspector(QObject *parent)
|
|||||||
connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector()));
|
connect(m_connectionTimer, SIGNAL(timeout()), SLOT(pollInspector()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Inspector::~Inspector()
|
Inspector::~Inspector()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -562,3 +569,68 @@ bool Inspector::addQuotesForData(const QVariant &value) const
|
|||||||
|
|
||||||
return false;
|
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
|
QmlProjectWithCppPlugins
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QHash< QString, QList< QPair< QmlJS::AST::UiObjectMember*, int > > > m_initialTable;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Inspector(QObject *parent = 0);
|
Inspector(QObject *parent = 0);
|
||||||
virtual ~Inspector();
|
virtual ~Inspector();
|
||||||
@@ -115,6 +117,8 @@ private slots:
|
|||||||
void disconnectWidgets();
|
void disconnectWidgets();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
|
|
||||||
|
void objectTreeUpdated(const QDeclarativeDebugObjectReference &ref);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Debugger::DebuggerRunControl *createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
|
Debugger::DebuggerRunControl *createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig,
|
||||||
const QString &executableFile = QString(),
|
const QString &executableFile = QString(),
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ void QmlJSLiveTextPreview::changeSelectedElement(int offset, const QString &word
|
|||||||
ClientProxy *clientProxy = ClientProxy::instance();
|
ClientProxy *clientProxy = ClientProxy::instance();
|
||||||
QUrl url = QUrl::fromLocalFile(m_currentEditor.data()->file()->fileName());
|
QUrl url = QUrl::fromLocalFile(m_currentEditor.data()->file()->fileName());
|
||||||
QmlJS::Document::Ptr doc = modelManager()->snapshot().document(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;
|
QDeclarativeDebugObjectReference objectRef;
|
||||||
|
|
||||||
@@ -72,9 +72,10 @@ void QmlJSLiveTextPreview::changeSelectedElement(int offset, const QString &word
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME
|
||||||
if (objectRef.debugId() == -1 && offset >= 0) {
|
if (objectRef.debugId() == -1 && offset >= 0) {
|
||||||
objectRef = info.objectReferenceForOffset(offset);
|
objectRef = info.objectReferenceForOffset(offset);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if (objectRef.debugId() != -1)
|
if (objectRef.debugId() != -1)
|
||||||
emit selectedItemsChanged(QList<QDeclarativeDebugObjectReference>() << objectRef);
|
emit selectedItemsChanged(QList<QDeclarativeDebugObjectReference>() << objectRef);
|
||||||
@@ -96,20 +97,28 @@ void QmlJSLiveTextPreview::setEditor(Core::IEditor *editor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
|
void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
|
||||||
{
|
{
|
||||||
|
/* FIXME
|
||||||
Core::ICore *core = Core::ICore::instance();
|
Core::ICore *core = Core::ICore::instance();
|
||||||
const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
|
const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
|
||||||
|
|
||||||
if (!core->hasContext(dbgcontext))
|
if (!core->hasContext(dbgcontext))
|
||||||
return;
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()) {
|
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()) {
|
||||||
|
|
||||||
|
if (m_debugIds.isEmpty())
|
||||||
|
m_debugIds = m_initialTable.value(doc->fileName());
|
||||||
Delta delta;
|
Delta delta;
|
||||||
delta(doc, m_previousDoc);
|
m_debugIds = delta(m_previousDoc, doc, m_debugIds);
|
||||||
m_previousDoc = doc;
|
m_previousDoc = doc;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace QmlJSInspector
|
} // namespace QmlJSInspector
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace Internal {
|
|||||||
class QmlJSLiveTextPreview : public QObject
|
class QmlJSLiveTextPreview : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QmlJSLiveTextPreview(QObject *parent = 0);
|
explicit QmlJSLiveTextPreview(QObject *parent = 0);
|
||||||
static QmlJS::ModelManagerInterface *modelManager();
|
static QmlJS::ModelManagerInterface *modelManager();
|
||||||
@@ -39,6 +40,9 @@ public:
|
|||||||
void setActiveObject(const QDeclarativeDebugObjectReference &object);
|
void setActiveObject(const QDeclarativeDebugObjectReference &object);
|
||||||
void mapObjectToQml(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:
|
signals:
|
||||||
void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects);
|
void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user