Show the window before slow location probes and wrap git.exe for commit, diff, and merge.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-24 16:31:56 +02:00
parent a3c54bbb03
commit 9efb306979
42 changed files with 3184 additions and 77 deletions

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Explorer.Domain;
using Explorer.Domain.Abstractions;
using Microsoft.Extensions.Logging;
@@ -12,6 +13,11 @@ public sealed class SourceManager
private readonly IClock _clock;
private readonly ILogger<SourceManager> _logger;
private readonly object _refreshLock = new();
private Task<IReadOnlyList<Source>>? _refreshInFlight;
private long _refreshCacheTimestamp;
private static readonly TimeSpan RefreshCacheTtl = TimeSpan.FromSeconds(2);
public SourceManager(
IIndexStore store,
IVolumeService volumes,
@@ -34,8 +40,37 @@ public sealed class SourceManager
await RefreshOnlineStateAsync(cancellationToken).ConfigureAwait(false);
}
public async Task<IReadOnlyList<Source>> RefreshOnlineStateAsync(CancellationToken cancellationToken = default)
public Task<IReadOnlyList<Source>> RefreshOnlineStateAsync(CancellationToken cancellationToken = default)
=> RefreshOnlineStateAsync(forceRefresh: false, cancellationToken);
public Task<IReadOnlyList<Source>> RefreshOnlineStateAsync(bool forceRefresh, CancellationToken cancellationToken = default)
{
lock (_refreshLock)
{
if (!forceRefresh && _refreshInFlight is { IsCompleted: false })
{
return _refreshInFlight;
}
if (!forceRefresh
&& BoundedWait.IsFresh(_refreshCacheTimestamp, RefreshCacheTtl))
{
return _store.Sources.GetAllAsync(cancellationToken);
}
if (forceRefresh && _refreshInFlight is { IsCompleted: false })
{
return _refreshInFlight;
}
_refreshInFlight = RefreshOnlineStateCoreAsync(cancellationToken);
return _refreshInFlight;
}
}
private async Task<IReadOnlyList<Source>> RefreshOnlineStateCoreAsync(CancellationToken cancellationToken)
{
var started = Stopwatch.GetTimestamp();
var known = (await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false)).ToList();
var online = _volumes.EnumerateOnlineVolumes();
var seenIds = new HashSet<long>();
@@ -47,6 +82,7 @@ public sealed class SourceManager
if (match.Source is not null && !match.Ambiguous)
{
source = match.Source;
var wasOffline = source.Status == SourceStatus.Offline;
source.LastRootPath = fp.RootPath;
source.DisplayName = fp.DisplayName ?? source.DisplayName;
source.Label = fp.Label ?? source.Label;
@@ -58,10 +94,16 @@ public sealed class SourceManager
source.LastSeenUtc = _clock.UtcNow;
source.Status = await ResolveReachableStatusAsync(source, cancellationToken).ConfigureAwait(false);
source.LastError = null;
await _store.Sources.UpsertAsync(source, cancellationToken).ConfigureAwait(false);
if (source.IsIndexed)
await TryIndexWrite(
() => _store.Sources.UpsertAsync(source, cancellationToken),
"source upsert",
source.Id).ConfigureAwait(false);
if (source.IsIndexed && wasOffline)
{
await _store.Entries.MarkSourceOnlinePresentAsync(source.Id, cancellationToken).ConfigureAwait(false);
await TryIndexWrite(
() => _store.Entries.MarkSourceOnlinePresentAsync(source.Id, cancellationToken),
"mark online",
source.Id).ConfigureAwait(false);
}
}
else if (fp.Kind.IsNetwork())
@@ -99,17 +141,24 @@ public sealed class SourceManager
continue;
}
var reachable = source.LastRootPath is not null && _volumes.IsPathReachable(source.LastRootPath);
if (reachable)
if (source.LastRootPath is not null
&& (source.Kind.IsNetwork() || PathRules.IsUnc(source.LastRootPath))
&& _volumes.IsPathReachable(source.LastRootPath))
{
continue;
}
if (source.Status != SourceStatus.Offline)
{
await _store.Sources.UpdateStatusAsync(source.Id, SourceStatus.Offline, null, cancellationToken)
.ConfigureAwait(false);
await _store.Entries.MarkSourceOfflineAsync(source.Id, cancellationToken).ConfigureAwait(false);
await TryIndexWrite(
() => _store.Sources.UpdateStatusAsync(source.Id, SourceStatus.Offline, null, cancellationToken),
"mark source offline",
source.Id).ConfigureAwait(false);
await TryIndexWrite(
() => _store.Entries.MarkSourceOfflineAsync(source.Id, cancellationToken),
"mark entries offline",
source.Id).ConfigureAwait(false);
source.Status = SourceStatus.Offline;
}
}
@@ -125,7 +174,29 @@ public sealed class SourceManager
await AddUncAsync(unc, cancellationToken).ConfigureAwait(false);
}
return await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false);
var result = await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false);
lock (_refreshLock)
{
_refreshCacheTimestamp = Stopwatch.GetTimestamp();
}
_logger.LogInformation(
"Refreshed {Count} sources in {Ms} ms",
result.Count,
(long)Stopwatch.GetElapsedTime(started).TotalMilliseconds);
return result;
}
private async Task TryIndexWrite(Func<Task> work, string what, long id)
{
try
{
await work().ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Skipped {What} for source {Id}; index may be busy", what, id);
}
}
public async Task<Source> AddUncAsync(string path, CancellationToken cancellationToken = default)
@@ -148,6 +219,7 @@ public sealed class SourceManager
existing.Status = _volumes.IsPathReachable(root) ? SourceStatus.Online : SourceStatus.Offline;
await _store.Sources.UpsertAsync(existing, cancellationToken).ConfigureAwait(false);
RememberUnc(root);
InvalidateRefreshCache();
return existing;
}
@@ -163,6 +235,7 @@ public sealed class SourceManager
};
source.Id = await _store.Sources.UpsertAsync(source, cancellationToken).ConfigureAwait(false);
RememberUnc(root);
InvalidateRefreshCache();
return source;
}
@@ -210,6 +283,7 @@ public sealed class SourceManager
}
_logger.LogInformation("Forgot disconnected source {DisplayName} ({Path})", source.DisplayName, source.LastRootPath);
InvalidateRefreshCache();
return true;
}
@@ -258,6 +332,7 @@ public sealed class SourceManager
? await ResolveReachableStatusAsync(existing, cancellationToken).ConfigureAwait(false)
: SourceStatus.Offline;
await _store.Sources.UpsertAsync(existing, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return existing;
}
@@ -280,6 +355,7 @@ public sealed class SourceManager
source.LastSeenUtc = _clock.UtcNow;
source.Status = _volumes.IsPathReachable(fp.RootPath) ? SourceStatus.Online : SourceStatus.Offline;
await _store.Sources.UpsertAsync(source, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return source;
}
@@ -303,6 +379,7 @@ public sealed class SourceManager
LastSeenUtc = _clock.UtcNow
};
created.Id = await _store.Sources.UpsertAsync(created, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return created;
}
@@ -373,6 +450,14 @@ public sealed class SourceManager
}
}
private void InvalidateRefreshCache()
{
lock (_refreshLock)
{
_refreshCacheTimestamp = 0;
}
}
public Task<Source?> GetAsync(long id, CancellationToken cancellationToken = default)
=> _store.Sources.GetAsync(id, cancellationToken);