gui: revert incremental parsing, use full reparse always
Incremental parsing caused phantom blocks and disappearing content. The 150ms coalescing on the daemon side is sufficient for perf. updateVisibleWindow() extraction kept for clean separation.
This commit is contained in:
parent
c46f38fc93
commit
7703d06430
|
|
@ -106,80 +106,17 @@ QHash<int, QByteArray> ChatBlockModel::roleNames() const
|
|||
void ChatBlockModel::setRawText(const QString &text)
|
||||
{
|
||||
if (text == m_rawText) return;
|
||||
|
||||
// If text is just appended (common during streaming), only reparse from the last block
|
||||
bool isAppend = text.startsWith(m_rawText) && !m_rawText.isEmpty();
|
||||
m_rawText = text;
|
||||
|
||||
if (isAppend && !m_allBlocks.isEmpty()) {
|
||||
reparseIncremental();
|
||||
} else {
|
||||
// Full reparse (session switch, initial load)
|
||||
m_allBlocks.clear();
|
||||
m_lastBlockTextOffset = 0;
|
||||
beginResetModel();
|
||||
m_blocks.clear();
|
||||
m_visibleStart = 0;
|
||||
endResetModel();
|
||||
reparse();
|
||||
}
|
||||
reparse();
|
||||
emit rawTextChanged();
|
||||
}
|
||||
|
||||
static constexpr int INITIAL_BLOCKS = 20;
|
||||
static constexpr int LOAD_MORE_COUNT = 30;
|
||||
|
||||
void ChatBlockModel::reparseIncremental()
|
||||
{
|
||||
// Fast path: just update the last block's content from the raw text tail.
|
||||
// Only do a full reparse if a new block header appeared.
|
||||
QString tail = m_rawText.mid(m_lastBlockTextOffset);
|
||||
|
||||
// Check if a new block header appeared in the NEW portion (after old last-block content)
|
||||
int oldLastBlockLen = m_allBlocks.isEmpty() ? 0 : m_allBlocks.last().content.size();
|
||||
// The header itself is at m_lastBlockTextOffset, content starts after it + newline
|
||||
// Simple heuristic: if the tail contains another header pattern, do full reparse
|
||||
static const QRegularExpression headerRe(
|
||||
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])",
|
||||
QRegularExpression::MultilineOption);
|
||||
auto it = headerRe.globalMatch(tail);
|
||||
int headerCount = 0;
|
||||
while (it.hasNext()) { it.next(); headerCount++; }
|
||||
|
||||
if (headerCount > 1) {
|
||||
// New block boundary appeared — fall back to full reparse
|
||||
reparse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Common case: just the last block grew. Update its content directly.
|
||||
if (!m_allBlocks.isEmpty()) {
|
||||
QList<ChatBlock> tailBlocks = parseBlocks(tail);
|
||||
if (!tailBlocks.isEmpty()) {
|
||||
m_allBlocks.last() = tailBlocks.last();
|
||||
}
|
||||
}
|
||||
|
||||
updateVisibleWindow();
|
||||
}
|
||||
|
||||
void ChatBlockModel::reparse()
|
||||
{
|
||||
m_allBlocks = parseBlocks(m_rawText);
|
||||
// Track where the last block starts for future incremental parses
|
||||
if (!m_allBlocks.isEmpty()) {
|
||||
static const QRegularExpression headerRe(
|
||||
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])",
|
||||
QRegularExpression::MultilineOption);
|
||||
auto it = headerRe.globalMatch(m_rawText);
|
||||
int lastPos = 0;
|
||||
while (it.hasNext()) {
|
||||
auto match = it.next();
|
||||
lastPos = match.capturedStart();
|
||||
}
|
||||
m_lastBlockTextOffset = lastPos;
|
||||
}
|
||||
|
||||
updateVisibleWindow();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ signals:
|
|||
|
||||
private:
|
||||
void reparse();
|
||||
void reparseIncremental();
|
||||
void updateVisibleWindow();
|
||||
QList<ChatBlock> parseBlocks(const QString &text) const;
|
||||
QVariantList parseSubBlocks(const QString &content) const;
|
||||
|
|
@ -83,7 +82,6 @@ private:
|
|||
QList<ChatBlock> m_allBlocks; // all parsed blocks
|
||||
QList<ChatBlock> m_blocks; // visible window (tail of m_allBlocks)
|
||||
int m_visibleStart = 0; // index into m_allBlocks where visible window starts
|
||||
int m_lastBlockTextOffset = 0; // char offset in m_rawText where last block starts
|
||||
mutable QHash<QString, QString> m_highlightCache;
|
||||
};
|
||||
|
||||
|
|
|
|||
Reference in New Issue