2026-08-17 11:52:48 +08:00
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
2026-08-17 12:11:27 +08:00
|
|
|
using System.Runtime.Serialization.Json;
|
2026-08-17 11:52:48 +08:00
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace Prophet.CoopWithDsh
|
|
|
|
|
{
|
|
|
|
|
internal sealed class SnapshotPublisher : IDisposable
|
|
|
|
|
{
|
2026-08-17 12:11:27 +08:00
|
|
|
private static readonly DataContractJsonSerializer Serializer = new DataContractJsonSerializer(typeof(OpenFileSnapshot));
|
2026-08-17 11:52:48 +08:00
|
|
|
private readonly object gate = new object();
|
|
|
|
|
internal string InstanceId { get; } = Guid.NewGuid().ToString("N");
|
|
|
|
|
internal string FilePath { get; }
|
|
|
|
|
|
|
|
|
|
internal SnapshotPublisher(string? localAppData = null)
|
|
|
|
|
{
|
|
|
|
|
var root = localAppData ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|
|
|
|
if (string.IsNullOrWhiteSpace(root)) throw new InvalidOperationException("LOCALAPPDATA is unavailable.");
|
|
|
|
|
var directory = Path.Combine(root, "Prophet", "dsh-plugin-coop-with-vs", "v1", "instances");
|
|
|
|
|
FilePath = Path.Combine(directory, $"visualstudio-{InstanceId}.json");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void Publish(OpenFileSnapshot snapshot)
|
|
|
|
|
{
|
|
|
|
|
lock (gate)
|
|
|
|
|
{
|
|
|
|
|
var directory = Path.GetDirectoryName(FilePath)!;
|
|
|
|
|
Directory.CreateDirectory(directory);
|
|
|
|
|
var temporary = FilePath + "." + Guid.NewGuid().ToString("N") + ".tmp";
|
2026-08-17 12:11:27 +08:00
|
|
|
using (var stream = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
Serializer.WriteObject(stream, snapshot);
|
|
|
|
|
File.WriteAllText(temporary, Encoding.UTF8.GetString(stream.ToArray()) + Environment.NewLine, new UTF8Encoding(false));
|
|
|
|
|
}
|
2026-08-17 11:52:48 +08:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (File.Exists(FilePath)) File.Replace(temporary, FilePath, null);
|
|
|
|
|
else File.Move(temporary, FilePath);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
TryDelete(temporary);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose() => TryDelete(FilePath);
|
|
|
|
|
|
|
|
|
|
private static void TryDelete(string path)
|
|
|
|
|
{
|
|
|
|
|
try { if (File.Exists(path)) File.Delete(path); } catch { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|