...
 
Commits (4)
......@@ -24,7 +24,7 @@
#include "debugview.h"
#include <QFile>
#include <QRegExp>
#include <QRegularExpression>
#include <QTimer>
#include <klocalizedstring.h>
......@@ -262,23 +262,24 @@ void DebugView::slotContinue()
issueCommand(QStringLiteral("continue"));
}
static QRegExp breakpointList(QStringLiteral("Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What.*"));
static QRegExp breakpointListed(QStringLiteral("(\\d)\\s+breakpoint\\s+keep\\sy\\s+0x[\\da-f]+\\sin\\s.+\\sat\\s([^:]+):(\\d+).*"));
static QRegExp stackFrameAny(QStringLiteral("#(\\d+)\\s(.*)"));
static QRegExp stackFrameFile(QStringLiteral("#(\\d+)\\s+(?:0x[\\da-f]+\\s*in\\s)*(\\S+)(\\s\\(.*\\)) at ([^:]+):(\\d+).*"));
static QRegExp changeFile(QStringLiteral("(?:(?:Temporary\\sbreakpoint|Breakpoint)\\s*\\d+,\\s*|0x[\\da-f]+\\s*in\\s*)?[^\\s]+\\s*\\([^)]*\\)\\s*at\\s*([^:]+):(\\d+).*"));
static QRegExp changeLine(QStringLiteral("(\\d+)\\s+.*"));
static QRegExp breakPointReg(QStringLiteral("Breakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+file\\s+([^\\,]+)\\,\\s+line\\s+(\\d+).*"));
static QRegExp breakPointMultiReg(QStringLiteral("Breakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+([^\\,]+):(\\d+).*"));
static QRegExp breakPointDel(QStringLiteral("Deleted\\s+breakpoint.*"));
static QRegExp exitProgram(QStringLiteral("(?:Program|.*Inferior.*)\\s+exited.*"));
static QRegExp threadLine(QStringLiteral("\\**\\s+(\\d+)\\s+Thread.*"));
static const QRegularExpression breakpointList(QStringLiteral("\\ANum\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What.*\\z"));
static const QRegularExpression breakpointListed(QStringLiteral("\\A(\\d)\\s+breakpoint\\s+keep\\sy\\s+0x[\\da-f]+\\sin\\s.+\\sat\\s([^:]+):(\\d+).*\\z"));
static const QRegularExpression stackFrameAny(QStringLiteral("\\A#(\\d+)\\s(.*)\\z"));
static const QRegularExpression stackFrameFile(QStringLiteral("\\A#(\\d+)\\s+(?:0x[\\da-f]+\\s*in\\s)*(\\S+)(\\s\\(.*\\)) at ([^:]+):(\\d+).*\\z"));
static const QRegularExpression changeFile(QStringLiteral("\\A(?:(?:Temporary\\sbreakpoint|Breakpoint)\\s*\\d+,\\s*|0x[\\da-f]+\\s*in\\s*)?[^\\s]+\\s*\\([^)]*\\)\\s*at\\s*([^:]+):(\\d+).*\\z"));
static const QRegularExpression changeLine(QStringLiteral("\\A(\\d+)\\s+.*\\z"));
static const QRegularExpression breakPointReg(QStringLiteral("\\ABreakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+file\\s+([^\\,]+)\\,\\s+line\\s+(\\d+).*\\z"));
static const QRegularExpression breakPointMultiReg(QStringLiteral("\\ABreakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+([^\\,]+):(\\d+).*\\z"));
static const QRegularExpression breakPointDel(QStringLiteral("\\ADeleted\\s+breakpoint.*\\z"));
static const QRegularExpression exitProgram(QStringLiteral("\\A(?:Program|.*Inferior.*)\\s+exited.*\\z"));
static const QRegularExpression threadLine(QStringLiteral("\\A\\**\\s+(\\d+)\\s+Thread.*\\z"));
void DebugView::processLine(QString line)
{
if (line.isEmpty())
return;
static QRegularExpressionMatch match;
switch (m_state) {
case none:
case ready:
......@@ -289,36 +290,36 @@ void DebugView::processLine(QString line)
break;
case executingCmd:
if (breakpointList.exactMatch(line)) {
if (breakpointList.match(line).hasMatch()) {
m_state = listingBreakpoints;
emit clearBreakpointMarks();
m_breakPointList.clear();
} else if (line.contains(QLatin1String("No breakpoints or watchpoints."))) {
emit clearBreakpointMarks();
m_breakPointList.clear();
} else if (stackFrameAny.exactMatch(line)) {
} else if ((match = stackFrameAny.match(line)).hasMatch()) {
if (m_lastCommand.contains(QLatin1String("info stack"))) {
emit stackFrameInfo(stackFrameAny.cap(1), stackFrameAny.cap(2));
emit stackFrameInfo(match.captured(1), match.captured(2));
} else {
m_subState = (m_subState == normal) ? stackFrameSeen : stackTraceSeen;
m_newFrameLevel = stackFrameAny.cap(1).toInt();
m_newFrameLevel = match.captured(1).toInt();
if (stackFrameFile.exactMatch(line)) {
m_newFrameFile = stackFrameFile.cap(4);
if ((match = stackFrameFile.match(line)).hasMatch()) {
m_newFrameFile = match.captured(4);
}
}
} else if (changeFile.exactMatch(line)) {
m_currentFile = changeFile.cap(1).trimmed();
int lineNum = changeFile.cap(2).toInt();
} else if ((match = changeFile.match(line)).hasMatch()) {
m_currentFile = match.captured(1).trimmed();
int lineNum = match.captured(2).toInt();
if (!m_nextCommands.contains(QLatin1String("continue"))) {
// GDB uses 1 based line numbers, kate uses 0 based...
emit debugLocationChanged(resolveFileName(m_currentFile), lineNum - 1);
}
m_debugLocationChanged = true;
} else if (changeLine.exactMatch(line)) {
int lineNum = changeLine.cap(1).toInt();
} else if ((match = changeLine.match(line)).hasMatch()) {
int lineNum = match.captured(1).toInt();
if (m_subState == stackFrameSeen) {
m_currentFile = m_newFrameFile;
......@@ -328,21 +329,21 @@ void DebugView::processLine(QString line)
emit debugLocationChanged(resolveFileName(m_currentFile), lineNum - 1);
}
m_debugLocationChanged = true;
} else if (breakPointReg.exactMatch(line)) {
} else if ((match = breakPointReg.match(line)).hasMatch()) {
BreakPoint breakPoint;
breakPoint.number = breakPointReg.cap(1).toInt();
breakPoint.file = resolveFileName(breakPointReg.cap(2));
breakPoint.line = breakPointReg.cap(3).toInt();
breakPoint.number = match.captured(1).toInt();
breakPoint.file = resolveFileName(match.captured(2));
breakPoint.line = match.captured(3).toInt();
m_breakPointList << breakPoint;
emit breakPointSet(breakPoint.file, breakPoint.line - 1);
} else if (breakPointMultiReg.exactMatch(line)) {
} else if ((match = breakPointMultiReg.match(line)).hasMatch()) {
BreakPoint breakPoint;
breakPoint.number = breakPointMultiReg.cap(1).toInt();
breakPoint.file = resolveFileName(breakPointMultiReg.cap(2));
breakPoint.line = breakPointMultiReg.cap(3).toInt();
breakPoint.number = match.captured(1).toInt();
breakPoint.file = resolveFileName(match.captured(2));
breakPoint.line = match.captured(3).toInt();
m_breakPointList << breakPoint;
emit breakPointSet(breakPoint.file, breakPoint.line - 1);
} else if (breakPointDel.exactMatch(line)) {
} else if (breakPointDel.match(line).hasMatch()) {
line.remove(QStringLiteral("Deleted breakpoint"));
line.remove(QLatin1Char('s')); // in case of multiple breakpoints
QStringList numbers = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
......@@ -355,7 +356,7 @@ void DebugView::processLine(QString line)
}
}
}
} else if (exitProgram.exactMatch(line) || line.contains(QLatin1String("The program no longer exists")) || line.contains(QLatin1String("Kill the program being debugged"))) {
} else if (exitProgram.match(line).hasMatch() || line.contains(QLatin1String("The program no longer exists")) || line.contains(QLatin1String("Kill the program being debugged"))) {
// if there are still commands to execute remove them to remove unneeded output
// except if the "kill was for "re-run"
if ((!m_nextCommands.empty()) && !m_nextCommands[0].contains(QLatin1String("file"))) {
......@@ -374,12 +375,12 @@ void DebugView::processLine(QString line)
}
break;
case listingBreakpoints:
if (breakpointListed.exactMatch(line)) {
case listingBreakpoints:;
if ((match = breakpointListed.match(line)).hasMatch()) {
BreakPoint breakPoint;
breakPoint.number = breakpointListed.cap(1).toInt();
breakPoint.file = resolveFileName(breakpointListed.cap(2));
breakPoint.line = breakpointListed.cap(3).toInt();
breakPoint.number = match.captured(1).toInt();
breakPoint.file = resolveFileName(match.captured(2));
breakPoint.line = match.captured(3).toInt();
m_breakPointList << breakPoint;
emit breakPointSet(breakPoint.file, breakPoint.line - 1);
} else if (PromptStr == line) {
......@@ -417,16 +418,16 @@ void DebugView::processLine(QString line)
m_state = ready;
emit stackFrameInfo(QString(), QString());
QTimer::singleShot(0, this, &DebugView::issueNextCommand);
} else if (stackFrameAny.exactMatch(line)) {
emit stackFrameInfo(stackFrameAny.cap(1), stackFrameAny.cap(2));
} else if ((match = stackFrameAny.match(line)).hasMatch()) {
emit stackFrameInfo(match.captured(1), match.captured(2));
}
break;
case infoThreads:
if (PromptStr == line) {
m_state = ready;
QTimer::singleShot(0, this, &DebugView::issueNextCommand);
} else if (threadLine.exactMatch(line)) {
emit threadInfo(threadLine.cap(1).toInt(), (line[0] == QLatin1Char('*')));
} else if ((match = threadLine.match(line)).hasMatch()) {
emit threadInfo(match.captured(1).toInt(), (line[0] == QLatin1Char('*')));
}
break;
}
......
......@@ -29,7 +29,7 @@ LocalsView::LocalsView(QWidget *parent)
headers << i18n("Symbol");
headers << i18n("Value");
setHeaderLabels(headers);
setAutoScroll(false);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
LocalsView::~LocalsView()
......@@ -53,6 +53,9 @@ void LocalsView::createWrappedItem(QTreeWidgetItem *parent, const QString &name,
label->setWordWrap(true);
setItemWidget(item, 1, label);
item->setData(1, Qt::UserRole, value);
item->setToolTip(0, QStringLiteral("<qt>%1<qt>").arg(name));
item->setToolTip(1, QStringLiteral("<qt>%1<qt>").arg(value));
parent->setToolTip(0, QStringLiteral("<qt>%1<qt>").arg(parent->text(0)));
}
void LocalsView::createWrappedItem(QTreeWidget *parent, const QString &name, const QString &value)
......@@ -61,16 +64,18 @@ void LocalsView::createWrappedItem(QTreeWidget *parent, const QString &name, con
QLabel *label = new QLabel(value);
label->setWordWrap(true);
setItemWidget(item, 1, label);
item->setToolTip(0, QStringLiteral("<qt>%1<qt>").arg(name));
item->setToolTip(1, QStringLiteral("<qt>%1<qt>").arg(value));
}
void LocalsView::addLocal(const QString &vString)
{
static QRegExp isValue(QStringLiteral("(\\S*)\\s=\\s(.*)"));
static QRegExp isStruct(QStringLiteral("\\{\\S*\\s=\\s.*"));
static QRegExp isStartPartial(QStringLiteral("\\S*\\s=\\s\\S*\\s=\\s\\{"));
static QRegExp isPrettyQList(QStringLiteral("\\s*\\[\\S*\\]\\s=\\s\\S*"));
static QRegExp isPrettyValue(QStringLiteral("(\\S*)\\s=\\s(\\S*)\\s=\\s(.*)"));
static QRegExp isThisValue(QStringLiteral("\\$\\d+"));
static const QRegularExpression isValue(QStringLiteral("\\A(\\S*)\\s=\\s(.*)\\z"));
static const QRegularExpression isStruct(QStringLiteral("\\A\\{\\S*\\s=\\s.*\\z"));
static const QRegularExpression isStartPartial(QStringLiteral("\\A\\S*\\s=\\s\\S*\\s=\\s\\{\\z"));
static const QRegularExpression isPrettyQList(QStringLiteral("\\A\\s*\\[\\S*\\]\\s=\\s\\S*\\z"));
static const QRegularExpression isPrettyValue(QStringLiteral("\\A(\\S*)\\s=\\s(\\S*)\\s=\\s(.*)\\z"));
static const QRegularExpression isThisValue(QStringLiteral("\\A\\$\\d+\\z"));
if (m_allAdded) {
clear();
......@@ -81,11 +86,14 @@ void LocalsView::addLocal(const QString &vString)
m_allAdded = true;
return;
}
if (isStartPartial.exactMatch(vString)) {
QRegularExpressionMatch match = isStartPartial.match(vString);
if (match.hasMatch()) {
m_local = vString;
return;
}
if (isPrettyQList.exactMatch(vString)) {
match = isPrettyQList.match(vString);
if (match.hasMatch()) {
m_local += vString.trimmed();
if (m_local.endsWith(QLatin1Char(',')))
m_local += QLatin1Char(' ');
......@@ -102,24 +110,27 @@ void LocalsView::addLocal(const QString &vString)
if (vString == QLatin1String("No symbol table info available.")) {
return; /* this is not an error */
}
if (!isValue.exactMatch(vString)) {
match = isValue.match(vString);
if (!match.hasMatch()) {
qDebug() << "Could not parse:" << vString;
return;
}
symbolAndValue << isValue.cap(1);
symbolAndValue << match.captured(1);
value = match.captured(2);
// check out for "print *this"
if (isThisValue.exactMatch(symbolAndValue[0])) {
match = isThisValue.match(symbolAndValue[0]);
if (match.hasMatch()) {
symbolAndValue[0] = QStringLiteral("*this");
}
value = isValue.cap(2);
} else {
if (!isPrettyValue.exactMatch(m_local)) {
match = isPrettyValue.match(m_local);
if (!match.hasMatch()) {
qDebug() << "Could not parse:" << m_local;
m_local.clear();
return;
}
symbolAndValue << isPrettyValue.cap(1) << isPrettyValue.cap(2);
value = isPrettyValue.cap(3);
symbolAndValue << match.captured(1) << match.captured(2);
value = match.captured(3);
}
QTreeWidgetItem *item;
......@@ -128,7 +139,8 @@ void LocalsView::addLocal(const QString &vString)
item = new QTreeWidgetItem(this, symbolAndValue);
addArray(item, value.mid(1, value.size() - 2));
} else {
if (isStruct.exactMatch(value)) {
match = isStruct.match(value);
if (match.hasMatch()) {
item = new QTreeWidgetItem(this, symbolAndValue);
addStruct(item, value.mid(1, value.size() - 2));
} else {
......@@ -144,8 +156,8 @@ void LocalsView::addLocal(const QString &vString)
void LocalsView::addStruct(QTreeWidgetItem *parent, const QString &vString)
{
static QRegExp isArray(QStringLiteral("\\{\\.*\\s=\\s.*"));
static QRegExp isStruct(QStringLiteral("\\.*\\s=\\s.*"));
static const QRegularExpression isArray(QStringLiteral("\\A\\{\\.*\\s=\\s.*\\z"));
static const QRegularExpression isStruct(QStringLiteral("\\A\\.*\\s=\\s.*\\z"));
QTreeWidgetItem *item;
QStringList symbolAndValue;
QString subValue;
......@@ -193,10 +205,10 @@ void LocalsView::addStruct(QTreeWidgetItem *parent, const QString &vString)
end++;
}
subValue = vString.mid(start, end - start);
if (isArray.exactMatch(subValue)) {
if (isArray.match(subValue).hasMatch()) {
item = new QTreeWidgetItem(parent, symbolAndValue);
addArray(item, subValue);
} else if (isStruct.exactMatch(subValue)) {
} else if (isStruct.match(subValue).hasMatch()) {
item = new QTreeWidgetItem(parent, symbolAndValue);
addStruct(item, subValue);
} else {
......
......@@ -37,17 +37,14 @@
using namespace KTextEditorPreview;
// 300 ms as initial proposal, was found to be delay which worked for the
// author with the use-case of quickly peeking over to the preview while
// typing to see if things are working out as intended, without getting a
// having-to-wait feeling.
// Surely could get some more serious research what is a proper (default) value.
// Perhaps the whole update logic cpuld also be overhauled, to only do an
// update once there was at least xxx ms idle time to meet the use-case of
// quickly-peeking-over. And otherwise update in bigger intervals of
// 500-2000(?) ms, to cover the use-case of seeing from the corner of one's
// eye that something is changing while one is editing the sources.
static const int updateDelay = 300; // ms
// There are two timers that run on update. One timer is fast, but is
// cancelled each time a new updated comes in. Another timer is slow, but is
// not cancelled if another update comes in. With this, "while typing", the
// preview is updated every 1000ms, thus one sees that something is happening
// from the corner of one's eyes. After stopping typing, the preview is
// updated quickly after 150ms so that the preview has the newest version.
static const int updateDelayFast = 150; // ms
static const int updateDelaySlow = 1000; // ms
KPartView::KPartView(const KService::Ptr &service, QObject *parent)
: QObject(parent)
......@@ -62,9 +59,13 @@ KPartView::KPartView(const KService::Ptr &service, QObject *parent)
delete m_part;
m_errorLabel = new QLabel(QStringLiteral("KPart provides no widget."));
} else {
m_updateSquashingTimer.setSingleShot(true);
m_updateSquashingTimer.setInterval(updateDelay);
connect(&m_updateSquashingTimer, &QTimer::timeout, this, &KPartView::updatePreview);
m_updateSquashingTimerFast.setSingleShot(true);
m_updateSquashingTimerFast.setInterval(updateDelayFast);
connect(&m_updateSquashingTimerFast, &QTimer::timeout, this, &KPartView::updatePreview);
m_updateSquashingTimerSlow.setSingleShot(true);
m_updateSquashingTimerSlow.setInterval(updateDelaySlow);
connect(&m_updateSquashingTimerSlow, &QTimer::timeout, this, &KPartView::updatePreview);
auto browserExtension = m_part->browserExtension();
if (browserExtension) {
......@@ -110,7 +111,8 @@ void KPartView::setDocument(KTextEditor::Document *document)
if (m_document) {
disconnect(m_document, &KTextEditor::Document::textChanged, this, &KPartView::triggerUpdatePreview);
m_updateSquashingTimer.stop();
m_updateSquashingTimerFast.stop();
m_updateSquashingTimerSlow.stop();
}
m_document = document;
......@@ -143,21 +145,28 @@ void KPartView::setAutoUpdating(bool autoUpdating)
updatePreview();
}
} else {
m_updateSquashingTimer.stop();
m_updateSquashingTimerSlow.stop();
m_updateSquashingTimerFast.stop();
}
}
void KPartView::triggerUpdatePreview()
{
m_previewDirty = true;
if (m_part->widget()->isVisible() && m_autoUpdating && !m_updateSquashingTimer.isActive()) {
m_updateSquashingTimer.start();
if (m_part->widget()->isVisible() && m_autoUpdating) {
// Reset fast timer each time
m_updateSquashingTimerFast.start();
// Start slow timer, if not already running (don't reset!)
if(!m_updateSquashingTimerSlow.isActive())
m_updateSquashingTimerSlow.start();
}
}
void KPartView::updatePreview()
{
m_updateSquashingTimerSlow.stop();
m_updateSquashingTimerFast.stop();
if (!m_part->widget()->isVisible()) {
return;
}
......
......@@ -118,7 +118,8 @@ private:
bool m_autoUpdating = true;
bool m_previewDirty = true;
QTimer m_updateSquashingTimer;
QTimer m_updateSquashingTimerFast;
QTimer m_updateSquashingTimerSlow;
QTemporaryFile *m_bufferFile = nullptr;
};
......
......@@ -372,7 +372,7 @@ void KateSessionManageDialog::updateSessionList()
m_sessionList->header()->setSectionResizeMode(0, QHeaderView::Stretch); // stretch "Session Name" column
m_sessionList->resizeColumnToContents(1); // Fit "Files" column
m_sessionList->resizeColumnToContents(2); // Fit "Last update" column
m_sessionList->sortByColumn(2, Qt::DescendingOrder); // sort by "Last update" column.. don't worry, it only sorts when the model data changes.
m_sessionList->sortByColumn(0, Qt::DescendingOrder); // sort by "Session Name" column.. don't worry, it only sorts when the model data changes.
if (!preferredItem) {
preferredItem = currSessionItem ? currSessionItem : activeSessionItem;
......