Hello Rudy,

here's the new patch which includes the change and the new test.

Best
Tim


---

* lisp/ob-plantuml.el (org-babel-plantuml-make-body):
Refactor org-babel-plantuml-make-body to properly parse and preserve
content around PlantUML diagram blocks.

Previously, the function would apply variable expansion to the entire
body content and only check for @start keyword to decide whether to
wrap with @startuml/@enduml tags.

Changes:
- Parse body into 4 components: pre-body, keyword-type (e.g., uml), inner-body,
  and post-body
- Only apply variable expansion to the inner-body, not the entire body
- Require exact matching between @start<type> and @end<type> keywords
  (e.g., @startuml must pair with @enduml, not @endsalt)
- If @start<type> exists but no matching @end<type> is found, treat
  the entire body as diagram content and wrap with default
  keywords (@startuml ... @enduml)
- Preserve any content before the first @start keyword (pre-body) and
  after the last @end keyword (post-body) without modification
  (except trimming)

This ensures variable substitution only affects the actual diagram
content.

Examples now supported:
- Content before/after diagram blocks
- Proper handling of mismatched start/end keyword pairs

* testing/lisp/test-ob-plantuml.el
(test-ob-plantuml/single-var-on-body-with-start-end-keywords-and-content-outside-the-keywords):
Added an additional test for aboves use case (var definition on a BODY
with pre-existing @startxxx ... @endxxx keywords and pre-body / post-body 
content).


On 16 Feb 2026, at 10:30, Rudolf Adamkovič wrote:

> Tim Hansinger <[email protected]> writes:
>
>> Hello Rudy,
>
> Tim, hi!
>
>> sorry for the long time it took.
>
> Looks like everyone is busy this winter.  Me too! :)
>
>> I haven't created a patch yet but thought I would just send the code
>> [...] Main changes to your code are: [...]
>
> Great work!  Love the use back-references.  Smart!
>
>> I tested it thoroughly and couldn't find any issues with this code.
>
> I would be great to capture (at least) "the spirit" of the change in
> `test-ob-plantuml.el' so we avoid future regressions.
>
>> Would appreciate your feedback but I believe it is quite clean.
>
> It looks great.
>
>> If you like we could of course move the whole (rx ...) part directly
>> into the (_ (string-match regex body)) part so we don't define a
>> variable regex... but other than that I wouldn't change much.
>
> Your call.  As always, maintainability is the top priority.
>
> Rudy
> --
> "It is no paradox to say that in our most theoretical moods we may be
> nearest to our most practical applications."
>
> --- Alfred North Whitehead, 1861-1947
>
> Rudolf Adamkovič <[email protected]> [he/him]
> http://adamkovic.org
From e4e51a3426e059c795a1a0673e90af6af5afb268 Mon Sep 17 00:00:00 2001
From: Tim Hansinger <[email protected]>
Date: Mon, 23 Feb 2026 20:18:12 +0100
Subject: [PATCH] ob-plantuml.el: Fix body parsing to handle @start/@end
 keywords

* lisp/ob-plantuml.el (org-babel-plantuml-make-body):
Refactor org-babel-plantuml-make-body to properly parse and preserve
content around PlantUML diagram blocks.

Previously, the function would apply variable expansion to the entire
body content and only check for @start keyword to decide whether to
wrap with @startuml/@enduml tags.

Changes:
- Parse body into 4 components: pre-body, keyword-type (e.g., uml), inner-body,
  and post-body
- Only apply variable expansion to the inner-body, not the entire body
- Require exact matching between @start<type> and @end<type> keywords
  (e.g., @startuml must pair with @enduml, not @endsalt)
- If @start<type> exists but no matching @end<type> is found, treat
  the entire body as diagram content and wrap with default
  keywords (@startuml ... @enduml)
- Preserve any content before the first @start keyword (pre-body) and
  after the last @end keyword (post-body) without modification
  (except trimming)

This ensures variable substitution only affects the actual diagram
content.

Examples now supported:
- Content before/after diagram blocks
- Proper handling of mismatched start/end keyword pairs

* testing/lisp/test-ob-plantuml.el
(test-ob-plantuml/single-var-on-body-with-start-end-keywords-and-content-outside-the-keywords):
Added an additional test for aboves use case (var definition on a BODY
with pre-existing @startxxx ... @endxxx keywords and pre-body / post-body 
content).

Reported-by: "Tim Hansinger" <[email protected]>
Link: 
https://list.orgmode.org/[email protected]/
---
 lisp/ob-plantuml.el              | 48 +++++++++++++++++++++++++++-----
 testing/lisp/test-ob-plantuml.el | 30 ++++++++++++++++++++
 2 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/lisp/ob-plantuml.el b/lisp/ob-plantuml.el
index 93b183b96..1b499d057 100644
--- a/lisp/ob-plantuml.el
+++ b/lisp/ob-plantuml.el
@@ -102,13 +102,47 @@ of source block parameters.  This function relies on the
 from PARAMS and on the `org-babel-variable-assignments:plantuml'
 function to convert variables to PlantUML assignments.
 
