Skip to content

Commit ed97832

Browse files
committed
[Lua] Editor: line numbers and syntax highlighting.
1 parent f204200 commit ed97832

File tree

6 files changed

+359
-47
lines changed

6 files changed

+359
-47
lines changed

gui/qt/CEmu.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ HEADERS += utils.h \
288288
keyhistory.h \
289289
memoryvisualizer.h \
290290
basiccodeviewerwindow.h \
291+
luaeditor.h \
291292
keypad/qtkeypadbridge.h \
292293
keypad/keymap.h \
293294
keypad/keypadwidget.h \

gui/qt/luaeditor.cpp

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/* Inspired from a code in the Qt Examples - BSD License */
2+
3+
#include <QtWidgets>
4+
5+
#include "luaeditor.h"
6+
7+
LuaEditor::LuaEditor(QWidget *parent) : QPlainTextEdit(parent)
8+
{
9+
highlighter = new LuaHighlighter(document());
10+
11+
lineNumberArea = new LineNumberArea(this);
12+
13+
connect(this, &QPlainTextEdit::blockCountChanged, this, &LuaEditor::updateLineNumberAreaWidth);
14+
connect(this, &QPlainTextEdit::updateRequest, this, &LuaEditor::updateLineNumberArea);
15+
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &LuaEditor::highlightCurrentLine);
16+
17+
updateLineNumberAreaWidth(0);
18+
highlightCurrentLine();
19+
}
20+
21+
int LuaEditor::lineNumberAreaWidth()
22+
{
23+
int digits = 1;
24+
int max = qMax(1, blockCount());
25+
while (max >= 10) {
26+
max /= 10;
27+
++digits;
28+
}
29+
30+
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
31+
32+
return space;
33+
}
34+
35+
void LuaEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
36+
{
37+
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
38+
}
39+
40+
void LuaEditor::updateLineNumberArea(const QRect &rect, int dy)
41+
{
42+
if (dy)
43+
lineNumberArea->scroll(0, dy);
44+
else
45+
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
46+
47+
if (rect.contains(viewport()->rect()))
48+
updateLineNumberAreaWidth(0);
49+
}
50+
51+
void LuaEditor::resizeEvent(QResizeEvent *e)
52+
{
53+
QPlainTextEdit::resizeEvent(e);
54+
55+
QRect cr = contentsRect();
56+
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
57+
}
58+
59+
void LuaEditor::highlightCurrentLine()
60+
{
61+
QList<QTextEdit::ExtraSelection> extraSelections;
62+
63+
if (!isReadOnly()) {
64+
QTextEdit::ExtraSelection selection;
65+
66+
QColor lineColor = QColor(Qt::yellow).lighter(160);
67+
68+
selection.format.setBackground(lineColor);
69+
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
70+
selection.cursor = textCursor();
71+
selection.cursor.clearSelection();
72+
extraSelections.append(selection);
73+
}
74+
75+
setExtraSelections(extraSelections);
76+
}
77+
78+
void LuaEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
79+
{
80+
QPainter painter(lineNumberArea);
81+
painter.fillRect(event->rect(), Qt::lightGray);
82+
painter.setPen(Qt::black);
83+
84+
QTextBlock block = firstVisibleBlock();
85+
int blockNumber = block.blockNumber();
86+
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
87+
int bottom = top + (int) blockBoundingRect(block).height();
88+
89+
while (block.isValid() && top <= event->rect().bottom())
90+
{
91+
if (block.isVisible() && bottom >= event->rect().top())
92+
{
93+
QString number = QString::number(blockNumber + 1);
94+
painter.drawText(-2, top, lineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, number);
95+
}
96+
block = block.next();
97+
top = bottom;
98+
bottom = top + (int) blockBoundingRect(block).height();
99+
++blockNumber;
100+
}
101+
}
102+
103+
/* Highlighter */
104+
105+
LuaHighlighter::LuaHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
106+
{
107+
HighlightingRule rule;
108+
109+
keywordFormat.setForeground(Qt::darkBlue);
110+
keywordFormat.setFontWeight(QFont::Bold);
111+
QStringList keywordPatterns;
112+
keywordPatterns << "\\band\\b" << "\\bbreak\\b" << "\\bdo\\b" << "\\belse\\b" << "\\belseif\\b"
113+
<< "\\bend\\b" << "\\bfor\\b" << "\\bif\\b" << "\\bin\\b" << "\\blocal\\b"
114+
<< "\\bnot\\b" << "\\bor\\b" << "\\brepeat\\b" << "\\breturn\\b" << "\\bthen\\b"
115+
<< "\\buntil\\b" << "\\bwhile\\n";
116+
foreach (const QString &pattern, keywordPatterns) {
117+
rule.pattern = QRegExp(pattern);
118+
rule.format = keywordFormat;
119+
highlightingRules.append(rule);
120+
}
121+
122+
builtinFormat.setForeground(Qt::darkCyan);
123+
builtinFormat.setFontWeight(QFont::Bold);
124+
QStringList builtinPatterns;
125+
builtinPatterns << "\\b_G\\b" << "\\b_VERSION\\b" << "\\bassert\\b" << "\\bcollectgarbage\\b"
126+
<< "\\bdofile\\b" << "\\berror\\b" << "\\bgetfenv\\b" << "\\bgetmetatable\\b"
127+
<< "\\bipairs\\b" << "\\bload\\b" << "\\bloadfile\\b" << "\\bloadstring\\b"
128+
<< "\\bmodule\\b" << "\\bnext\\b" << "\\bpairs\\b" << "\\bpcall\\b" << "\\bprint\\b"
129+
<< "\\brawequal\\b" << "\\brawget\\b" << "\\brawset\\b" << "\\brequire\\b"
130+
<< "\\bselect\\b" << "\\bsetfenv\\b" << "\\bsetmetatable\\b" << "\\btonumber\\b"
131+
<< "\\btostring\\b" << "\\btype\\b" << "\\bunpack\\b" << "\\bxpcall\\b"
132+
<< "\\bcoroutine\\b" << "\\bdebug\\b" << "\\bio\\b" << "\\bmath\\b" << "\\bos\\b"
133+
<< "\\bpackage\\b" << "\\bstring\\b" << "\\btable\\b";
134+
foreach (const QString &pattern, builtinPatterns) {
135+
rule.pattern = QRegExp(pattern);
136+
rule.format = builtinFormat;
137+
highlightingRules.append(rule);
138+
}
139+
140+
literalFormat.setFontWeight(QFont::Bold);
141+
literalFormat.setForeground(Qt::darkMagenta);
142+
rule.pattern = QRegExp("\\b(nil|true|false)\\b");
143+
rule.format = literalFormat;
144+
highlightingRules.append(rule);
145+
146+
cemuGlobalsFormat.setFontUnderline(true);
147+
cemuGlobalsFormat.setForeground(Qt::red);
148+
rule.pattern = QRegExp("\\b(cpu|devices|mem|gui|R|F)\\b");
149+
rule.format = cemuGlobalsFormat;
150+
highlightingRules.append(rule);
151+
152+
numberFormat.setForeground(Qt::darkMagenta);
153+
rule.pattern = QRegExp("(\\b0[xX][0-9a-fA-F]+\\b)|(((\\b[0-9]+)?\\.)?\\b[0-9]+([eE][-+]?[0-9]+)?\\b)");
154+
rule.format = numberFormat;
155+
highlightingRules.append(rule);
156+
157+
quotationFormat.setForeground(Qt::darkGreen);
158+
rule.pattern = QRegExp("(\"[^\"]*\")|('[^']*')|(\\[=*\\[.*\\]=*\\])");
159+
rule.format = quotationFormat;
160+
highlightingRules.append(rule);
161+
162+
functionFormat.setForeground(Qt::blue);
163+
rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()");
164+
rule.format = functionFormat;
165+
highlightingRules.append(rule);
166+
167+
singleLineCommentFormat.setForeground(Qt::gray);
168+
rule.pattern = QRegExp("--[^\n]*");
169+
rule.format = singleLineCommentFormat;
170+
highlightingRules.append(rule);
171+
172+
multiLineCommentFormat.setForeground(Qt::gray);
173+
174+
commentStartExpression = QRegExp("--\\[=*\\[");
175+
commentEndExpression = QRegExp("\\]=*\\]");
176+
}
177+
178+
void LuaHighlighter::highlightBlock(const QString &text)
179+
{
180+
foreach (const HighlightingRule &rule, highlightingRules) {
181+
QRegExp expression(rule.pattern);
182+
int index = expression.indexIn(text);
183+
while (index >= 0) {
184+
int length = expression.matchedLength();
185+
setFormat(index, length, rule.format);
186+
index = expression.indexIn(text, index + length);
187+
}
188+
}
189+
setCurrentBlockState(0);
190+
191+
int startIndex = 0;
192+
if (previousBlockState() != 1)
193+
startIndex = commentStartExpression.indexIn(text);
194+
195+
while (startIndex >= 0) {
196+
int endIndex = commentEndExpression.indexIn(text, startIndex);
197+
int commentLength;
198+
if (endIndex == -1) {
199+
setCurrentBlockState(1);
200+
commentLength = text.length() - startIndex;
201+
} else {
202+
commentLength = endIndex - startIndex + commentEndExpression.matchedLength();
203+
}
204+
setFormat(startIndex, commentLength, multiLineCommentFormat);
205+
startIndex = commentStartExpression.indexIn(text, startIndex + commentLength);
206+
}
207+
}

