chore: format llm c files
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
JS/TS / lint (pull_request) Successful in 9s
JS/TS / fmt (pull_request) Successful in 16s
C++ / clang-tidy (pull_request) Failing after 43s
Python / static (pull_request) Failing after 57s
Rust / fmt (pull_request) Successful in 1m30s
Rust / build (pull_request) Successful in 2m5s
Rust / clippy (pull_request) Successful in 1m55s
Python / verify (pull_request) Successful in 2m23s

This commit is contained in:
2026-08-31 19:02:26 +02:00
parent dbb51ceadd
commit 52feb6006a
27 changed files with 741 additions and 1190 deletions
+21 -40
View File
@@ -13,15 +13,12 @@ namespace ZShell::llm {
QVariantList MarkdownParser::parse(const QString& source) {
QVariantList blocks;
if (source.trimmed().isEmpty())
return blocks;
if (source.trimmed().isEmpty()) return blocks;
const QStringList lines = source.split('\n');
// cmark-gfm line/column numbers are 1-based and inclusive.
auto sliceSource = [&](int startLine, int endLine) -> QString {
if (startLine < 1 || endLine < startLine)
return QString();
if (startLine < 1 || endLine < startLine) return QString();
const int from = startLine;
const int to = qMin(endLine, static_cast<int>(lines.size()));
return lines.mid(from - 1, to - from + 1).join('\n').trimmed();
@@ -29,15 +26,11 @@ QVariantList MarkdownParser::parse(const QString& source) {
const QByteArray utf8 = source.toUtf8();
cmark_node* doc = cmark_parse_document(
utf8.constData(), static_cast<size_t>(utf8.size()),
utf8.constData(),
static_cast<size_t>(utf8.size()),
CMARK_OPT_DEFAULT | CMARK_OPT_SOURCEPOS);
if (!doc)
return blocks;
if (!doc) return blocks;
// cmark-gfm has no math extension, so $$...$$ parses as an ordinary
// paragraph. Re-detect display math (a $$...$$ pair) here and split it
// out as its own block. Single-$ inline math is intentionally left
// untouched (rendered raw) for now.
const QRegularExpression mathRe(
QStringLiteral("\\$\\$(.+?)\\$\\$"),
QRegularExpression::DotMatchesEverythingOption);
@@ -45,29 +38,22 @@ QVariantList MarkdownParser::parse(const QString& source) {
auto makeBlock = [&](LlmMarkdown::Type type) {
QVariantMap block;
block.insert("type", static_cast<int>(type));
// Stable per-position identity ("index:type") for the QML
// ScriptModel: blocks that survive a re-parse keep their id, so
// their delegates are updated in place instead of recreated
// (which would drop code highlights mid-stream). The type is
// part of the id so a block that changes type is rebuilt.
block.insert("id",
QString::number(static_cast<int>(blocks.size()))
+ QLatin1Char(':')
+ QString::number(static_cast<int>(type)));
block.insert(
"id",
QString::number(static_cast<int>(blocks.size())) +
QLatin1Char(':') + QString::number(static_cast<int>(type)));
return block;
};
auto appendText = [&](const QString& text) {
if (text.trimmed().isEmpty())
return;
if (text.trimmed().isEmpty()) return;
QVariantMap block = makeBlock(LlmMarkdown::Type::Text);
block.insert("text", text);
blocks.append(block);
};
auto appendMath = [&](const QString& latex) {
if (latex.trimmed().isEmpty())
return;
if (latex.trimmed().isEmpty()) return;
QVariantMap block = makeBlock(LlmMarkdown::Type::Math);
block.insert("latex", latex);
blocks.append(block);
@@ -81,8 +67,7 @@ QVariantList MarkdownParser::parse(const QString& source) {
};
auto appendHeading = [&](int level, const QString& text) {
if (text.trimmed().isEmpty())
return;
if (text.trimmed().isEmpty()) return;
QVariantMap block = makeBlock(LlmMarkdown::Type::Heading);
block.insert("level", level);
block.insert("text", text);
@@ -90,7 +75,7 @@ QVariantList MarkdownParser::parse(const QString& source) {
};
for (cmark_node* node = cmark_node_first_child(doc); node;
node = cmark_node_next(node)) {
node = cmark_node_next(node)) {
const cmark_node_type type = cmark_node_get_type(node);
const int startLine = cmark_node_get_start_line(node);
const int endLine = cmark_node_get_end_line(node);
@@ -98,21 +83,20 @@ QVariantList MarkdownParser::parse(const QString& source) {
if (type == CMARK_NODE_CODE_BLOCK) {
const char* literal = cmark_node_get_literal(node);
QString code = literal ? QString::fromUtf8(literal) : QString();
// Fenced block literals carry a trailing newline; drop one.
if (code.endsWith('\n'))
code.chop(1);
if (code.endsWith('\n')) code.chop(1);
QString language;
if (const char* info = cmark_node_get_fence_info(node); info)
language = QString::fromUtf8(info).section(' ', 0, 0).trimmed().toLower();
language = QString::fromUtf8(info)
.section(' ', 0, 0)
.trimmed()
.toLower();
appendCode(language, code);
continue;
}
if (type == CMARK_NODE_HEADING) {
// Inline content only (no leading #), so QML can style by
// level.
const char* content = cmark_node_get_string_content(node);
appendHeading(
cmark_node_get_heading_level(node),
@@ -125,11 +109,11 @@ QVariantList MarkdownParser::parse(const QString& source) {
int cursor = 0;
bool anyMath = false;
for (auto it = mathRe.globalMatch(text, cursor); it.hasNext();
it = mathRe.globalMatch(text, cursor)) {
it = mathRe.globalMatch(text, cursor)) {
const QRegularExpressionMatch m = it.next();
anyMath = true;
appendText(
text.mid(cursor, static_cast<int>(m.capturedStart() - cursor)));
appendText(text.mid(
cursor, static_cast<int>(m.capturedStart() - cursor)));
appendMath(m.captured(1).trimmed());
cursor = static_cast<int>(m.capturedEnd());
}
@@ -140,9 +124,6 @@ QVariantList MarkdownParser::parse(const QString& source) {
continue;
}
// Lists, block quotes, tables, horizontal rules, custom blocks:
// hand the raw markdown source to QML (rendered via
// Text.MarkdownText).
appendText(sliceSource(startLine, endLine));
}