diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 6c861db438..5cec667b85 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -455,6 +455,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -585,6 +590,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -725,6 +735,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -855,6 +870,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -874,6 +894,7 @@
+
@@ -1243,6 +1264,24 @@
+
+ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing syntax_highlighter.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing syntax_highlighter.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing syntax_highlighter.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing syntax_highlighter.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index c78ede2cde..226ac19247 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -90,6 +90,9 @@
{114eef25-61bf-4df5-8867-09156cf51b8a}
+
+ {adb985ae-88db-4274-a6fd-6bcf230451ca}
+
@@ -488,6 +491,21 @@
Gui
+
+ Generated Files\Release - LLVM
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Generated Files\Debug - LLVM
+
+
+ Gui\syntax highlighter
+
Gui\dev tools
@@ -527,6 +545,9 @@
Gui\message dialog
+
+ Generated Files\Debug
+
@@ -636,6 +657,9 @@
Gui\saves
+
+ Gui\syntax highlighter
+
Gui\misc dialogs
@@ -696,6 +720,9 @@
Gui\message dialog
+
+ Generated Files
+
diff --git a/rpcs3/rpcs3qt/cg_disasm_window.cpp b/rpcs3/rpcs3qt/cg_disasm_window.cpp
index 7d17489c71..24a4839d93 100644
--- a/rpcs3/rpcs3qt/cg_disasm_window.cpp
+++ b/rpcs3/rpcs3qt/cg_disasm_window.cpp
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "cg_disasm_window.h"
+#include "syntax_highlighter.h"
#include
#include
@@ -39,6 +40,119 @@ cg_disasm_window::cg_disasm_window(std::shared_ptr xSettings): xgu
m_glsl_text->setWordWrapMode(QTextOption::NoWrap);
m_glsl_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ // m_disasm_text syntax highlighter
+ syntax_highlighter* sh_asm = new syntax_highlighter(m_disasm_text->document());
+ sh_asm->AddCommentRule("#", QColor(Qt::darkGreen));
+ sh_asm->AddCommentRule("\/\*", QColor(Qt::darkGreen), true, "\*\/");
+ sh_asm->AddSimpleRule(QStringList("^([^\\s]+).*$"), QColor(Qt::darkBlue)); // Instructions
+ sh_asm->AddSimpleRule(QStringList(",?\\s(-?R\\d[^,;\\s]*)"), QColor(Qt::darkRed)); // -R0.*
+ sh_asm->AddSimpleRule(QStringList(",?\\s(-?H\\d[^,;\\s]*)"), QColor(Qt::red)); // -H1.*
+ sh_asm->AddSimpleRule(QStringList(",?\\s(-?v\\[\\d\\]*[^,;\\s]*)"), QColor(Qt::darkCyan)); // -v[xyz].*
+ sh_asm->AddSimpleRule(QStringList(",?\\s(-?o\\[\\d\\]*[^,;\\s]*)"), QColor(Qt::darkMagenta)); // -o[xyz].*
+ sh_asm->AddSimpleRule(QStringList(",?\\s(-?c\\[\\d\\]*[^,;\\s]*)"), QColor(Qt::darkYellow)); // -c[xyz].*
+ //sh_asm->AddMultiRule(
+ // "^([^\\s]+)(?:,?\\s*([^,\\;\\s]+))?(?:,?\\s*([^,\\;\\s]+))?(?:,?\\s*([^,\\;\\s]+))?(?:,?\\s*([^,\\;\\s]+))?.*$",
+ // { QColor(Qt::black), QColor(Qt::darkBlue), QColor(Qt::darkRed), QColor(Qt::darkMagenta), QColor(Qt::darkYellow), QColor(Qt::darkCyan) });
+
+ // m_glsl_text syntax highlighter
+ QStringList glsl_syntax = QStringList()
+ // Selection-Iteration-Jump Statements:
+ << "if" << "else" << "switch" << "case" << "default"
+ << "for" << "while" << "do" << "foreach" //?
+ << "return" << "break" << "continue" << "discard"
+ // Misc:
+ << "void" << "char" << "short" << "long" << "template"
+ << "class" << "struct" << "union" << "enum"
+ << "static" << "virtual" << "inline" << "explicit"
+ << "public" << "private" << "protected" << "namespace"
+ << "typedef" << "typename" << "signed" << "unsigned"
+ << "friend" << "operator" << "signals" << "slots"
+ // Qualifiers:
+ << "in" << "packed" << "precision" << "const" << "smooth" << "sample"
+ << "out" << "shared" << "highp" << "invariant" << "noperspective" << "centroid"
+ << "inout" << "std140" << "mediump" << "uniform" << "flat" << "patch"
+ << "buffer" << "std430" << "lowp"
+ << "image"
+ // Removed Qualifiers?
+ << "attribute" << "varying"
+ // Memory Qualifiers
+ << "coherent" << "volatile" << "restrict" << "readonly" << "writeonly"
+ // Layout Qualifiers:
+ //<< "subroutine" << "layout" << "xfb_buffer" << "textureGrad" << "texture" << "dFdx" << "dFdy"
+ //<< "location" << "component" << "binding" << "offset"
+ //<< "xfb_offset" << "xfb_stride" << "vertices" << "max_vertices"
+ // Scalars and Vectors:
+ << "bool" << "int" << "uint" << "float" << "double"
+ << "bvec2" << "ivec2" << "uvec2" << "vec2" << "dvec2"
+ << "bvec3" << "ivec3" << "uvec3" << "vec3" << "dvec3"
+ << "bvec4" << "ivec4" << "uvec4" << "vec4" << "dvec4"
+ // Matrices:
+ << "mat2" << "mat2x3" << "mat2x4"
+ << "mat3" << "mat3x2" << "mat3x4"
+ << "mat4" << "mat4x2" << "mat4x3"
+ // Sampler Types:
+ << "sampler1D" << "isampler1D" << "usampler1D"
+ << "sampler2D" << "isampler2D" << "usampler2D"
+ << "sampler3D" << "isampler3D" << "usampler3D"
+ << "samplerCube" << "isamplerCube" << "usamplerCube"
+ << "sampler2DRect" << "isampler2DRect" << "usampler2DRect"
+ << "sampler1DArray" << "isampler1DArray" << "usampler1DArray"
+ << "sampler2DArray" << "isampler2DArray" << "usampler2DArray"
+ << "samplerCubeArray" << "isamplerCubeArray" << "usamplerCubeArray"
+ << "samplerBuffer" << "isamplerBuffer" << "usamplerBuffer"
+ << "sampler2DMS" << "isampler2DMS" << "usampler2DMS"
+ << "sampler2DMSArray" << "isampler2DMSArray" << "usampler2DMSArray"
+ // Shadow Samplers:
+ << "sampler1DShadow"
+ << "sampler2DShadow"
+ << "samplerCubeShadow"
+ << "sampler2DRectShadow"
+ << "sampler1DArrayShadow"
+ << "sampler2DArrayShadow"
+ << "samplerCubeArrayShadow"
+ // Image Types:
+ << "image1D" << "iimage1D" << "uimage1D"
+ << "image2D" << "iimage2D" << "uimage2D"
+ << "image3D" << "iimage3D" << "uimage3D"
+ << "imageCube" << "iimageCube" << "uimageCube"
+ << "image2DRect" << "iimage2DRect" << "uimage2DRect"
+ << "image1DArray" << "iimage1DArray" << "uimage1DArray"
+ << "image2DArray" << "iimage2DArray" << "uimage2DArray"
+ << "imageCubeArray" << "iimageCubeArray" << "uimageCubeArray"
+ << "imageBuffer" << "iimageBuffer" << "uimageBuffer"
+ << "image2DMS" << "iimage2DMS" << "uimage2DMS"
+ << "image2DMSArray" << "iimage2DMSArray" << "uimage2DMSArray"
+ // Image Formats:
+ // Floating-point: // Signed integer:
+ << "rgba32f" << "rgba32i"
+ << "rgba16f" << "rgba16i"
+ << "rg32f" << "rgba8i"
+ << "rg16f" << "rg32i"
+ << "r11f_g11f_b10f" << "rg16i"
+ << "r32f" << "rg8i"
+ << "r16f" << "r32i"
+ << "rgba16" << "r16i"
+ << "rgb10_a2" << "r8i"
+ << "rgba8" << "r8ui"
+ << "rg16" // Unsigned integer:
+ << "rg8" << "rgba32ui"
+ << "r16" << "rgba16ui"
+ << "r8" << "rgb10_a2ui"
+ << "rgba16_snorm" << "rgba8ui"
+ << "rgba8_snorm" << "rg32ui"
+ << "rg16_snorm" << "rg16ui"
+ << "rg8_snorm" << "rg8ui"
+ << "r16_snorm" << "r32ui"
+ << "r8_snorm" << "r16ui"
+ ;
+ syntax_highlighter* sh_glsl = new syntax_highlighter(m_glsl_text->document());
+ sh_glsl->AddWordRule(glsl_syntax, QColor(Qt::darkBlue)); // normal words like: soka, nani, or gomen
+ sh_glsl->AddSimpleRule(QStringList("\\bGL_(?:[A-Z]|_)+\\b"), QColor(Qt::darkMagenta)); // constants like: GL_OMAE_WA_MOU_SHINDEIRU
+ sh_glsl->AddSimpleRule(QStringList("\\bgl_(?:[A-Z]|[a-z]|_)+\\b"), QColor(Qt::darkCyan)); // reserved types like: gl_exploooooosion
+ sh_glsl->AddSimpleRule(QStringList("\\B#[^\\s]+\\b"), QColor(Qt::darkGray)); // preprocessor instructions like: #waifu megumin
+ sh_glsl->AddCommentRule("\/\/", QColor(Qt::darkGreen)); // comments like: // No comment
+ sh_glsl->AddCommentRule("\/\*", QColor(Qt::darkGreen), true, "\*\/"); // comments like: /* I am trapped! Please help me! */
+
QSplitter* splitter = new QSplitter();
splitter->addWidget(m_disasm_text);
splitter->addWidget(m_glsl_text);
diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h
index 7f42022fde..069b018333 100644
--- a/rpcs3/rpcs3qt/game_list_frame.h
+++ b/rpcs3/rpcs3qt/game_list_frame.h
@@ -103,9 +103,9 @@ namespace category // (see PARAM.SFO in psdevwiki.com) TODO: Disc Categories
inline bool CategoryInMap(const std::string& cat, const q_from_char& map)
{
- auto map_contains_category = [cat](std::pair p)
+ auto map_contains_category = [s = qstr(cat)](const auto& p)
{
- return p.second == qstr(cat);
+ return p.second == s;
};
return std::find_if(map.begin(), map.end(), map_contains_category) != map.end();
diff --git a/rpcs3/rpcs3qt/syntax_highlighter.cpp b/rpcs3/rpcs3qt/syntax_highlighter.cpp
new file mode 100644
index 0000000000..2db72e0957
--- /dev/null
+++ b/rpcs3/rpcs3qt/syntax_highlighter.cpp
@@ -0,0 +1,183 @@
+#include "syntax_highlighter.h"
+
+syntax_highlighter::syntax_highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
+{
+}
+
+void syntax_highlighter::AddSimpleRule(SimpleRule rule)
+{
+ rule.expressions.erase(std::remove_if(rule.expressions.begin(), rule.expressions.end(), [&](const auto &e){
+ return IsInvalidExpression(e) || e.captureCount() > 1;
+ }), rule.expressions.end());
+
+ if (rule.expressions.isEmpty())
+ {
+ return;
+ }
+ m_rules.append(rule);
+}
+
+void syntax_highlighter::AddSimpleRule(const QStringList& expressions, const QColor& color)
+{
+ QTextCharFormat format;
+ format.setForeground(color);
+ QVector regexs;
+ for (const auto& expr : expressions)
+ {
+ regexs.append(QRegularExpression(expr));
+ }
+ AddSimpleRule(SimpleRule(regexs, format));
+}
+
+void syntax_highlighter::AddWordRule(const QStringList& words, const QColor& color)
+{
+ QTextCharFormat format;
+ format.setForeground(color);
+ QVector regexs;
+ for (const auto& word : words)
+ {
+ regexs.append(QRegularExpression("\\b" + word + "\\b"));
+ }
+ AddSimpleRule(SimpleRule(regexs, format));
+}
+
+void syntax_highlighter::AddMultiRule(const MultiRule& rule)
+{
+ if (IsInvalidExpression(rule.expression) || rule.formats.length() <= rule.expression.captureCount())
+ {
+ return;
+ }
+
+ m_multi_rules.append(rule);
+}
+void syntax_highlighter::AddMultiRule(const QString& expression, const QVector& colors)
+{
+ QVector formats;
+ for (const auto& color : colors)
+ {
+ QTextCharFormat format;
+ format.setForeground(color);
+ formats.append(format);
+ }
+ AddMultiRule(MultiRule(QRegularExpression(expression), formats));
+}
+
+void syntax_highlighter::AddCommentRule(const CommentRule& rule)
+{
+ if (IsInvalidExpression(rule.start_expression) || (rule.multi_line && IsInvalidExpression(rule.end_expression)))
+ {
+ return;
+ }
+ m_comment_rules.append(rule);
+}
+
+void syntax_highlighter::AddCommentRule(const QString& start, const QColor& color, bool multi_line, const QString& end)
+{
+ QTextCharFormat format;
+ format.setForeground(color);
+ AddCommentRule(CommentRule(QRegularExpression(start), QRegularExpression(end), format, multi_line));
+}
+
+void syntax_highlighter::highlightBlock(const QString &text)
+{
+ m_current_block = text;
+
+ for (const auto& rule : m_multi_rules)
+ {
+ // Search for all the matching strings
+ QRegularExpressionMatchIterator iter = rule.expression.globalMatch(m_current_block);
+
+ // Iterate through the matching strings
+ while (iter.hasNext())
+ {
+ // Apply formats to their respective found groups
+ QRegularExpressionMatch match = iter.next();
+
+ for (int i = 0; i <= match.lastCapturedIndex(); i++)
+ {
+ setFormat(match.capturedStart(i), match.capturedLength(i), rule.formats[i]);
+ }
+ }
+ }
+
+ for (const auto& rule : m_rules)
+ {
+ for (const auto& expression : rule.expressions)
+ {
+ // Search for all the matching strings
+ QRegularExpressionMatchIterator iter = expression.globalMatch(m_current_block);
+ bool contains_group = expression.captureCount() > 0;
+
+ // Iterate through the matching strings
+ while (iter.hasNext())
+ {
+ // Apply format to the matching string
+ QRegularExpressionMatch match = iter.next();
+ if (contains_group)
+ {
+ setFormat(match.capturedStart(1), match.capturedLength(1), rule.format);
+ }
+ else
+ {
+ setFormat(match.capturedStart(), match.capturedLength(), rule.format);
+ }
+ }
+ }
+ }
+
+ for (const auto& rule : m_comment_rules)
+ {
+ int comment_start = 0; // Current comment's start position in the text block
+ int comment_end = 0; // Current comment's end position in the text block
+ int comment_length = 0; // Current comment length
+
+ // We assume we end outside a comment until we know better
+ setCurrentBlockState(ENDED_OUTSIDE_COMMENT);
+
+ // Search for the first comment in this block if we start outside or don't want to search for multiline comments
+ if (!rule.multi_line || previousBlockState() != ENDED_INSIDE_COMMENT)
+ {
+ comment_start = m_current_block.indexOf(rule.start_expression);
+ }
+
+ // Set format for the rest of this block/line
+ if (!rule.multi_line)
+ {
+ comment_length = m_current_block.length() - comment_start;
+ setFormat(comment_start, comment_length, rule.format);
+ break;
+ }
+
+ // Format all comments in this block (if they exist)
+ while (comment_start >= 0)
+ {
+ // Search for end of comment in the remaining text
+ QRegularExpressionMatch match = rule.end_expression.match(m_current_block, comment_start);
+ comment_end = match.capturedStart();
+ match.captured(1);
+
+ if (comment_end == -1)
+ {
+ // We end inside a comment and want to format the entire remaining text
+ setCurrentBlockState(ENDED_INSIDE_COMMENT);
+ comment_length = m_current_block.length() - comment_start;
+ }
+ else
+ {
+ // We found the end of the comment so we need to go another round
+ comment_length = comment_end - comment_start + match.capturedLength();
+ }
+
+ // Set format for this text segment
+ setFormat(comment_start, comment_length, rule.format);
+
+ // Search for the next comment
+ comment_start = m_current_block.indexOf(rule.start_expression, comment_start + comment_length);
+ }
+ }
+}
+
+bool syntax_highlighter::IsInvalidExpression(const QRegularExpression& expression)
+{
+ return !expression.isValid() || expression.pattern().isEmpty();
+}
diff --git a/rpcs3/rpcs3qt/syntax_highlighter.h b/rpcs3/rpcs3qt/syntax_highlighter.h
new file mode 100644
index 0000000000..0ff076c06c
--- /dev/null
+++ b/rpcs3/rpcs3qt/syntax_highlighter.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include
+#include
+
+class syntax_highlighter : public QSyntaxHighlighter
+{
+ Q_OBJECT
+
+ enum BlockState
+ {
+ ENDED_OUTSIDE_COMMENT,
+ ENDED_INSIDE_COMMENT
+ };
+
+public:
+ syntax_highlighter(QTextDocument *parent = 0);
+
+ struct SimpleRule
+ {
+ QVector expressions;
+ QTextCharFormat format;
+ SimpleRule(){}
+ SimpleRule(const QVector& expressions, const QTextCharFormat& format)
+ : expressions(expressions), format(format) {}
+ };
+ struct MultiRule
+ {
+ QRegularExpression expression;
+ QVector formats;
+ MultiRule(){}
+ MultiRule(const QRegularExpression& expr, const QVector& formats)
+ : expression(expr), formats(formats) {}
+ };
+ struct CommentRule
+ {
+ QRegularExpression start_expression;
+ QRegularExpression end_expression;
+ QTextCharFormat format;
+ bool multi_line = false;
+ CommentRule(){}
+ CommentRule(const QRegularExpression& start, const QRegularExpression& end, const QTextCharFormat& format, bool multi_line)
+ : start_expression(start), end_expression(end), format(format), multi_line(multi_line) {}
+ };
+
+ /**
+ Add a simple highlighting rule that applies the given format to all given expressions.
+ You can add up to one Group to the expression. The full match group will be ignored
+ */
+ void AddSimpleRule(SimpleRule rule);
+ void AddSimpleRule(const QStringList& expressions, const QColor& color);
+ /** Add a simple highlighting rule for words. Not supposed to be used with any other expression */
+ void AddWordRule(const QStringList& words, const QColor& color);
+
+ /**
+ Add a complex highlighting rule that applies different formats to the expression's groups.
+ Make sure you don't have more groups in your expression than formats !!!
+ Group 0 is always the full match, so the first color has to be for that !!!
+ Example expression for string "rdch $4,$6,$8,$5" with 6 groups (5 + full match):
+ "^(?[^\s]+) (?[^,]+),(?[^,]+),(?[^,]+),(?[^,]+)$"
+ */
+ void AddMultiRule(const MultiRule& rule);
+ void AddMultiRule(const QString& expression, const QVector& colors);
+
+ /**
+ Add a comment highlighting rule. Add them in ascending priority.
+ We only need rule.end_expression in case of rule.multi_line.
+ A block ends at the end of a line anyway.
+ */
+ void AddCommentRule(const CommentRule& rule);
+ void AddCommentRule(const QString& start, const QColor& color, bool multi_line = false, const QString& end = "");
+
+protected:
+ void highlightBlock(const QString &text) override;
+
+private:
+ /** Checks if an expression is invalid or empty */
+ static bool IsInvalidExpression(const QRegularExpression& expression);
+
+ QVector m_rules;
+ QVector m_comment_rules;
+ QVector m_multi_rules;
+
+ QString m_current_block;
+};