gui/qt/luaeditor.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* Inspired from a code in the Qt Examples - BSD License */
2+
3+
#ifndef LUAEDITOR_H
4+
#define LUAEDITOR_H
5+
6+
#include <QtWidgets/QPlainTextEdit>
7+
#include <QtCore/QObject>
8+
#include <QtGui/QSyntaxHighlighter>
9+
#include <QtGui/QTextCharFormat>
10+
11+
QT_BEGIN_NAMESPACE
12+
class QPaintEvent;
13+
class QResizeEvent;
14+
class QSize;
15+
class QWidget;
16+
class QTextDocument;
17+
QT_END_NAMESPACE
18+
19+
20+
class LuaHighlighter : public QSyntaxHighlighter
21+
{
22+
Q_OBJECT
23+
24+
public:
25+
LuaHighlighter(QTextDocument *parent = 0);
26+
27+
protected:
28+
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
29+
30+
private:
31+
struct HighlightingRule
32+
{
33+
QRegExp pattern;
34+
QTextCharFormat format;
35+
};
36+
QVector<HighlightingRule> highlightingRules;
37+
38+
QRegExp commentStartExpression;
39+
QRegExp commentEndExpression;
40+
41+
QTextCharFormat keywordFormat;
42+
QTextCharFormat numberFormat;
43+
QTextCharFormat builtinFormat;
44+
QTextCharFormat literalFormat;
45+
QTextCharFormat cemuGlobalsFormat;
46+
QTextCharFormat singleLineCommentFormat;
47+
QTextCharFormat multiLineCommentFormat;
48+
QTextCharFormat quotationFormat;
49+
QTextCharFormat functionFormat;
50+
};
51+
52+
53+
class LuaEditor : public QPlainTextEdit
54+
{
55+
Q_OBJECT
56+
57+
public:
58+
LuaEditor(QWidget *parent = 0);
59+
60+
void lineNumberAreaPaintEvent(QPaintEvent *event);
61+
int lineNumberAreaWidth();
62+
63+
protected:
64+
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
65+
66+
private slots:
67+
void updateLineNumberAreaWidth(int newBlockCount);
68+
void highlightCurrentLine();
69+
void updateLineNumberArea(const QRect &, int);
70+
71+
private:
72+
QWidget *lineNumberArea;
73+
LuaHighlighter *highlighter;
74+
};
75+
76+
77+
class LineNumberArea : public QWidget
78+
{
79+
public:
80+
LineNumberArea(LuaEditor *editor) : QWidget(editor) {
81+
luaEditor = editor;
82+
}
83+
84+
QSize sizeHint() const Q_DECL_OVERRIDE {
85+
return QSize(luaEditor->lineNumberAreaWidth(), 0);
86+
}
87+
88+
protected:
89+
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE {
90+
luaEditor->lineNumberAreaPaintEvent(event);
91+
}
92+
93+
private:
94+
LuaEditor *luaEditor;
95+
};
96+
97+
#endif

gui/qt/mainwindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "keyhistory.h"
2323
#include "keypad/qtkeypadbridge.h"
2424
#include "qhexedit/qhexedit.h"
25+
#include "luaeditor.h"
2526
#include "png.h"
2627
class DockWidget;
2728

0 commit comments

Comments
 (0)