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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user