-If BODY does not contain @startXXX ... @endXXX clauses, @startuml
-... @enduml will be added."
-  (let ((full-body
-        (org-babel-expand-body:generic
-         body params (org-babel-variable-assignments:plantuml params))))
-    (if (string-prefix-p "@start" body t) full-body
-      (format "@startuml\n%s\n@enduml" full-body))))
+The function parses BODY to find matching @startXXX ... @endXXX keyword pairs:
+- If a matching pair is found (e.g., @startuml/@enduml or @startsalt/@endsalt),
+  variable expansion is applied only to the content between the keywords,
+  preserving any content outside the keywords.
+- If @startXXX is found but no matching @endXXX, or if no @startXXX is found,
+  the entire BODY is treated as diagram content and wrapped with
+  @startuml ... @enduml keywords after variable expansion."
+  (let* ((regex (rx
+                 (group (zero-or-more anything))
+                 line-start
+                 (zero-or-more blank)
+                 "@start"
+                 (group (one-or-more (not whitespace)))
+                 (zero-or-more blank)
+                 line-end
+                 (zero-or-one whitespace)
+                 (group (zero-or-more anychar))
+                 line-start
+                 (zero-or-more blank)
+                 "@end"
+                 (backref 2)
+                 (zero-or-more blank)
+                 line-end
+                 (group (zero-or-more anything))))
+         (_ (string-match regex body))
+         (pre-body (string-trim (or (match-string 1 body) "")))
+         (keyword-type (or (match-string 2 body) "uml"))
+         (inner-body (string-trim (or (match-string 3 body) body)))
+         (post-body (string-trim (or (match-string 4 body) ""))))
+
+    (string-join
+     (remove ""
+             (list pre-body
+                   (string-join (list "@start" keyword-type))
+                   (org-babel-expand-body:generic
+                    inner-body
+                    params
+                    (org-babel-variable-assignments:plantuml params))
+                   (string-join (list "@end" keyword-type))
+                   post-body))
+     "\n")))
 
 (defun org-babel-execute:plantuml (body params)
   "Execute a block of plantuml code with org-babel.
diff --git a/testing/lisp/test-ob-plantuml.el b/testing/lisp/test-ob-plantuml.el
index b45d38be6..6e05f8c58 100644
--- a/testing/lisp/test-ob-plantuml.el
+++ b/testing/lisp/test-ob-plantuml.el
@@ -45,6 +45,36 @@ class CLASSNAME
           (car src-block-info)
           (car (cdr src-block-info)))))))))
 
+(ert-deftest 
test-ob-plantuml/single-var-on-body-with-start-end-keywords-and-content-outside-the-keywords
 ()
+  "Test file output with input variable on BODY with @startXXX ... @endXXX 
keywords
+and pre-body / post-body content."
+  (should
+   (string=
+    "pre-body content
+@startuml
+!define CLASSNAME test_class
+class CLASSNAME
+@enduml
+post-body content"
+    (let ((org-plantuml-jar-path nil))
+      (org-test-with-temp-text
+       "#+name: variable_value
+: test_class
+
+#+header: :file tmp.puml
+#+header: :var CLASSNAME=variable_value
+#+begin_src plantuml
+pre-body content
+@startuml
+class CLASSNAME
+@enduml
+post-body content
+#+end_src"
+       (org-babel-next-src-block)
+       (let ((src-block-info (cdr (org-babel-get-src-block-info))))
+        (org-babel-plantuml-make-body
+         (car src-block-info)
+         (car (cdr src-block-info)))))))))
 
 (ert-deftest test-ob-plantuml/prologue ()
   "Test file output with prologue."
-- 
2.51.2

Reply via email to