-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protect against overwriting external changes #4734
Conversation
3735792
to
6a392b3
Compare
11fd349
to
43cbb1a
Compare
When saving, check that the file has not changed before our last known save before writing the file. Only overwrite external changes if asked to force.
43cbb1a
to
a17e53d
Compare
@@ -979,6 +1000,7 @@ impl Document { | |||
rev | |||
); | |||
self.last_saved_revision = rev; | |||
self.last_saved_time = SystemTime::now(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the file isn't saved, I don't think the last_saved_time should be set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking here is to mimic the way History handles revisions and last_saved_revision here. They have an initial value as if the file had been saved at the time it was opened, this avoids adding a lot of unnecessary handling for an Option<>.
} | ||
} | ||
} | ||
|
||
let mut file = File::create(&path).await?; | ||
to_writer(&mut file, encoding, &text).await?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should set save time here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this would be correct, or possible due to this happening inside the future, self would need to be captured. The state is only committed when the DocumentSavedEvent that is created here gets processed by the caller of the future. I decided to set the time in set_last_saved_revision() because there is no reason I can think of you'd want to call that without updating the time, and that ensures we do not forget to do it if more call sites are added for set_last_saved_revision().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently got bitten pretty badly by helix habing nor protection fkr this case and lost about a days work. I looked into a watchmen based solution (which is more accurate as it uses files to synchronize os events and also offers a lot for other features so it's something I want to look into in the future) but I concluded that we want mtime based overwrite protection for a couple of reasons:
- watchmen is an external program, this functionality is basic/important enough that it should work without watchmen
- watchmen is designed to watch a directories. We can use the root dir but this doesn't work when helix opens other random files with
:open
or with the LSP.
For these reasons I was looking to implement mtime based overwrite protection as a fallback. I got sidetracked and just found that there is an existing PR for this so it's a good thing I didn't already get started with this.
Implementation wise this is pretty simple and LGTM. I would have implemented this the same way. I think it makes sense to treat last_saved_time
the same as last_saved_rev
.
The only nitpick: last_saved_time
should be added to the Debug
implementation of Document
I think this should be configurable: I don't quite remember the context where this has happened to me previously (maybe it involved containers, virtual machines or external formatters) but if you're in a situation where mtime isn't reliable then you have to fight the editor to save files every time. |
Perhaps we could check if the buffer's content differs from the file's if the file was written at a later mtime? |
That sounds reasonable and something I didn't think about. Adding a simple configuration flag like |
That would work but I think that would add a bunch of extra overhead. Especially when writing large files so probably not ideal. I can't really conceive how you would get a false positive here (false negatives are possible because any fs operation is fundamentally racy but there is no way around that) but in the unlikely case they occur you can simply use |
@@ -322,6 +322,12 @@ impl AppBuilder { | |||
} | |||
} | |||
|
|||
pub async fn run_event_loop_to_idle(app: &mut Application) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub async fn run_event_loop_to_idle(app: &mut Application) { | |
pub async fn run_event_loop_until_idle(app: &mut Application) { |
matching app.event_loop_until_idle(...)
Since a lot of people are waiting on this feature, i took the liberty to apply requested feedback and merge with the latest @kov I have opened a PR against your branch, but the merge from master makes it a bit hard to read. You can also cherry-pick commits if you want and rebase everything on top of a recent master (the conflicts are trivial to solve). @pascalkuthe i can also open a new PR and rebase everything on top of the latest master, if that's simpler for maintainers. |
Since there wasn't repose here in a while you can just open a new PR that supersedes. Aslong as you keep the original commits around to give credit and give credit in the PR description that is fine. |
Alright, done in #5805 |
I'm closing this in favour of #5805 thank you =) |
When saving, check that the file has not changed before our last known save before writing the file. Only overwrite external changes if asked to force.
I've almost lost a bunch of work because I did it on a separate instance of helix in another terminal window, but had the same file opened on an earlier instance and saved the file without realizing. Thankfully I had a backup (a cat in another tab xD)