From 67879a1e5b82de38383939c43df8e24b37d54019 Mon Sep 17 00:00:00 2001
From: Michael Davis <mcarsondavis@gmail.com>
Date: Mon, 10 Mar 2025 10:30:00 -0400
Subject: [PATCH] Avoid inserting final newlines in empty files

This matches the behavior described by the EditorConfig spec for its
`insert_final_newline` option:

> Editors must not insert newlines in empty files when saving those
> files, even if `insert_final_newline = true`.

Co-authored-by: Axlefublr <101342105+Axlefublr@users.noreply.github.com>
---
 helix-term/src/commands/typed.rs        |  2 +-
 helix-term/tests/test/commands/write.rs | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 07374f77..9661689c 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -415,7 +415,7 @@ fn trim_final_newlines(doc: &mut Document, view_id: ViewId) {
 /// Ensure that the document is terminated with a line ending.
 fn insert_final_newline(doc: &mut Document, view_id: ViewId) {
     let text = doc.text();
-    if line_ending::get_line_ending(&text.slice(..)).is_none() {
+    if text.len_chars() > 0 && line_ending::get_line_ending(&text.slice(..)).is_none() {
         let eof = Selection::point(text.len_chars());
         let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into());
         doc.apply(&insert, view_id);
diff --git a/helix-term/tests/test/commands/write.rs b/helix-term/tests/test/commands/write.rs
index 38ab643c..f7123f68 100644
--- a/helix-term/tests/test/commands/write.rs
+++ b/helix-term/tests/test/commands/write.rs
@@ -482,6 +482,21 @@ async fn test_write_insert_final_newline_added_if_missing() -> anyhow::Result<()
     Ok(())
 }
 
+#[tokio::test(flavor = "multi_thread")]
+async fn test_write_insert_final_newline_unchanged_if_empty() -> anyhow::Result<()> {
+    let mut file = tempfile::NamedTempFile::new()?;
+    let mut app = helpers::AppBuilder::new()
+        .with_file(file.path(), None)
+        .with_input_text("#[|]#")
+        .build()?;
+
+    test_key_sequence(&mut app, Some(":w<ret>"), None, false).await?;
+
+    helpers::assert_file_has_content(&mut file, "")?;
+
+    Ok(())
+}
+
 #[tokio::test(flavor = "multi_thread")]
 async fn test_write_insert_final_newline_unchanged_if_not_missing() -> anyhow::Result<()> {
     let mut file = tempfile::NamedTempFile::new()?;