Commit 3c7528d5 authored by Olivier Goffart's avatar Olivier Goffart
Browse files

QmlJsDelta: refactor to split the stuff specific to the QML Live Preview out

parent e708eb3d
......@@ -315,9 +315,6 @@ static QHash<QString, UiObjectMember*> extractProperties(UiObjectDefinition *obj
void Delta::insert(UiObjectMember *member, UiObjectMember *parentMember, const QList<QDeclarativeDebugObjectReference > &debugReferences, const Document::Ptr &doc)
{
if (doNotSendChanges)
return;
if (!member || !parentMember)
return;
......@@ -341,8 +338,7 @@ void Delta::insert(UiObjectMember *member, UiObjectMember *parentMember, const Q
+ QLatin1Char(':') + QString::number(uiObjectDef->firstSourceLocation().startLine-importList.count());
foreach(const QDeclarativeDebugObjectReference &ref, debugReferences) {
if (ref.debugId() != -1) {
_referenceRefreshRequired = true;
ClientProxy::instance()->createQmlObject(qmlText, ref, importList, filename);
createObject(qmlText, ref, importList, filename);
}
}
newObjects += member;
......@@ -354,9 +350,6 @@ void Delta::update(UiObjectDefinition* oldObject, const QmlJS::Document::Ptr& ol
UiObjectDefinition* newObject, const QmlJS::Document::Ptr& newDoc,
const QList< QDeclarativeDebugObjectReference >& debugReferences)
{
if (doNotSendChanges)
return;
Q_ASSERT (oldObject && newObject);
QSet<QString> presentBinding;
......@@ -391,18 +384,15 @@ void Delta::update(UiObjectDefinition* oldObject, const QmlJS::Document::Ptr& ol
}
}
if (doNotSendChanges)
return;
//reset property that are not present in the new object.
for (QHash<QString, UiObjectMember *>::const_iterator it2 = oldProperties.constBegin();
it2 != oldProperties.constEnd(); ++it2) {
if (!newProperties.contains(it2.key())) {
if (UiScriptBinding *previousScript = cast<UiScriptBinding *>(*it2)) {
if (cast<UiScriptBinding *>(*it2)) {
foreach (const QDeclarativeDebugObjectReference &ref, debugReferences) {
if (ref.debugId() != -1)
ClientProxy::instance()->resetBindingForObject(ref.debugId(), it2.key()); // ### remove
resetBindingForObject(ref.debugId(), it2.key());
}
}
}
......@@ -413,7 +403,7 @@ void Delta::remove(const QList< QDeclarativeDebugObjectReference >& debugReferen
{
foreach (const QDeclarativeDebugObjectReference &ref, debugReferences) {
if (ref.debugId() != -1)
ClientProxy::instance()->destroyQmlObject(ref.debugId()); // ### remove
removeObject(ref.debugId());
}
}
......@@ -426,7 +416,6 @@ Delta::DebugIdMap Delta::operator()(const Document::Ptr &doc1, const Document::P
QHash< UiObjectMember*, QList<QDeclarativeDebugObjectReference > > newDebuggIds;
Map M = Mapping(doc1, doc2);
_referenceRefreshRequired = false;
BuildParentHash parents2;
doc2->qmlProgram()->accept(&parents2);
......@@ -486,186 +475,28 @@ Delta::DebugIdMap Delta::operator()(const Document::Ptr &doc1, const Document::P
return newDebuggIds;
}
static bool isLiteralValue(ExpressionNode *expr)
{
if (cast<NumericLiteral*>(expr))
return true;
else if (cast<StringLiteral*>(expr))
return true;
else if (UnaryPlusExpression *plusExpr = cast<UnaryPlusExpression*>(expr))
return isLiteralValue(plusExpr->expression);
else if (UnaryMinusExpression *minusExpr = cast<UnaryMinusExpression*>(expr))
return isLiteralValue(minusExpr->expression);
else if (cast<TrueLiteral*>(expr))
return true;
else if (cast<FalseLiteral*>(expr))
return true;
else
return false;
}
static inline bool isLiteralValue(UiScriptBinding *script)
{
if (!script || !script->statement)
return false;
ExpressionStatement *exprStmt = cast<ExpressionStatement *>(script->statement);
if (exprStmt)
return isLiteralValue(exprStmt->expression);
else
return false;
}
static inline QString stripQuotes(const QString &str)
{
if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
|| (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
return str.mid(1, str.length() - 2);
return str;
}
static inline QString deEscape(const QString &value)
{
QString result = value;
result.replace(QLatin1String("\\\\"), QLatin1String("\\"));
result.replace(QLatin1String("\\\""), QLatin1String("\""));
result.replace(QLatin1String("\\\t"), QLatin1String("\t"));
result.replace(QLatin1String("\\\r"), QLatin1String("\\\r"));
result.replace(QLatin1String("\\\n"), QLatin1String("\n"));
return result;
}
static QString cleanExpression(const QString &expression, UiScriptBinding *scriptBinding)
{
QString trimmedExpression = expression.trimmed();
if (ExpressionStatement *expStatement = cast<ExpressionStatement*>(scriptBinding->statement)) {
if (expStatement->semicolonToken.isValid())
trimmedExpression.chop(1);
}
return deEscape(stripQuotes(trimmedExpression));
}
static QVariant castToLiteral(const QString &expression, UiScriptBinding *scriptBinding)
{
const QString cleanedValue = cleanExpression(expression, scriptBinding);
QVariant castedExpression;
ExpressionStatement *expStatement = cast<ExpressionStatement*>(scriptBinding->statement);
switch(expStatement->expression->kind) {
case Node::Kind_NumericLiteral:
case Node::Kind_UnaryPlusExpression:
case Node::Kind_UnaryMinusExpression:
castedExpression = QVariant(cleanedValue).toReal();
break;
case Node::Kind_StringLiteral:
castedExpression = QVariant(cleanedValue).toString();
break;
case Node::Kind_TrueLiteral:
case Node::Kind_FalseLiteral:
castedExpression = QVariant(cleanedValue).toBool();
break;
default:
castedExpression = cleanedValue;
break;
}
return castedExpression;
}
void Delta::updateMethodBody(const QDeclarativeDebugObjectReference &objectReference,
UiScriptBinding *scriptBinding,
const QString &methodName,
const QString &methodBody)
{
Change change;
change.script = scriptBinding;
change.ref = objectReference;
change.isLiteral = false;
_changes.append(change);
ClientProxy::instance()->setMethodBodyForObject(objectReference.debugId(), methodName, methodBody); // ### remove
}
void Delta::updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
UiScriptBinding *scriptBinding,
const QString &propertyName,
const QString &scriptCode)
{
if (doNotSendChanges)
return;
QVariant expr = scriptCode;
const bool isLiteral = isLiteralValue(scriptBinding);
if (isLiteral)
expr = castToLiteral(scriptCode, scriptBinding);
Change change;
change.script = scriptBinding;
change.ref = objectReference;
change.isLiteral = isLiteral;
_changes.append(change);
ClientProxy::instance()->setBindingForObject(objectReference.debugId(), propertyName, expr, isLiteral); // ### remove
}
bool Delta::compare(UiQualifiedId *id, UiQualifiedId *other)
{
if (id == other)
return true;
else if (id && other) {
if (id->name && other->name) {
if (id->name->asString() == other->name->asString())
return compare(id->next, other->next);
}
}
return false;
}
bool Delta::compare(UiSourceElement *source, UiSourceElement *other)
{
if (source == other)
return true;
else if (source && other) {
if (source->sourceElement && other->sourceElement) {
FunctionDeclaration *decl = cast<FunctionDeclaration*>(source->sourceElement);
FunctionDeclaration *otherDecl = cast<FunctionDeclaration*>(other->sourceElement);
if (decl && otherDecl
&& decl->name && otherDecl->name
&& decl->name->asString() == otherDecl->name->asString())
{
return true;
}
}
}
return false;
}
Document::Ptr Delta::document() const
{
return _doc;
return m_currentDoc;
}
Document::Ptr Delta::previousDocument() const
{
return _previousDoc;
return m_previousDoc;
}
QList<Delta::Change> Delta::changes() const
{
return _changes;
}
void QmlJSInspector::Internal::Delta::createObject(const QString &, const QDeclarativeDebugObjectReference &,
const QStringList &, const QString&)
{}
void QmlJSInspector::Internal::Delta::removeObject(int)
{}
void QmlJSInspector::Internal::Delta::resetBindingForObject(int, const QString &)
{}
void QmlJSInspector::Internal::Delta::updateMethodBody(const QDeclarativeDebugObjectReference &,
UiScriptBinding *, const QString &, const QString &)
{}
void QmlJSInspector::Internal::Delta::updateScriptBinding(const QDeclarativeDebugObjectReference &,
UiScriptBinding *, const QString &, const QString &)
{}
bool Delta::referenceRefreshRequired() const
{
return _referenceRefreshRequired;
}
......@@ -46,34 +46,15 @@ namespace Internal {
class Delta
{
public:
Delta() : doNotSendChanges(false) {}
bool doNotSendChanges;
QSet<UiObjectMember *> newObjects;
struct Change {
Change(): script(0), isLiteral(false) {}
QmlJS::AST::UiScriptBinding *script;
bool isLiteral;
QDeclarativeDebugObjectReference ref;
};
typedef QHash< UiObjectMember*, QList<QDeclarativeDebugObjectReference > > DebugIdMap;
DebugIdMap operator()(const QmlJS::Document::Ptr &doc1, const QmlJS::Document::Ptr &doc2, const DebugIdMap &debugIds);
QList<Change> changes() const;
QmlJS::Document::Ptr document() const;
QmlJS::Document::Ptr previousDocument() const;
public:
bool referenceRefreshRequired() const;
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:
void insert(UiObjectMember *member, UiObjectMember *parentMember,
const QList<QDeclarativeDebugObjectReference> &debugReferences, const Document::Ptr &doc);
......@@ -82,22 +63,23 @@ private:
const QList<QDeclarativeDebugObjectReference >& debugReferences);
void remove(const QList< QDeclarativeDebugObjectReference > &debugReferences);
void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
protected:
virtual void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference,
QmlJS::AST::UiScriptBinding *scriptBinding,
const QString &propertyName,
const QString &scriptCode);
void updateMethodBody(const QDeclarativeDebugObjectReference &objectReference,
virtual void updateMethodBody(const QDeclarativeDebugObjectReference &objectReference,
UiScriptBinding *scriptBinding,
const QString &methodName,
const QString &methodBody);
virtual void resetBindingForObject(int debugId, const QString &propertyName);
virtual void removeObject(int debugId);
virtual void createObject(const QString &qmlText, const QDeclarativeDebugObjectReference &ref,
const QStringList &importList, const QString &filename);
private:
QmlJS::Document::Ptr _doc;
QmlJS::Document::Ptr _previousDoc;
QList<Change> _changes;
QUrl _url;
bool _referenceRefreshRequired;
QmlJS::Document::Ptr m_currentDoc;
QmlJS::Document::Ptr m_previousDoc;
};
} // namespace Internal
......
......@@ -269,7 +269,6 @@ void QmlJSLiveTextPreview::updateDebugIds(const QDeclarativeDebugObjectReference
m_debugIds = visitor.result;
if (doc != m_previousDoc) {
Delta delta;
delta.doNotSendChanges = true;
m_debugIds = delta(doc, m_previousDoc, m_debugIds);
}
}
......@@ -301,7 +300,6 @@ void QmlJSLiveTextPreview::updateDebugIds(const QDeclarativeDebugObjectReference
Delta::DebugIdMap debugIds = visitor.result;
if (doc != m_previousDoc) {
Delta delta;
delta.doNotSendChanges = true;
debugIds = delta(doc, m_previousDoc, debugIds);
}
for(Delta::DebugIdMap::const_iterator it2 = debugIds.constBegin();
......@@ -311,6 +309,141 @@ void QmlJSLiveTextPreview::updateDebugIds(const QDeclarativeDebugObjectReference
}
}
class UpdateObserver : public Delta {
private:
static inline QString stripQuotes(const QString &str)
{
if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
|| (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
return str.mid(1, str.length() - 2);
return str;
}
static inline QString deEscape(const QString &value)
{
QString result = value;
result.replace(QLatin1String("\\\\"), QLatin1String("\\"));
result.replace(QLatin1String("\\\""), QLatin1String("\""));
result.replace(QLatin1String("\\\t"), QLatin1String("\t"));
result.replace(QLatin1String("\\\r"), QLatin1String("\\\r"));
result.replace(QLatin1String("\\\n"), QLatin1String("\n"));
return result;
}
static QString cleanExpression(const QString &expression, UiScriptBinding *scriptBinding)
{
QString trimmedExpression = expression.trimmed();
if (ExpressionStatement *expStatement = cast<ExpressionStatement*>(scriptBinding->statement)) {
if (expStatement->semicolonToken.isValid())
trimmedExpression.chop(1);
}
return deEscape(stripQuotes(trimmedExpression));
}
static bool isLiteralValue(ExpressionNode *expr)
{
if (cast<NumericLiteral*>(expr))
return true;
else if (cast<StringLiteral*>(expr))
return true;
else if (UnaryPlusExpression *plusExpr = cast<UnaryPlusExpression*>(expr))
return isLiteralValue(plusExpr->expression);
else if (UnaryMinusExpression *minusExpr = cast<UnaryMinusExpression*>(expr))
return isLiteralValue(minusExpr->expression);
else if (cast<TrueLiteral*>(expr))
return true;
else if (cast<FalseLiteral*>(expr))
return true;
else
return false;
}
static inline bool isLiteralValue(UiScriptBinding *script)
{
if (!script || !script->statement)
return false;
ExpressionStatement *exprStmt = cast<ExpressionStatement *>(script->statement);
if (exprStmt)
return isLiteralValue(exprStmt->expression);
else
return false;
}
static QVariant castToLiteral(const QString &expression, UiScriptBinding *scriptBinding)
{
const QString cleanedValue = cleanExpression(expression, scriptBinding);
QVariant castedExpression;
ExpressionStatement *expStatement = cast<ExpressionStatement*>(scriptBinding->statement);
switch(expStatement->expression->kind) {
case Node::Kind_NumericLiteral:
case Node::Kind_UnaryPlusExpression:
case Node::Kind_UnaryMinusExpression:
castedExpression = QVariant(cleanedValue).toReal();
break;
case Node::Kind_StringLiteral:
castedExpression = QVariant(cleanedValue).toString();
break;
case Node::Kind_TrueLiteral:
case Node::Kind_FalseLiteral:
castedExpression = QVariant(cleanedValue).toBool();
break;
default:
castedExpression = cleanedValue;
break;
}
return castedExpression;
}
protected:
virtual void updateMethodBody(const QDeclarativeDebugObjectReference& objectReference,
UiScriptBinding* scriptBinding, const QString& methodName, const QString& methodBody)
{
Q_UNUSED(scriptBinding);
ClientProxy::instance()->setMethodBodyForObject(objectReference.debugId(), methodName, methodBody);
}
virtual void updateScriptBinding(const QDeclarativeDebugObjectReference& objectReference,
UiScriptBinding* scriptBinding, const QString& propertyName, const QString& scriptCode)
{
QVariant expr = scriptCode;
const bool isLiteral = isLiteralValue(scriptBinding);
if (isLiteral)
expr = castToLiteral(scriptCode, scriptBinding);
ClientProxy::instance()->setBindingForObject(objectReference.debugId(), propertyName, expr, isLiteral);
}
virtual void resetBindingForObject(int debugId, const QString &propertyName)
{
ClientProxy::instance()->resetBindingForObject(debugId, propertyName);
}
virtual void removeObject(int debugId)
{
ClientProxy::instance()->destroyQmlObject(debugId);
}
virtual void createObject(const QString& qmlText, const QDeclarativeDebugObjectReference& ref,
const QStringList& importList, const QString& filename)
{
referenceRefreshRequired = true;
ClientProxy::instance()->createQmlObject(qmlText, ref, importList, filename);
}
public:
UpdateObserver() : referenceRefreshRequired(false) {}
bool referenceRefreshRequired;
};
void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
{
if (doc->fileName() != m_previousDoc->fileName())
......@@ -325,10 +458,10 @@ void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()
&& doc->qmlProgram() && m_previousDoc->qmlProgram())
{
Delta delta;
UpdateObserver delta;
m_debugIds = delta(m_previousDoc, doc, m_debugIds);
if (delta.referenceRefreshRequired())
if (delta.referenceRefreshRequired)
ClientProxy::instance()->refreshObjectTree();
m_previousDoc = doc;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment