Erste Version
This commit is contained in:
450
src/highlighter/SyntaxHighlighter.cpp
Normal file
450
src/highlighter/SyntaxHighlighter.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
#include "SyntaxHighlighter.h"
|
||||
#include <QTextDocument>
|
||||
|
||||
// ===========================================================================
|
||||
// SyntaxHighlighter – Basis
|
||||
// ===========================================================================
|
||||
SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
for (const HighlightRule &rule : m_rules)
|
||||
{
|
||||
QRegularExpressionMatchIterator it = rule.pattern.globalMatch(text);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
setFormat(
|
||||
static_cast<int>(match.capturedStart()),
|
||||
static_cast<int>(match.capturedLength()),
|
||||
rule.format
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_hasMultiLineComment)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
|
||||
int startIndex = 0;
|
||||
if (previousBlockState() != 1)
|
||||
{
|
||||
QRegularExpressionMatch m = m_commentStartExpression.match(text);
|
||||
startIndex = m.hasMatch() ? static_cast<int>(m.capturedStart()) : -1;
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
QRegularExpressionMatch endMatch = m_commentEndExpression.match(text, startIndex);
|
||||
int commentLength = 0;
|
||||
|
||||
if (endMatch.hasMatch())
|
||||
{
|
||||
commentLength = static_cast<int>(endMatch.capturedStart())
|
||||
- startIndex
|
||||
+ static_cast<int>(endMatch.capturedLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
setCurrentBlockState(1);
|
||||
commentLength = text.length() - startIndex;
|
||||
}
|
||||
|
||||
setFormat(startIndex, commentLength, m_multiLineCommentFormat);
|
||||
|
||||
if (!endMatch.hasMatch())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch nextStart =
|
||||
m_commentStartExpression.match(text, startIndex + commentLength);
|
||||
startIndex = nextStart.hasMatch()
|
||||
? static_cast<int>(nextStart.capturedStart())
|
||||
: -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// CppHighlighter
|
||||
// ===========================================================================
|
||||
CppHighlighter::CppHighlighter(QTextDocument *parent)
|
||||
: SyntaxHighlighter(parent)
|
||||
{
|
||||
m_hasMultiLineComment = true;
|
||||
|
||||
QTextCharFormat keywordFormat;
|
||||
keywordFormat.setForeground(QColor("#569CD6"));
|
||||
keywordFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
const QStringList keywords = {
|
||||
"alignas","alignof","and","and_eq","asm","auto","bitand","bitor",
|
||||
"bool","break","case","catch","char","char8_t","char16_t","char32_t",
|
||||
"class","compl","concept","const","consteval","constexpr","constinit",
|
||||
"const_cast","continue","co_await","co_return","co_yield","decltype",
|
||||
"default","delete","do","double","dynamic_cast","else","enum",
|
||||
"explicit","export","extern","false","float","for","friend","goto",
|
||||
"if","inline","int","long","mutable","namespace","new","noexcept",
|
||||
"not","not_eq","nullptr","operator","or","or_eq","private","protected",
|
||||
"public","register","reinterpret_cast","requires","return","short",
|
||||
"signed","sizeof","static","static_assert","static_cast","struct",
|
||||
"switch","template","this","thread_local","throw","true","try",
|
||||
"typedef","typeid","typename","union","unsigned","using","virtual",
|
||||
"void","volatile","wchar_t","while","xor","xor_eq","override","final"
|
||||
};
|
||||
|
||||
for (const QString &kw : keywords)
|
||||
{
|
||||
HighlightRule rule;
|
||||
rule.pattern = QRegularExpression(QString("\\b%1\\b").arg(kw));
|
||||
rule.format = keywordFormat;
|
||||
m_rules.append(rule);
|
||||
}
|
||||
|
||||
QTextCharFormat preprocFormat;
|
||||
preprocFormat.setForeground(QColor("#C586C0"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression("^\\s*#\\s*\\w+"); r.format = preprocFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat stringFormat;
|
||||
stringFormat.setForeground(QColor("#CE9178"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"("(?:[^"\\]|\\.)*")"); r.format = stringFormat; m_rules.append(r); }
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"('(?:[^'\\]|\\.)*')"); r.format = stringFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat numberFormat;
|
||||
numberFormat.setForeground(QColor("#B5CEA8"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\b(0[xX][0-9A-Fa-f]+[uUlL]*|[0-9]+\.?[0-9]*([eE][+-]?[0-9]+)?[fFlLuU]*)\b)"); r.format = numberFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat commentFormat;
|
||||
commentFormat.setForeground(QColor("#6A9955"));
|
||||
commentFormat.setFontItalic(true);
|
||||
{ HighlightRule r; r.pattern = QRegularExpression("//[^\n]*"); r.format = commentFormat; m_rules.append(r); }
|
||||
|
||||
m_multiLineCommentFormat = commentFormat;
|
||||
m_commentStartExpression = QRegularExpression(R"(/\*)");
|
||||
m_commentEndExpression = QRegularExpression(R"(\*/)");
|
||||
}
|
||||
|
||||
void CppHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
SyntaxHighlighter::highlightBlock(text);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// CssHighlighter
|
||||
// ===========================================================================
|
||||
CssHighlighter::CssHighlighter(QTextDocument *parent)
|
||||
: SyntaxHighlighter(parent)
|
||||
{
|
||||
m_hasMultiLineComment = true;
|
||||
|
||||
// Selektoren: .klasse #id element ::pseudo :pseudo
|
||||
QTextCharFormat selectorFormat;
|
||||
selectorFormat.setForeground(QColor("#D7BA7D"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"([.#]?[\w-]+\s*(?=\s*[,{]))"); r.format = selectorFormat; m_rules.append(r); }
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(:{1,2}[\w-]+)"); r.format = selectorFormat; m_rules.append(r); }
|
||||
|
||||
// Eigenschaften (property:)
|
||||
QTextCharFormat propFormat;
|
||||
propFormat.setForeground(QColor("#9CDCFE"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"([\w-]+\s*(?=:))"); r.format = propFormat; m_rules.append(r); }
|
||||
|
||||
// Werte – Farben #hex
|
||||
QTextCharFormat colorFormat;
|
||||
colorFormat.setForeground(QColor("#CE9178"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(#[0-9A-Fa-f]{3,8}\b)"); r.format = colorFormat; m_rules.append(r); }
|
||||
|
||||
// Zahlen + Einheiten
|
||||
QTextCharFormat numberFormat;
|
||||
numberFormat.setForeground(QColor("#B5CEA8"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\b\d+\.?\d*(px|em|rem|%|vh|vw|pt|cm|mm|s|ms)?\b)"); r.format = numberFormat; m_rules.append(r); }
|
||||
|
||||
// Strings
|
||||
QTextCharFormat stringFormat;
|
||||
stringFormat.setForeground(QColor("#CE9178"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')"); r.format = stringFormat; m_rules.append(r); }
|
||||
|
||||
// !important
|
||||
QTextCharFormat importantFormat;
|
||||
importantFormat.setForeground(QColor("#F44747"));
|
||||
importantFormat.setFontWeight(QFont::Bold);
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(!important)"); r.format = importantFormat; m_rules.append(r); }
|
||||
|
||||
// @-Regeln
|
||||
QTextCharFormat atFormat;
|
||||
atFormat.setForeground(QColor("#C586C0"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(@[\w-]+)"); r.format = atFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat commentFormat;
|
||||
commentFormat.setForeground(QColor("#6A9955"));
|
||||
commentFormat.setFontItalic(true);
|
||||
m_multiLineCommentFormat = commentFormat;
|
||||
m_commentStartExpression = QRegularExpression(R"(/\*)");
|
||||
m_commentEndExpression = QRegularExpression(R"(\*/)");
|
||||
}
|
||||
|
||||
void CssHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
SyntaxHighlighter::highlightBlock(text);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// HtmlHighlighter
|
||||
// ===========================================================================
|
||||
HtmlHighlighter::HtmlHighlighter(QTextDocument *parent)
|
||||
: SyntaxHighlighter(parent)
|
||||
{
|
||||
// Tag-Namen <div </div />
|
||||
QTextCharFormat tagFormat;
|
||||
tagFormat.setForeground(QColor("#569CD6"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(</?[\w:-]+)"); r.format = tagFormat; m_rules.append(r); }
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(/?>)"); r.format = tagFormat; m_rules.append(r); }
|
||||
|
||||
// Attribute name=
|
||||
QTextCharFormat attrFormat;
|
||||
attrFormat.setForeground(QColor("#9CDCFE"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\b[\w:-]+=)"); r.format = attrFormat; m_rules.append(r); }
|
||||
|
||||
// Attributwerte "wert" 'wert'
|
||||
QTextCharFormat valueFormat;
|
||||
valueFormat.setForeground(QColor("#CE9178"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')"); r.format = valueFormat; m_rules.append(r); }
|
||||
|
||||
// DOCTYPE
|
||||
QTextCharFormat doctypeFormat;
|
||||
doctypeFormat.setForeground(QColor("#808080"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(<!DOCTYPE[^>]*>)", QRegularExpression::CaseInsensitiveOption); r.format = doctypeFormat; m_rules.append(r); }
|
||||
|
||||
// Entities & {
|
||||
QTextCharFormat entityFormat;
|
||||
entityFormat.setForeground(QColor("#D7BA7D"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(&(?:#\d+|#x[0-9A-Fa-f]+|[\w]+);)"); r.format = entityFormat; m_rules.append(r); }
|
||||
|
||||
// Kommentare <!-- ... --> (mehrzeilig)
|
||||
QTextCharFormat commentFormat;
|
||||
commentFormat.setForeground(QColor("#6A9955"));
|
||||
commentFormat.setFontItalic(true);
|
||||
m_multiLineCommentFormat = commentFormat;
|
||||
m_commentStartExpression = QRegularExpression("<!--");
|
||||
m_commentEndExpression = QRegularExpression("-->");
|
||||
m_hasMultiLineComment = true;
|
||||
}
|
||||
|
||||
void HtmlHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
SyntaxHighlighter::highlightBlock(text);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// PhpHighlighter
|
||||
// ===========================================================================
|
||||
PhpHighlighter::PhpHighlighter(QTextDocument *parent)
|
||||
: SyntaxHighlighter(parent)
|
||||
{
|
||||
// ---- HTML-Regeln (Basis, für den Teil außerhalb von <?php ?>) ----
|
||||
|
||||
QTextCharFormat tagFormat;
|
||||
tagFormat.setForeground(QColor("#569CD6"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(</?[\w:-]+)"); r.format = tagFormat; m_rules.append(r); }
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(/?>)"); r.format = tagFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat attrFormat;
|
||||
attrFormat.setForeground(QColor("#9CDCFE"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\b[\w:-]+=)"); r.format = attrFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat valueFormat;
|
||||
valueFormat.setForeground(QColor("#CE9178"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')"); r.format = valueFormat; m_rules.append(r); }
|
||||
|
||||
QTextCharFormat entityFormat;
|
||||
entityFormat.setForeground(QColor("#D7BA7D"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(&(?:#\d+|#x[0-9A-Fa-f]+|[\w]+);)"); r.format = entityFormat; m_rules.append(r); }
|
||||
|
||||
// HTML-Kommentare
|
||||
QTextCharFormat htmlCommentFormat;
|
||||
htmlCommentFormat.setForeground(QColor("#6A9955"));
|
||||
htmlCommentFormat.setFontItalic(true);
|
||||
m_multiLineCommentFormat = htmlCommentFormat;
|
||||
m_commentStartExpression = QRegularExpression("<!--");
|
||||
m_commentEndExpression = QRegularExpression("-->");
|
||||
m_hasMultiLineComment = true;
|
||||
|
||||
// ---- PHP-Tags hervorheben ----
|
||||
m_phpTagFormat.setForeground(QColor("#C586C0"));
|
||||
m_phpTagFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
// ---- PHP-spezifische Regeln ----
|
||||
m_phpStringFormat.setForeground(QColor("#CE9178"));
|
||||
m_phpCommentFormat.setForeground(QColor("#6A9955"));
|
||||
m_phpCommentFormat.setFontItalic(true);
|
||||
|
||||
// Keywords
|
||||
QTextCharFormat kwFormat;
|
||||
kwFormat.setForeground(QColor("#569CD6"));
|
||||
kwFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
const QStringList phpKeywords = {
|
||||
"abstract","and","array","as","break","callable","case","catch",
|
||||
"class","clone","const","continue","declare","default","die","do",
|
||||
"echo","else","elseif","empty","enddeclare","endfor","endforeach",
|
||||
"endif","endswitch","endwhile","enum","extends","final","finally",
|
||||
"fn","for","foreach","function","global","goto","if","implements",
|
||||
"include","include_once","instanceof","insteadof","interface",
|
||||
"isset","list","match","namespace","new","or","print","private",
|
||||
"protected","public","readonly","require","require_once","return",
|
||||
"static","switch","throw","trait","try","unset","use","var",
|
||||
"while","xor","yield","null","true","false","NULL","TRUE","FALSE"
|
||||
};
|
||||
|
||||
for (const QString &kw : phpKeywords)
|
||||
{
|
||||
HighlightRule r;
|
||||
r.pattern = QRegularExpression(QString("\\b%1\\b").arg(kw));
|
||||
r.format = kwFormat;
|
||||
m_phpRules.append(r);
|
||||
}
|
||||
|
||||
// Variablen $var
|
||||
QTextCharFormat varFormat;
|
||||
varFormat.setForeground(QColor("#9CDCFE"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\$[\w]+)"); r.format = varFormat; m_phpRules.append(r); }
|
||||
|
||||
// Strings
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')"); r.format = m_phpStringFormat; m_phpRules.append(r); }
|
||||
|
||||
// Zahlen
|
||||
QTextCharFormat numFormat;
|
||||
numFormat.setForeground(QColor("#B5CEA8"));
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"(\b\d+\.?\d*\b)"); r.format = numFormat; m_phpRules.append(r); }
|
||||
|
||||
// Einzeilige Kommentare
|
||||
{ HighlightRule r; r.pattern = QRegularExpression(R"((//|#)[^\n]*)"); r.format = m_phpCommentFormat; m_phpRules.append(r); }
|
||||
|
||||
// Eingebaute Funktionen (Auswahl der häufigsten)
|
||||
QTextCharFormat builtinFormat;
|
||||
builtinFormat.setForeground(QColor("#DCDCAA"));
|
||||
const QStringList builtins = {
|
||||
"array_map","array_filter","array_keys","array_values","array_merge",
|
||||
"array_push","array_pop","array_shift","array_slice","array_splice",
|
||||
"count","strlen","substr","strpos","strtolower","strtoupper","trim",
|
||||
"ltrim","rtrim","explode","implode","str_replace","preg_match",
|
||||
"preg_replace","sprintf","printf","print_r","var_dump","isset",
|
||||
"empty","unset","intval","floatval","strval","is_array","is_string",
|
||||
"is_int","is_float","is_null","is_bool","is_numeric","date","time",
|
||||
"mktime","json_encode","json_decode","header","session_start",
|
||||
"htmlspecialchars","htmlentities","strip_tags","nl2br","round",
|
||||
"floor","ceil","abs","min","max","rand","in_array","array_key_exists",
|
||||
"sort","rsort","usort","ksort","krsort","ob_start","ob_get_clean"
|
||||
};
|
||||
|
||||
for (const QString &fn : builtins)
|
||||
{
|
||||
HighlightRule r;
|
||||
r.pattern = QRegularExpression(QString("\\b%1\\b").arg(fn));
|
||||
r.format = builtinFormat;
|
||||
m_phpRules.append(r);
|
||||
}
|
||||
}
|
||||
|
||||
void PhpHighlighter::highlightPhpRange(const QString &text, int start, int length)
|
||||
{
|
||||
if (length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QString phpText = text.mid(start, length);
|
||||
|
||||
for (const HighlightRule &rule : m_phpRules)
|
||||
{
|
||||
QRegularExpressionMatchIterator it = rule.pattern.globalMatch(phpText);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
setFormat(
|
||||
start + static_cast<int>(match.capturedStart()),
|
||||
static_cast<int>(match.capturedLength()),
|
||||
rule.format
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhpHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
// Zuerst HTML-Basis-Regeln auf den gesamten Text anwenden
|
||||
SyntaxHighlighter::highlightBlock(text);
|
||||
|
||||
// Dann PHP-Blöcke <?php ... ?> und <?= ... ?> finden und überschreiben
|
||||
// Block-Zustände: 0 = HTML, 2 = innerhalb PHP-Block
|
||||
setCurrentBlockState(0);
|
||||
|
||||
static const QRegularExpression phpOpen(R"(<\?(?:php|=)?\s?)",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
static const QRegularExpression phpClose(R"(\?>)");
|
||||
|
||||
int pos = 0;
|
||||
|
||||
if (previousBlockState() == 2)
|
||||
{
|
||||
// Wir befinden uns bereits in einem PHP-Block
|
||||
QRegularExpressionMatch closeMatch = phpClose.match(text, 0);
|
||||
if (closeMatch.hasMatch())
|
||||
{
|
||||
const int end = static_cast<int>(closeMatch.capturedStart())
|
||||
+ static_cast<int>(closeMatch.capturedLength());
|
||||
highlightPhpRange(text, 0, end);
|
||||
setFormat(static_cast<int>(closeMatch.capturedStart()),
|
||||
static_cast<int>(closeMatch.capturedLength()),
|
||||
m_phpTagFormat);
|
||||
pos = end;
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightPhpRange(text, 0, text.length());
|
||||
setCurrentBlockState(2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (pos < text.length())
|
||||
{
|
||||
QRegularExpressionMatch openMatch = phpOpen.match(text, pos);
|
||||
if (!openMatch.hasMatch())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const int openStart = static_cast<int>(openMatch.capturedStart());
|
||||
const int openEnd = openStart + static_cast<int>(openMatch.capturedLength());
|
||||
|
||||
// <?php-Tag selbst einfärben
|
||||
setFormat(openStart, static_cast<int>(openMatch.capturedLength()), m_phpTagFormat);
|
||||
|
||||
QRegularExpressionMatch closeMatch = phpClose.match(text, openEnd);
|
||||
if (closeMatch.hasMatch())
|
||||
{
|
||||
const int closeStart = static_cast<int>(closeMatch.capturedStart());
|
||||
const int closeEnd = closeStart + static_cast<int>(closeMatch.capturedLength());
|
||||
|
||||
highlightPhpRange(text, openEnd, closeStart - openEnd);
|
||||
setFormat(closeStart, static_cast<int>(closeMatch.capturedLength()), m_phpTagFormat);
|
||||
|
||||
pos = closeEnd;
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PHP-Block geht über Zeilenende hinaus
|
||||
highlightPhpRange(text, openEnd, text.length() - openEnd);
|
||||
setCurrentBlockState(2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user