feat: v0.8.0 —— 修复围栏空行断状态,代码块改为真正的语法高亮

修复
- highlightBlock 遇到空行直接 return,把围栏状态重置了:代码块中间只要有一条
  空行,后面的行就不再按代码处理(这正是 #include 之后代码变回普通文本的原因)
- 空行现在会延续围栏状态;闭合围栏行也不再被误判成新的开围栏

语法高亮
- 新增 CodeHighlight:自己写的轻量 tokenizer,不依赖第三方库
- 支持 C / C++ / Python / JavaScript / TypeScript / Java / Rust / Go /
  JSON / Bash / SQL / YAML,按 info string 识别
- 关键字、类型、字符串、数字、行/块注释(支持跨行)、C/C++ 预处理指令分色
- 围栏行在源码模式下改成灰色标记色,不再是代码红
- 编辑模式与源代码模式共用同一套 token 着色

其它
- 主题新增 5 个代码 token 颜色(浅色 / 深色各一套)
- 版本 0.8.0,README 补充支持语言
This commit is contained in:
2026-10-22 18:14:34 +08:00
parent a136ed1eaa
commit 233d732604
11 changed files with 505 additions and 28 deletions
+4 -1
View File
@@ -2,7 +2,7 @@
一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。 一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。
当前版本 **0.7.0**。 当前版本 **0.8.0**。
## 界面 ## 界面
@@ -26,6 +26,9 @@
- 行号栏、当前行高亮 - 行号栏、当前行高亮
- 围栏代码块显示成带圆角边框的面板,右上角有「复制」按钮 - 围栏代码块显示成带圆角边框的面板,右上角有「复制」按钮
(源代码模式下不显示这些装饰) (源代码模式下不显示这些装饰)
- 代码块按语言做语法高亮:C / C++ / Python / JavaScript / TypeScript /
Java / Rust / Go / JSON / Bash / SQL / YAML,关键字、字符串、数字、
注释、预处理指令分色;不认识的标记语言就按等宽纯文本显示
- `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进 - `Tab` / `Shift+Tab` 整行缩进,选中多行时批量缩进
- 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号 - 自动换行(`Alt+Z`)、`Ctrl+滚轮` 或 `Ctrl++` / `Ctrl+-` 缩放字号
- 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显 - 撤销 / 重做、剪切 / 复制 / 粘贴,菜单项会跟随可用状态自动灰显
+3 -1
View File
@@ -1,6 +1,6 @@
TEMPLATE = app TEMPLATE = app
TARGET = VoiletMark TARGET = VoiletMark
VERSION = 0.7.0 VERSION = 0.8.0
QT += core gui widgets printsupport QT += core gui widgets printsupport
@@ -23,6 +23,7 @@ SOURCES += \
src/MarkdownEditor.cpp \ src/MarkdownEditor.cpp \
src/MarkdownHighlighter.cpp \ src/MarkdownHighlighter.cpp \
src/MarkdownRenderer.cpp \ src/MarkdownRenderer.cpp \
src/CodeHighlight.cpp \
src/LanguageManager.cpp \ src/LanguageManager.cpp \
src/ThemeManager.cpp src/ThemeManager.cpp
@@ -31,6 +32,7 @@ HEADERS += \
src/MarkdownEditor.h \ src/MarkdownEditor.h \
src/MarkdownHighlighter.h \ src/MarkdownHighlighter.h \
src/MarkdownRenderer.h \ src/MarkdownRenderer.h \
src/CodeHighlight.h \
src/LanguageManager.h \ src/LanguageManager.h \
src/ThemeManager.h src/ThemeManager.h
Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 114 KiB

+334
View File
@@ -0,0 +1,334 @@
#include "CodeHighlight.h"
#include <QHash>
#include <QSet>
namespace CodeHighlight {
namespace {
struct LanguageDef
{
QSet<QString> keywords;
QSet<QString> types;
QStringList lineComments;
QString blockStart;
QString blockEnd;
QString quotes = QStringLiteral("\"'");
bool preprocessor = false;
bool caseInsensitive = false;
};
QSet<QString> words(const char *list)
{
QSet<QString> set;
const QString text = QString::fromLatin1(list);
for (const QString &word : text.split(QLatin1Char(' '), Qt::SkipEmptyParts))
set.insert(word);
return set;
}
const QHash<int, LanguageDef> &definitions()
{
static const QHash<int, LanguageDef> table = [] {
QHash<int, LanguageDef> defs;
LanguageDef c;
c.keywords = words("auto break case const continue default do else enum extern for goto if "
"inline long register restrict return short signed sizeof static struct "
"switch typedef union unsigned void volatile while _Bool _Static_assert");
c.types = words("char double float int size_t ssize_t ptrdiff_t FILE int8_t int16_t int32_t "
"int64_t uint8_t uint16_t uint32_t uint64_t bool");
c.lineComments = QStringList{QStringLiteral("//")};
c.blockStart = QStringLiteral("/*");
c.blockEnd = QStringLiteral("*/");
c.preprocessor = true;
defs.insert(int(Language::C), c);
LanguageDef cpp = c;
cpp.keywords.unite(words("alignas alignof and asm bool catch class concept constexpr const_cast "
"co_await co_return co_yield decltype delete dynamic_cast explicit export "
"false friend mutable namespace new noexcept not nullptr operator or "
"override private protected public reinterpret_cast static_cast template "
"this throw true try typeid typename using virtual xor final requires"));
cpp.types.unite(words("std string vector map set unique_ptr shared_ptr size_t"));
defs.insert(int(Language::Cpp), cpp);
LanguageDef py;
py.keywords = words("and as assert async await break class continue def del elif else except False "
"finally for from global if import in is lambda None nonlocal not or pass raise "
"return True try while with yield match case");
py.types = words("int float str bytes list dict set tuple bool object");
py.lineComments = QStringList{QStringLiteral("#")};
py.quotes = QStringLiteral("\"'");
defs.insert(int(Language::Python), py);
LanguageDef js;
js.keywords = words("async await break case catch class const continue debugger default delete do "
"else export extends finally for function if import in instanceof let new of "
"return static super switch this throw try typeof var void while with yield "
"null true false undefined");
js.types = words("String Number Boolean Object Array Promise Map Set JSON Math Date RegExp");
js.lineComments = QStringList{QStringLiteral("//")};
js.blockStart = QStringLiteral("/*");
js.blockEnd = QStringLiteral("*/");
js.quotes = QStringLiteral("\"'\x60");
defs.insert(int(Language::JavaScript), js);
LanguageDef ts = js;
ts.keywords.unite(words("abstract declare enum implements interface keyof namespace never readonly "
"satisfies type unknown infer is asserts override private protected public "
"any symbol bigint"));
ts.types.unite(words("string number boolean any unknown never void"));
defs.insert(int(Language::TypeScript), ts);
LanguageDef java;
java.keywords = words("abstract assert break case catch class const continue default do else enum "
"extends final finally for goto if implements import instanceof interface "
"native new package private protected public return static strictfp super "
"switch synchronized this throw throws transient try volatile while var "
"true false null");
java.types = words("byte short int long float double char boolean void String Object List Map Set");
java.lineComments = QStringList{QStringLiteral("//")};
java.blockStart = QStringLiteral("/*");
java.blockEnd = QStringLiteral("*/");
defs.insert(int(Language::Java), java);
LanguageDef rust;
rust.keywords = words("as async await break const continue crate dyn else enum extern false fn for "
"if impl in let loop match mod move mut pub ref return self Self static "
"struct super trait true type unsafe use where while yield");
rust.types = words("i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 bool char str String Vec Option "
"Result Box");
rust.lineComments = QStringList{QStringLiteral("//")};
rust.blockStart = QStringLiteral("/*");
rust.blockEnd = QStringLiteral("*/");
defs.insert(int(Language::Rust), rust);
LanguageDef go;
go.keywords = words("break case chan const continue default defer else fallthrough for func go goto "
"if import interface map package range return select struct switch type var "
"true false nil iota");
go.types = words("bool byte complex64 complex128 error float32 float64 int int8 int16 int32 int64 "
"rune string uint uint8 uint16 uint32 uint64 uintptr any");
go.lineComments = QStringList{QStringLiteral("//")};
go.blockStart = QStringLiteral("/*");
go.blockEnd = QStringLiteral("*/");
go.quotes = QStringLiteral("\"'\x60");
defs.insert(int(Language::Go), go);
LanguageDef json;
json.keywords = words("true false null");
json.lineComments = QStringList();
json.quotes = QStringLiteral("\"");
defs.insert(int(Language::Json), json);
LanguageDef bash;
bash.keywords = words("if then else elif fi for while until do done case esac function in select "
"time return exit local export readonly declare unset echo printf cd source "
"alias set shift trap true false");
bash.lineComments = QStringList{QStringLiteral("#")};
bash.quotes = QStringLiteral("\"'\x60");
defs.insert(int(Language::Bash), bash);
LanguageDef sql;
sql.keywords = words("select from where insert into values update set delete create table drop alter "
"index view join left right inner outer full on group by order having limit "
"offset union all as and or not null is in like between distinct case when "
"then else end exists primary key foreign references default unique check");
sql.types = words("int integer bigint smallint varchar char text date time timestamp boolean float "
"double decimal numeric");
sql.lineComments = QStringList{QStringLiteral("--")};
sql.blockStart = QStringLiteral("/*");
sql.blockEnd = QStringLiteral("*/");
sql.caseInsensitive = true;
defs.insert(int(Language::Sql), sql);
LanguageDef yaml;
yaml.keywords = words("true false null yes no on off");
yaml.lineComments = QStringList{QStringLiteral("#")};
defs.insert(int(Language::Yaml), yaml);
return defs;
}();
return table;
}
const LanguageDef *definition(Language language)
{
const auto &defs = definitions();
const auto it = defs.constFind(int(language));
return it == defs.constEnd() ? nullptr : &it.value();
}
} // namespace
Language fromInfoString(const QString &info)
{
QString name = info.trimmed().toLower();
if (name.isEmpty())
return Language::None;
name = name.section(QLatin1Char(' '), 0, 0);
static const QHash<QString, Language> aliases = {
{QStringLiteral("c"), Language::C},
{QStringLiteral("h"), Language::C},
{QStringLiteral("cpp"), Language::Cpp},
{QStringLiteral("c++"), Language::Cpp},
{QStringLiteral("cc"), Language::Cpp},
{QStringLiteral("cxx"), Language::Cpp},
{QStringLiteral("hpp"), Language::Cpp},
{QStringLiteral("python"), Language::Python},
{QStringLiteral("py"), Language::Python},
{QStringLiteral("javascript"), Language::JavaScript},
{QStringLiteral("js"), Language::JavaScript},
{QStringLiteral("jsx"), Language::JavaScript},
{QStringLiteral("mjs"), Language::JavaScript},
{QStringLiteral("typescript"), Language::TypeScript},
{QStringLiteral("ts"), Language::TypeScript},
{QStringLiteral("tsx"), Language::TypeScript},
{QStringLiteral("java"), Language::Java},
{QStringLiteral("rust"), Language::Rust},
{QStringLiteral("rs"), Language::Rust},
{QStringLiteral("go"), Language::Go},
{QStringLiteral("golang"), Language::Go},
{QStringLiteral("json"), Language::Json},
{QStringLiteral("bash"), Language::Bash},
{QStringLiteral("sh"), Language::Bash},
{QStringLiteral("shell"), Language::Bash},
{QStringLiteral("zsh"), Language::Bash},
{QStringLiteral("sql"), Language::Sql},
{QStringLiteral("yaml"), Language::Yaml},
{QStringLiteral("yml"), Language::Yaml},
};
return aliases.value(name, Language::None);
}
QVector<Span> tokenize(const QString &text, Language language, bool inBlockComment,
bool *newBlockComment)
{
QVector<Span> spans;
if (newBlockComment)
*newBlockComment = inBlockComment;
const LanguageDef *def = definition(language);
if (!def)
return spans;
const int length = text.size();
bool comment = inBlockComment;
int index = 0;
if (def->preprocessor) {
int start = 0;
while (start < length && text.at(start).isSpace())
++start;
if (start < length && text.at(start) == QLatin1Char('#')) {
int end = start + 1;
while (end < length
&& (text.at(end).isLetterOrNumber() || text.at(end) == QLatin1Char('_')))
++end;
spans.append({start, end - start, Token::Preprocessor});
index = end;
}
}
while (index < length) {
if (comment) {
const int end = text.indexOf(def->blockEnd, index);
if (end < 0) {
spans.append({index, length - index, Token::Comment});
index = length;
break;
}
spans.append({index, end + def->blockEnd.size() - index, Token::Comment});
index = end + def->blockEnd.size();
comment = false;
continue;
}
bool lineComment = false;
for (const QString &prefix : def->lineComments) {
if (text.mid(index, prefix.size()) == prefix) {
spans.append({index, length - index, Token::Comment});
index = length;
lineComment = true;
break;
}
}
if (lineComment)
break;
if (!def->blockStart.isEmpty()
&& text.mid(index, def->blockStart.size()) == def->blockStart) {
const int end = text.indexOf(def->blockEnd, index + def->blockStart.size());
if (end < 0) {
spans.append({index, length - index, Token::Comment});
comment = true;
index = length;
} else {
spans.append({index, end + def->blockEnd.size() - index, Token::Comment});
index = end + def->blockEnd.size();
}
continue;
}
const QChar current = text.at(index);
if (def->quotes.contains(current)) {
int end = index + 1;
while (end < length) {
if (text.at(end) == QLatin1Char('\\')) {
end += 2;
continue;
}
if (text.at(end) == current) {
++end;
break;
}
++end;
}
end = qMin(end, length);
spans.append({index, end - index, Token::String});
index = end;
continue;
}
if (current.isDigit()) {
int end = index + 1;
while (end < length) {
const QChar ch = text.at(end);
if (ch.isLetterOrNumber() || ch == QLatin1Char('.') || ch == QLatin1Char('_'))
++end;
else
break;
}
spans.append({index, end - index, Token::Number});
index = end;
continue;
}
if (current.isLetter() || current == QLatin1Char('_')) {
int end = index + 1;
while (end < length
&& (text.at(end).isLetterOrNumber() || text.at(end) == QLatin1Char('_')))
++end;
const QString word = text.mid(index, end - index);
const QString probe = def->caseInsensitive ? word.toLower() : word;
if (def->types.contains(probe))
spans.append({index, end - index, Token::Type});
else if (def->keywords.contains(probe))
spans.append({index, end - index, Token::Keyword});
index = end;
continue;
}
++index;
}
if (newBlockComment)
*newBlockComment = comment;
return spans;
}
} // namespace CodeHighlight
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <QString>
#include <QVector>
/// 围栏代码块里的轻量语法高亮。
///
/// 不依赖任何第三方库,按语言给出一组关键字 + 注释 / 字符串规则,
/// 逐行扫描出 token,由 MarkdownHighlighter 上色。
namespace CodeHighlight {
enum class Language {
None = 0,
C,
Cpp,
Python,
JavaScript,
TypeScript,
Java,
Rust,
Go,
Json,
Bash,
Sql,
Yaml,
Count
};
enum class Token { Keyword, Type, String, Number, Comment, Preprocessor };
struct Span {
int start = 0;
int length = 0;
Token token = Token::Keyword;
};
/// 从 ```cpp 这类 info string 里识别语言(大小写不敏感)。
Language fromInfoString(const QString &info);
/// 扫描一行,返回需要着色的区间;inBlockComment 传入 / 传出跨行注释状态。
QVector<Span> tokenize(const QString &text, Language language, bool inBlockComment,
bool *newBlockComment);
} // namespace CodeHighlight
+96 -26
View File
@@ -8,7 +8,22 @@
namespace { namespace {
constexpr int StateNormal = 0; constexpr int StateNormal = 0;
constexpr int StateInFence = 1;
// 围栏状态:1 + 语言编号 * 2 + 是否在块注释里。
int fenceState(int language, bool inComment)
{
return 1 + language * 2 + (inComment ? 1 : 0);
}
int stateLanguage(int state)
{
return (state - 1) / 2;
}
bool stateInComment(int state)
{
return (state - 1) % 2 != 0;
}
QStringList monospaceFamilies() QStringList monospaceFamilies()
{ {
@@ -103,6 +118,22 @@ void MarkdownHighlighter::rebuildFormats()
m_fenceFormat.setFontFixedPitch(true); m_fenceFormat.setFontFixedPitch(true);
m_fenceFormat.setForeground(c.editorText); m_fenceFormat.setForeground(c.editorText);
m_fenceMarkerFormat = QTextCharFormat();
m_fenceMarkerFormat.setFontFamilies(monospaceFamilies());
m_fenceMarkerFormat.setFontFixedPitch(true);
m_fenceMarkerFormat.setForeground(c.placeholder);
auto tokenColor = [](const QColor &color) {
QTextCharFormat format;
format.setForeground(color);
return format;
};
m_codeKeywordFormat = tokenColor(c.codeKeyword);
m_codeStringFormat = tokenColor(c.codeString);
m_codeNumberFormat = tokenColor(c.codeNumber);
m_codeCommentFormat = tokenColor(c.codeComment);
m_codePreprocessorFormat = tokenColor(c.codePreprocessor);
m_linkFormat = QTextCharFormat(); m_linkFormat = QTextCharFormat();
m_linkFormat.setForeground(c.link); m_linkFormat.setForeground(c.link);
m_linkFormat.setFontUnderline(true); m_linkFormat.setFontUnderline(true);
@@ -141,6 +172,24 @@ QTextCharFormat MarkdownHighlighter::compose(const QTextCharFormat &specific) co
return result; return result;
} }
const QTextCharFormat &MarkdownHighlighter::tokenFormat(CodeHighlight::Token token) const
{
switch (token) {
case CodeHighlight::Token::Keyword:
case CodeHighlight::Token::Type:
return m_codeKeywordFormat;
case CodeHighlight::Token::String:
return m_codeStringFormat;
case CodeHighlight::Token::Number:
return m_codeNumberFormat;
case CodeHighlight::Token::Comment:
return m_codeCommentFormat;
case CodeHighlight::Token::Preprocessor:
return m_codePreprocessorFormat;
}
return m_codeKeywordFormat;
}
QTextCharFormat MarkdownHighlighter::hiddenFormat(const QColor &background) const QTextCharFormat MarkdownHighlighter::hiddenFormat(const QColor &background) const
{ {
// 用“和背景同色 + 极小字号”把标记藏起来:文字仍然留在文档里, // 用“和背景同色 + 极小字号”把标记藏起来:文字仍然留在文档里,
@@ -187,46 +236,67 @@ void MarkdownHighlighter::applyDelimited(const QRegularExpression &pattern, cons
void MarkdownHighlighter::highlightBlock(const QString &text) void MarkdownHighlighter::highlightBlock(const QString &text)
{ {
const int previousState = previousBlockState();
setCurrentBlockState(StateNormal); setCurrentBlockState(StateNormal);
m_blockBase = QTextCharFormat(); m_blockBase = QTextCharFormat();
if (text.isEmpty())
const bool wasInFence = previousState > 0;
const int fenceLanguageId = wasInFence ? stateLanguage(previousState) : 0;
const auto fenceLanguage = static_cast<CodeHighlight::Language>(fenceLanguageId);
const bool fenceComment = wasInFence && stateInComment(previousState);
if (text.isEmpty()) {
// 空行也要把围栏状态传下去,否则代码块会被一条空行截断。
if (wasInFence)
setCurrentBlockState(fenceState(fenceLanguageId, fenceComment));
return; return;
}
const ThemeColors &c = ThemeManager::instance()->colors(); const ThemeColors &c = ThemeManager::instance()->colors();
const bool rendered = m_rendered; const bool rendered = m_rendered;
const bool active = isActiveLine(); const bool active = isActiveLine();
static const QRegularExpression fencePattern(QStringLiteral("^\\s{0,3}(?:```|~~~)")); static const QRegularExpression fencePattern(QStringLiteral("^\\s{0,3}(?:```|~~~)"));
const bool inFence = previousBlockState() == StateInFence;
const bool fenceLine = fencePattern.match(text).hasMatch(); const bool fenceLine = fencePattern.match(text).hasMatch();
// ---- 围栏代码块 ----------------------------------------------------- // ---- 围栏代码块 -----------------------------------------------------
if (inFence) { if (wasInFence) {
if (!fenceLine) if (fenceLine) {
setCurrentBlockState(StateInFence); // 闭合围栏行
if (rendered) { if (rendered && !active)
if (fenceLine && !active) {
apply(0, text.length(), hiddenFormat(c.editorBase));
} else {
m_blockBase = m_fenceFormat;
apply(0, text.length(), m_fenceFormat);
}
} else {
m_blockBase = m_codeFormat;
apply(0, text.length(), m_codeFormat);
}
return;
}
if (fenceLine) {
setCurrentBlockState(StateInFence);
if (rendered) {
if (!active)
apply(0, text.length(), hiddenFormat(c.editorBase)); apply(0, text.length(), hiddenFormat(c.editorBase));
else else
apply(0, text.length(), m_fenceFormat); apply(0, text.length(), m_fenceMarkerFormat);
} else { return;
apply(0, text.length(), m_codeFormat);
} }
bool inComment = fenceComment;
bool endsInComment = inComment;
m_blockBase = m_fenceFormat;
apply(0, text.length(), m_fenceFormat);
if (fenceLanguage != CodeHighlight::Language::None) {
const QVector<CodeHighlight::Span> spans =
CodeHighlight::tokenize(text, fenceLanguage, inComment, &endsInComment);
for (const CodeHighlight::Span &span : spans)
apply(span.start, span.length, compose(tokenFormat(span.token)));
}
setCurrentBlockState(fenceState(fenceLanguageId, endsInComment));
return;
}
if (fenceLine) {
// 开围栏行:从 info string 里认语言
QString info = text.trimmed();
info.remove(QRegularExpression(QStringLiteral("^(?:```|~~~)")));
const CodeHighlight::Language language = CodeHighlight::fromInfoString(info);
setCurrentBlockState(fenceState(int(language), false));
if (rendered && !active)
apply(0, text.length(), hiddenFormat(c.editorBase));
else
apply(0, text.length(), m_fenceMarkerFormat);
return; return;
} }
+9
View File
@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "CodeHighlight.h"
#include <QColor> #include <QColor>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
@@ -42,6 +44,7 @@ private:
const QTextCharFormat &contentFormat, int contentGroup, const QTextCharFormat &contentFormat, int contentGroup,
const QColor &hideAgainst); const QColor &hideAgainst);
QTextCharFormat compose(const QTextCharFormat &specific) const; QTextCharFormat compose(const QTextCharFormat &specific) const;
const QTextCharFormat &tokenFormat(CodeHighlight::Token token) const;
QTextCharFormat hiddenFormat(const QColor &background) const; QTextCharFormat hiddenFormat(const QColor &background) const;
bool isActiveLine() const; bool isActiveLine() const;
@@ -56,6 +59,12 @@ private:
QTextCharFormat m_emphasisFormat; QTextCharFormat m_emphasisFormat;
QTextCharFormat m_codeFormat; QTextCharFormat m_codeFormat;
QTextCharFormat m_fenceFormat; QTextCharFormat m_fenceFormat;
QTextCharFormat m_fenceMarkerFormat;
QTextCharFormat m_codeKeywordFormat;
QTextCharFormat m_codeStringFormat;
QTextCharFormat m_codeNumberFormat;
QTextCharFormat m_codeCommentFormat;
QTextCharFormat m_codePreprocessorFormat;
QTextCharFormat m_linkFormat; QTextCharFormat m_linkFormat;
QTextCharFormat m_urlFormat; QTextCharFormat m_urlFormat;
QTextCharFormat m_quoteFormat; QTextCharFormat m_quoteFormat;
+10
View File
@@ -93,6 +93,11 @@ ThemeColors lightColors()
c.emphasis = hex("#4b535c"); c.emphasis = hex("#4b535c");
c.code = hex("#b4295a"); c.code = hex("#b4295a");
c.codeBackground = hex("#f2f4f7"); c.codeBackground = hex("#f2f4f7");
c.codeKeyword = hex("#0550ae");
c.codeString = hex("#0a7d3a");
c.codeNumber = hex("#953800");
c.codeComment = hex("#6e7781");
c.codePreprocessor = hex("#8250df");
c.link = hex("#0969da"); c.link = hex("#0969da");
c.linkUrl = hex("#6b7480"); c.linkUrl = hex("#6b7480");
c.quote = hex("#6b7480"); c.quote = hex("#6b7480");
@@ -138,6 +143,11 @@ ThemeColors darkColors()
c.emphasis = hex("#c3c9d1"); c.emphasis = hex("#c3c9d1");
c.code = hex("#ff8fbf"); c.code = hex("#ff8fbf");
c.codeBackground = hex("#292c33"); c.codeBackground = hex("#292c33");
c.codeKeyword = hex("#79b8ff");
c.codeString = hex("#7ee787");
c.codeNumber = hex("#ffab70");
c.codeComment = hex("#8b949e");
c.codePreprocessor = hex("#d2a8ff");
c.link = hex("#62a8ff"); c.link = hex("#62a8ff");
c.linkUrl = hex("#7f8791"); c.linkUrl = hex("#7f8791");
c.quote = hex("#8b939d"); c.quote = hex("#8b939d");
+5
View File
@@ -44,6 +44,11 @@ struct ThemeColors
QColor emphasis; QColor emphasis;
QColor code; QColor code;
QColor codeBackground; QColor codeBackground;
QColor codeKeyword;
QColor codeString;
QColor codeNumber;
QColor codeComment;
QColor codePreprocessor;
QColor link; QColor link;
QColor linkUrl; QColor linkUrl;
QColor quote; QColor quote;