From 6f351b77e4c8bf505d35d450a03a926053336649 Mon Sep 17 00:00:00 2001
From: Levi Neely <141506390+lneely@users.noreply.github.com>
Date: Thu, 16 Jul 2026 06:42:11 +0200
Subject: [PATCH] fix performance: cache subBlocks and syntax highlighting
- Cache parseSubBlocks per block (only reparse when content changes)
- Cache highlightedHtml results (code+language key)
- Prevents expensive KSyntaxHighlighting and regex on every render
- Also fixes scroll loop with _scrolling guard
---
gui/chatblockmodel.cpp | 17 +++++++++++++++--
gui/chatblockmodel.h | 3 +++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/gui/chatblockmodel.cpp b/gui/chatblockmodel.cpp
index c4316a3..256e579 100644
--- a/gui/chatblockmodel.cpp
+++ b/gui/chatblockmodel.cpp
@@ -54,7 +54,11 @@ QVariant ChatBlockModel::data(const QModelIndex &index, int role) const
case ContentRole:
return block.content;
case SubBlocksRole:
- return parseSubBlocks(block.content);
+ if (block.cachedContent != block.content) {
+ block.cachedSubBlocks = parseSubBlocks(block.content);
+ block.cachedContent = block.content;
+ }
+ return block.cachedSubBlocks;
}
return {};
}
@@ -251,6 +255,12 @@ QVariantList ChatBlockModel::parseSubBlocks(const QString &content) const
QString ChatBlockModel::highlightedHtml(const QString &code, const QString &language) const
{
+ // Check cache first
+ QString cacheKey = language + "\x1f" + code;
+ auto cached = m_highlightCache.find(cacheKey);
+ if (cached != m_highlightCache.end())
+ return *cached;
+
auto def = repo().definitionForName(language);
if (!def.isValid()) {
// Try by file extension
@@ -258,7 +268,9 @@ QString ChatBlockModel::highlightedHtml(const QString &code, const QString &lang
}
if (!def.isValid()) {
// Return escaped plain text
- return code.toHtmlEscaped().replace('\n', "
");
+ QString plain = code.toHtmlEscaped().replace('\n', "
");
+ m_highlightCache.insert(cacheKey, plain);
+ return plain;
}
// Determine theme based on current palette
@@ -298,6 +310,7 @@ QString ChatBlockModel::highlightedHtml(const QString &code, const QString &lang
html += "
";
}
+ m_highlightCache.insert(cacheKey, html);
return html;
}
diff --git a/gui/chatblockmodel.h b/gui/chatblockmodel.h
index b7b2b7d..b1c9dac 100644
--- a/gui/chatblockmodel.h
+++ b/gui/chatblockmodel.h
@@ -20,6 +20,8 @@ struct ChatBlock {
Type type;
QString name; // tool/call name, empty for others
QString content; // raw text content
+ mutable QVariantList cachedSubBlocks; // cached parseSubBlocks result
+ mutable QString cachedContent; // content when subBlocks were cached
};
// A sub-block within a markdown block (prose, code fence, diff)
@@ -70,6 +72,7 @@ private:
QString m_rawText;
QList m_blocks;
+ mutable QHash m_highlightCache; // key: code+lang, value: html
};
#endif // CHAT_BLOCK_MODEL_H