<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (<EMAIL>)
**
**
** 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 <EMAIL>.
**
**************************************************************************/

#include "watchhandler.h"

#include "breakhandler.h"
#include "debuggerinternalconstants.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include "watchutils.h"

#if USE_WATCH_MODEL_TEST
#include "modeltest.h"
#endif

#include <utils/qtcassert.h>
#include <utils/savedaction.h>

#include <cplusplus/CppRewriter.h>

#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtCore/QFile>
#include <QtCore/QProcess>
#include <QtCore/QTextStream>
#include <QtCore/QtAlgorithms>

#include <QtGui/QLabel>
#include <QtGui/QTextEdit>

#include <ctype.h>
#include <utils/qtcassert.h>


namespace Debugger {
namespace Internal {

// Creates debug output for accesses to the model.
enum { debugModel = 0 };

#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
#define MODEL_DEBUGX(s) qDebug() << s

QHash<QByteArray, int> WatchHandler::m_watcherNames;
QHash<QByteArray, int> WatchHandler::m_typeFormats;

static QByteArray stripTemplate(const QByteArray &ba)
{
    int pos = ba.indexOf('<');
    return pos == -1 ? ba : ba.left(pos);
}


static int m_unprintableBase = -1;

void WatchHandler::setUnprintableBase(int base)
{
    m_unprintableBase = base;
    emitAllChanged();
}

int WatchHandler::unprintableBase()
{
    return m_unprintableBase;
}

////////////////////////////////////////////////////////////////////
//
// WatchItem
//
////////////////////////////////////////////////////////////////////

class WatchItem : public WatchData
{
public:
    WatchItem() { parent = 0; }

    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

    WatchItem(const WatchData &data) : WatchData(data)
        { parent = 0; }

    void setData(const WatchData &data)
        { static_cast<WatchData &>(*this) = data; }

    WatchItem *parent;
    QList<WatchItem *> children;  // fetched children
};


///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////

WatchModel::WatchModel(WatchHandler *handler, WatchType type)
    : QAbstractItemModel(handler), m_generationCounter(0),
      m_handler(handler), m_type(type)
{
    m_root = new WatchItem;
    m_root->hasChildren = 1;
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");
    m_root->parent = 0;

    switch (m_type) {
        case ReturnWatch:
            m_root->iname = "return";
            m_root->name = WatchHandler::tr("Return Value");
            break;
        case LocalsWatch:
            m_root->iname = "local";
            m_root->name = WatchHandler::tr("Locals");
            break;
        case WatchersWatch:
            m_root->iname = "watch";
            m_root->name = WatchHandler::tr("Watchers");
            break;
        case TooltipsWatch:
            m_root->iname = "tooltip";
            m_root->name = WatchHandler::tr("Tooltip");
            break;
    }
}

WatchModel::~WatchModel()
{
    delete m_root;
}

WatchItem *WatchModel::rootItem() const
{
    return m_root;
}

void WatchModel::reinitialize()
{
    int n = m_root->children.size();
    if (n == 0)
        return;
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
    QModelIndex index = watchIndex(m_root);
    beginRemoveRows(index, 0, n - 1);
    qDeleteAll(m_root->children);
    m_root->children.clear();
    endRemoveRows();
}

void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

void WatchModel::beginCycle(bool fullCycle)
{
    if (fullCycle)
        m_generationCounter++;

    emit enableUpdates(false);
}

void WatchModel::endCycle()
{
    removeOutdated();
    m_fetchTriggered.clear();
    emit enableUpdates(true);
}

DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}

void WatchModel::dump()
{
    qDebug() << "\n";
    foreach (WatchItem *child, m_root->children)
        dumpHelper(child);
}

void WatchModel::dumpHelper(WatchItem *item)
{
    qDebug() << "ITEM: " << item->iname
        << (item->parent ? item->parent->iname : "<none>")
        << item->generation;
    foreach (WatchItem *child, item->children)
        dumpHelper(child);
}

void WatchModel::removeOutdated()
{
    foreach (WatchItem *child, m_root->children)
        removeOutdatedHelper(child);
#if DEBUG_MODEL
#if USE_WATCH_MODEL_TEST
    (void) new ModelTest(this, this);
#endif
#endif
}

void WatchModel::removeOutdatedHelper(WatchItem *item)
{
    if (item->generation < m_generationCounter) {
        destroyItem(item);
    } else {
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
    }
}

void WatchModel::destroyItem(WatchItem *item)
{
    WatchItem *parent = item->parent;
    QModelIndex index = watchIndex(parent);
    int n = parent->children.indexOf(item);
    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
    beginRemoveRows(index, n, n);
    parent->children.removeAt(n);
    endRemoveRows();
    delete item;
}

static QByteArray parentName(const QByteArray &iname)
{
    int pos = iname.lastIndexOf('.');
    if (pos == -1)
        return QByteArray();
    return iname.left(pos);
}

static QString niceTypeHelper(const QByteArray &typeIn)
{
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
    if (it != cache.constEnd())
        return it.value();
    const QString simplified = CPlusPlus::simplifySTLType(typeIn);
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
}

QString WatchModel::removeNamespaces(QString str) const
{
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
        str.remove(QLatin1String("std::"));
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
        if (!qtNamespace.isEmpty())
            str.remove(qtNamespace);
    }
    return str;
}

