fix(themes): keep last-known-good custom theme on transient file-lock
This commit is contained in:
@@ -63,7 +63,11 @@ internal static class ThemeJsonLoader
|
|||||||
|
|
||||||
public static Theme LoadFromFile(string path)
|
public static Theme LoadFromFile(string path)
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(path);
|
// FileShare.Read lets concurrent readers and well-behaved editors share
|
||||||
|
// the handle; atomic-replace editors still raise IOException, caught upstream.
|
||||||
|
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
var json = reader.ReadToEnd();
|
||||||
return LoadFromString(json);
|
return LoadFromString(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ public sealed class ThemeRegistry
|
|||||||
|
|
||||||
public void Switch(string slug) => _active = Get(slug);
|
public void Switch(string slug) => _active = Get(slug);
|
||||||
|
|
||||||
|
// 0x80070020 = SHARING_VIOLATION, 0x80070021 = LOCK_VIOLATION. Other
|
||||||
|
// IO failures are permanent and get the theme dropped instead of retried.
|
||||||
|
internal static bool IsRecoverableFileLock(Exception? ex)
|
||||||
|
{
|
||||||
|
if (ex is not IOException io)
|
||||||
|
return false;
|
||||||
|
var code = (uint)io.HResult;
|
||||||
|
return code == 0x80070020u || code == 0x80070021u;
|
||||||
|
}
|
||||||
|
|
||||||
// Custom-Themes werden lazy aus dem Verzeichnis geladen, Cache mit
|
// Custom-Themes werden lazy aus dem Verzeichnis geladen, Cache mit
|
||||||
// LastWriteTime-Token. Eine geänderte JSON wird beim nächsten Lookup
|
// LastWriteTime-Token. Eine geänderte JSON wird beim nächsten Lookup
|
||||||
// neu eingelesen.
|
// neu eingelesen.
|
||||||
@@ -89,12 +99,16 @@ public sealed class ThemeRegistry
|
|||||||
theme.RecomputeAbgrCache();
|
theme.RecomputeAbgrCache();
|
||||||
_customCache[key] = (theme, stamp);
|
_customCache[key] = (theme, stamp);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex) when (IsRecoverableFileLock(ex))
|
||||||
|
{
|
||||||
|
// Editor mid-save: keep the cached snapshot, leave the stamp
|
||||||
|
// alone so the next refresh retries automatically.
|
||||||
|
Plugin.Log.Debug($"Custom theme {Path.GetFileName(path)} is locked, keeping last known good");
|
||||||
|
if (cached.Theme is not null)
|
||||||
|
theme = cached.Theme;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// Logging passiert in Plugin.cs durch den Aufrufer; hier still
|
|
||||||
// ignorieren, damit ein einzelnes kaputtes JSON nicht alle
|
|
||||||
// Custom-Themes blockt.
|
|
||||||
_ = ex;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user