QML: Remove Apply on Save functionality

The feature has been actually been disabled since commit ac771eb552,
but now it's time to also remove the dead code ...

Fixing the feature and bringing it out of the experimental state
would require quite some effort that apparently nobody is willing
to spend. So it's better to remove it.

The enablers in the qmldebug library, as well as the QmlJSDelta utility
class in qmljs library, are left in though.

Change-Id: Idf98a2f946d0db86bef2f20d2349d6ffedba219c
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
Kai Koehne
2015-03-16 14:48:13 +01:00
committed by hjk
parent 6f7e7a8b93
commit 3bf81efbe7
13 changed files with 2 additions and 1107 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

View File

@@ -168,17 +168,6 @@
\section1 Applying QML Changes at Runtime
\omit
// currently broken & disabled
If you change property values or add properties in the code editor, the
debugger can update the properties in the running application when you save
the file. This is enabled by default. To disable it, click the
\inlineimage qml-observer-bar-reload.png "Apply Changes on Save button"
(\uicontrol {Apply Changes on Save}) button on the toolbar.
\endomit
When you change property values in the \uicontrol {QML/JS Console} or in the
\uicontrol {Locals and Expressions} view, they are immediately updated in the running
application, but not in the source code.

View File

@@ -153,7 +153,6 @@ QtcPlugin {
"qmlengine.cpp", "qmlengine.h",
"qmlinspectoradapter.cpp", "qmlinspectoradapter.h",
"qmlinspectoragent.cpp", "qmlinspectoragent.h",
"qmllivetextpreview.cpp", "qmllivetextpreview.h",
"qmlv8debuggerclient.cpp", "qmlv8debuggerclient.h",
"qmlv8debuggerclientconstants.h",
"qscriptdebuggerclient.cpp", "qscriptdebuggerclient.h"

View File

@@ -655,11 +655,6 @@ DebuggerSettings::DebuggerSettings()
item->setSettingsKey(qmlInspectorGroup, QLatin1String("QmlInspector.ShowAppOnTop"));
item->setDefaultValue(false);
insertItem(ShowAppOnTop, item);
item = new SavedAction(this);
item->setSettingsKey(qmlInspectorGroup, QLatin1String("QmlInspector.FromQml"));
item->setDefaultValue(false);
insertItem(QmlUpdateOnSave, item);
}
DebuggerSettings::~DebuggerSettings()

View File

@@ -185,8 +185,7 @@ enum DebuggerActionCode
// QML Tools
ShowQmlObjectTree,
ShowAppOnTop,
QmlUpdateOnSave
ShowAppOnTop
};
} // namespace Internal

View File

@@ -63,7 +63,6 @@ const char RESET[] = "Debugger.Reset";
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
const char OPERATE_NATIVE_MIXED[] = "Debugger.OperateNativeMixed";
const char QML_SHOW_APP_ON_TOP[] = "Debugger.QmlShowAppOnTop";
const char QML_UPDATE_ON_SAVE[] = "Debugger.QmlUpdateOnSave";
const char QML_SELECTTOOL[] = "Debugger.QmlSelectTool";
const char QML_ZOOMTOOL[] = "Debugger.QmlZoomTool";

View File

@@ -958,9 +958,6 @@ public slots:
bool parseArguments(const QStringList &args, QString *errorMessage);
void parseCommandLineArguments();
void updateQmlActions() {
action(QmlUpdateOnSave)->setEnabled(boolSetting(ShowQmlObjectTree));
}
public:
DebuggerMainWindow *m_mainWindow;
@@ -2957,11 +2954,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
connect(action(SettingsDialog), &QAction::triggered,
[] { ICore::showOptionsDialog(DEBUGGER_COMMON_SETTINGS_ID); });
// QML Actions
connect(action(ShowQmlObjectTree), &SavedAction::valueChanged,
this, &DebuggerPluginPrivate::updateQmlActions);
updateQmlActions();
// Toolbar
QWidget *toolbarContainer = new QWidget;

View File

@@ -8,7 +8,6 @@ HEADERS += \
$$PWD/interactiveinterpreter.h \
$$PWD/qmlv8debuggerclientconstants.h \
$$PWD/qmlinspectoragent.h \
$$PWD/qmllivetextpreview.h \
$$PWD/qmlinspectoradapter.h
SOURCES += \
@@ -20,5 +19,4 @@ SOURCES += \
$$PWD/qmlv8debuggerclient.cpp \
$$PWD/interactiveinterpreter.cpp \
$$PWD/qmlinspectoragent.cpp \
$$PWD/qmllivetextpreview.cpp \
$$PWD/qmlinspectoradapter.cpp

View File

@@ -40,6 +40,7 @@
#include <qmldebug/qdebugmessageclient.h>
#include <qmldebug/qmloutputparser.h>
#include <qmljs/iscriptevaluator.h>
#include <qmljs/qmljsdocument.h>
QT_FORWARD_DECLARE_CLASS(QTextDocument)

View File