QString WatchModel::removeInitialNamespace(QString str) const
{
    if (str.startsWith(QLatin1String("std::"))
            && debuggerCore()->boolSetting(ShowStdNamespace))
        str = str.mid(5);
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
        const QByteArray qtNamespace = engine()->qtNamespace();
        if (!qtNamespace.isEmpty() && str.startsWith(qtNamespace))
            str = str.mid(qtNamespace.size());
    }
    return str;
}

QString WatchModel::displayType(const WatchData &data) const
{
    QString base = data.displayedType.isEmpty()
        ? niceTypeHelper(data.type)
        : data.displayedType;
    if (data.bitsize)
        base += QString(":%1").arg(data.bitsize);
    base.remove('\'');
    return base;
}

static int formatToIntegerBase(int format)
{
    switch (format) {
        case HexadecimalFormat:
            return 16;
        case BinaryFormat:
            return 2;
        case OctalFormat:
            return 8;
    }
    return 10;
}

template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
        case HexadecimalFormat:
            return ("(hex) ") + QString::number(value, 16);
        case BinaryFormat:
            return ("(bin) ") + QString::number(value, 2);
        case OctalFormat:
            return ("(oct) ") + QString::number(value, 8);
    }
    return QString::number(value); // not reached
}

// Format printable (char-type) characters
static QString reformatCharacter(int code, int format)
{
    const QString codeS = reformatInteger(code, format);
    if (code < 0) // Append unsigned value.
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format);
    if (code >= 32 && code < 128)
        return codeS + QLatin1String(" '") + QChar(code) + QLatin1Char('\'');
    switch (code) {
    case 0:
        return codeS + QLatin1String(" '\\0'");
    case '\r':
        return codeS + QLatin1String(" '\\r'");
    case '\t':
        return codeS + QLatin1String(" '\\t'");
    case '\n':
        return codeS + QLatin1String(" '\\n'");
    }
    return codeS;
}

static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;

    QString encoded;
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
            if (u >= 32 && u < 127)
                encoded += c;
            else if (u == '\r')
                encoded += "\\r";
            else if (u == '\t')
                encoded += "\\t";
            else if (u == '\n')
                encoded += "\\n";
            else
                encoded += QString("\\%1")
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
            encoded += QString("\\%1")
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
            encoded += QString("\\u%1")
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

