diff --git a/src/DiffEngineViewer.Tests/RevealFileTests.cs b/src/DiffEngineViewer.Tests/RevealFileTests.cs
new file mode 100644
index 00000000..59a7f0ac
--- /dev/null
+++ b/src/DiffEngineViewer.Tests/RevealFileTests.cs
@@ -0,0 +1,53 @@
+///
+/// What "reveal" opens. The path it is given is often one that does not exist: revealing a pending
+/// move points at the target, and for a snapshot being written for the first time nothing is there
+/// yet.
+///
+public class RevealFileTests :
+ IDisposable
+{
+ [Test]
+ public async Task A_file_that_is_there_is_selected()
+ {
+ var file = Path.Combine(directory, "sample.verified.txt");
+ File.WriteAllText(file, "");
+
+ var resolved = RevealFile.Resolve(file);
+
+ await Assert.That(resolved!.Value.Target).IsEqualTo(file);
+ await Assert.That(resolved.Value.Select).IsTrue();
+ }
+
+ ///
+ /// Explorer opens the default folder when asked to select a path that is not there - Documents,
+ /// nothing to do with the review - and open -R errors. The directory the file is about
+ /// to be written into is the useful answer.
+ ///
+ [Test]
+ public async Task A_file_that_is_not_there_yet_falls_back_to_its_directory()
+ {
+ var resolved = RevealFile.Resolve(Path.Combine(directory, "new.verified.txt"));
+
+ await Assert.That(resolved!.Value.Target).IsEqualTo(directory);
+ await Assert.That(resolved.Value.Select).IsFalse();
+ }
+
+ [Test]
+ public async Task Nothing_is_opened_for_a_path_with_no_directory_either()
+ {
+ var missing = Path.Combine(directory, "gone", "new.verified.txt");
+
+ await Assert.That(RevealFile.Resolve(missing)).IsNull();
+ }
+
+ public RevealFileTests()
+ {
+ directory = Path.Combine(Path.GetTempPath(), $"RevealFileTests_{Guid.NewGuid()}");
+ Directory.CreateDirectory(directory);
+ }
+
+ public void Dispose() =>
+ Directory.Delete(directory, true);
+
+ readonly string directory;
+}
diff --git a/src/DiffEngineViewer/RevealFile.cs b/src/DiffEngineViewer/RevealFile.cs
index 7532e5d6..fd9cc87f 100644
--- a/src/DiffEngineViewer/RevealFile.cs
+++ b/src/DiffEngineViewer/RevealFile.cs
@@ -7,11 +7,17 @@ static class RevealFile
{
public static void Show(string path)
{
+ if (Resolve(path) is not var (target, select))
+ {
+ return;
+ }
+
try
{
if (OperatingSystem.IsWindows())
{
- Process.Start(new ProcessStartInfo("explorer.exe", $"/select,\"{path}\"")
+ var arguments = select ? $"/select,\"{target}\"" : $"\"{target}\"";
+ Process.Start(new ProcessStartInfo("explorer.exe", arguments)
{
UseShellExecute = true
});
@@ -20,16 +26,48 @@ public static void Show(string path)
if (OperatingSystem.IsMacOS())
{
- Process.Start("open", ["-R", path]);
+ Process.Start("open", select ? ["-R", target] : [target]);
return;
}
// No cross-desktop way to select a file, so the directory is the target.
- Process.Start("xdg-open", [Path.GetDirectoryName(path) ?? path]);
+ Process.Start("xdg-open", [select ? Path.GetDirectoryName(target) ?? target : target]);
}
catch (Exception exception)
{
- Console.Error.WriteLine($"Could not open a file manager on {path}: {exception.Message}");
+ Console.Error.WriteLine($"Could not open a file manager on {target}: {exception.Message}");
+ }
+ }
+
+ ///
+ /// What to open, and whether the file manager can be asked to select it.
+ ///
+ /// A path that is not there cannot be selected, and revealing a move used to hand one over
+ /// whenever the snapshot was new: the target of the move is where the file is going, not
+ /// somewhere it has been. Explorer answers that by opening the default folder - Documents,
+ /// nothing to do with the review - and open -R by erroring. Linux happened to work,
+ /// having only ever opened the directory.
+ ///
+ ///
+ /// So the directory is what is shown for a path that is not there yet, which is where the
+ /// file is about to be written. Null when even that is absent, since there is nothing useful
+ /// left to open.
+ ///
+ ///
+ internal static (string Target, bool Select)? Resolve(string path)
+ {
+ if (File.Exists(path))
+ {
+ return (path, true);
+ }
+
+ var directory = Path.GetDirectoryName(path);
+ if (directory is {Length: > 0} &&
+ Directory.Exists(directory))
+ {
+ return (directory, false);
}
+
+ return null;
}
}