Commit cb0b3424 authored by Aurindam Jana's avatar Aurindam Jana

QmlJSScriptConsole: Refactored ScriptConsole, added functionality.

The ScriptConsole is renamed to QmlJSScriptConsole. The console can be used
to evaluate expressions during a QML application debug session.
Changes:
* Console can be used even when application is not on a debug break
* The object context is the currently selected object in Inspector
* ContextMenu has a Clear option
* Multiple line expressions are supported
* Traverse Command History using up and down arrow keys

Change-Id: I4e1cd8763d60be43dbba395ead2a9a086d6bab7d
Reviewed-by: default avatarRoberto Raggi <roberto.raggi@nokia.com>
Reviewed-by: default avatarKai Koehne <kai.koehne@nokia.com>
parent 87943ca6
......@@ -170,7 +170,7 @@ public:
bool isWaiting() const;
Q_SIGNALS:
void stateChanged(QDeclarativeDebugQuery::State);
void stateChanged(QmlJsDebugClient::QDeclarativeDebugQuery::State);
protected:
QDeclarativeDebugQuery(QObject *);
......
......@@ -6,3 +6,4 @@ include(../../plugins/texteditor/texteditor.pri)
include(../../libs/cplusplus/cplusplus.pri)
include(../../libs/utils/utils.pri)
include(../../libs/symbianutils/symbianutils.pri)
include(../../libs/qmljs/qmljs.pri)
......@@ -112,7 +112,7 @@
#include <utils/statuslabel.h>
#include <utils/fileutils.h>
#include <qml/scriptconsole.h>
#include <qml/qmljsscriptconsole.h>
#include <QtCore/QTimer>
#include <QtCore/QtPlugin>
......@@ -785,7 +785,7 @@ public slots:
void aboutToSaveSession();
void executeDebuggerCommand(const QString &command);
void scriptExpressionEntered(const QString &expression);
void evaluateExpression(const QString &expression);
void coreShutdown();
public slots:
......@@ -1079,7 +1079,7 @@ public:
QAbstractItemView *m_stackWindow;
QAbstractItemView *m_threadsWindow;
LogWindow *m_logWindow;
ScriptConsole *m_scriptConsoleWindow;
QmlJSScriptConsoleWidget *m_scriptConsoleWindow;
bool m_busy;
QString m_lastPermanentStatusMessage;
......@@ -1972,6 +1972,16 @@ void DebuggerPluginPrivate::connectEngine(DebuggerEngine *engine)
//m_threadBox->setModel(engine->threadsModel());
//m_threadBox->setModelColumn(ThreadData::ComboNameColumn);
m_watchersWindow->setModel(engine->watchersModel());
//Initialize QmlJSConsole
QmlEngine *qmlEngine = qobject_cast<QmlEngine *>(engine);
QmlCppEngine *qmlCppEngine = qobject_cast<QmlCppEngine *>(engine);
if (qmlCppEngine)
qmlEngine = qobject_cast<QmlEngine *>(qmlCppEngine->qmlEngine());
if (qmlEngine) {
m_scriptConsoleWindow->setQmlAdapter(qmlEngine->adapter());
}
engine->watchHandler()->rebuildModel();
}
......@@ -2047,7 +2057,6 @@ void DebuggerPluginPrivate::setBusyCursor(bool busy)
m_threadsWindow->setCursor(cursor);
m_watchersWindow->setCursor(cursor);
m_snapshotWindow->setCursor(cursor);
m_scriptConsoleWindow->setCursor(cursor);
}
void DebuggerPluginPrivate::setInitialState()
......@@ -2239,8 +2248,11 @@ void DebuggerPluginPrivate::updateState(DebuggerEngine *engine)
if (qmlCppEngine)
qmlEngine = qobject_cast<QmlEngine *>(qmlCppEngine->qmlEngine());
if (qmlEngine) {
m_scriptConsoleWindow->setEnabled(stopped);
if (qmlEngine && (state == InferiorRunOk || state == InferiorStopOk)) {
m_scriptConsoleWindow->setEnabled(true);
m_scriptConsoleWindow->setInferiorStopped(state == InferiorStopOk);
} else {
m_scriptConsoleWindow->setEnabled(false);
}
}
......@@ -2387,7 +2399,7 @@ void DebuggerPluginPrivate::showStatusMessage(const QString &msg0, int timeout)
m_statusLabel->showStatusMessage(msg, timeout);
}
void DebuggerPluginPrivate::scriptExpressionEntered(const QString &expression)
void DebuggerPluginPrivate::evaluateExpression(const QString &expression)
{
currentEngine()->executeDebuggerCommand(expression);
}
......@@ -2757,11 +2769,11 @@ void DebuggerPluginPrivate::extensionsInitialized()
m_localsWindow->setObjectName(QLatin1String("CppDebugLocals"));
m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
m_watchersWindow->setObjectName(QLatin1String("CppDebugWatchers"));
m_scriptConsoleWindow = new ScriptConsole;
m_scriptConsoleWindow = new QmlJSScriptConsoleWidget;
m_scriptConsoleWindow->setWindowTitle(tr("QML Script Console"));
m_scriptConsoleWindow->setObjectName(DOCKWIDGET_QML_SCRIPTCONSOLE);
connect(m_scriptConsoleWindow, SIGNAL(expressionEntered(QString)),
SLOT(scriptExpressionEntered(QString)));
connect(m_scriptConsoleWindow, SIGNAL(evaluateExpression(QString)),
SLOT(evaluateExpression(QString)));
// Snapshot
m_snapshotHandler = new SnapshotHandler;
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "interactiveinterpreter.h"
namespace Debugger {
namespace Internal {
bool InteractiveInterpreter::canEvaluate()
{
int yyaction = 0;
int yytoken = -1;
int yytos = -1;
setCode(m_code, 1);
m_tokens.append(T_FEED_JS_PROGRAM);
do {
if (++yytos == m_stateStack.size())
m_stateStack.resize(m_stateStack.size() * 2);
m_stateStack[yytos] = yyaction;
again:
if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) {
if (m_tokens.isEmpty())
yytoken = lex();
else
yytoken = m_tokens.takeFirst();
}
yyaction = t_action(yyaction, yytoken);
if (yyaction > 0) {
if (yyaction == ACCEPT_STATE) {
--yytos;
return true;
}
yytoken = -1;
} else if (yyaction < 0) {
const int ruleno = -yyaction - 1;
yytos -= rhs[ruleno];
yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT);
}
} while (yyaction);
const int errorState = m_stateStack[yytos];
if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) {
yyaction = errorState;
m_tokens.prepend(yytoken);
yytoken = T_SEMICOLON;
goto again;
}
if (yytoken != EOF_SYMBOL)
return true;
return false;
}
}
}
......@@ -30,57 +30,42 @@
**
**************************************************************************/
#ifndef QMLJSSCRIPTCONSOLE_H
#define QMLJSSCRIPTCONSOLE_H
#ifndef INTERACTIVEINTERPRETER_H
#define INTERACTIVEINTERPRETER_H
#include <QtGui/QWidget>
#include <QtGui/QToolButton>
#include <QtGui/QPlainTextEdit>
#include <qmljs/parser/qmljslexer_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <utils/fancylineedit.h>
namespace QmlJSEditor {
class Highlighter;
}
#include <QtCore/QVector>
#include <QtCore/QString>
#include <QtCore/QList>
namespace Debugger {
namespace Internal {
class ScriptConsole : public QWidget
class InteractiveInterpreter: QmlJS::Lexer
{
Q_OBJECT
public:
ScriptConsole(QWidget *parent = 0);
public slots:
void appendResult(const QString &result);
signals:
void expressionEntered(const QString &expr);
protected slots:
void clearTextEditor();
void executeExpression();
InteractiveInterpreter()
: Lexer(&m_engine),
m_stateStack(128)
{
protected:
bool eventFilter(QObject *obj, QEvent *event);
void setFontSettings();
void clear();
}
// QToolButton *m_clearButton;
QPlainTextEdit *m_textEdit;
Utils::FancyLineEdit *m_lineEdit;
QString m_prompt;
QString m_expr;
QString m_lastExpr;
void clearText() { m_code.clear(); }
void appendText(const QString &text) { m_code += text; }
QString m_title;
QmlJSEditor::Highlighter *m_highlighter;
QString code() const { return m_code; }
bool canEvaluate();
private:
QmlJS::Engine m_engine;
QVector<int> m_stateStack;
QList<int> m_tokens;
QString m_code;
};
}
} //end namespaces
#endif
}
#endif // INTERACTIVEINTERPRETER_H
include($$PWD/../../../libs/qmljsdebugclient/qmljsdebugclient.pri)
include($$PWD/../../../libs/qmljs/parser/parser.pri)
include($$PWD/../../../shared/json/json.pri)
DEFINES += JSON_INCLUDE_PRI
......@@ -8,15 +9,18 @@ HEADERS += \
$$PWD/qmldebuggerclient.h \
$$PWD/qmljsprivateapi.h \
$$PWD/qmlcppengine.h \
$$PWD/scriptconsole.h \
$$PWD/qmljsscriptconsole.h \
$$PWD/qscriptdebuggerclient.h \
$$PWD/qmlv8debuggerclient.h
$$PWD/qmlv8debuggerclient.h \
$$PWD/interactiveinterpreter.h
SOURCES += \
$$PWD/qmlengine.cpp \
$$PWD/qmladapter.cpp \
$$PWD/qmldebuggerclient.cpp \
$$PWD/qmlcppengine.cpp \
$$PWD/scriptconsole.cpp \
$$PWD/qmljsscriptconsole.cpp \
$$PWD/qscriptdebuggerclient.cpp \
$$PWD/qmlv8debuggerclient.cpp
$$PWD/qmlv8debuggerclient.cpp \
$$PWD/interactiveinterpreter.cpp
......@@ -54,7 +54,9 @@ public:
explicit QmlAdapterPrivate(DebuggerEngine *engine)
: m_engine(engine)
, m_qmlClient(0)
, m_engineDebugClient(0)
, m_conn(0)
, m_currentSelectedDebugId(-1)
{
m_connectionTimer.setInterval(4000);
m_connectionTimer.setSingleShot(true);
......@@ -62,9 +64,12 @@ public:
QWeakPointer<DebuggerEngine> m_engine;
QmlDebuggerClient *m_qmlClient;
QmlJsDebugClient::QDeclarativeEngineDebug *m_engineDebugClient;
QTimer m_connectionTimer;
QDeclarativeDebugConnection *m_conn;
QHash<QString, QmlDebuggerClient*> debugClients;
int m_currentSelectedDebugId;
QString m_currentSelectedDebugName;
};
} // namespace Internal
......@@ -272,6 +277,34 @@ QHash<QString, Internal::QmlDebuggerClient*> QmlAdapter::debuggerClients()
{
return d->debugClients;
}
QmlJsDebugClient::QDeclarativeEngineDebug *QmlAdapter::engineDebugClient() const
{
return d->m_engineDebugClient;
}
void QmlAdapter::setEngineDebugClient(QmlJsDebugClient::QDeclarativeEngineDebug *client)
{
d->m_engineDebugClient = client;
}
int QmlAdapter::currentSelectedDebugId() const
{
return d->m_currentSelectedDebugId;
}
QString QmlAdapter::currentSelectedDisplayName() const
{
return d->m_currentSelectedDebugName;
}
void QmlAdapter::setCurrentSelectedDebugInfo(int currentDebugId, const QString &displayName)
{
d->m_currentSelectedDebugId = currentDebugId;
d->m_currentSelectedDebugName = displayName;
emit selectionChanged();
}
void QmlAdapter::logServiceStatusChange(const QString &service,
QDeclarativeDebugClient::Status newStatus)
{
......
......@@ -73,6 +73,13 @@ public:
Internal::QmlDebuggerClient *activeDebuggerClient();
QHash<QString, Internal::QmlDebuggerClient*> debuggerClients();
QmlJsDebugClient::QDeclarativeEngineDebug *engineDebugClient() const;
void setEngineDebugClient(QmlJsDebugClient::QDeclarativeEngineDebug *client);
int currentSelectedDebugId() const;
QString currentSelectedDisplayName() const;
void setCurrentSelectedDebugInfo(int debugId, const QString &displayName = QString());
public slots:
void logServiceStatusChange(const QString &service, QDeclarativeDebugClient::Status newStatus);
void logServiceActivity(const QString &service, const QString &logMessage);
......@@ -83,6 +90,7 @@ signals:
void connectionStartupFailed();
void connectionError(QAbstractSocket::SocketError socketError);
void serviceConnectionError(const QString serviceName);
void selectionChanged();
private slots:
void connectionErrorOccurred(QAbstractSocket::SocketError socketError);
......
......@@ -676,7 +676,7 @@ void QmlEngine::requestModuleSymbols(const QString &moduleName)
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
{
// This is processed by QML inspector, which has dependencies to
// This is processed by QML inspector, which has dependencies to
// the qml js editor. Makes life easier.
emit tooltipRequested(mousePos, editor, ctx.position);
return true;
......@@ -811,6 +811,11 @@ void QmlEngine::logMessage(LogDirection direction, const QString &message)
showMessage(msg, LogDebug);
}
QmlAdapter *QmlEngine::adapter() const
{
return &d->m_adapter;
}
QmlEngine *createQmlEngine(const DebuggerStartParameters &sp,
DebuggerEngine *masterEngine)
{
......
......@@ -40,6 +40,9 @@
#include <QtNetwork/QAbstractSocket>
namespace Debugger {
class QmlAdapter;
namespace Internal {
class QmlEnginePrivate;
......@@ -71,6 +74,8 @@ public:
void logMessage(LogDirection direction, const QString &str);
QmlAdapter *adapter() const;
public slots:
void disconnected();
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljsscriptconsole.h"
#include "interactiveinterpreter.h"
#include "qmladapter.h"
#include "debuggerstringutils.h"
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <extensionsystem/pluginmanager.h>
#include <coreplugin/coreconstants.h>
#include <utils/statuslabel.h>
#include <QtGui/QMenu>
#include <QtGui/QTextBlock>
#include <QtGui/QHBoxLayout>
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
namespace Debugger {
namespace Internal {
class QmlJSScriptConsolePrivate
{
public:
QmlJSScriptConsolePrivate()
: prompt(QLatin1String("> ")),
startOfEditableArea(-1),
lastKnownPosition(0),
inferiorStopped(false)
{
resetCache();
}
void resetCache();
void appendToHistory(const QString &script);
bool canEvaluateScript(const QString &script);
QWeakPointer<QmlAdapter> adapter;
QString prompt;
int startOfEditableArea;
int lastKnownPosition;
QStringList scriptHistory;
int scriptHistoryIndex;
InteractiveInterpreter interpreter;
bool inferiorStopped;
QList<QTextEdit::ExtraSelection> selections;
};
void QmlJSScriptConsolePrivate::resetCache()
{
scriptHistory.clear();
scriptHistory.append(QLatin1String(""));
scriptHistoryIndex = scriptHistory.count();
selections.clear();
}
void QmlJSScriptConsolePrivate::appendToHistory(const QString &script)
{
scriptHistoryIndex = scriptHistory.count();
scriptHistory.replace(scriptHistoryIndex - 1,script);
scriptHistory.append(QLatin1String(""));
scriptHistoryIndex = scriptHistory.count();
}
bool QmlJSScriptConsolePrivate::canEvaluateScript(const QString &script)
{
interpreter.clearText();
interpreter.appendText(script);
return interpreter.canEvaluate();
}
///////////////////////////////////////////////////////////////////////
//
// QmlJSScriptConsoleWidget
//
///////////////////////////////////////////////////////////////////////
QmlJSScriptConsoleWidget::QmlJSScriptConsoleWidget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setMargin(0);
vbox->setSpacing(0);
QWidget *statusbarContainer = new QWidget;
QHBoxLayout *hbox = new QHBoxLayout(statusbarContainer);
hbox->setMargin(0);
hbox->setSpacing(0);
//Clear Button
QToolButton *clearButton = new QToolButton;
QAction *clearAction = new QAction(tr("Clear Console"), this);
clearAction->setIcon(QIcon(_(Core::Constants::ICON_CLEAN_PANE)));
clearButton->setDefaultAction(clearAction);
//Status Label
m_statusLabel = new Utils::StatusLabel;
hbox->addWidget(m_statusLabel, 20, Qt::AlignLeft);
hbox->addWidget(clearButton, 0, Qt::AlignRight);
m_console = new QmlJSScriptConsole;
connect(m_console, SIGNAL(evaluateExpression(QString)), this,
SIGNAL(evaluateExpression(QString)));
connect(m_console, SIGNAL(updateStatusMessage(const QString &, int)), m_statusLabel,
SLOT(showStatusMessage(const QString &, int)));
connect(clearAction, SIGNAL(triggered()), m_console, SLOT(clear()));
vbox->addWidget(statusbarContainer);
vbox->addWidget(m_console);
}
void QmlJSScriptConsoleWidget::setQmlAdapter(QmlAdapter *adapter)
{
m_console->setQmlAdapter(adapter);
}
void QmlJSScriptConsoleWidget::setInferiorStopped(bool inferiorStopped)
{
m_console->setInferiorStopped(inferiorStopped);
}
void QmlJSScriptConsoleWidget::appendResult(const QString &result)
{
m_console->appendResult(result);
}
///////////////////////////////////////////////////////////////////////
//
// QmlJSScriptConsole
//
///////////////////////////////////////////////////////////////////////
QmlJSScriptConsole::QmlJSScriptConsole(QWidget *parent)
: QPlainTextEdit(parent),
d(new QmlJSScriptConsolePrivate())
{
connect(this, SIGNAL(cursorPositionChanged()), SLOT(onCursorPositionChanged()));
setFrameStyle(QFrame::NoFrame);
setUndoRedoEnabled(false);
setBackgroundVisible(false);
const TextEditor::FontSettings &fs = TextEditor::TextEditorSettings::instance()->fontSettings();