QString WatchModel::formattedValue(const WatchData &data) const
{
    const QByteArray qtNamespace = engine()->qtNamespace();
    const QString &value = data.value;

    int format = itemFormat(data);
    if (isIntType(data.type)) {
        if (value.isEmpty())
            return value;
        // Do not reformat booleans (reported as 'true, false').
        const QChar firstChar = value.at(0);
        if (!firstChar.isDigit() && firstChar != QLatin1Char('-'))
            return value;
        // Append quoted, printable character also for decimal.
        if (data.type.endsWith("char")) {
            bool ok;
            const int code = value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : value;
        }
        // Rest: Leave decimal as is
        if (format <= 0)
            return value;
        // Evil hack, covers 'unsigned' as well as quint64.
        if (data.type.contains('u'))
            return reformatInteger(value.toULongLong(0, 0), format);
        return reformatInteger(value.toLongLong(), format);
    }

    if (data.type == "va_list")
        return value;

    if (!isPointerType(data.type) && !isVTablePointer(data.type)) {
        bool ok = false;
        qulonglong integer = value.toULongLong(&ok, 0);
        if (ok)
           return reformatInteger(integer, format);
    }

    QString result = value;
    if (result.startsWith(QLatin1Char('<'))) {
        if (result == QLatin1String("<Edit>"))
            result = WatchHandler::tr("<Edit>");
        else if (result == QLatin1String("<empty>"))
            result = WatchHandler::tr("<empty>");
        else if (result == QLatin1String("<uninitialized>"))
            result = WatchHandler::tr("<uninitialized>");
        else if (result == QLatin1String("<invalid>"))
            result = WatchHandler::tr("<invalid>");
        else if (result == QLatin1String("<not accessible>"))
            result = WatchHandler::tr("<not accessible>");
        else if (result.endsWith(" items>")) {
            // '<10 items>' or '<>10 items>' (more than)
            bool ok;
            const bool moreThan = result.at(1) == QLatin1Char('>');
            const int numberPos = moreThan ? 2 : 1;
            const int len = result.indexOf(' ') - numberPos;
            const int size = result.mid(numberPos, len).toInt(&ok);
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
                qPrintable(result)))
            result = moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }

    return quoteUnprintable(result);
}

// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
// "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
    const int blankPos = data.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        data.truncate(blankPos);
    data.remove(QLatin1Char('`'));
    return data.toULongLong(0, 0);
}