@@ -32,7 +32,6 @@
#include "qmladapter.h"
#include "qmlinspectoragent.h"
#include "qmllivetextpreview.h"
#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggerstringutils.h>
@@ -48,8 +47,6 @@
#include <qmldebug/declarativetoolsclient.h>
#include <qmldebug/qmlenginedebugclient.h>
#include <qmldebug/qmltoolsclient.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
@@ -75,13 +72,11 @@ QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
, m_targetToSync(NoTarget)
, m_debugIdToSelect(-1)
, m_currentSelectedDebugId(-1)
, m_listeningToEditorManager(false)
, m_toolsClientConnected(false)
, m_inspectorToolsContext("Debugger.QmlInspector")
, m_selectAction(new QAction(this))
, m_zoomAction(new QAction(this))
, m_showAppOnTopAction(action(ShowAppOnTop))
, m_updateOnSaveAction(action(QmlUpdateOnSave))
, m_engineClientConnected(false)
{
if (!m_engine->isMasterEngine())
@@ -143,11 +138,9 @@ QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
m_selectAction->setCheckable(true);
m_zoomAction->setCheckable(true);
m_showAppOnTopAction->setCheckable(true);
m_updateOnSaveAction->setCheckable(true);
m_selectAction->setEnabled(false);
m_zoomAction->setEnabled(false);
m_showAppOnTopAction->setEnabled(false);
m_updateOnSaveAction->setEnabled(false);
connect(m_selectAction, SIGNAL(triggered(bool)),
SLOT(onSelectActionTriggered(bool)));
@@ -155,8 +148,6 @@ QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
SLOT(onZoomActionTriggered(bool)));
connect(m_showAppOnTopAction, SIGNAL(triggered(bool)),
SLOT(onShowAppOnTopChanged(bool)));
connect(m_updateOnSaveAction, SIGNAL(triggered(bool)),
SLOT(onUpdateOnSaveChanged(bool)));
}
QmlInspectorAdapter::~QmlInspectorAdapter()
@@ -225,9 +216,6 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
Core::ActionManager::registerAction(m_showAppOnTopAction,
Core::Id(Constants::QML_SHOW_APP_ON_TOP),
m_inspectorToolsContext);
Core::ActionManager::registerAction(m_updateOnSaveAction,
Core::Id(Constants::QML_UPDATE_ON_SAVE),
m_inspectorToolsContext);
Core::ICore::addAdditionalContext(m_inspectorToolsContext);
@@ -246,8 +234,6 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
Core::ActionManager::unregisterAction(m_zoomAction, Core::Id(Constants::QML_ZOOMTOOL));
Core::ActionManager::unregisterAction(m_showAppOnTopAction,
Core::Id(Constants::QML_SHOW_APP_ON_TOP));
Core::ActionManager::unregisterAction(m_updateOnSaveAction,
Core::Id(Constants::QML_UPDATE_ON_SAVE));
Core::ICore::removeAdditionalContext(m_inspectorToolsContext);
@@ -256,7 +242,6 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
m_selectAction->setCheckable(false);
m_zoomAction->setCheckable(false);
m_showAppOnTopAction->setCheckable(false);
m_updateOnSaveAction->setCheckable(false);
}
}
@@ -271,7 +256,6 @@ void QmlInspectorAdapter::engineClientStateChanged(QmlDebugClient::State state)
setActiveEngineClient(client);
} else if (m_engineClientConnected && client == m_engineClient) {
m_engineClientConnected = false;
deletePreviews();
}
}
@@ -293,85 +277,6 @@ void QmlInspectorAdapter::onObjectFetched(const ObjectReference &ref)
}
}
void QmlInspectorAdapter::createPreviewForEditor(Core::IEditor *newEditor)
{
if (!m_engineClientConnected)
return;
if (!newEditor || newEditor->document()->id()
!= QmlJSEditor::Constants::C_QMLJSEDITOR_ID)
return;
QString filename = newEditor->document()->filePath().toString();
QmlJS::ModelManagerInterface *modelManager =
QmlJS::ModelManagerInterface::instance();
if (modelManager) {
QmlJS::Document::Ptr doc = modelManager->snapshot().document(filename);
if (!doc) {
if (filename.endsWith(QLatin1String(".qml")) || filename.endsWith(QLatin1String(".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() && !filename.endsWith(QLatin1String(".js")))
return;
QmlJS::Document::Ptr initdoc = m_loadedSnapshot.document(filename);
if (!initdoc)
initdoc = doc;
if (m_textPreviews.contains(filename)) {
QmlLiveTextPreview *preview = m_textPreviews.value(filename);
preview->associateEditor(newEditor);
} else {
QmlLiveTextPreview *preview
= new QmlLiveTextPreview(doc, initdoc, this, this);
preview->setApplyChangesToQmlInspector(action(QmlUpdateOnSave)->isChecked());
connect(preview, SIGNAL(reloadRequest()),
this, SLOT(onReload()));
m_textPreviews.insert(newEditor->document()->filePath().toString(), preview);
preview->associateEditor(newEditor);
preview->updateDebugIds();
}
}
}
void QmlInspectorAdapter::removePreviewForEditor(Core::IEditor *editor)
{
if (QmlLiveTextPreview *preview
= m_textPreviews.value(editor->document()->filePath().toString())) {
preview->unassociateEditor(editor);
}
}
void QmlInspectorAdapter::updatePendingPreviewDocuments(QmlJS::Document::Ptr doc)
{
int idx = -1;
idx = m_pendingPreviewDocumentNames.indexOf(doc->fileName());
if (idx == -1)
return;
QList<Core::IEditor *> editors
= Core::DocumentModel::editorsForFilePath(doc->fileName());
if (editors.isEmpty())
return;
m_pendingPreviewDocumentNames.removeAt(idx);
Core::IEditor *editor = editors.takeFirst();
createPreviewForEditor(editor);
QmlLiveTextPreview *preview
= m_textPreviews.value(editor->document()->filePath().toString());
foreach (Core::IEditor *editor, editors)
preview->associateEditor(editor);
}
void QmlInspectorAdapter::onSelectActionTriggered(bool checked)
{
QTC_ASSERT(toolsClient(), return);
@@ -402,16 +307,6 @@ void QmlInspectorAdapter::onShowAppOnTopChanged(bool checked)
toolsClient()->showAppOnTop(checked);
}
void QmlInspectorAdapter::onUpdateOnSaveChanged(bool checked)
{
QTC_ASSERT(toolsClient(), return);
for (QHash<QString, QmlLiveTextPreview *>::const_iterator it
= m_textPreviews.constBegin();
it != m_textPreviews.constEnd(); ++it) {
it.value()->setApplyChangesToQmlInspector(checked);
}
}
void QmlInspectorAdapter::setActiveEngineClient(BaseEngineDebugClient *client)
{
if (m_engineClient == client)
@@ -420,54 +315,6 @@ void QmlInspectorAdapter::setActiveEngineClient(BaseEngineDebugClient *client)
m_engineClient = client;
m_agent->setEngineClient(m_engineClient);
m_engineClientConnected = true;
if (m_engineClient &&
m_engineClient->state() == QmlDebugClient::Enabled) {
QmlJS::ModelManagerInterface *modelManager
= QmlJS::ModelManagerInterface::instance();
if (modelManager) {
QmlJS::Snapshot snapshot = modelManager->snapshot();
for (QHash<QString, QmlLiveTextPreview *>::const_iterator it
= m_textPreviews.constBegin();
it != m_textPreviews.constEnd(); ++it) {
QmlJS::Document::Ptr doc = snapshot.document(it.key());
it.value()->resetInitialDoc(doc);
}
initializePreviews();
}
}
}
void QmlInspectorAdapter::initializePreviews()
{
QmlJS::ModelManagerInterface *modelManager
= QmlJS::ModelManagerInterface::instance();
if (modelManager) {
m_loadedSnapshot = modelManager->snapshot();
if (!m_listeningToEditorManager) {
m_listeningToEditorManager = true;
QObject *em = Core::EditorManager::instance();
connect(em, SIGNAL(editorAboutToClose(Core::IEditor*)),
this, SLOT(removePreviewForEditor(Core::IEditor*)));
connect(em, SIGNAL(editorOpened(Core::IEditor*)),
this, SLOT(createPreviewForEditor(Core::IEditor*)));
connect(modelManager,
SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
this, SLOT(updatePendingPreviewDocuments(QmlJS::Document::Ptr)));
}
// initial update
foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments()) {
QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document);
createPreviewForEditor(editors.takeFirst());
QmlLiveTextPreview *preview
= m_textPreviews.value(document->filePath().toString());
foreach (Core::IEditor *editor, editors)
preview->associateEditor(editor);
}
}
}
void QmlInspectorAdapter::showConnectionStateMessage(const QString &message)
@@ -500,60 +347,20 @@ void QmlInspectorAdapter::selectObject(const ObjectReference &obj,
agent()->selectObjectInTree(obj.debugId());
}
void QmlInspectorAdapter::deletePreviews()
{
qDeleteAll(m_textPreviews);
m_textPreviews.clear();
}
void QmlInspectorAdapter::enableTools(const bool enable)
{
if (!m_toolsClientConnected)
return;
m_selectAction->setEnabled(enable);
m_showAppOnTopAction->setEnabled(enable);
m_updateOnSaveAction->setEnabled(enable);
// only enable zoom action for Qt 4.x/old client
// (zooming is integrated into selection tool in Qt 5).
if (!qobject_cast<QmlToolsClient*>(m_toolsClient))
m_zoomAction->setEnabled(enable);
}
void QmlInspectorAdapter::onReload()
{
QHash<QString, QByteArray> changesHash;
for (QHash<QString, QmlLiveTextPreview *>::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);
}
}
if (m_toolsClient)
m_toolsClient->reload(changesHash);
}
void QmlInspectorAdapter::onReloaded()
{
QmlJS::ModelManagerInterface *modelManager =
QmlJS::ModelManagerInterface::instance();
if (modelManager) {
QmlJS::Snapshot snapshot = modelManager->snapshot();
m_loadedSnapshot = snapshot;
for (QHash<QString, QmlLiveTextPreview *>::const_iterator it
= m_textPreviews.constBegin();
it != m_textPreviews.constEnd(); ++it) {
QmlJS::Document::Ptr doc = snapshot.document(it.key());
it.value()->resetInitialDoc(doc);
}
}
m_agent->reloadEngines();
}

