From f386ff795d4833cce02d57de921999284aadded3 Mon Sep 17 00:00:00 2001 From: Clément Delafargue Date: Wed, 8 Feb 2023 17:09:19 +0100 Subject: Check for external file modifications when writing (#5805) `:write` and other file-saving commands now check the file modification time before writing to protect against overwriting external changes. Co-authored-by: Gustavo Noronha Silva Co-authored-by: LeoniePhiline <22329650+LeoniePhiline@users.noreply.github.com> Co-authored-by: Pascal Kuthe --- helix-view/src/document.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'helix-view/src') diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 798b5400..d308d013 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -19,6 +19,7 @@ use std::future::Future; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; +use std::time::SystemTime; use helix_core::{ encoding, @@ -135,6 +136,10 @@ pub struct Document { pub savepoint: Option, + // Last time we wrote to the file. This will carry the time the file was last opened if there + // were no saves. + last_saved_time: SystemTime, + last_saved_revision: usize, version: i32, // should be usize? pub(crate) modified_since_accessed: bool, @@ -160,6 +165,7 @@ impl fmt::Debug for Document { .field("changes", &self.changes) .field("old_state", &self.old_state) // .field("history", &self.history) + .field("last_saved_time", &self.last_saved_time) .field("last_saved_revision", &self.last_saved_revision) .field("version", &self.version) .field("modified_since_accessed", &self.modified_since_accessed) @@ -382,6 +388,7 @@ impl Document { version: 0, history: Cell::new(History::default()), savepoint: None, + last_saved_time: SystemTime::now(), last_saved_revision: 0, modified_since_accessed: false, language_server: None, @@ -577,9 +584,11 @@ impl Document { let encoding = self.encoding; + let last_saved_time = self.last_saved_time; + // We encode the file according to the `Document`'s encoding. let future = async move { - use tokio::fs::File; + use tokio::{fs, fs::File}; if let Some(parent) = path.parent() { // TODO: display a prompt asking the user if the directories should be created if !parent.exists() { @@ -591,6 +600,17 @@ impl Document { } } + // Protect against overwriting changes made externally + if !force { + if let Ok(metadata) = fs::metadata(&path).await { + if let Ok(mtime) = metadata.modified() { + if last_saved_time < mtime { + bail!("file modified by an external process, use :w! to overwrite"); + } + } + } + } + let mut file = File::create(&path).await?; to_writer(&mut file, encoding, &text).await?; @@ -668,6 +688,8 @@ impl Document { self.append_changes_to_history(view); self.reset_modified(); + self.last_saved_time = SystemTime::now(); + self.detect_indent_and_line_ending(); match provider_registry.get_diff_base(&path) { @@ -1016,6 +1038,7 @@ impl Document { rev ); self.last_saved_revision = rev; + self.last_saved_time = SystemTime::now(); } /// Get the document's latest saved revision. -- cgit v1.2.3-70-g09d2