// Return the type used for editing
static inline int editType(const WatchData &d)
{<commit_msg>debugger: don't try to escape translated strings<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (<EMAIL>)
**
**
** 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 <EMAIL>.
**
**************************************************************************/

#include "watchhandler.h"

#include "breakhandler.h"
#include "debuggerinternalconstants.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include "watchutils.h"

#if USE_WATCH_MODEL_TEST
#include "modeltest.h"
#endif

#include <utils/qtcassert.h>
#include <utils/savedaction.h>

#include <cplusplus/CppRewriter.h>

#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtCore/QFile>
#include <QtCore/QProcess>
#include <QtCore/QTextStream>
#include <QtCore/QtAlgorithms>

#include <QtGui/QLabel>
#include <QtGui/QTextEdit>

#include <ctype.h>
#include <utils/qtcassert.h>


namespace Debugger {
namespace Internal {

// Creates debug output for accesses to the model.
enum { debugModel = 0 };

#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
#define MODEL_DEBUGX(s) qDebug() << s

QHash<QByteArray, int> WatchHandler::m_watcherNames;
QHash<QByteArray, int> WatchHandler::m_typeFormats;

static QByteArray stripTemplate(const QByteArray &ba)
{
    int pos = ba.indexOf('<');
    return pos == -1 ? ba : ba.left(pos);
}


static int m_unprintableBase = -1;

void WatchHandler::setUnprintableBase(int base)
{
    m_unprintableBase = base;
    emitAllChanged();
}

int WatchHandler::unprintableBase()
{
    return m_unprintableBase;
}

////////////////////////////////////////////////////////////////////
//
// WatchItem
//
////////////////////////////////////////////////////////////////////

class WatchItem : public WatchData
{
public:
    WatchItem() { parent = 0; }

    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

    WatchItem(const WatchData &data) : WatchData(data)
        { parent = 0; }

    void setData(const WatchData &data)
        { static_cast<WatchData &>(*this) = data; }

    WatchItem *parent;
    QList<WatchItem *> children;  // fetched children
};


///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////

WatchModel::WatchModel(WatchHandler *handler, WatchType type)
    : QAbstractItemModel(handler), m_generationCounter(0),
      m_handler(handler), m_type(type)
{
    m_root = new WatchItem;
    m_root->hasChildren = 1;
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");
    m_root->parent = 0;

    switch (m_type) {
        case ReturnWatch:
            m_root->iname = "return";
            m_root->name = WatchHandler::tr("Return Value");
            break;
        case LocalsWatch:
            m_root->iname = "local";
            m_root->name = WatchHandler::tr("Locals");
            break;
        case WatchersWatch:
            m_root->iname = "watch";
            m_root->name = WatchHandler::tr("Watchers");
            break;
        case TooltipsWatch:
            m_root->iname = "tooltip";
            m_root->name = WatchHandler::tr("Tooltip");
            break;
    }
}

WatchModel::~WatchModel()
{
    delete m_root;
}

WatchItem *WatchModel::rootItem() const
{
    return m_root;
}

void WatchModel::reinitialize()
{
    int n = m_root->children.size();
    if (n == 0)
        return;
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
    QModelIndex index = watchIndex(m_root);
    beginRemoveRows(index, 0, n - 1);
    qDeleteAll(m_root->children);
    m_root->children.clear();
    endRemoveRows();
}

void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

void WatchModel::beginCycle(bool fullCycle)
{
    if (fullCycle)
        m_generationCounter++;

    emit enableUpdates(false);
}

void WatchModel::endCycle()
{
    removeOutdated();
    m_fetchTriggered.clear();
    emit enableUpdates(true);
}

DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}

void WatchModel::dump()
{
    qDebug() << "\n";
    foreach (WatchItem *child, m_root->children)
        dumpHelper(child);
}

void WatchModel::dumpHelper(WatchItem *item)
{
    qDebug() << "ITEM: " << item->iname
        << (item->parent ? item->parent->iname : "<none>")
        << item->generation;
    foreach (WatchItem *child, item->children)
        dumpHelper(child);
}

void WatchModel::removeOutdated()
{
    foreach (WatchItem *child, m_root->children)
        removeOutdatedHelper(child);
#if DEBUG_MODEL
#if USE_WATCH_MODEL_TEST
    (void) new ModelTest(this, this);
#endif
#endif
}

void WatchModel::removeOutdatedHelper(WatchItem *item)
{
    if (item->generation < m_generationCounter) {
        destroyItem(item);
    } else {
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
    }
}

void WatchModel::destroyItem(WatchItem *item)
{
    WatchItem *parent = item->parent;
    QModelIndex index = watchIndex(parent);
    int n = parent->children.indexOf(item);
    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
    beginRemoveRows(index, n, n);
    parent->children.removeAt(n);
    endRemoveRows();
    delete item;
}

static QByteArray parentName(const QByteArray &iname)
{
    int pos = iname.lastIndexOf('.');
    if (pos == -1)
        return QByteArray();
    return iname.left(pos);
}

static QString niceTypeHelper(const QByteArray &typeIn)
{
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
    if (it != cache.constEnd())
        return it.value();
    const QString simplified = CPlusPlus::simplifySTLType(typeIn);
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
}

QString WatchModel::removeNamespaces(QString str) const
{
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
        str.remove(QLatin1String("std::"));
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
        if (!qtNamespace.isEmpty())
            str.remove(qtNamespace);
    }
    return str;
}

QString WatchModel::removeInitialNamespace(QString str) const
{
    if (str.startsWith(QLatin1String("std::"))
            && debuggerCore()->boolSetting(ShowStdNamespace))
        str = str.mid(5);
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
        const QByteArray qtNamespace = engine()->qtNamespace();
        if (!qtNamespace.isEmpty() && str.startsWith(qtNamespace))
            str = str.mid(qtNamespace.size());
    }
    return str;
}

QString WatchModel::displayType(const WatchData &data) const
{
    QString base = data.displayedType.isEmpty()
        ? niceTypeHelper(data.type)
        : data.displayedType;
    if (data.bitsize)
        base += QString(":%1").arg(data.bitsize);
    base.remove('\'');
    return base;
}

static int formatToIntegerBase(int format)
{
    switch (format) {
        case HexadecimalFormat:
            return 16;
        case BinaryFormat:
            return 2;
        case OctalFormat:
            return 8;
    }
    return 10;
}

