Show folder names immediately and refresh stale index sizes without walking the whole drive.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 11:21:20 +02:00
parent 6fc7506eb7
commit e2916aef9c
88 changed files with 5373 additions and 544 deletions

View File

@@ -26,18 +26,33 @@ public sealed class FolderReconciler
_preferences = preferences;
}
public async Task ReconcileAsync(Source source, string pathRel, CancellationToken cancellationToken)
public Task ReconcileAsync(Source source, string pathRel, CancellationToken cancellationToken)
=> ReconcileAsync(source, pathRel, cancellationToken, verifyChildren: false);
public Task<bool> ReconcileAsync(
Source source,
string pathRel,
CancellationToken cancellationToken,
bool verifyChildren)
=> ReconcileCoreAsync(source, pathRel, verifyChildren, remaining: 48, cancellationToken);
private async Task<bool> ReconcileCoreAsync(
Source source,
string pathRel,
bool verifyChildren,
int remaining,
CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(source.LastRootPath))
{
return;
return false;
}
var full = PathRules.Combine(source.LastRootPath, pathRel);
var parent = await _store.Entries.GetByPathAsync(source.Id, pathRel, cancellationToken).ConfigureAwait(false);
if (parent is null)
{
return;
return false;
}
if (!parent.IsDirectory)
@@ -48,20 +63,20 @@ public sealed class FolderReconciler
{
await _archives.ExpandIfNeededAsync(source, parent, DateTimeOffset.UtcNow, source.ScanGeneration, cancellationToken)
.ConfigureAwait(false);
return false;
}
else
{
await _store.Entries.TombstoneByPathPrefixAsync(source.Id, parent.PathRel, DateTimeOffset.UtcNow, cancellationToken)
.ConfigureAwait(false);
}
await _store.Entries.TombstoneByPathPrefixAsync(source.Id, parent.PathRel, DateTimeOffset.UtcNow, cancellationToken)
.ConfigureAwait(false);
return true;
}
return;
return false;
}
if (!Directory.Exists(full) || LocationClassifier.IsRecycleBinName(parent.Name))
{
return;
return false;
}
var live = await _providers.EnrichAsync(_enumerator.EnumerateChildrenSafe(full, out _), cancellationToken)
@@ -73,6 +88,7 @@ public sealed class FolderReconciler
var archivesToExpand = new List<IndexEntry>();
var archivesToTomb = new List<string>();
var prefs = _preferences?.Load() ?? UiPreferences.Default;
var changed = false;
await _store.RunWriteAsync(async s =>
{
@@ -118,11 +134,13 @@ public sealed class FolderReconciler
var delta = item.SizeBytes - oldSize;
if (existing is null)
{
changed = true;
await s.Entries.ApplySizeDeltaToAncestorsAsync(parent.Id, item.SizeBytes, 1, 0, cancellationToken)
.ConfigureAwait(false);
}
else if (delta != 0)
{
changed = true;
await s.Entries.ApplySizeDeltaToAncestorsAsync(parent.Id, delta, 0, 0, cancellationToken)
.ConfigureAwait(false);
}
@@ -134,6 +152,7 @@ public sealed class FolderReconciler
}
else if (existing is null)
{
changed = true;
await s.Entries.ApplySizeDeltaToAncestorsAsync(parent.Id, 0, 0, 1, cancellationToken)
.ConfigureAwait(false);
}
@@ -143,8 +162,13 @@ public sealed class FolderReconciler
{
if (!liveNames.Contains(old.NameNorm))
{
changed = true;
await s.Entries.TombstoneAsync(old.Id, now, cancellationToken).ConfigureAwait(false);
if (!old.IsDirectory && ArchiveFormats.IsArchive(old.Name))
if (old.IsDirectory)
{
archivesToTomb.Add(old.PathRel);
}
else if (ArchiveFormats.IsArchive(old.Name))
{
archivesToTomb.Add(old.PathRel);
}
@@ -166,5 +190,59 @@ public sealed class FolderReconciler
.ConfigureAwait(false);
}
}
if (!verifyChildren || remaining <= 0)
{
return changed;
}
changed |= await WalkSizedChildrenAsync(source, indexed, liveNames, remaining, cancellationToken)
.ConfigureAwait(false);
return changed;
}
private async Task<bool> WalkSizedChildrenAsync(
Source source,
IReadOnlyList<IndexEntry> indexed,
HashSet<string> liveNames,
int remaining,
CancellationToken cancellationToken)
{
var changed = false;
foreach (var child in indexed
.Where(e => e.IsDirectory
&& liveNames.Contains(e.NameNorm)
&& (e.AggregateSize > 0 || e.ChildFileCount > 0 || e.ChildDirCount > 0))
.OrderByDescending(e => e.AggregateSize))
{
if (remaining <= 0)
{
break;
}
remaining--;
var full = PathRules.Combine(source.LastRootPath!, child.PathRel);
var liveKids = _enumerator.EnumerateChildrenSafe(full, out var error);
if (error is not null)
{
continue;
}
var indexedKids = await _store.Entries.GetChildrenAsync(source.Id, child.Id, EntryStatus.Present, cancellationToken)
.ConfigureAwait(false);
var childLiveNames = new HashSet<string>(liveKids.Select(i => NameNormalizer.Normalize(i.Name)), StringComparer.Ordinal);
var matches = liveKids.Count == indexedKids.Count && indexedKids.All(e => childLiveNames.Contains(e.NameNorm));
if (!matches)
{
changed |= await ReconcileCoreAsync(source, child.PathRel, verifyChildren: true, remaining, cancellationToken)
.ConfigureAwait(false);
continue;
}
changed |= await WalkSizedChildrenAsync(source, indexedKids, childLiveNames, remaining, cancellationToken)
.ConfigureAwait(false);
}
return changed;
}
}