fix(save): a buffer with no file has no baseline to have moved - #723
Merged
Conversation
The external-change guard asks the disk "has this file changed since I last saw it?" before every overwrite, and answers "changed" when the read fails — the safe answer for a file the tab has written before, and the wrong question entirely for a path the Save dialog has only just named. Nothing is there to read yet, so the first save of every new document was refused: no file written, the buffer still dirty, and the conflict bar up on a tab whose Reload has no file to reload. Retrying under another name went down the same path and was refused the same way, so a new document could not be saved at all. An untitled buffer is a version OF nothing: there is no baseline for a third party to have moved, and nothing for the guard to protect. Saving onto a file that does exist is the one case that looks like an overwrite, and the Save dialog has already asked about replacing it — this check would be a worse second copy of that question, asked against a baseline this tab never had.
Collaborator
Author
|
@alecdotdev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
In 2.7.5, a new document cannot be saved. Ctrl+S on an untitled buffer opens the Save dialog, the user names the file, and nothing is written: the buffer stays dirty, the conflict bar goes up, and closing the tab asks about unsaved changes. Renaming and trying again is refused the same way, so the document has no exit but the clipboard. Reported by @PathGao on macOS 2.7.5; the path is platform-independent.
Files already on disk are unaffected — their baseline matches, so their saves go through as before.
Mechanism
fileDiffersFromBaseline(#698) compares the file on disk againsttab.originalContentbefore every overwrite, and treats an unreadable file as changed:"Unreadable means changed" is right for a file this tab has written before — mid-write or gone are both reasons to stop and ask. It is not a question that can be asked about a path the dialog produced one line earlier, which names no file by construction. And the bar it raises is unanswerable here:
resolveExternalChangeByReloadingreturns on!tab.path, so Reload does nothing, and only "keep mine" (allowOverwriteOnce) lets the next save through.What changed
fileDiffersFromBaselineanswersfalsefor a tab with no path. An untitled buffer is a version OF nothing, so there is no baseline for a third party to have moved.Considered instead: skipping the call in
saveContentwhen the dialog produced the path. Same condition (saveContentopens the dialog exactly when!tab.path), but it leaves the wrong answer inside the predicate for the next caller to find, and short-circuiting pastoverwriteAllowed.deletewould strand an authorization. Also considered treating "file does not exist" as unchanged in thecatch: that widens the hole for a tab whose file was deleted underneath it, which is a case the guard should keep refusing.Saving an untitled buffer onto a file that does exist is the one case that looks like an overwrite. The Save dialog has already asked "replace?" and the user answered it; this check would be a second, worse copy of that question, asked against a baseline this tab never had. That is what the second test pins.
Scope
Only the untitled case. The guard still refuses an overwrite when a tab's own file moved under it (third test), and Save As is untouched — it never had this check, which is why it kept working and is the workaround for anyone on 2.7.5.
Tests
scripts/untitledFirstSaveWrites.spec.ts, 3 tests. Reverting the fix and keeping the tests turns 2 of the 3 red (the first save, and the save onto an existing file); the third — a real tab still refusing a changed file — is green either way by design, since it is the behavior being preserved.The stub is why this shipped. Existing specs answer a read of an unknown path with
''instead of rejecting, so under test the missing file looked like an empty one and the guard compared two empty strings. This file's stub rejects the wayread_file_content_checkeddoes.Verification
npm auditclean,npm run check817 files / 0 errors,npm test987 pass,cargo testunchanged (no Rust touched).npm run test:vitestadds 3 passing and no new failures; that suite already has 14 failures on a cleanorigin/master(tab navigation history, window tags, settings persistence, undo history) which this branch neither causes nor fixes.Not verified by hand. A macOS release build of this branch exists and nobody has driven it yet; the manual check to run is new document, type, Ctrl+S, no rename, and see the file appear with the tab clean. Nothing in the path is platform-specific, and Windows and Linux were not tried either.