template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
        case HexadecimalFormat:
            return ("(hex) ") + QString::number(value, 16);
        case BinaryFormat:
            return ("(bin) ") + QString::number(value, 2);
        case OctalFormat:
            return ("(oct) ") + QString::number(value, 8);
    }
    return QString::number(value); // not reached
}

// Format printable (char-type) characters
static QString reformatCharacter(int code, int format)
{
    const QString codeS = reformatInteger(code, format);
    if (code < 0) // Append unsigned value.
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format);
    if (code >= 32 && code < 128)
        return codeS + QLatin1String(" '") + QChar(code) + QLatin1Char('\'');
    switch (code) {
    case 0:
        return codeS + QLatin1String(" '\\0'");
    case '\r':
        return codeS + QLatin1String(" '\\r'");
    case '\t':
        return codeS + QLatin1String(" '\\t'");
    case '\n':
        return codeS + QLatin1String(" '\\n'");
    }
    return codeS;
}

static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;

    QString encoded;
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
            if (u >= 32 && u < 127)
                encoded += c;
            else if (u == '\r')
                encoded += "\\r";
            else if (u == '\t')
                encoded += "\\t";
            else if (u == '\n')
                encoded += "\\n";
            else
                encoded += QString("\\%1")
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
            encoded += QString("\\%1")
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
            encoded += QString("\\u%1")
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

QString WatchModel::formattedValue(const WatchData &data) const
{
    const QByteArray qtNamespace = engine()->qtNamespace();
    const QString &value = data.value;

    int format = itemFormat(data);
    if (isIntType(data.type)) {
        if (value.isEmpty())
            return value;
        // Do not reformat booleans (reported as 'true, false').
        const QChar firstChar = value.at(0);
        if (!firstChar.isDigit() && firstChar != QLatin1Char('-'))
            return value;
        // Append quoted, printable character also for decimal.
        if (data.type.endsWith("char")) {
            bool ok;
            const int code = value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : value;
        }
        // Rest: Leave decimal as is
        if (format <= 0)
            return value;
        // Evil hack, covers 'unsigned' as well as quint64.
        if (data.type.contains('u'))
            return reformatInteger(value.toULongLong(0, 0), format);
        return reformatInteger(value.toLongLong(), format);
    }

    if (data.type == "va_list")
        return value;

    if (!isPointerType(data.type) && !isVTablePointer(data.type)) {
        bool ok = false;
        qulonglong integer = value.toULongLong(&ok, 0);
        if (ok)
           return reformatInteger(integer, format);
    }

    QString result = value;
    if (result.startsWith(QLatin1Char('<'))) {
        if (result == QLatin1String("<Edit>"))
            return WatchHandler::tr("<Edit>");
        if (result == QLatin1String("<empty>"))
            return WatchHandler::tr("<empty>");
        if (result == QLatin1String("<uninitialized>"))
            return WatchHandler::tr("<uninitialized>");
        if (result == QLatin1String("<invalid>"))
            return WatchHandler::tr("<invalid>");
        if (result == QLatin1String("<not accessible>"))
            return WatchHandler::tr("<not accessible>");
        if (result.endsWith(" items>")) {
            // '<10 items>' or '<>10 items>' (more than)
            bool ok;
            const bool moreThan = result.at(1) == QLatin1Char('>');
            const int numberPos = moreThan ? 2 : 1;
            const int len = result.indexOf(' ') - numberPos;
            const int size = result.mid(numberPos, len).toInt(&ok);
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
                qPrintable(result)))
            return moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }

    return quoteUnprintable(result);
}

// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
// "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
    const int blankPos = data.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        data.truncate(blankPos);
    data.remove(QLatin1Char('`'));
    return data.toULongLong(0, 0);
}

// Return the type used for editing
static inline int editType(const WatchData &d)
{
{'fraction_non_alphanumeric': 0.164542209920606, 'fraction_numerical': 0.01056158496613009, 'mean_word_length': 2.5997640272679603, 'pattern_counts': {'":': 2, '<': 141, '<?xml version=': 0, '>': 163, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7233397', 'n_tokens_mistral': 8428, 'n_tokens_neox': 8090, 'n_words': 2089}