From acd65f4667e21c29664103c111d51e97856189bb Mon Sep 17 00:00:00 2001
From: Mingtong Lin <mt.oss@fastmail.com>
Date: Thu, 27 Nov 2025 14:42:16 -0500
Subject: [PATCH 1/6] ob-core.el: Fix incorrect interposition of Noweb prefixes

* lisp/ob-core.el (org-babel-expand-noweb-references): Fix incorrect
interposition of Noweb prefixes.

Currently, the following example

,#+name: a string
,#+begin_src emacs-lisp
"hello"
,#+end_src

,#+begin_src emacs-lisp :tangle tangle-bug.el :noweb yes :comments noweb
prefix<<a string>>
,#+end_src

tangles to

,#+begin_src emacs-lisp
;; [[file:org-babel-tangle.org::*Incorrect interposition of Noweb prefix when =:comments noweb=][Incorrect interposition of Noweb prefix when =:comments noweb=:2]]
prefix;; [[file:org-babel-tangle.org::a string][a string]]
prefix"hello"
prefix;; a string ends here
prefix
;; Incorrect interposition of Noweb prefix when =:comments noweb=:2 ends here
,#+end_src

where we get one more occurrence of "prefix" than expected at the end of
the expansion.  This is because org-babel-expand-noweb-references adds
an empty line after the Noweb comments blindly, *before* interposition
happens.

In this patch, the empty line is only added when needed (i.e., when
Noweb comments are desired, and the block body does not end with a Noweb
reference), and is done after the interposition.

* testing/lisp/test-ob.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-core.el         | 27 +++++++++++++++++++--------
 testing/lisp/test-ob.el | 22 +++++++++++++++++++++-
 2 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index a8ca1ccd0..1f48b66ac 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -3236,7 +3236,7 @@ block but are passed literally to the \"example-block\"."
 		         (let ((cs (org-babel-tangle-comment-links ,i)))
 		           (concat (c-wrap (car cs)) "\n"
 			           b "\n"
-			           (c-wrap (cadr cs)) "\n")))))
+			           (c-wrap (cadr cs)))))))
 	          (expand-references
 	            (ref)
 	            `(pcase (gethash ,ref org-babel-expand-noweb-references--cache)
@@ -3258,6 +3258,7 @@ block but are passed literally to the \"example-block\"."
 	                (error "Cannot resolve %s (see `org-babel-noweb-error-langs')"
 		               (org-babel-noweb-wrap ,ref)))
 	               (_ ""))))
+      (let ((result
       (replace-regexp-in-string
        noweb-re
        (lambda (m)
@@ -3326,13 +3327,23 @@ block but are passed literally to the \"example-block\"."
 			      (push info (gethash ref org-babel-expand-noweb-references--cache))))))
                        (puthash 'buffer-processed t org-babel-expand-noweb-references--cache)
 		       (expand-references id)))))
-	       ;; Interpose PREFIX between every line.
-               (if noweb-prefix
-		   (mapconcat #'identity
-			      (split-string expansion "[\n\r]")
-			      (concat "\n" prefix))
-                 expansion)))))
-       body t t 2))))
+	       (let ((interposed
+	              ;; Interpose PREFIX between every line.
+		      (if noweb-prefix
+			  (mapconcat #'identity
+				     (split-string expansion "[\n\r]")
+				     (concat "\n" prefix))
+			expansion)))
+		 ;; Make sure each end comment is on its own line.
+		 (if comment (concat interposed "\n")
+		   interposed))))))
+       body t t 2)))
+	(if (and comment
+		 (string-suffix-p org-babel-noweb-wrap-end body))
+	    ;; Strip the last "\n" if last thing in the result is the
+	    ;; Noweb expansion.
+	    (substring result 0 -1)
+	  result)))))
 
 (defun org-babel--script-escape-inner (str)
   (let (in-single in-double backslash out)
diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el
index f91681f9e..d5d5d69a3 100644
--- a/testing/lisp/test-ob.el
+++ b/testing/lisp/test-ob.el
@@ -985,7 +985,27 @@ x
 "
             (goto-char (point-min))
             (search-forward "begin_src")
-            (org-babel-expand-noweb-references nil nil :eval)))))
+            (org-babel-expand-noweb-references nil nil :eval))))
+  ;; Test interposition of Noweb prefix under :comments noweb.
+  (should
+   (org-test-with-temp-text-in-file
+ "* H
+#+name: inner
+#+begin_src emacs-lisp
+1
+#+end_src
+
+#+header: :comments noweb :noweb yes
+#+begin_src emacs-lisp<point>
+prefix<<inner>>
+#+end_src"
+ (let ((file (file-name-nondirectory (buffer-file-name))))
+   (equal
+    (format "prefix;; [[file:%s::inner][inner]]
+prefix1
+prefix;; inner ends here"
+            file file)
+    (org-babel-expand-noweb-references nil nil :eval))))))
 
 (ert-deftest test-ob/splitting-variable-lists-in-references ()
   (org-test-with-temp-text ""
-- 
2.39.5 (Apple Git-154)

