From faa6736d29191bbaebee62f948e513a9755b7c06 Mon Sep 17 00:00:00 2001 From: Isaac Mills Date: Fri, 30 May 2025 10:16:47 -0600 Subject: [PATCH] Add ability to configure atomic saving --- helix-core/src/editor_config.rs | 11 +++++++++++ helix-view/src/document.rs | 10 +++++++++- helix-view/src/editor.rs | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/helix-core/src/editor_config.rs b/helix-core/src/editor_config.rs index 714f577c5..7515dd231 100644 --- a/helix-core/src/editor_config.rs +++ b/helix-core/src/editor_config.rs @@ -34,6 +34,7 @@ pub struct EditorConfig { // pub spelling_language: Option, pub trim_trailing_whitespace: Option, pub insert_final_newline: Option, + pub atomic_save: Option, pub max_line_length: Option, } @@ -159,6 +160,13 @@ impl EditorConfig { "false" => Some(false), _ => None, }); + let atomic_save = pairs + .get("atomic_save") + .and_then(|value| match value.as_ref() { + "true" => Some(true), + "false" => Some(false), + _ => None, + }); // This option is not in the spec but is supported by some editors. // let max_line_length = pairs @@ -172,6 +180,7 @@ impl EditorConfig { encoding, trim_trailing_whitespace, insert_final_newline, + atomic_save, max_line_length, } } @@ -305,6 +314,7 @@ mod test { [docs/**.txt] insert_final_newline = true + atomic_write = true "#; assert_eq!( @@ -326,6 +336,7 @@ mod test { EditorConfig { indent_style: Some(IndentStyle::Spaces(4)), insert_final_newline: Some(true), + atomic_save: Some(true), ..Default::default() } ); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index fb89e2e0c..458016492 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -980,6 +980,7 @@ impl Document { // mark changes up to now as saved let current_rev = self.get_current_revision(); let doc_id = self.id(); + let atomic_write = self.atomic_save(); let encoding_with_bom_info = (self.encoding, self.has_bom); let last_saved_time = self.last_saved_time; @@ -1029,7 +1030,7 @@ impl Document { // Assume it is a hardlink to prevent data loss if the metadata cant be read (e.g. on certain Windows configurations) let is_hardlink = helix_stdx::faccess::hardlink_count(&write_path).unwrap_or(2) > 1; - let backup = if path.exists() { + let backup = if path.exists() && atomic_write { let path_ = write_path.clone(); // hacks: we use tempfile to handle the complex task of creating // non clobbered temporary path for us we don't want @@ -1914,6 +1915,13 @@ impl Document { .unwrap_or_else(|| self.config.load().insert_final_newline) } + /// Whether the document should write it's contents to a backup file, then rename the backup to the target file when saving. This prevents data loss when saving, but may confuse some file watching/hot reloading programs. + pub fn atomic_save(&self) -> bool { + self.editor_config + .atomic_save + .unwrap_or_else(|| self.config.load().atomic_save) + } + /// Whether the document should trim whitespace preceding line endings on save. pub fn trim_trailing_whitespace(&self) -> bool { self.editor_config diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index bc811b88b..3a0b48d05 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -346,6 +346,8 @@ pub struct Config { pub default_line_ending: LineEndingConfig, /// Whether to automatically insert a trailing line-ending on write if missing. Defaults to `true`. pub insert_final_newline: bool, + /// Whether the document should write it's contents to a backup file, then rename the backup to the target file when saving. This prevents data loss when saving, but may confuse some file watching/hot reloading programs. Defaults to `true` + pub atomic_save: bool, /// Whether to automatically remove all trailing line-endings after the final one on write. /// Defaults to `false`. pub trim_final_newlines: bool, @@ -1017,6 +1019,7 @@ impl Default for Config { workspace_lsp_roots: Vec::new(), default_line_ending: LineEndingConfig::default(), insert_final_newline: true, + atomic_save: true, trim_final_newlines: false, trim_trailing_whitespace: false, smart_tab: Some(SmartTabConfig::default()),