Erste Version

This commit is contained in:
2026-05-23 13:14:30 +02:00
commit 36e074f43d
39 changed files with 3430 additions and 0 deletions

View 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 &amp; &#123;
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;
}
}
}