View File

@@ -35,9 +35,6 @@
#include <coreplugin/icontext.h>
#include <qmldebug/qmldebugclient.h>
#include <qmljs/qmljsdocument.h>
namespace Core { class IEditor; }
namespace QmlDebug {
class BaseEngineDebugClient;
@@ -50,10 +47,8 @@ namespace Debugger {
namespace Internal {
class DebuggerEngine;
class WatchTreeView;
class QmlAdapter;
class QmlInspectorAgent;
class QmlLiveTextPreview;
class QmlInspectorAdapter : public QObject
{
@@ -84,15 +79,9 @@ private slots:
void selectObjectsFromToolsClient(const QList<int> &debugIds);
void onObjectFetched(const QmlDebug::ObjectReference &ref);
void createPreviewForEditor(Core::IEditor *newEditor);
void removePreviewForEditor(Core::IEditor *editor);
void updatePendingPreviewDocuments(QmlJS::Document::Ptr doc);
void onSelectActionTriggered(bool checked);
void onZoomActionTriggered(bool checked);
void onShowAppOnTopChanged(bool checked);
void onUpdateOnSaveChanged(bool checked);
void onReload();
void onReloaded();
void onDestroyedObject(int);
void jumpToObjectDefinitionInEditor(const QmlDebug::FileReference &objSource, int debugId = -1);
@@ -100,14 +89,12 @@ private slots:
private:
void setActiveEngineClient(QmlDebug::BaseEngineDebugClient *client);
void initializePreviews();
void showConnectionStateMessage(const QString &message);
enum SelectionTarget { NoTarget, ToolTarget, EditorTarget };
void selectObject(
const QmlDebug::ObjectReference &objectReference,
SelectionTarget target);
void deletePreviews();
void enableTools(const bool enable);
@@ -124,19 +111,12 @@ private:
int m_currentSelectedDebugId;
QString m_currentSelectedDebugName;
// Qml/JS editor integration
bool m_listeningToEditorManager;
QHash<QString, QmlLiveTextPreview *> m_textPreviews;
QmlJS::Snapshot m_loadedSnapshot; //the snapshot loaded by the viewer
QStringList m_pendingPreviewDocumentNames;
// toolbar
bool m_toolsClientConnected;
Core::Context m_inspectorToolsContext;
QAction *m_selectAction;
QAction *m_zoomAction;
QAction *m_showAppOnTopAction;
QAction *m_updateOnSaveAction;
bool m_engineClientConnected;
};

View File

@@ -1,743 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmllivetextpreview.h"
#include "qmlinspectoradapter.h"
#include "qmlinspectoragent.h"
#include <coreplugin/infobar.h>
#include <texteditor/textdocument.h>
#include <qmldebug/basetoolsclient.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsdelta.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/qtcassert.h>
using namespace QmlDebug;
using namespace QmlJS;
using namespace QmlJS::AST;
namespace Debugger {
namespace Internal {
const char INFO_OUT_OF_SYNC[] = "Debugger.Inspector.OutOfSyncWarning";
/*!
Associates the UiObjectMember* to their QDeclarativeDebugObjectReference.
*/
class MapObjectWithDebugReference : public Visitor
{
public:
typedef QList<int> DebugIdList;
MapObjectWithDebugReference() : activated(0) {}
virtual void endVisit(UiObjectDefinition *ast);
virtual void endVisit(UiObjectBinding *ast);
virtual bool visit(UiObjectDefinition *ast);
virtual bool visit(UiObjectBinding *ast);
QHash<QPair<int, int>, DebugIdList> ids;
QString filename;
QHash<UiObjectMember *, DebugIdList> result;
QSet<UiObjectMember *> lookupObjects;
private:
void process(UiObjectMember *ast);
void process(UiObjectBinding *ast);
private:
int activated;
};
class UpdateInspector : 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(DebugId debugId,
UiObjectMember *parentDefinition,
UiScriptBinding *scriptBinding,
const QString &methodName,
const QString &methodBody)
{
Q_UNUSED(scriptBinding);
Q_UNUSED(parentDefinition);
appliedChangesToViewer = true;
if (m_inspectorAdapter->engineClient())
m_inspectorAdapter->engineClient()->setMethodBody(debugId,
methodName, methodBody);
}
virtual void updateScriptBinding(DebugId debugId,
UiObjectMember *parentDefinition,
UiScriptBinding *scriptBinding,
const QString &propertyName,
const QString &scriptCode)
{
if (unsyncronizableChanges
== QmlLiveTextPreview::NoUnsyncronizableChanges) {
if (propertyName == QLatin1String("id")) {
unsyncronizableElementName = propertyName;
unsyncronizableChanges
= QmlLiveTextPreview::AttributeChangeWarning;
unsyncronizableChangeLine
= parentDefinition->firstSourceLocation().startLine;
unsyncronizableChangeColumn
= parentDefinition->firstSourceLocation().startColumn;
}
}
QVariant expr = scriptCode;
const bool isLiteral = isLiteralValue(scriptBinding);
if (isLiteral)
expr = castToLiteral(scriptCode, scriptBinding);
appliedChangesToViewer = true;
if (m_inspectorAdapter->engineClient())
m_inspectorAdapter->engineClient()->setBindingForObject(
debugId, propertyName, expr,
isLiteral, document()->fileName(),
scriptBinding->firstSourceLocation().startLine);
}
virtual void resetBindingForObject(int debugId, const QString &propertyName)
{
appliedChangesToViewer = true;
if (m_inspectorAdapter->engineClient())
m_inspectorAdapter->engineClient()->resetBindingForObject(debugId, propertyName);
}
virtual void removeObject(int debugId)
{
appliedChangesToViewer = true;
if (m_inspectorAdapter->toolsClient())
m_inspectorAdapter->toolsClient()->destroyQmlObject(debugId);
}
virtual void createObject(const QString &qmlText, DebugId ref,
const QStringList &importList,
const QString &filename,
int order)
{
appliedChangesToViewer = true;
referenceRefreshRequired = true;
if (m_inspectorAdapter->toolsClient())
m_inspectorAdapter->toolsClient()->createQmlObject(qmlText, ref, importList, filename, order);
}
virtual void reparentObject(int debugId, int newParent)
{
appliedChangesToViewer = true;
if (m_inspectorAdapter->toolsClient())
m_inspectorAdapter->toolsClient()->reparentQmlObject(debugId, newParent);
}
void notifyUnsyncronizableElementChange(UiObjectMember *parent)
{
if (unsyncronizableChanges == QmlLiveTextPreview::NoUnsyncronizableChanges) {
UiObjectDefinition *parentDefinition = cast<UiObjectDefinition *>(parent);
if (parentDefinition && parentDefinition->qualifiedTypeNameId
&& !parentDefinition->qualifiedTypeNameId->name.isEmpty())
{
unsyncronizableElementName
= parentDefinition->qualifiedTypeNameId->name.toString();
unsyncronizableChanges
= QmlLiveTextPreview::ElementChangeWarning;
unsyncronizableChangeLine
= parentDefinition->firstSourceLocation().startLine;
unsyncronizableChangeColumn
= parentDefinition->firstSourceLocation().startColumn;
}
}
}
public:
UpdateInspector(QmlInspectorAdapter *inspectorAdapter)
: appliedChangesToViewer(false)
, referenceRefreshRequired(false)
, unsyncronizableChanges(QmlLiveTextPreview::NoUnsyncronizableChanges)
, m_inspectorAdapter(inspectorAdapter)
{
}
bool appliedChangesToViewer;
bool referenceRefreshRequired;
QString unsyncronizableElementName;
QmlLiveTextPreview::UnsyncronizableChangeType unsyncronizableChanges;
unsigned unsyncronizableChangeLine;
unsigned unsyncronizableChangeColumn;
QmlInspectorAdapter *m_inspectorAdapter;
};
bool MapObjectWithDebugReference::visit(UiObjectDefinition *ast)
{
if (lookupObjects.contains(ast))
activated++;
return true;
}
bool MapObjectWithDebugReference::visit(UiObjectBinding *ast)
{
if (lookupObjects.contains(ast))
activated++;
return true;
}
void MapObjectWithDebugReference::endVisit(UiObjectDefinition *ast)
{
process(ast);
if (lookupObjects.contains(ast))
activated--;
}
void MapObjectWithDebugReference::endVisit(UiObjectBinding *ast)
{
process(ast);
if (lookupObjects.contains(ast))
activated--;
}
void MapObjectWithDebugReference::process(UiObjectMember *ast)
{
if (lookupObjects.isEmpty() || activated) {
SourceLocation loc = ast->firstSourceLocation();
QHash<QPair<int, int>, DebugIdList>::const_iterator it
= ids.constFind(qMakePair<int, int>(loc.startLine, loc.startColumn));
if (it != ids.constEnd())
result[ast].append(*it);
}
}
void MapObjectWithDebugReference::process(UiObjectBinding *ast)
{
if (lookupObjects.isEmpty() || activated) {
SourceLocation loc = ast->qualifiedTypeNameId->identifierToken;
QHash<QPair<int, int>, DebugIdList>::const_iterator it
= ids.constFind(qMakePair<int, int>(loc.startLine, loc.startColumn));
if (it != ids.constEnd())
result[ast].append(*it);
}
}
/*!
* Manages a Qml/JS document for the inspector
*/
QmlLiveTextPreview::QmlLiveTextPreview(const Document::Ptr &doc,
const Document::Ptr &initDoc,
QmlInspectorAdapter *inspectorAdapter,
QObject *parent)
: QObject(parent)
, m_previousDoc(doc)
, m_initialDoc(initDoc)
, m_applyChangesToQmlInspector(true)
, m_inspectorAdapter(inspectorAdapter)
, m_nodeForOffset(0)
, m_updateNodeForOffset(false)
, m_changesUnsynchronizable(false)
, m_contentsChanged(false)
{
QTC_CHECK(doc->fileName() == initDoc->fileName());
ModelManagerInterface *modelManager
= ModelManagerInterface::instance();
if (modelManager) {
connect(modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
SLOT(documentChanged(QmlJS::Document::Ptr)));
}
connect(m_inspectorAdapter->agent(), SIGNAL(objectTreeUpdated()),
SLOT(updateDebugIds()));
connect(this,
SIGNAL(fetchObjectsForLocation(QString,int,int)),
m_inspectorAdapter->agent(),
SLOT(fetchContextObjectsForLocation(QString,int,int)));
connect(m_inspectorAdapter->agent(), SIGNAL(automaticUpdateFailed()),
SLOT(onAutomaticUpdateFailed()));
}
QmlLiveTextPreview::~QmlLiveTextPreview()
{
removeOutofSyncInfo();
}
void QmlLiveTextPreview::associateEditor(Core::IEditor *editor)
{
QTC_ASSERT(editor, return);
using namespace TextEditor;
if (editor->document()->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID) {
QTC_ASSERT(QLatin1String(editor->widget()->metaObject()->className()) ==
QLatin1String("QmlJSEditor::Internal::QmlJSEditorWidget"),
return);
TextEditorWidget *editWidget
= qobject_cast<TextEditorWidget*>(editor->widget());
QTC_ASSERT(editWidget, return);
if (!m_editors.contains(editWidget)) {
m_editors << editWidget;
if (m_inspectorAdapter) {
connect(editWidget, SIGNAL(textChanged()), SLOT(editorContentsChanged()));
connect(editWidget,
SIGNAL(selectedElementsChanged(QList<QmlJS::AST::UiObjectMember*>,QString)),
SLOT(changeSelectedElements(QList<QmlJS::AST::UiObjectMember*>,QString)));
}
}
}
}
void QmlLiveTextPreview::unassociateEditor(Core::IEditor *oldEditor)
{
using namespace TextEditor;
if (oldEditor && oldEditor->document()->id()
== QmlJSEditor::Constants::C_QMLJSEDITOR_ID) {
TextEditorWidget *editWidget
= qobject_cast<TextEditorWidget*>(oldEditor->widget());
QTC_ASSERT(editWidget, return);
if (m_editors.contains(editWidget)) {
m_editors.removeOne(editWidget);
disconnect(editWidget, 0, this, 0);
}
}
}
void QmlLiveTextPreview::resetInitialDoc(const Document::Ptr &doc)
{
m_initialDoc = doc;
m_previousDoc = 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)
{
if (applyChanges && !m_applyChangesToQmlInspector) {
if (m_docWithUnappliedChanges) {
m_applyChangesToQmlInspector = true;
documentChanged(m_docWithUnappliedChanges);
}
}
m_applyChangesToQmlInspector = applyChanges;
}
void QmlLiveTextPreview::updateDebugIds()
{
if (!m_initialDoc->qmlProgram())
return;
DebugIdHash::const_iterator it
= m_inspectorAdapter->agent()->debugIdHash().constFind(
qMakePair<QString, int>(m_initialDoc->fileName(), 0));
if (it != m_inspectorAdapter->agent()->debugIdHash().constEnd()) {
// Map all the object that comes from the document as it has been loaded
// by the server.
const Document::Ptr &doc = m_initialDoc;
MapObjectWithDebugReference visitor;
visitor.ids = (*it);
visitor.filename = doc->fileName();
doc->qmlProgram()->accept(&visitor);
m_debugIds = visitor.result;
if (doc != m_previousDoc) {
Delta delta;
m_debugIds = delta(doc, m_previousDoc, m_debugIds);
}
}
const Document::Ptr &doc = m_previousDoc;
if (!doc->qmlProgram())
return;
// Map the root nodes of the document.
if (doc->qmlProgram()->members && doc->qmlProgram()->members->member) {
UiObjectMember *root = doc->qmlProgram()->members->member;
QHashIterator<int,QString> rIds(m_inspectorAdapter->agent()->rootObjectIds());
QList<int> r;
while (rIds.hasNext()) {
rIds.next();
if (rIds.value() == doc->componentName())
r += rIds.key();
}
if (!r.isEmpty())
m_debugIds[root] += r;
}
// Map the node of the later created objects.
for (QHash<Document::Ptr,QSet<UiObjectMember*> >::const_iterator it
= m_createdObjects.constBegin();
it != m_createdObjects.constEnd(); ++it) {
const Document::Ptr &doc = it.key();
DebugIdHash::const_iterator id_it = m_inspectorAdapter->agent()->debugIdHash().constFind(
qMakePair<QString, int>(doc->fileName(), doc->editorRevision()));
if (id_it == m_inspectorAdapter->agent()->debugIdHash().constEnd())
continue;
MapObjectWithDebugReference visitor;
visitor.ids = *id_it;
visitor.filename = doc->fileName();
visitor.lookupObjects = it.value();
doc->qmlProgram()->accept(&visitor);
Delta::DebugIdMap debugIds = visitor.result;
if (doc != m_previousDoc) {
Delta delta;
debugIds = delta(doc, m_previousDoc, debugIds);
}
for (Delta::DebugIdMap::const_iterator it2 = debugIds.constBegin();
it2 != debugIds.constEnd(); ++it2) {
m_debugIds[it2.key()] += it2.value();
}
}
if (m_updateNodeForOffset)
changeSelectedElements(m_lastOffsets, QString());
}
void QmlLiveTextPreview::changeSelectedElements(const QList<UiObjectMember*> offsetObjects,
const QString &wordAtCursor)
{
if (m_editors.isEmpty() || !m_previousDoc)
return;
QList<int> offsets;
foreach (UiObjectMember *member, offsetObjects)
offsets << member->firstSourceLocation().offset;
if (!changeSelectedElements(offsets, wordAtCursor) && m_initialDoc && offsetObjects.count()) {
m_updateNodeForOffset = true;
emit fetchObjectsForLocation(m_initialDoc->fileName(),
offsetObjects.first()->firstSourceLocation().startLine,
offsetObjects.first()->firstSourceLocation().startColumn);
}
}
bool QmlLiveTextPreview::changeSelectedElements(const QList<int> offsets,
const QString &wordAtCursor)
{
m_updateNodeForOffset = false;
m_lastOffsets = offsets;
ObjectReference objectRefUnderCursor;
objectRefUnderCursor
= m_inspectorAdapter->agent()->objectForName(wordAtCursor);
QList<int> selectedReferences;
bool containsReferenceUnderCursor = false;
foreach (int offset, offsets) {
if (offset >= 0) {
QList<int> list = objectReferencesForOffset(offset);
if (!containsReferenceUnderCursor
&& objectRefUnderCursor.isValid()) {
foreach (int id, list) {
if (id == objectRefUnderCursor.debugId()) {
containsReferenceUnderCursor = true;
break;
}
}
}
selectedReferences << list;
}
}
// fallback: use ref under cursor if nothing else is found
if (selectedReferences.isEmpty()
&& !containsReferenceUnderCursor
&& objectRefUnderCursor.isValid()) {
selectedReferences << objectRefUnderCursor.debugId();
}
if (selectedReferences.isEmpty())
return false;
emit selectedItemsChanged(selectedReferences);
return true;
}
void QmlLiveTextPreview::documentChanged(Document::Ptr doc)
{
if (doc->fileName() != m_previousDoc->fileName())
return;
// Changes to be applied when changes were made from the editor.
// m_contentsChanged ensures that the changes were made by the user in
// the editor before starting with the comparisons.
if (!m_contentsChanged)
return;
if (m_applyChangesToQmlInspector) {
m_docWithUnappliedChanges.clear();
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()) {
if (doc->fileName().endsWith(QLatin1String(".js"))) {
showSyncWarning(JSChangeWarning, QString(), 0, 0);
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()->queryEngineContext();
if (delta.unsyncronizableChanges != NoUnsyncronizableChanges) {
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;
if (m_inspectorAdapter->toolsClient())
m_inspectorAdapter->toolsClient()->clearComponentCache();
}
}
} else {
m_docWithUnappliedChanges = doc;
}
m_contentsChanged = false;
}
void QmlLiveTextPreview::editorContentsChanged()
{
m_contentsChanged = true;
}
void QmlLiveTextPreview::onAutomaticUpdateFailed()
{
showSyncWarning(AutomaticUpdateFailed, QString(), UINT_MAX, UINT_MAX);
}
QList<int> QmlLiveTextPreview::objectReferencesForOffset(quint32 offset)
{
QList<int> result;
QHashIterator<UiObjectMember*, QList<int> > iter(m_debugIds);
UiObjectMember *possibleNode = 0;
while (iter.hasNext()) {
iter.next();
UiObjectMember *member = iter.key();
quint32 startOffset = member->firstSourceLocation().offset;
quint32 endOffset = member->lastSourceLocation().offset;
if (startOffset <= offset && offset <= endOffset) {
if (!possibleNode)
possibleNode = member;
if (possibleNode->firstSourceLocation().offset <= startOffset &&
endOffset <= possibleNode->lastSourceLocation().offset)
possibleNode = member;
}
}
if (possibleNode) {
if (possibleNode != m_nodeForOffset) {
//We have found a better match, set flag so that we can
//query again to check if this is the best match for the offset
m_updateNodeForOffset = true;
m_nodeForOffset = possibleNode;
}
result = m_debugIds.value(possibleNode);
}
return result;
}
void QmlLiveTextPreview::showSyncWarning(
UnsyncronizableChangeType unsyncronizableChangeType,
const QString &elementName, unsigned line, unsigned column)
{
QString errorMessage;
switch (unsyncronizableChangeType) {
case AttributeChangeWarning:
errorMessage = tr("The %1 attribute at line %2, column %3 cannot be "
"changed without reloading the QML application. ")
.arg(elementName, QString::number(line), QString::number(column));
break;
case ElementChangeWarning:
errorMessage = tr("The %1 element at line %2, column %3 cannot be "
"changed without reloading the QML application. ")
.arg(elementName, QString::number(line), QString::number(column));
break;
case JSChangeWarning:
errorMessage = tr("The changes in JavaScript cannot be applied "
"without reloading the QML application. ");
break;
case AutomaticUpdateFailed:
errorMessage = tr("The changes made cannot be applied without "
"reloading the QML application. ");
break;
case QmlLiveTextPreview::NoUnsyncronizableChanges:
default:
return;
}
m_changesUnsynchronizable = true;
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::TextEditorWidget *editor, m_editors) {
if (editor) {
Core::InfoBar *infoBar = editor->textDocument()->infoBar();
Core::InfoBarEntry info(Core::Id(INFO_OUT_OF_SYNC), errorMessage);
BaseToolsClient *toolsClient = m_inspectorAdapter->toolsClient();
if (toolsClient && toolsClient->supportReload())
info.setCustomButtonInfo(tr("Reload QML"), [this]() {
removeOutofSyncInfo();
emit reloadRequest();
});
infoBar->addInfo(info);
}
}
}
void QmlLiveTextPreview::removeOutofSyncInfo()
{
foreach (TextEditor::TextEditorWidget *editor, m_editors) {
if (editor) {
Core::InfoBar *infoBar = editor->textDocument()->infoBar();
infoBar->removeInfo(Core::Id(INFO_OUT_OF_SYNC));
}
}
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,121 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLLIVETEXTPREVIEW_H
#define QMLLIVETEXTPREVIEW_H
#include <texteditor/texteditor.h>
#include <qmljs/qmljsdocument.h>
namespace Core { class IEditor; }
namespace QmlJS { class ModelManagerInterface; }
namespace Debugger {
namespace Internal {
class UpdateInspector;
class QmlInspectorAdapter;
class QmlLiveTextPreview : public QObject
{
Q_OBJECT
public:
QmlLiveTextPreview(const QmlJS::Document::Ptr &doc,
const QmlJS::Document::Ptr &initDoc,
QmlInspectorAdapter *inspectorAdapter,
QObject *parent = 0);
~QmlLiveTextPreview();
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<int> &debugIds);
void fetchObjectsForLocation(const QString &file,
int lineNumber, int columnNumber);
void reloadRequest();
public slots:
void setApplyChangesToQmlInspector(bool applyChanges);
void updateDebugIds();
private slots:
void changeSelectedElements(const QList<QmlJS::AST::UiObjectMember *> offsets,
const QString &wordAtCursor);
void documentChanged(QmlJS::Document::Ptr doc);
void editorContentsChanged();
void onAutomaticUpdateFailed();
private:
enum UnsyncronizableChangeType {
NoUnsyncronizableChanges,
AttributeChangeWarning,
ElementChangeWarning,
JSChangeWarning,
AutomaticUpdateFailed
};
bool changeSelectedElements(const QList<int> offsets, const QString &wordAtCursor);
QList<int> objectReferencesForOffset(quint32 offset);
void showSyncWarning(UnsyncronizableChangeType unsyncronizableChangeType,
const QString &elementName,
unsigned line, unsigned column);
void removeOutofSyncInfo();
private:
QHash<QmlJS::AST::UiObjectMember*, QList<int> > m_debugIds;
QHash<QmlJS::Document::Ptr, QSet<QmlJS::AST::UiObjectMember *> > m_createdObjects;
QmlJS::Document::Ptr m_previousDoc;
QmlJS::Document::Ptr m_initialDoc; //the document that was loaded by the server
QList<QPointer<TextEditor::TextEditorWidget> > m_editors;
bool m_applyChangesToQmlInspector;
QmlJS::Document::Ptr m_docWithUnappliedChanges;
QmlInspectorAdapter *m_inspectorAdapter;
QList<int> m_lastOffsets;
QmlJS::AST::UiObjectMember *m_nodeForOffset;
bool m_updateNodeForOffset;
bool m_changesUnsynchronizable;
bool m_contentsChanged;
friend class UpdateInspector;
};
} // namespace Internal
} // namespace Debugger
#endif // QMLLIVETEXTPREVIEW_H