Preserve selection during auto-refresh

- Only refresh status model when git output actually changes (skip
  beginResetModel/endResetModel if raw output is identical)
- Filesystem watcher triggers updateStatus only, not refreshAll —
  branch/stash/worktree lists only rebuild on explicit user actions
- Branch list refreshes on HEAD file change (branch switch) with delay
  to avoid interrupting interactions
This commit is contained in:
Levi Neely 2026-07-16 04:42:07 +02:00
parent 3febe2f184
commit 3642f8b743
3 changed files with 12 additions and 3 deletions

View File

@ -430,7 +430,7 @@ GitPlusPlusView::GitPlusPlusView(GitPlusPlusPlugin *plugin, KTextEditor::MainWin
// Debounce timer for filesystem changes (avoid spamming refreshes) // Debounce timer for filesystem changes (avoid spamming refreshes)
m_fsDebounce.setSingleShot(true); m_fsDebounce.setSingleShot(true);
m_fsDebounce.setInterval(500); m_fsDebounce.setInterval(500);
connect(&m_fsDebounce, &QTimer::timeout, this, &GitPlusPlusView::refreshAll); connect(&m_fsDebounce, &QTimer::timeout, this, &GitPlusPlusView::updateStatus);
// Initial refresh // Initial refresh
QMetaObject::invokeMethod(this, &GitPlusPlusView::updateStatus, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &GitPlusPlusView::updateStatus, Qt::QueuedConnection);
@ -549,7 +549,7 @@ void GitPlusPlusView::setupFileWatcher()
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &) { connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &) {
m_fsDebounce.start(); m_fsDebounce.start();
}); });
// Still watch individual files as a fallback for systems where dir watching is coarse // Watch HEAD file specifically — changes indicate branch switch
QString indexFile = gitDir + QStringLiteral("/index"); QString indexFile = gitDir + QStringLiteral("/index");
QString headFile = gitDir + QStringLiteral("/HEAD"); QString headFile = gitDir + QStringLiteral("/HEAD");
QStringList filePaths; QStringList filePaths;
@ -557,7 +557,7 @@ void GitPlusPlusView::setupFileWatcher()
if (QFileInfo::exists(headFile)) filePaths << headFile; if (QFileInfo::exists(headFile)) filePaths << headFile;
if (!filePaths.isEmpty()) { if (!filePaths.isEmpty()) {
m_watcher->addPaths(filePaths); m_watcher->addPaths(filePaths);
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) { connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this, headFile](const QString &path) {
// Re-add: atomic renames remove the watch // Re-add: atomic renames remove the watch
if (!m_watcher->files().contains(path)) { if (!m_watcher->files().contains(path)) {
QTimer::singleShot(200, this, [this, path]() { QTimer::singleShot(200, this, [this, path]() {
@ -566,6 +566,10 @@ void GitPlusPlusView::setupFileWatcher()
}); });
} }
m_fsDebounce.start(); m_fsDebounce.start();
// HEAD change means branch switch — refresh branches
if (path == headFile) {
QTimer::singleShot(600, this, [this]() { refreshBranches(); });
}
}); });
} }
} }

View File

@ -153,6 +153,10 @@ Qt::ItemFlags GitStatusModel::flags(const QModelIndex &index) const
void GitStatusModel::parseStatus(const QByteArray &raw) void GitStatusModel::parseStatus(const QByteArray &raw)
{ {
if (raw == m_lastRaw)
return;
m_lastRaw = raw;
beginResetModel(); beginResetModel();
for (int i = 0; i < NodeCount; ++i) for (int i = 0; i < NodeCount; ++i)

View File

@ -61,6 +61,7 @@ public:
private: private:
static constexpr quintptr TopLevelId = 0xFFFFFFFF; static constexpr quintptr TopLevelId = 0xFFFFFFFF;
QByteArray m_lastRaw;
QList<GitStatusEntry> m_entries[NodeCount]; QList<GitStatusEntry> m_entries[NodeCount];
QString m_nodeNames[NodeCount]; QString m_nodeNames[NodeCount];
QIcon m_nodeIcons[NodeCount]; QIcon m_nodeIcons[NodeCount];