Commit b5717a53 authored by Ulf Hermann's avatar Ulf Hermann

QmlJS: Lazy-load console items to allow for recursion

Using Utils:TreeView automatically gives us the capability for loading
item as they are expanded. This way we can show recursive structure in
the console as well as load data from the debug server on demand.

Also, properly print error messages received from unsuccessful
command evaluations.

Task-number: QTCREATORBUG-14931
Change-Id: I66d440eedd9723b04670169b27db1ee18f3f2891
Reviewed-by: default avatarhjk <hjk@theqtcompany.com>
parent 0b328328
......@@ -38,122 +38,130 @@ namespace QmlJS {
//
///////////////////////////////////////////////////////////////////////
ConsoleItem::ConsoleItem(ConsoleItem *parent, ConsoleItem::ItemType itemType,
const QString &text)
: m_parentItem(parent),
itemType(itemType),
line(-1)
QString addZeroWidthSpace(QString text)
{
setText(text);
for (int i = 0; i < text.length(); ++i) {
if (text.at(i).isPunct())
text.insert(++i, QChar(0x200b)); // ZERO WIDTH SPACE
}
return text;
}
ConsoleItem::~ConsoleItem()
ConsoleItem::ConsoleItem(ItemType itemType, const QString &expression, const QString &file,
int line) :
m_itemType(itemType), m_text(addZeroWidthSpace(expression)), m_file(file), m_line(line)
{
qDeleteAll(m_childItems);
setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
(itemType == InputType ? Qt::ItemIsEditable : Qt::NoItemFlags)));
}
ConsoleItem *ConsoleItem::child(int number)
ConsoleItem::ConsoleItem(ConsoleItem::ItemType itemType, const QString &expression,
std::function<void(ConsoleItem *)> doFetch) :
m_itemType(itemType), m_text(addZeroWidthSpace(expression)), m_line(-1), m_doFetch(doFetch)
{
return m_childItems.value(number);
setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
(itemType == InputType ? Qt::ItemIsEditable : Qt::NoItemFlags)));
}
int ConsoleItem::childCount() const
ConsoleItem::ItemType ConsoleItem::itemType() const
{
return m_childItems.size();
return m_itemType;
}
int ConsoleItem::childNumber() const
QString ConsoleItem::text() const
{
if (m_parentItem)
return m_parentItem->m_childItems.indexOf(const_cast<ConsoleItem *>(this));
return 0;
return m_text;
}
bool ConsoleItem::insertChildren(int position, int count)
QString ConsoleItem::file() const
{
if (position < 0 || position > m_childItems.size())
return false;
for (int row = 0; row < count; ++row) {
ConsoleItem *item = new ConsoleItem(this, ConsoleItem::UndefinedType,
QString());
m_childItems.insert(position, item);
}
return true;
return m_file;
}
void ConsoleItem::insertChild(ConsoleItem *item, bool sorted)
int ConsoleItem::line() const
{
if (!sorted) {
m_childItems.insert(m_childItems.count(), item);
return;
}
int i = 0;
for (; i < m_childItems.count(); i++) {
if (item->m_text < m_childItems[i]->m_text)
break;
}
m_childItems.insert(i, item);
return m_line;
}
bool ConsoleItem::insertChild(int position, ConsoleItem *item)
QVariant ConsoleItem::data(int column, int role) const
{
if (position < 0 || position > m_childItems.size())
return false;
m_childItems.insert(position, item);
return true;
}
ConsoleItem *ConsoleItem::parent()
{
return m_parentItem;
if (column != 0)
return QVariant();
switch (role)
{
case TypeRole:
return m_itemType;
case FileRole:
return m_file;
case LineRole:
return m_line;
case ExpressionRole:
return expression();
case Qt::DisplayRole:
return m_text;
default:
return TreeItem::data(column, role);
}
}
bool ConsoleItem::removeChildren(int position, int count)
bool ConsoleItem::setData(int column, const QVariant &data, int role)
{
if (position < 0 || position + count > m_childItems.size())
if (column != 0)
return false;
for (int row = 0; row < count; ++row)
delete m_childItems.takeAt(position);
return true;
switch (role)
{
case TypeRole:
m_itemType = ItemType(data.toInt());
return true;
case FileRole:
m_file = data.toString();
return true;
case LineRole:
m_line = data.toInt();
return true;
case ExpressionRole:
m_text = addZeroWidthSpace(data.toString());
return true;
case Qt::DisplayRole:
m_text = data.toString();
return true;
default:
return TreeItem::setData(column, data, role);
}
}
bool ConsoleItem::detachChild(int position)
bool ConsoleItem::canFetchMore() const
{
if (position < 0 || position > m_childItems.size())
return false;
m_childItems.removeAt(position);
// Always fetch all children, too, as the labels depend on them.
foreach (TreeItem *child, children()) {
if (static_cast<ConsoleItem *>(child)->m_doFetch)
return true;
}
return true;
return bool(m_doFetch);
}
void ConsoleItem::setText(const QString &text)
void ConsoleItem::fetchMore()
{
m_text = text;
for (int i = 0; i < m_text.length(); ++i) {
if (m_text.at(i).isPunct())
m_text.insert(++i, QChar(0x200b)); // ZERO WIDTH SPACE
if (m_doFetch) {
m_doFetch(this);
m_doFetch = std::function<void(ConsoleItem *)>();
}
}
QString ConsoleItem::text() const
{
return m_text;
foreach (TreeItem *child, children()) {
ConsoleItem *item = static_cast<ConsoleItem *>(child);
if (item->m_doFetch) {
item->m_doFetch(item);
item->m_doFetch = m_doFetch;
}
}
}
QString ConsoleItem::expression() const
{
QString text = m_text;
return text.remove(QChar(0x200b)); // ZERO WIDTH SPACE
return text().remove(QChar(0x200b)); // ZERO WIDTH SPACE
}
} // QmlJS
......@@ -32,53 +32,56 @@
#define CONSOLEITEM_H
#include "qmljs_global.h"
#include <utils/treemodel.h>
#include <QList>
#include <QString>
#include <functional>
namespace QmlJS {
class QMLJS_EXPORT ConsoleItem
class QMLJS_EXPORT ConsoleItem : public Utils::TreeItem
{
public:
enum Roles {
TypeRole = Qt::UserRole,
FileRole,
LineRole,
ExpressionRole
};
enum ItemType
{
UndefinedType = 0x01, // Can be used for unknown and for Return values
DebugType = 0x02,
WarningType = 0x04,
ErrorType = 0x08,
InputType = 0x10,
DefaultTypes = InputType | UndefinedType
DefaultType = 0x01, // Can be used for unknown and for Return values
DebugType = 0x02,
WarningType = 0x04,
ErrorType = 0x08,
InputType = 0x10,
};
Q_DECLARE_FLAGS(ItemTypes, ItemType)
ConsoleItem(ConsoleItem *parent,
ConsoleItem::ItemType type = ConsoleItem::UndefinedType,
const QString &data = QString());
~ConsoleItem();
ConsoleItem(ItemType itemType = ConsoleItem::DefaultType, const QString &expression = QString(),
const QString &file = QString(), int line = -1);
ConsoleItem(ItemType itemType, const QString &expression,
std::function<void(ConsoleItem *)> doFetch);
ConsoleItem *child(int number);
int childCount() const;
bool insertChildren(int position, int count);
void insertChild(ConsoleItem *item, bool sorted);
bool insertChild(int position, ConsoleItem *item);
ConsoleItem *parent();
bool removeChildren(int position, int count);
bool detachChild(int position);
int childNumber() const;
void setText(const QString &text);
QString text() const;
ItemType itemType() const;
QString expression() const;
QString text() const;
QString file() const;
int line() const;
QVariant data(int column, int role) const;
bool setData(int column, const QVariant &data, int role);
bool canFetchMore() const;
void fetchMore();
private:
ConsoleItem *m_parentItem;
QList<ConsoleItem *> m_childItems;
ItemType m_itemType;
QString m_text;
QString m_file;
int m_line;
public:
ConsoleItem::ItemType itemType;
QString file;
int line;
std::function<void(ConsoleItem *)> m_doFetch;
};
} // QmlJS
......
......@@ -50,8 +50,6 @@ public:
virtual void showConsolePane() = 0;
virtual ConsoleItem *rootItem() const = 0;
virtual void setScriptEvaluator(IScriptEvaluator *scriptEvaluator) = 0;
virtual void setContext(const QString &context) = 0;
......
......@@ -530,7 +530,7 @@ void DebuggerEngine::showMessage(const QString &msg, int channel, int timeout) c
// qDebug() << qPrintable(msg) << "IN STATE" << state();
QmlJS::ConsoleManagerInterface *consoleManager = QmlJS::ConsoleManagerInterface::instance();
if (channel == ConsoleOutput && consoleManager)
consoleManager->printToConsolePane(QmlJS::ConsoleItem::UndefinedType, msg);
consoleManager->printToConsolePane(QmlJS::ConsoleItem::DefaultType, msg);
Internal::showMessage(msg, channel, timeout);
if (d->m_runControl) {
......
This diff is collapsed.
......@@ -250,12 +250,9 @@ void appendDebugOutput(QtMsgType type, const QString &message, const QDebugConte
return;
}
if (auto consoleManager = ConsoleManagerInterface::instance()) {
ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message);
item->file = info.file;
item->line = info.line;
consoleManager->printToConsolePane(item);
}
if (auto consoleManager = ConsoleManagerInterface::instance())
consoleManager->printToConsolePane(new ConsoleItem(itemType, message, info.file,
info.line));
}
void clearExceptionSelection()
......
......@@ -105,6 +105,8 @@ const char REFS[] = "refs";
const char BODY[] = "body";
const char NAME[] = "name";
const char VALUE[] = "value";
const char SUCCESS[] = "success";
const char MESSAGE[] = "message";
const char OBJECT[] = "{}";
const char ARRAY[] = "[]";
......
......@@ -215,10 +215,10 @@ void QmlConsoleEdit::handleUpKey()
if (model->hasIndex(currentRow, 0)) {
QModelIndex index = model->index(currentRow, 0);
if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data(
index, QmlConsoleItemModel::TypeRole).toInt()) {
index, ConsoleItem::TypeRole).toInt()) {
m_historyIndex = index;
replaceCurrentScript(
model->data(index, QmlConsoleItemModel::ExpressionRole).toString());
model->data(index, ConsoleItem::ExpressionRole).toString());
break;
}
}
......@@ -235,13 +235,13 @@ void QmlConsoleEdit::handleDownKey()
if (model->hasIndex(currentRow, 0)) {
QModelIndex index = model->index(currentRow, 0);
if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data(
index, QmlConsoleItemModel::TypeRole).toInt()) {
index, ConsoleItem::TypeRole).toInt()) {
m_historyIndex = index;
if (currentRow == model->rowCount() - 1) {
replaceCurrentScript(m_cachedScript);
} else {
replaceCurrentScript(
model->data(index, QmlConsoleItemModel::ExpressionRole).toString());
model->data(index, ConsoleItem::ExpressionRole).toString());
}
break;
}
......
......@@ -92,7 +92,7 @@ QColor QmlConsoleItemDelegate::drawBackground(QPainter *painter, const QRect &re
{
painter->save();
ConsoleItem::ItemType itemType = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
QColor backgroundColor;
switch (itemType) {
case ConsoleItem::DebugType:
......@@ -138,7 +138,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
QColor textColor;
QIcon taskIcon;
ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
switch (type) {
case ConsoleItem::DebugType:
textColor = QColor(CONSOLE_LOG_TEXT_COLOR);
......@@ -175,7 +175,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
}
int width = view->width() - level * view->indentation() - view->verticalScrollBar()->width();
bool showTypeIcon = index.parent() == QModelIndex();
bool showExpandableIcon = type == ConsoleItem::UndefinedType;
bool showExpandableIcon = type == ConsoleItem::DefaultType;
QRect rect(opt.rect.x(), opt.rect.top(), width, opt.rect.height());
ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon);
......@@ -206,7 +206,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
if (showExpandableIcon) {
// Paint ExpandableIconArea:
QIcon expandCollapseIcon;
if (index.model()->rowCount(index)) {
if (index.model()->rowCount(index) || index.model()->canFetchMore(index)) {
if (view->isExpanded(index))
expandCollapseIcon = m_collapseIcon;
else
......@@ -219,7 +219,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
if (showFileLineInfo) {
// Check for file info
QString file = index.data(QmlConsoleItemModel::FileRole).toString();
QString file = index.data(ConsoleItem::FileRole).toString();
const QUrl fileUrl = QUrl(file);
if (fileUrl.isLocalFile())
file = fileUrl.toLocalFile();
......@@ -244,7 +244,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
}
// Paint LineArea
QString lineText = index.data(QmlConsoleItemModel::LineRole).toString();
QString lineText = index.data(ConsoleItem::LineRole).toString();
painter->setClipRect(positions.lineArea());
const int realLineWidth = fm.width(lineText);
painter->drawText(positions.lineAreaRight() - realLineWidth,
......@@ -277,9 +277,9 @@ QSize QmlConsoleItemDelegate::sizeHint(const QStyleOptionViewItem &option,
return QSize(width, m_cachedHeight);
ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
bool showTypeIcon = index.parent() == QModelIndex();
bool showExpandableIcon = type == ConsoleItem::UndefinedType;
bool showExpandableIcon = type == ConsoleItem::DefaultType;
QRect rect(level * view->indentation(), 0, width, 0);
ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon);
......@@ -322,7 +322,7 @@ void QmlConsoleItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QmlConsoleEdit *edtr = qobject_cast<QmlConsoleEdit *>(editor);
edtr->insertPlainText(index.data(QmlConsoleItemModel::ExpressionRole).toString());
edtr->insertPlainText(index.data(ConsoleItem::ExpressionRole).toString());
}
void QmlConsoleItemDelegate::setModelData(QWidget *editor,
......@@ -330,8 +330,8 @@ void QmlConsoleItemDelegate::setModelData(QWidget *editor,
const QModelIndex &index) const
{
QmlConsoleEdit *edtr = qobject_cast<QmlConsoleEdit *>(editor);
model->setData(index, edtr->getCurrentScript(), Qt::DisplayRole);
model->setData(index, ConsoleItem::InputType, QmlConsoleItemModel::TypeRole);
model->setData(index, edtr->getCurrentScript(), ConsoleItem::ExpressionRole);
model->setData(index, ConsoleItem::InputType, ConsoleItem::TypeRole);
}
void QmlConsoleItemDelegate::updateEditorGeometry(QWidget *editor,
......
......@@ -45,89 +45,55 @@ namespace Internal {
///////////////////////////////////////////////////////////////////////
QmlConsoleItemModel::QmlConsoleItemModel(QObject *parent) :
QAbstractItemModel(parent),
m_hasEditableRow(false),
m_rootItem(new ConsoleItem(0)),
Utils::TreeModel(new ConsoleItem, parent),
m_maxSizeOfFileName(0)
{
}
QmlConsoleItemModel::~QmlConsoleItemModel()
{
delete m_rootItem;
clear();
}
void QmlConsoleItemModel::clear()
{
beginResetModel();
delete m_rootItem;
m_rootItem = new ConsoleItem(0);
endResetModel();
if (m_hasEditableRow)
appendEditableRow();
Utils::TreeModel::clear();
appendItem(new ConsoleItem(ConsoleItem::InputType));
emit selectEditableRow(index(0, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect);
}
bool QmlConsoleItemModel::appendItem(ConsoleItem *item, int position)
void QmlConsoleItemModel::appendItem(ConsoleItem *item, int position)
{
if (position < 0)
position = m_rootItem->childCount() - 1;
position = rootItem()->childCount() - 1; // append before editable row
if (position < 0)
position = 0;
beginInsertRows(QModelIndex(), position, position);
bool success = m_rootItem->insertChild(position, item);
endInsertRows();
return success;
rootItem()->insertChild(position, item);
}
bool QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType,
void QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType,
const QString &message, int position)
{
return appendItem(new ConsoleItem(m_rootItem, itemType, message), position);
}
void QmlConsoleItemModel::setHasEditableRow(bool hasEditableRow)
{
if (m_hasEditableRow && !hasEditableRow)
removeEditableRow();
if (!m_hasEditableRow && hasEditableRow)
appendEditableRow();
m_hasEditableRow = hasEditableRow;
appendItem(new ConsoleItem(itemType, message), position);
}
bool QmlConsoleItemModel::hasEditableRow() const
void QmlConsoleItemModel::shiftEditableRow()
{
return m_hasEditableRow;
}
int position = rootItem()->childCount();
Q_ASSERT(position > 0);
void QmlConsoleItemModel::appendEditableRow()
{
int position = m_rootItem->childCount();
if (appendItem(new ConsoleItem(m_rootItem, ConsoleItem::InputType), position))
emit selectEditableRow(index(position, 0), QItemSelectionModel::ClearAndSelect);
}
// Disable editing for old editable row
rootItem()->lastChild()->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
void QmlConsoleItemModel::removeEditableRow()
{
if (m_rootItem->child(m_rootItem->childCount() - 1)->itemType == ConsoleItem::InputType)
removeRow(m_rootItem->childCount() - 1);
appendItem(new ConsoleItem(ConsoleItem::InputType), position);
emit selectEditableRow(index(position, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect);
}
int QmlConsoleItemModel::sizeOfFile(const QFont &font)
{
int lastReadOnlyRow = m_rootItem->childCount();
if (m_hasEditableRow)
lastReadOnlyRow -= 2;
else
lastReadOnlyRow -= 1;
int lastReadOnlyRow = rootItem()->childCount();
lastReadOnlyRow -= 2; // skip editable row
if (lastReadOnlyRow < 0)
return 0;
QString filename = m_rootItem->child(lastReadOnlyRow)->file;
QString filename = static_cast<ConsoleItem *>(rootItem()->child(lastReadOnlyRow))->file();
const int pos = filename.lastIndexOf(QLatin1Char('/'));
if (pos != -1)
filename = filename.mid(pos + 1);
......@@ -144,141 +110,5 @@ int QmlConsoleItemModel::sizeOfLineNumber(const QFont &font)
return fm.width(QLatin1String("88888"));
}
QVariant QmlConsoleItemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
ConsoleItem *item = getItem(index);
if (role == Qt::DisplayRole )
return item->text();
else if (role == QmlConsoleItemModel::TypeRole)
return int(item->itemType);
else if (role == QmlConsoleItemModel::FileRole)
return item->file;
else if (role == QmlConsoleItemModel::LineRole)
return item->line;
else if (role == QmlConsoleItemModel::ExpressionRole)
return item->expression();
else
return QVariant();
}
QModelIndex QmlConsoleItemModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
if (column > 0)
return QModelIndex();
ConsoleItem *parentItem = getItem(parent);
ConsoleItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex QmlConsoleItemModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
ConsoleItem *childItem = getItem(index);
ConsoleItem *parentItem = childItem->parent();
if (parentItem == m_rootItem)