Skip to content

Commit 66c275e

Browse files
committed
GUI: Added code editor for quick inspection of bugs
1 parent 0e6e8ec commit 66c275e

5 files changed

Lines changed: 335 additions & 0 deletions

File tree

gui/codeeditor.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <QtWidgets>
2+
3+
#include "codeeditor.h"
4+
5+
6+
Highlighter::Highlighter(QTextDocument *parent)
7+
: QSyntaxHighlighter(parent)
8+
{
9+
HighlightingRule rule;
10+
11+
keywordFormat.setForeground(Qt::darkBlue);
12+
keywordFormat.setFontWeight(QFont::Bold);
13+
QStringList keywordPatterns;
14+
keywordPatterns << "\\bchar\\b" << "\\bclass\\b" << "\\bconst\\b"
15+
<< "\\bdouble\\b" << "\\benum\\b" << "\\bexplicit\\b"
16+
<< "\\bfriend\\b" << "\\binline\\b" << "\\bint\\b"
17+
<< "\\blong\\b" << "\\bnamespace\\b" << "\\boperator\\b"
18+
<< "\\bprivate\\b" << "\\bprotected\\b" << "\\bpublic\\b"
19+
<< "\\bshort\\b" << "\\bsignals\\b" << "\\bsigned\\b"
20+
<< "\\bslots\\b" << "\\bstatic\\b" << "\\bstruct\\b"
21+
<< "\\btemplate\\b" << "\\btypedef\\b" << "\\btypename\\b"
22+
<< "\\bunion\\b" << "\\bunsigned\\b" << "\\bvirtual\\b"
23+
<< "\\bvoid\\b" << "\\bvolatile\\b" << "\\bbool\\b";
24+
foreach (const QString &pattern, keywordPatterns) {
25+
rule.pattern = QRegularExpression(pattern);
26+
rule.format = keywordFormat;
27+
highlightingRules.append(rule);
28+
}
29+
30+
classFormat.setFontWeight(QFont::Bold);
31+
classFormat.setForeground(Qt::darkMagenta);
32+
rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b");
33+
rule.format = classFormat;
34+
highlightingRules.append(rule);
35+
36+
quotationFormat.setForeground(Qt::darkGreen);
37+
rule.pattern = QRegularExpression("\".*\"");
38+
rule.format = quotationFormat;
39+
highlightingRules.append(rule);
40+
41+
functionFormat.setFontItalic(true);
42+
functionFormat.setForeground(Qt::blue);
43+
rule.pattern = QRegularExpression("\\b[A-Za-z0-9_]+(?=\\()");
44+
rule.format = functionFormat;
45+
highlightingRules.append(rule);
46+
47+
singleLineCommentFormat.setForeground(Qt::gray);
48+
rule.pattern = QRegularExpression("//[^\n]*");
49+
rule.format = singleLineCommentFormat;
50+
highlightingRules.append(rule);
51+
52+
multiLineCommentFormat.setForeground(Qt::gray);
53+
54+
commentStartExpression = QRegularExpression("/\\*");
55+
commentEndExpression = QRegularExpression("\\*/");
56+
}
57+
58+
void Highlighter::highlightBlock(const QString &text)
59+
{
60+
foreach (const HighlightingRule &rule, highlightingRules) {
61+
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
62+
while (matchIterator.hasNext()) {
63+
QRegularExpressionMatch match = matchIterator.next();
64+
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
65+
}
66+
}
67+
68+
setCurrentBlockState(0);
69+
70+
int startIndex = 0;
71+
if (previousBlockState() != 1)
72+
startIndex = text.indexOf(commentStartExpression);
73+
74+
while (startIndex >= 0) {
75+
QRegularExpressionMatch match = commentEndExpression.match(text, startIndex);
76+
int endIndex = match.capturedStart();
77+
int commentLength = 0;
78+
if (endIndex == -1) {
79+
setCurrentBlockState(1);
80+
commentLength = text.length() - startIndex;
81+
} else {
82+
commentLength = endIndex - startIndex
83+
+ match.capturedLength();
84+
}
85+
setFormat(startIndex, commentLength, multiLineCommentFormat);
86+
startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);
87+
}
88+
}
89+
90+
91+
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
92+
{
93+
lineNumberArea = new LineNumberArea(this);
94+
highlighter = new Highlighter(this->document());
95+
mErrorPosition = -1;
96+
97+
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
98+
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
99+
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
100+
101+
updateLineNumberAreaWidth(0);
102+
}
103+
104+
static int getPos(const QString &fileData, int lineNumber)
105+
{
106+
for (int pos = 0, line = 1; pos < fileData.size(); ++pos) {
107+
if (fileData[pos] != '\n')
108+
continue;
109+
++line;
110+
if (line == lineNumber)
111+
return pos + 1;
112+
}
113+
return fileData.size();
114+
}
115+
116+
void CodeEditor::setErrorLine(int errorLine)
117+
{
118+
mErrorPosition = getPos(toPlainText(), errorLine);
119+
QTextCursor tc = textCursor();
120+
tc.setPosition(mErrorPosition);
121+
setTextCursor(tc);
122+
centerCursor();
123+
}
124+
125+
int CodeEditor::lineNumberAreaWidth()
126+
{
127+
int digits = 1;
128+
int max = qMax(1, blockCount());
129+
while (max >= 10) {
130+
max /= 10;
131+
++digits;
132+
}
133+
134+
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
135+
return space;
136+
}
137+
138+
void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
139+
{
140+
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
141+
}
142+
143+
void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
144+
{
145+
if (dy)
146+
lineNumberArea->scroll(0, dy);
147+
else
148+
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
149+
150+
if (rect.contains(viewport()->rect()))
151+
updateLineNumberAreaWidth(0);
152+
}
153+
154+
void CodeEditor::resizeEvent(QResizeEvent *event)
155+
{
156+
QPlainTextEdit::resizeEvent(event);
157+
QRect cr = contentsRect();
158+
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
159+
}
160+
161+
void CodeEditor::highlightCurrentLine()
162+
{
163+
QTextCursor tc = textCursor();
164+
tc.setPosition(mErrorPosition);
165+
setTextCursor(tc);
166+
167+
QList<QTextEdit::ExtraSelection> extraSelections;
168+
169+
QTextEdit::ExtraSelection selection;
170+
171+
QColor lineColor = QColor(255,220,220);
172+
173+
selection.format.setBackground(lineColor);
174+
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
175+
selection.cursor = textCursor();
176+
selection.cursor.clearSelection();
177+
extraSelections.append(selection);
178+
179+
setExtraSelections(extraSelections);
180+
}
181+
182+
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
183+
{
184+
QPainter painter(lineNumberArea);
185+
painter.fillRect(event->rect(), QColor(240,240,240));
186+
187+
QTextBlock block = firstVisibleBlock();
188+
int blockNumber = block.blockNumber();
189+
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
190+
int bottom = top + (int) blockBoundingRect(block).height();
191+
192+
while (block.isValid() && top <= event->rect().bottom()) {
193+
if (block.isVisible() && bottom >= event->rect().top()) {
194+
QString number = QString::number(blockNumber + 1);
195+
painter.setPen(Qt::black);
196+
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
197+
Qt::AlignRight, number);
198+
}
199+
200+
block = block.next();
201+
top = bottom;
202+
bottom = top + (int) blockBoundingRect(block).height();
203+
++blockNumber;
204+
}
205+
}

