diff --git a/Coder.Editor/DocumentStore.cs b/Coder.Editor/DocumentStore.cs index 8d69eab..38b99c4 100644 --- a/Coder.Editor/DocumentStore.cs +++ b/Coder.Editor/DocumentStore.cs @@ -61,6 +61,13 @@ public DocumentResult Load(string path) return DocumentResult.Failed($"Could not read {path}: {ex.Message}"); } + // An interrupted save or a newly created file leaves nothing in it, and the deserializer + // refuses empty text as a bad argument, which would read to the user as a parse failure. + if (string.IsNullOrWhiteSpace(yaml)) + { + return DocumentResult.Failed($"{path} is empty."); + } + AstNode? root; try { diff --git a/Coder.Test/Editor/CoderEditorAppTests.cs b/Coder.Test/Editor/CoderEditorAppTests.cs index 562083a..e8f32fd 100644 --- a/Coder.Test/Editor/CoderEditorAppTests.cs +++ b/Coder.Test/Editor/CoderEditorAppTests.cs @@ -235,6 +235,27 @@ public void Open_ReportsAFileThatIsNotADocument() Assert.IsNull(app.DocumentPath); } + /// + /// Tests that an empty or whitespace-only file is reported as empty rather than as a document + /// the editor cannot parse, since an interrupted save leaves exactly that behind. + /// + /// What the file holds. + [TestMethod] + [DataRow("")] + [DataRow(" \n")] + public void Open_ReportsAnEmptyFile(string content) + { + DocumentStore store = NewStore(); + string empty = Path.Combine(root, "empty" + DocumentStore.Extension); + File.WriteAllText(empty, content); + CoderEditorApp app = NewApp(store); + + Assert.IsFalse(app.Open(empty)); + + Assert.AreEqual($"{empty} is empty.", app.Status); + Assert.IsNull(app.DocumentPath); + } + /// /// Tests that a write the filesystem refuses is reported rather than thrown, so a bad /// destination cannot take the editor down mid-session.