From 97f1539a055268ee31ffb59e231b46b2207eba46 Mon Sep 17 00:00:00 2001
From: Mingtong Lin <mt.oss@fastmail.com>
Date: Thu, 27 Nov 2025 18:11:51 -0500
Subject: [PATCH 2/6] ob-tangle.el: Fix incorrect trimming behavior on tangled
 blocks

* lisp/ob-tangle.el (org-babel-tangle-single-block): Remove incorrect
`org-trim' calls.

* lisp/ob-emacs-lisp.el (org-babel-expand-body:elisp): Remove the
trailing empty line.

* lisp/ob-java.el (org-babel-expand-body:java): Only insert blank line
separator before the class declaration when a package or imports are
present.

* lisp/ob-fortran.el (org-babel-expand-body:fortran): Filter nil
includes/defines from the list before joining, so that absent
includes/defines do not produce spurious leading newlines.  Also
remove the stray trailing newline element from the list.

* lisp/ob-sql.el (org-babel-expand-body:sql):
* lisp/ob-sqlite.el (org-babel-expand-body:sqlite): Filter nil
prologue/epilogue from the list before joining, so that absent
prologue/epilogue do not produce spurious leading/trailing newlines.

All the trailing empty lines in tangled blocks are trimmed by
`org-babel-tangle-single-block', likely as a workaround for trailing
empty lines produced by various org-babel-expand-body:xxx functions.

It is the org-babel-expand-body:xxx functions that should be fixed,
rather than an overkilling remedy at tangle time.

Before:
,#+begin_src emacs-lisp :tangle xxx.el
"hello"

,#+end_src

tangles to

,#+begin_src emacs-lisp
"hello"
,#+end_src

Now:

,#+begin_src emacs-lisp
"hello"

,#+end_src

* testing/lisp/test-ob-tangle.el: Add test.

Reported-by: "Mingtong Lin" <mt.oss@fastmail.com>
Link: https://list.orgmode.org/f43360bb-dc8f-41bb-b40e-dfdd38ebb87b@app.fastmail.com/
---
 lisp/ob-emacs-lisp.el          |  2 +-
 lisp/ob-fortran.el             | 50 ++++++++++++++++++----------------
 lisp/ob-java.el                | 10 ++++++-
 lisp/ob-sql.el                 |  9 +++---
 lisp/ob-sqlite.el              |  9 +++---
 lisp/ob-tangle.el              |  4 +--
 testing/lisp/test-ob-tangle.el | 22 +++++++++++++++
 7 files changed, 68 insertions(+), 38 deletions(-)

diff --git a/lisp/ob-emacs-lisp.el b/lisp/ob-emacs-lisp.el
index 416f86185..cbbb54832 100644
--- a/lisp/ob-emacs-lisp.el
+++ b/lisp/ob-emacs-lisp.el
@@ -51,7 +51,7 @@ by `org-edit-src-code'.")
 	(print-length nil)
         (prologue (cdr (assq :prologue params)))
         (epilogue (cdr (assq :epilogue params))))
-    (if (null vars) (concat body "\n")
+    (if (null vars) body
       (format "(let (%s)\n%s%s%s\n)"
 	      (mapconcat
 	       (lambda (var)
diff --git a/lisp/ob-fortran.el b/lisp/ob-fortran.el
index ca457c325..9b6b96137 100644
--- a/lisp/ob-fortran.el
+++ b/lisp/ob-fortran.el
@@ -95,30 +95,32 @@ This function is called by `org-babel-execute-src-block'."
                   (or (cdr (assq :defines params))
                       (org-babel-read (org-entry-get nil "defines" t))))))
     (mapconcat 'identity
-	       (list
-		;; includes
-		(mapconcat
-		 (lambda (inc) (format "#include %s" inc))
-		 (if (listp includes) includes (list includes)) "\n")
-		;; defines
-		(mapconcat
-		 (lambda (inc) (format "#define %s" inc))
-		 (if (listp defines) defines (list defines)) "\n")
-		;; body
-		(if main-p
-		    (org-babel-fortran-ensure-main-wrap
-		     (concat
-		      ;; variables
-		      (mapconcat 'org-babel-fortran-var-to-fortran vars "\n")
-                      (and prologue (concat prologue "\n"))
-		      body
-                      (and prologue (concat prologue "\n")))
-		     params)
-                  (concat
-                   (and prologue (concat prologue "\n"))
-		   body
-                   (and epilogue (concat "\n" epilogue "\n"))))
-                "\n")
+	       (delq nil
+		(list
+		 ;; includes
+		 (when includes
+		   (mapconcat
+		    (lambda (inc) (format "#include %s" inc))
+		    (if (listp includes) includes (list includes)) "\n"))
+		 ;; defines
+		 (when defines
+		   (mapconcat
+		    (lambda (inc) (format "#define %s" inc))
+		    (if (listp defines) defines (list defines)) "\n"))
+		 ;; body
+		 (if main-p
+		     (org-babel-fortran-ensure-main-wrap
+		      (concat
+		       ;; variables
+		       (mapconcat 'org-babel-fortran-var-to-fortran vars "\n")
+                       (and prologue (concat prologue "\n"))
+		       body
+                       (and prologue (concat prologue "\n")))
+		      params)
+                   (concat
+                    (and prologue (concat prologue "\n"))
+		    body
+                    (and epilogue (concat "\n" epilogue "\n"))))))
                "\n")))
 
 (defun org-babel-fortran-ensure-main-wrap (body params)
diff --git a/lisp/ob-java.el b/lisp/ob-java.el
index ec0c99b5d..4298dee36 100644
--- a/lisp/ob-java.el
+++ b/lisp/ob-java.el
@@ -367,7 +367,8 @@ is simplest to expand the code block from the inside out."
       (when (not (re-search-forward org-babel-java--class-re nil t))
         (org-babel-java--move-past org-babel-java--package-re) ; if package is defined, move past it
         (org-babel-java--move-past org-babel-java--imports-re) ; if imports are defined, move past them
-        (insert (concat "\npublic class " (file-name-base classname) " {\n"))
+        (insert (concat (when (or packagename imports) "\n")
+                        "public class " (file-name-base classname) " {\n"))
         (indent-code-rigidly (point) (point-max) 4)
         (goto-char (point-max))
         (insert "\n}"))
@@ -391,6 +392,13 @@ is simplest to expand the code block from the inside out."
       (when (and packagename (not (re-search-forward org-babel-java--package-re nil t)))
         (insert (concat "package " packagename ";\n")))
 
+      ;; Remove leading empty lines, which may have been introduced
+      ;; by the class/main wrapping when there is no package or
+      ;; imports.
+      (goto-char (point-min))
+      (skip-chars-forward "\n")
+      (delete-region (point-min) (point))
+
       ;; return expanded body
       (buffer-string))))
 