gui/codeeditor.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#ifndef CODEEDITOR_H
2+
#define CODEEDITOR_H
3+
4+
#include <QSyntaxHighlighter>
5+
#include <QPlainTextEdit>
6+
#include <QObject>
7+
#include <QRegularExpression>
8+
9+
class QPaintEvent;
10+
class QResizeEvent;
11+
class QSize;
12+
class QWidget;
13+
14+
class LineNumberArea;
15+
16+
17+
class Highlighter : public QSyntaxHighlighter {
18+
Q_OBJECT
19+
20+
public:
21+
explicit Highlighter(QTextDocument *parent);
22+
23+
protected:
24+
void highlightBlock(const QString &text) override;
25+
26+
private:
27+
struct HighlightingRule {
28+
QRegularExpression pattern;
29+
QTextCharFormat format;
30+
};
31+
QVector<HighlightingRule> highlightingRules;
32+
33+
QRegularExpression commentStartExpression;
34+
QRegularExpression commentEndExpression;
35+
36+
QTextCharFormat keywordFormat;
37+
QTextCharFormat classFormat;
38+
QTextCharFormat singleLineCommentFormat;
39+
QTextCharFormat multiLineCommentFormat;
40+
QTextCharFormat quotationFormat;
41+
QTextCharFormat functionFormat;
42+
};
43+
44+
class CodeEditor : public QPlainTextEdit {
45+
Q_OBJECT
46+
47+
public:
48+
explicit CodeEditor(QWidget *parent);
49+
50+
void lineNumberAreaPaintEvent(QPaintEvent *event);
51+
int lineNumberAreaWidth();
52+
53+
void setErrorLine(int errorLine);
54+
55+
protected:
56+
void resizeEvent(QResizeEvent *event) override;
57+
58+
private slots:
59+
void updateLineNumberAreaWidth(int newBlockCount);
60+
void highlightCurrentLine();
61+
void updateLineNumberArea(const QRect &, int);
62+
63+
private:
64+
QWidget *lineNumberArea;
65+
Highlighter *highlighter;
66+
int mErrorPosition;
67+
};
68+
69+
70+
class LineNumberArea : public QWidget {
71+
public:
72+
explicit LineNumberArea(CodeEditor *editor) : QWidget(editor) {
73+
codeEditor = editor;
74+
}
75+
76+
QSize sizeHint() const override {
77+
return QSize(codeEditor->lineNumberAreaWidth(), 0);
78+
}
79+
80+
protected:
81+
void paintEvent(QPaintEvent *event) override {
82+
codeEditor->lineNumberAreaPaintEvent(event);
83+
}
84+
85+
private:
86+
CodeEditor *codeEditor;
87+
};
88+
89+
#endif // CODEEDITOR_H

