diff --git a/src/libs/qmldebug/basetoolsclient.h b/src/libs/qmldebug/basetoolsclient.h index ad6aa3dee87..436430dcbb3 100644 --- a/src/libs/qmldebug/basetoolsclient.h +++ b/src/libs/qmldebug/basetoolsclient.h @@ -44,7 +44,7 @@ public: BaseToolsClient(QmlDebugConnection *client, QLatin1String clientName); virtual void setCurrentObjects(const QList &debugIds) = 0; - virtual void reloadViewer() = 0; + virtual void reload(const QHash &changesHash) = 0; virtual void setDesignModeBehavior(bool inDesignMode) = 0; virtual void setAnimationSpeed(qreal slowDownFactor) = 0; virtual void setAnimationPaused(bool paused) = 0; @@ -81,7 +81,7 @@ signals: void animationPausedChanged(bool paused); void designModeBehaviorChanged(bool inDesignMode); void showAppOnTopChanged(bool showAppOnTop); - void reloaded(); // the server has reloadetd he document + void reloaded(); // the server has reloaded the document void logActivity(QString client, QString message); diff --git a/src/libs/qmldebug/declarativetoolsclient.cpp b/src/libs/qmldebug/declarativetoolsclient.cpp index 41fa6407066..5d8b0ec94f3 100644 --- a/src/libs/qmldebug/declarativetoolsclient.cpp +++ b/src/libs/qmldebug/declarativetoolsclient.cpp @@ -306,8 +306,11 @@ void DeclarativeToolsClient::clearComponentCache() sendMessage(message); } -void DeclarativeToolsClient::reloadViewer() +void DeclarativeToolsClient::reload(const QHash &changesHash) { + Q_UNUSED(changesHash); + if (!m_connection || !m_connection->isConnected()) return; diff --git a/src/libs/qmldebug/declarativetoolsclient.h b/src/libs/qmldebug/declarativetoolsclient.h index f7b4d6fd2f3..69f533b39e2 100644 --- a/src/libs/qmldebug/declarativetoolsclient.h +++ b/src/libs/qmldebug/declarativetoolsclient.h @@ -43,7 +43,7 @@ public: DeclarativeToolsClient(QmlDebugConnection *client); void setCurrentObjects(const QList &debugIds); - void reloadViewer(); + void reload(const QHash &changesHash); void setDesignModeBehavior(bool inDesignMode); void setAnimationSpeed(qreal slowDownFactor); void setAnimationPaused(bool paused); diff --git a/src/libs/qmldebug/qmltoolsclient.cpp b/src/libs/qmldebug/qmltoolsclient.cpp index 22bcb9b45c6..c1f74f70ec5 100644 --- a/src/libs/qmldebug/qmltoolsclient.cpp +++ b/src/libs/qmldebug/qmltoolsclient.cpp @@ -66,7 +66,8 @@ QmlToolsClient::QmlToolsClient(QmlDebugConnection *client) : BaseToolsClient(client, QLatin1String("QmlInspector")), m_connection(client), m_requestId(0), - m_slowDownFactor(1) + m_slowDownFactor(1), + m_reloadQueryId(-1) { setObjectName(name()); } @@ -82,6 +83,10 @@ void QmlToolsClient::messageReceived(const QByteArray &message) if (type == QByteArray(RESPONSE)) { bool success = false; ds >> success; + + if ((m_reloadQueryId != -1) && (m_reloadQueryId == requestId) && success) + emit reloaded(); + log(LogReceive, type, QString(QLatin1String("requestId: %1 success: %2")) .arg(QString::number(requestId)).arg(QString::number(success))); } else if (type == QByteArray(EVENT)) { @@ -150,15 +155,17 @@ void QmlToolsClient::clearComponentCache() sendMessage(message); } -void QmlToolsClient::reloadViewer() +void QmlToolsClient::reload(const QHash &changesHash) { if (!m_connection || !m_connection->isConnected()) return; + m_reloadQueryId = m_requestId; + QByteArray message; QDataStream ds(&message, QIODevice::WriteOnly); ds << QByteArray(REQUEST) << m_requestId++ - << QByteArray(RELOAD); + << QByteArray(RELOAD) << changesHash; log(LogSend, RELOAD); diff --git a/src/libs/qmldebug/qmltoolsclient.h b/src/libs/qmldebug/qmltoolsclient.h index 1e26068d387..67dfeb2308d 100644 --- a/src/libs/qmldebug/qmltoolsclient.h +++ b/src/libs/qmldebug/qmltoolsclient.h @@ -43,7 +43,7 @@ public: explicit QmlToolsClient(QmlDebugConnection *client); void setCurrentObjects(const QList &debugIds); - void reloadViewer(); + void reload(const QHash &changesHash); void setDesignModeBehavior(bool inDesignMode); void setAnimationSpeed(qreal slowDownFactor); void setAnimationPaused(bool paused); @@ -81,6 +81,7 @@ private: QmlDebugConnection *m_connection; int m_requestId; qreal m_slowDownFactor; + int m_reloadQueryId; }; } // namespace QmlDebug diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.cpp b/src/plugins/debugger/qml/qmlinspectoradapter.cpp index 2ce7fd86adb..2bef2681e39 100644 --- a/src/plugins/debugger/qml/qmlinspectoradapter.cpp +++ b/src/plugins/debugger/qml/qmlinspectoradapter.cpp @@ -202,6 +202,7 @@ void QmlInspectorAdapter::toolsClientStatusChanged(QmlDebug::ClientStatus status SLOT(selectObjectsFromToolsClient(QList))); connect(client, SIGNAL(logActivity(QString,QString)), m_debugAdapter, SLOT(logServiceActivity(QString,QString))); + connect(client, SIGNAL(reloaded()), SLOT(onReloaded())); // only enable zoom action for Qt 4.x/old client // (zooming is integrated into selection tool in Qt 5). @@ -340,14 +341,14 @@ void QmlInspectorAdapter::createPreviewForEditor(Core::IEditor *newEditor) QmlJS::ModelManagerInterface::instance(); QmlJS::Document::Ptr doc = modelManager->snapshot().document(filename); if (!doc) { - if (filename.endsWith(".qml")) { + if (filename.endsWith(".qml") || filename.endsWith(".js")) { // add to list of docs that we have to update when // snapshot figures out that there's a new document m_pendingPreviewDocumentNames.append(filename); } return; } - if (!doc->qmlProgram()) + if (!doc->qmlProgram() && !filename.endsWith(".js")) return; QmlJS::Document::Ptr initdoc = m_loadedSnapshot.document(filename); @@ -366,6 +367,8 @@ void QmlInspectorAdapter::createPreviewForEditor(Core::IEditor *newEditor) preview->setApplyChangesToQmlInspector( debuggerCore()->action(QmlUpdateOnSave)->isChecked()); + connect(preview, SIGNAL(reloadRequest()), + this, SLOT(onReload())); m_textPreviews.insert(newEditor->document()->fileName(), preview); preview->associateEditor(newEditor); @@ -606,5 +609,39 @@ void QmlInspectorAdapter::selectObject(const ObjectReference &obj, emit selectionChanged(); } +void QmlInspectorAdapter::onReload() +{ + QHash changesHash; + for (QHash::const_iterator it + = m_textPreviews.constBegin(); + it != m_textPreviews.constEnd(); ++it) { + if (it.value()->hasUnsynchronizableChange()) { + QFileInfo info = QFileInfo(it.value()->fileName()); + QFile changedFile(it.value()->fileName()); + QByteArray fileContents; + if (changedFile.open(QFile::ReadOnly)) + fileContents = changedFile.readAll(); + changedFile.close(); + changesHash.insert(info.fileName(), + fileContents); + } + } + m_toolsClient->reload(changesHash); +} + +void QmlInspectorAdapter::onReloaded() +{ + QmlJS::ModelManagerInterface *modelManager = + QmlJS::ModelManagerInterface::instance(); + QmlJS::Snapshot snapshot = modelManager->snapshot(); + m_loadedSnapshot = snapshot; + for (QHash::const_iterator it + = m_textPreviews.constBegin(); + it != m_textPreviews.constEnd(); ++it) { + QmlJS::Document::Ptr doc = snapshot.document(it.key()); + it.value()->resetInitialDoc(doc); + } +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.h b/src/plugins/debugger/qml/qmlinspectoradapter.h index 7ad99bc0d9b..221813d1b77 100644 --- a/src/plugins/debugger/qml/qmlinspectoradapter.h +++ b/src/plugins/debugger/qml/qmlinspectoradapter.h @@ -97,6 +97,8 @@ private slots: void onZoomActionTriggered(bool checked); void onShowAppOnTopChanged(const QVariant &value); void onUpdateOnSaveChanged(const QVariant &value); + void onReload(); + void onReloaded(); private: void setActiveEngineClient(QmlDebug::BaseEngineDebugClient *client); diff --git a/src/plugins/debugger/qml/qmllivetextpreview.cpp b/src/plugins/debugger/qml/qmllivetextpreview.cpp index ca3eae5061d..bf3538315aa 100644 --- a/src/plugins/debugger/qml/qmllivetextpreview.cpp +++ b/src/plugins/debugger/qml/qmllivetextpreview.cpp @@ -50,6 +50,8 @@ using namespace QmlJS::AST; namespace Debugger { namespace Internal { +const char INFO_OUT_OF_SYNC[] = "Debugger.Inspector.OutOfSyncWarning"; + /*! Associates the UiObjectMember* to their QDeclarativeDebugObjectReference. */ @@ -351,6 +353,7 @@ QmlLiveTextPreview::QmlLiveTextPreview(const QmlJS::Document::Ptr &doc, , m_inspectorAdapter(inspectorAdapter) , m_nodeForOffset(0) , m_updateNodeForOffset(false) + , m_changesUnsynchronizable(false) { QTC_CHECK(doc->fileName() == initDoc->fileName()); @@ -413,6 +416,13 @@ void QmlLiveTextPreview::resetInitialDoc(const QmlJS::Document::Ptr &doc) m_createdObjects.clear(); m_debugIds.clear(); m_docWithUnappliedChanges.clear(); + m_changesUnsynchronizable = false; + removeOutofSyncInfo(); +} + +const QString QmlLiveTextPreview::fileName() +{ + return m_previousDoc->fileName(); } void QmlLiveTextPreview::setApplyChangesToQmlInspector(bool applyChanges) @@ -582,27 +592,36 @@ void QmlLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc) if (m_applyChangesToQmlInspector) { m_docWithUnappliedChanges.clear(); - if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName() - && doc->qmlProgram() && m_previousDoc->qmlProgram()) - { - UpdateInspector delta(m_inspectorAdapter); - m_debugIds = delta(m_previousDoc, doc, m_debugIds); + if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()) { + if (doc->fileName().endsWith(".js")) { + m_changesUnsynchronizable = true; + showSyncWarning(JSChangeWarning, QString(), -1, -1); + m_previousDoc = doc; + return; + } + if (doc->qmlProgram() && m_previousDoc->qmlProgram()) { + UpdateInspector delta(m_inspectorAdapter); + m_debugIds = delta(m_previousDoc, doc, m_debugIds); - if (delta.referenceRefreshRequired) - m_inspectorAdapter->agent()->refreshObjectTree(); + if (delta.referenceRefreshRequired) + m_inspectorAdapter->agent()->refreshObjectTree(); - if (delta.unsyncronizableChanges != NoUnsyncronizableChanges) - showSyncWarning(delta.unsyncronizableChanges, - delta.unsyncronizableElementName, - delta.unsyncronizableChangeLine, - delta.unsyncronizableChangeColumn); + if (delta.unsyncronizableChanges != NoUnsyncronizableChanges) { + m_changesUnsynchronizable = true; + showSyncWarning(delta.unsyncronizableChanges, + delta.unsyncronizableElementName, + delta.unsyncronizableChangeLine, + delta.unsyncronizableChangeColumn); + m_previousDoc = doc; + return; + } + m_previousDoc = doc; + if (!delta.newObjects.isEmpty()) + m_createdObjects[doc] += delta.newObjects; - m_previousDoc = doc; - if (!delta.newObjects.isEmpty()) - m_createdObjects[doc] += delta.newObjects; - - m_inspectorAdapter->toolsClient()->clearComponentCache(); + m_inspectorAdapter->toolsClient()->clearComponentCache(); + } } } else { m_docWithUnappliedChanges = doc; @@ -655,6 +674,10 @@ void QmlLiveTextPreview::showSyncWarning( "changed without reloading the QML application. ") .arg(elementName, QString::number(line), QString::number(column)); break; + case JSChangeWarning: + errorMessage = tr("The changes in Java script cannot be applied " + "without reloading the QML application. "); + break; case QmlLiveTextPreview::NoUnsyncronizableChanges: default: return; @@ -662,12 +685,33 @@ void QmlLiveTextPreview::showSyncWarning( errorMessage.append(tr("You can continue debugging, but behavior can be unexpected.")); + // Clear infobars if present before showing the same. Otherwise multiple infobars + // will be shown in case the user changes and saves the file multiple times. + removeOutofSyncInfo(); + foreach (TextEditor::BaseTextEditorWidget *editor, m_editors) { if (editor) { Core::InfoBar *infoBar = editor->editorDocument()->infoBar(); - infoBar->addInfo(Core::InfoBarEntry( - QLatin1String("Debugger.Inspector.OutOfSyncWarning"), - errorMessage)); + Core::InfoBarEntry info(INFO_OUT_OF_SYNC, errorMessage); + info.setCustomButtonInfo(tr("Reload QML"), this, + SLOT(reloadQml())); + infoBar->addInfo(info); + } + } +} + +void QmlLiveTextPreview::reloadQml() +{ + removeOutofSyncInfo(); + emit reloadRequest(); +} + +void QmlLiveTextPreview::removeOutofSyncInfo() +{ + foreach (TextEditor::BaseTextEditorWidget *editor, m_editors) { + if (editor) { + Core::InfoBar *infoBar = editor->editorDocument()->infoBar(); + infoBar->removeInfo(INFO_OUT_OF_SYNC); } } } diff --git a/src/plugins/debugger/qml/qmllivetextpreview.h b/src/plugins/debugger/qml/qmllivetextpreview.h index ef4936cf0cb..adebbf2fd44 100644 --- a/src/plugins/debugger/qml/qmllivetextpreview.h +++ b/src/plugins/debugger/qml/qmllivetextpreview.h @@ -65,15 +65,19 @@ public: void associateEditor(Core::IEditor *editor); void unassociateEditor(Core::IEditor *editor); void resetInitialDoc(const QmlJS::Document::Ptr &doc); + const QString fileName(); + bool hasUnsynchronizableChange() { return m_changesUnsynchronizable; } signals: void selectedItemsChanged(const QList &debugIds); void fetchObjectsForLocation(const QString &file, int lineNumber, int columnNumber); + void reloadRequest(); public slots: void setApplyChangesToQmlInspector(bool applyChanges); void updateDebugIds(); + void reloadQml(); private slots: void changeSelectedElements(const QList offsets, @@ -84,7 +88,8 @@ private: enum UnsyncronizableChangeType { NoUnsyncronizableChanges, AttributeChangeWarning, - ElementChangeWarning + ElementChangeWarning, + JSChangeWarning }; bool changeSelectedElements(const QList offsets, const QString &wordAtCursor); @@ -92,6 +97,7 @@ private: void showSyncWarning(UnsyncronizableChangeType unsyncronizableChangeType, const QString &elementName, unsigned line, unsigned column); + void removeOutofSyncInfo(); private: QHash > m_debugIds; @@ -108,6 +114,7 @@ private: QList m_lastOffsets; QmlJS::AST::UiObjectMember *m_nodeForOffset; bool m_updateNodeForOffset; + bool m_changesUnsynchronizable; friend class UpdateInspector; };