diff --git a/lisp/ob-sql.el b/lisp/ob-sql.el
index 1d8234835..3f6fdf553 100644
--- a/lisp/ob-sql.el
+++ b/lisp/ob-sql.el
@@ -101,11 +101,10 @@
   (let ((prologue (cdr (assq :prologue params)))
 	(epilogue (cdr (assq :epilogue params))))
     (mapconcat 'identity
-               (list
-                prologue
-                (org-babel-sql-expand-vars
-                 body (org-babel--get-vars params))
-                epilogue)
+               (delq nil (list prologue
+                               (org-babel-sql-expand-vars
+                                body (org-babel--get-vars params))
+                               epilogue))
                "\n")))
 
 (defun org-babel-edit-prep:sql (info)
diff --git a/lisp/ob-sqlite.el b/lisp/ob-sqlite.el
index 35c0ed9ac..9996180fa 100644
--- a/lisp/ob-sqlite.el
+++ b/lisp/ob-sqlite.el
@@ -60,11 +60,10 @@
   (let ((prologue (cdr (assq :prologue params)))
 	(epilogue (cdr (assq :epilogue params))))
     (mapconcat 'identity
-               (list
-                prologue
-                (org-babel-sql-expand-vars
-                 body (org-babel--get-vars params) t)
-                epilogue)
+               (delq nil (list prologue
+                               (org-babel-sql-expand-vars
+                                body (org-babel--get-vars params) t)
+                               epilogue))
                "\n")))
 
 (defvar org-babel-sqlite3-command "sqlite3")
diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el
index 557104139..e61b2f0ed 100644
--- a/lisp/ob-tangle.el
+++ b/lisp/ob-tangle.el
@@ -659,8 +659,8 @@ non-nil, return the full association list to be used by
 		link
 		source-name
 		params
-		(if (org-src-preserve-indentation-p) (org-trim body t)
-		  (org-trim (org-remove-indentation body)))
+		(if (org-src-preserve-indentation-p) body
+		  (org-remove-indentation body))
 		comment)))
     (if only-this-block
         (let* ((file-names (org-babel-tangle--compute-targets file info)))
diff --git a/testing/lisp/test-ob-tangle.el b/testing/lisp/test-ob-tangle.el
index b71c32cb5..a560ed890 100644
--- a/testing/lisp/test-ob-tangle.el
+++ b/testing/lisp/test-ob-tangle.el
@@ -54,6 +54,28 @@
 ;;      (should-not (exp-p "no"))
 ;;      (should (exp-p "tangle"))))))
 
+(ert-deftest ob-tangle/tangle-preserve-trailing-empty-lines ()
+  "Test tangle with trailing empty lines."
+  (should
+   (equal
+    "1
+
+"
+    (org-test-with-temp-text-in-file
+    "
+#+header: :tangle \"test-ob-tangle.el\"
+#+begin_src emacs-lisp
+1
+
+#+end_src"
+    (unwind-protect
+        (progn
+          (org-babel-tangle)
+          (with-temp-buffer
+            (insert-file-contents "test-ob-tangle.el")
+            (buffer-string)))
+      (delete-file "test-ob-tangle.el"))))))
+
 (ert-deftest ob-tangle/no-excessive-id-insertion-on-tangle ()
   "Don't add IDs to headings without tangling code blocks."
   (org-test-at-id "ef06fd7f-012b-4fde-87a2-2ae91504ea7e"
-- 
2.39.5 (Apple Git-154)

