diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.cpp b/src/plugins/qmljsinspector/qmljsclientproxy.cpp index 777a609582417ef92ae09e817636f43f9bd21b76..7ab52f51d34b073f5704b260757a8717f65d6dfa 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.cpp +++ b/src/plugins/qmljsinspector/qmljsclientproxy.cpp @@ -113,6 +113,21 @@ bool ClientProxy::connectToViewer(const QString &host, quint16 port) return true; } +void ClientProxy::refreshObjectTree() +{ + if (!m_objectTreeQuery) { + m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this); + + if (!m_objectTreeQuery->isWaiting()) { + objectTreeFetched(); + } else { + connect(m_objectTreeQuery, + SIGNAL(stateChanged(QDeclarativeDebugQuery::State)), + SLOT(objectTreeFetched(QDeclarativeDebugQuery::State))); + } + } +} + void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds) { QList<QDeclarativeDebugObjectReference> selectedItems; @@ -126,10 +141,8 @@ void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds) // a) get some item and know its parent (although that's possible by adding it to a separate plugin) // b) add children to part of an existing tree. // So the only choice that remains is to update the complete tree when we have an unknown debug id. - if (!m_objectTreeQuery) { + if (!m_objectTreeQuery) m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this); - } - } } diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.h b/src/plugins/qmljsinspector/qmljsclientproxy.h index b231195d3a09faefbad4ad4eacad9f6382083e18..cfd2634c23d596d0acd59d7c4268ddca02353315 100644 --- a/src/plugins/qmljsinspector/qmljsclientproxy.h +++ b/src/plugins/qmljsinspector/qmljsclientproxy.h @@ -62,6 +62,7 @@ public: // returns the object references for the given url. QList<QDeclarativeDebugObjectReference> objectReferences(const QUrl &url = QUrl()) const; QDeclarativeDebugObjectReference objectReferenceForId(int debugId) const; + void refreshObjectTree(); bool isConnected() const; bool isUnconnected() const; diff --git a/src/plugins/qmljsinspector/qmljsdelta.cpp b/src/plugins/qmljsinspector/qmljsdelta.cpp index 35e9d7abca03d75e49130b0e4016ce97ffaa6f02..c4ee16aee2d3a113ddf4548cd838d7c4508313e8 100644 --- a/src/plugins/qmljsinspector/qmljsdelta.cpp +++ b/src/plugins/qmljsinspector/qmljsdelta.cpp @@ -300,6 +300,7 @@ void Delta::insert(UiObjectMember *member, UiObjectMember *parentMember, const Q foreach(const QDeclarativeDebugObjectReference &ref, debugReferences) { if (ref.debugId() != -1) { + _referenceRefreshRequired = true; ClientProxy::instance()->createQmlObject(qmlText, ref, importList, doc->fileName()); } } @@ -395,6 +396,7 @@ 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); @@ -635,3 +637,8 @@ QList<Delta::Change> Delta::changes() const { return _changes; } + +bool Delta::referenceRefreshRequired() const +{ + return _referenceRefreshRequired; +} diff --git a/src/plugins/qmljsinspector/qmljsdelta.h b/src/plugins/qmljsinspector/qmljsdelta.h index e4220e2fbeda4d7d7169f3a11bf16311b87dedf1..5cad4c68fd33d029bab35020e5aba01097ea70f7 100644 --- a/src/plugins/qmljsinspector/qmljsdelta.h +++ b/src/plugins/qmljsinspector/qmljsdelta.h @@ -64,6 +64,14 @@ public: 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); void updateScriptBinding(const QDeclarativeDebugObjectReference &objectReference, QmlJS::AST::UiScriptBinding *scriptBinding, const QString &propertyName, @@ -73,14 +81,6 @@ public: const QString &methodName, const QString &methodBody); - 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); - void update(UiObjectDefinition* oldObject, const QmlJS::Document::Ptr& oldDoc, UiObjectDefinition* newObject, const QmlJS::Document::Ptr& newDoc, const QList<QDeclarativeDebugObjectReference >& debugReferences); @@ -90,6 +90,7 @@ private: QmlJS::Document::Ptr _previousDoc; QList<Change> _changes; QUrl _url; + bool _referenceRefreshRequired; }; } // namespace Internal diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp index 1df09ebdc5fe2365bb5969fc557c01a5f340d29c..5a464f58e7dacc4c418325fad6ac64e159038329 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.cpp +++ b/src/plugins/qmljsinspector/qmljsinspector.cpp @@ -34,6 +34,8 @@ #include "qmljslivetextpreview.h" #include "qmljsprivateapi.h" +#include <qmljseditor/qmljseditorconstants.h> + #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsdocument.h> @@ -128,12 +130,6 @@ Inspector::Inspector(QObject *parent) QWidget *contextWidget = 0; m_context = new InspectorContext(contextWidget); - m_textPreview = new QmlJSLiveTextPreview(this); - - connect(m_textPreview, - SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), - SLOT(changeSelectedItems(QList<QDeclarativeDebugObjectReference>))); - connect(m_clientProxy, SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), SLOT(setSelectedItemsByObjectReference(QList<QDeclarativeDebugObjectReference>))); @@ -161,6 +157,9 @@ void Inspector::disconnectWidgets() void Inspector::disconnected() { + Core::EditorManager *em = Core::EditorManager::instance(); + disconnect(em, SIGNAL(editorAboutToClose(Core::IEditor*)), this, SLOT(removePreviewForEditor(Core::IEditor*))); + disconnect(em, SIGNAL(editorOpened(Core::IEditor*)), this, SLOT(createPreviewForEditor(Core::IEditor*))); resetViews(); updateMenuActions(); } @@ -201,7 +200,7 @@ void Inspector::pollInspector() const quint16 port = quint16(m_runConfigurationDebugData.serverPort); if (m_clientProxy->connectToViewer(host, port)) { - m_textPreview->updateDocuments(); + initializeDocuments(); m_connectionTimer->stop(); m_connectionAttempts = 0; } else if (m_connectionAttempts == MaxConnectionAttempts) { @@ -215,6 +214,57 @@ void Inspector::pollInspector() updateMenuActions(); } +QmlJS::ModelManagerInterface *Inspector::modelManager() +{ + return ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(); +} + +void Inspector::initializeDocuments() +{ + if (!modelManager()) + return; + + QmlJS::Snapshot snapshot = modelManager()->snapshot(); + Core::EditorManager *em = Core::EditorManager::instance(); + connect(em, SIGNAL(editorAboutToClose(Core::IEditor*)), SLOT(removePreviewForEditor(Core::IEditor*))); + connect(em, SIGNAL(editorOpened(Core::IEditor*)), SLOT(createPreviewForEditor(Core::IEditor*))); + + // initial update + foreach (QmlJS::Document::Ptr doc, snapshot) { + QmlJSLiveTextPreview *preview = new QmlJSLiveTextPreview(doc, this); + connect(preview, + SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), + SLOT(changeSelectedItems(QList<QDeclarativeDebugObjectReference>))); + m_textPreviews.insert(doc->fileName(), preview); + } +} + +void Inspector::removePreviewForEditor(Core::IEditor *oldEditor) +{ + if (QmlJSLiveTextPreview *preview = m_textPreviews.value(oldEditor->file()->fileName())) { + preview->unassociateEditor(oldEditor); + } +} + +void Inspector::createPreviewForEditor(Core::IEditor *newEditor) +{ + if (newEditor && newEditor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + QString filename = newEditor->file()->fileName(); + QmlJS::Document::Ptr doc = modelManager()->snapshot().document(filename); + + if (m_textPreviews.contains(filename)) { + m_textPreviews.value(filename)->associateEditor(newEditor); + } else { + + QmlJSLiveTextPreview *preview = new QmlJSLiveTextPreview(doc, this); + connect(preview, + SIGNAL(selectedItemsChanged(QList<QDeclarativeDebugObjectReference>)), + SLOT(changeSelectedItems(QList<QDeclarativeDebugObjectReference>))); + m_textPreviews.insert(newEditor->file()->fileName(), preview); + } + } +} + bool Inspector::setDebugConfigurationDataFromProject(ProjectExplorer::Project *projectToDebug) { if (!projectToDebug) { diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h index ea35ef0807c6bb4f292659609e66934ae6968dab..8c8174922d704cb11f4ee4fac9b8337387472cb2 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.h +++ b/src/plugins/qmljsinspector/qmljsinspector.h @@ -54,6 +54,10 @@ namespace Debugger { class DebuggerRunControl; } +namespace QmlJS { + class ModelManagerInterface; +} + namespace QmlJSInspector { namespace Internal { @@ -117,6 +121,9 @@ private slots: void disconnectWidgets(); void disconnected(); + void removePreviewForEditor(Core::IEditor *newEditor); + void createPreviewForEditor(Core::IEditor *newEditor); + private: Debugger::DebuggerRunControl *createDebuggerRunControl(ProjectExplorer::RunConfiguration *runConfig, const QString &executableFile = QString(), @@ -131,6 +138,9 @@ private: bool addQuotesForData(const QVariant &value) const; void resetViews(); + QmlJS::ModelManagerInterface *modelManager(); + void initializeDocuments(); + private: QWeakPointer<QDeclarativeEngineDebug> m_client; QmlProjectManager::QmlProjectRunConfigurationDebugData m_runConfigurationDebugData; @@ -144,7 +154,7 @@ private: ClientProxy *m_clientProxy; // Qml/JS integration - QmlJSLiveTextPreview *m_textPreview; + QHash<QString, QmlJSLiveTextPreview *> m_textPreviews; }; } // Internal diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp index a3f5a510cb0562f1931e5efca614d5b5d80ded41..b4307e644431efa2ed225bc55715d7abe27dac43 100644 --- a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp +++ b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp @@ -65,38 +65,56 @@ void MapObjectWithDebugReference::processRecursive(const QDeclarativeDebugObject } } -QmlJSLiveTextPreview::QmlJSLiveTextPreview(QObject *parent) : - QObject(parent) +QmlJS::ModelManagerInterface *QmlJSLiveTextPreview::modelManager() { - Core::EditorManager *editorManager = Core::EditorManager::instance(); - ClientProxy *clientProxy = ClientProxy::instance(); - connect(editorManager->instance(), - SIGNAL(currentEditorChanged(Core::IEditor*)), - SLOT(setEditor(Core::IEditor*))); - connect(clientProxy, - SIGNAL(objectTreeUpdated(QDeclarativeDebugObjectReference)), - SLOT(updateDebugIds(QDeclarativeDebugObjectReference))); + return ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(); } -QmlJS::ModelManagerInterface *QmlJSLiveTextPreview::modelManager() +void QmlJSLiveTextPreview::associateEditor(Core::IEditor *editor) { - return ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(); + if (editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + QmlJSEditor::Internal::QmlJSTextEditor* qmljsEditor = qobject_cast<QmlJSEditor::Internal::QmlJSTextEditor*>(editor->widget()); + if (qmljsEditor && !m_editors.contains(qmljsEditor)) { + m_editors << qmljsEditor; + connect(qmljsEditor, + SIGNAL(selectedElementsChanged(QList<int>,QString)), + SLOT(changeSelectedElements(QList<int>,QString))); + } + } } -void QmlJSLiveTextPreview::updateDocuments() +void QmlJSLiveTextPreview::unassociateEditor(Core::IEditor *oldEditor) { - if (!modelManager()) - return; + if (oldEditor && oldEditor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) { + QmlJSEditor::Internal::QmlJSTextEditor* qmljsEditor = qobject_cast<QmlJSEditor::Internal::QmlJSTextEditor*>(oldEditor->widget()); + if (qmljsEditor && m_editors.contains(qmljsEditor)) { + m_editors.removeOne(qmljsEditor); + disconnect(qmljsEditor, + SIGNAL(selectedElementsChanged(QList<int>,QString)), + this, + SLOT(changeSelectedElements(QList<int>,QString))); + } + } +} - m_snapshot = modelManager()->snapshot(); - setEditor(Core::EditorManager::instance()->currentEditor()); +QmlJSLiveTextPreview::QmlJSLiveTextPreview(QmlJS::Document::Ptr doc, QObject *parent) : + QObject(parent), m_previousDoc(doc) +{ + ClientProxy *clientProxy = ClientProxy::instance(); + m_filename = doc->fileName(); - // initial update - foreach (QmlJS::Document::Ptr doc, m_snapshot) { - documentChanged(doc); - } connect(modelManager(), SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)), SLOT(documentChanged(QmlJS::Document::Ptr))); + + connect(clientProxy, + SIGNAL(objectTreeUpdated(QDeclarativeDebugObjectReference)), + SLOT(updateDebugIds(QDeclarativeDebugObjectReference))); + + Core::EditorManager *em = Core::EditorManager::instance(); + QList<Core::IEditor *> editors = em->editorsForFileName(m_filename); + + foreach(Core::IEditor *editor, editors) + associateEditor(editor); } QList<QDeclarativeDebugObjectReference > QmlJSLiveTextPreview::objectReferencesForOffset(quint32 offset) const @@ -116,12 +134,9 @@ QList<QDeclarativeDebugObjectReference > QmlJSLiveTextPreview::objectReferencesF void QmlJSLiveTextPreview::changeSelectedElements(QList<int> offsets, const QString &wordAtCursor) { - if (!m_currentEditor || !m_previousDoc) + if (m_editors.isEmpty() || !m_previousDoc) return; - if (m_debugIds.isEmpty()) - m_debugIds = m_initialTable.value(m_previousDoc->fileName()); - ClientProxy *clientProxy = ClientProxy::instance(); QDeclarativeDebugObjectReference objectRefUnderCursor; @@ -160,47 +175,26 @@ void QmlJSLiveTextPreview::changeSelectedElements(QList<int> offsets, const QStr emit selectedItemsChanged(selectedReferences); } -void QmlJSLiveTextPreview::setEditor(Core::IEditor *editor) +void QmlJSLiveTextPreview::updateDebugIds(const QDeclarativeDebugObjectReference &rootReference) { - if (!m_currentEditor.isNull()) { - disconnect(m_currentEditor.data(), SIGNAL(selectedElementsChanged(QList<int>, QString)), this, SLOT(changeSelectedElements(QList<int>, QString))); - m_currentEditor.clear(); - m_previousDoc.clear(); - m_debugIds.clear(); - } + QmlJS::Document::Ptr doc = m_previousDoc; - if (editor) { - m_currentEditor = qobject_cast<QmlJSEditor::Internal::QmlJSTextEditor*>(editor->widget()); - if (m_currentEditor) { - connect(m_currentEditor.data(), SIGNAL(selectedElementsChanged(QList<int>, QString)), SLOT(changeSelectedElements(QList<int>, QString))); - m_previousDoc = m_snapshot.document(editor->file()->fileName()); - m_debugIds = m_initialTable.value(editor->file()->fileName()); - } - } -} + if (!doc->qmlProgram()) + return; -void QmlJSLiveTextPreview::updateDebugIds(const QDeclarativeDebugObjectReference &rootReference) -{ - 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 = rootReference; - QString filename = doc->fileName(); - visitor.filename = filename; - doc->qmlProgram()->accept(&visitor); - allDebugIds[filename] = visitor.result; - } + MapObjectWithDebugReference visitor; + visitor.root = rootReference; + visitor.filename = doc->fileName(); + doc->qmlProgram()->accept(&visitor); - m_initialTable = allDebugIds; - m_debugIds.clear(); + m_debugIds = visitor.result; } void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc) -{ +{ + if (doc->fileName() != m_previousDoc->fileName()) + return; + Core::ICore *core = Core::ICore::instance(); const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE); @@ -210,12 +204,11 @@ void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc) if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName() && doc->qmlProgram() && m_previousDoc->qmlProgram()) { - if (m_debugIds.isEmpty()) - m_debugIds = m_initialTable.value(doc->fileName()); - Delta delta; - m_debugIds = delta(m_previousDoc, doc, m_debugIds); - m_initialTable[doc->fileName()] = m_debugIds; + m_debugIds = delta(m_previousDoc, doc, m_debugIds); + + if (delta.referenceRefreshRequired()) + ClientProxy::instance()->refreshObjectTree(); m_previousDoc = doc; } diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.h b/src/plugins/qmljsinspector/qmljslivetextpreview.h index a5e060bc937432ab028e5abbabc4b10f9fcd441a..ce6f5171d348f8fcdd47c597033090371ca6e1fe 100644 --- a/src/plugins/qmljsinspector/qmljslivetextpreview.h +++ b/src/plugins/qmljsinspector/qmljslivetextpreview.h @@ -33,16 +33,15 @@ class QmlJSLiveTextPreview : public QObject Q_OBJECT public: - explicit QmlJSLiveTextPreview(QObject *parent = 0); + explicit QmlJSLiveTextPreview(QmlJS::Document::Ptr doc, QObject *parent = 0); static QmlJS::ModelManagerInterface *modelManager(); - void updateDocuments(); + //void updateDocuments(); + void associateEditor(Core::IEditor *editor); + void unassociateEditor(Core::IEditor *editor); 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); @@ -57,10 +56,13 @@ private: QVariant castToLiteral(const QString &expression, QmlJS::AST::UiScriptBinding *scriptBinding); private: + QHash<QmlJS::AST::UiObjectMember*, QList<QDeclarativeDebugObjectReference> > m_initialTable; + QHash<QmlJS::AST::UiObjectMember*, QList<QDeclarativeDebugObjectReference> > m_debugIds; + QmlJS::Document::Ptr m_previousDoc; + QString m_filename; - QmlJS::Snapshot m_snapshot; - QWeakPointer<QmlJSEditor::Internal::QmlJSTextEditor> m_currentEditor; + QList<QWeakPointer<QmlJSEditor::Internal::QmlJSTextEditor> > m_editors; };