gui/gui.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ HEADERS += aboutdialog.h \
8989
applicationlist.h \
9090
checkstatistics.h \
9191
checkthread.h \
92+
codeeditor.h \
9293
common.h \
9394
csvreport.h \
9495
erroritem.h \
@@ -123,6 +124,7 @@ SOURCES += aboutdialog.cpp \
123124
applicationlist.cpp \
124125
checkstatistics.cpp \
125126
checkthread.cpp \
127+
codeeditor.cpp \
126128
common.cpp \
127129
csvreport.cpp \
128130
erroritem.cpp \

gui/resultsview.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ void ResultsView::updateDetails(const QModelIndex &index)
365365
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(mUI.mTree->model());
366366
QStandardItem *item = model->itemFromIndex(index);
367367

368+
mUI.mCode->setPlainText(QString());
369+
368370
if (!item) {
369371
mUI.mDetails->setText(QString());
370372
return;
@@ -395,6 +397,19 @@ void ResultsView::updateDetails(const QModelIndex &index)
395397
if (mUI.mTree->showIdColumn())
396398
formattedMsg.prepend(tr("Id") + ": " + data["id"].toString() + "\n");
397399
mUI.mDetails->setText(formattedMsg);
400+
401+
const int lineNumber = data["line"].toInt();
402+
403+
QString filepath = data["file"].toString();
404+
if (!QFileInfo(filepath).exists() && QFileInfo(mUI.mTree->getCheckDirectory() + '/' + filepath).exists())
405+
filepath = mUI.mTree->getCheckDirectory() + '/' + filepath;
406+
407+
QFile file(filepath);
408+
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
409+
QTextStream in(&file);
410+
mUI.mCode->setPlainText(in.readAll());
411+
mUI.mCode->setErrorLine(lineNumber);
412+
}
398413
}
399414

400415
void ResultsView::log(const QString &str)

gui/resultsview.ui

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@
118118
</property>
119119
<item>
120120
<widget class="QTextEdit" name="mDetails">
121+
<property name="sizePolicy">
122+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
123+
<horstretch>0</horstretch>
124+
<verstretch>1</verstretch>
125+
</sizepolicy>
126+
</property>
121127
<property name="undoRedoEnabled">
122128
<bool>false</bool>
123129
</property>
@@ -126,6 +132,19 @@
126132
</property>
127133
</widget>
128134
</item>
135+
<item>
136+
<widget class="CodeEditor" name="mCode">
137+
<property name="sizePolicy">
138+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
139+
<horstretch>0</horstretch>
140+
<verstretch>4</verstretch>
141+
</sizepolicy>
142+
</property>
143+
<property name="readOnly">
144+
<bool>false</bool>
145+
</property>
146+
</widget>
147+
</item>
129148
</layout>
130149
</widget>
131150
</widget>
@@ -139,6 +158,11 @@
139158
<extends>QTreeView</extends>
140159
<header>resultstree.h</header>
141160
</customwidget>
161+
<customwidget>
162+
<class>CodeEditor</class>
163+
<extends>QPlainTextEdit</extends>
164+
<header>codeeditor.h</header>
165+
</customwidget>
142166
</customwidgets>
143167
<tabstops>
144168
<tabstop>mTree</tabstop>

0 commit comments

Comments
 (0)