Hello Rudy,

here's the new patch. I tried to add all your suggestions and advanced handling 
of prologue/epilogue to it.

I wrote the code in a way that no variables are redefined (all variables are 
defined only once - as if they are "immutable" - in case you don't want that I 
can rewrite the code which would make it a bit shorter ... but actually I 
prefer it as it is in the patch now for better clarity).

Let me know what you think.

Also: The test contains a prologue/epilogue comment (meaning a comment 
before/after the surrounding @startXXX @endXXX clause).

Best
Tim

---------------------------------------------------------------

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 prefix to decide whether to
wrap with @startuml/@enduml tags.

Changes:

- Parse body into 5 components: prologue, start tag, inner content,
end tag, and epilogue
- Only apply variable expansion to the inner diagram content, not
the entire body
- Require exact matching between @start<type> and @end<type> tags
(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 tags
- Preserve any content before the first @start tag (prologue) and
after the last @end tag (epilogue) without modification
(exception: trimming)

This allows for more complex PlantUML source blocks that may contain
configuration directives, comments, or multiple diagram types while
ensuring variable substitution only affects the actual diagram content.

Examples now supported:

- Content before/after diagram blocks
- Multiple diagram types (e.g. @startsalt/@endsalt, @startmindmap/@endmindmap)
- Proper handling of mismatched start/end tag pairs

---------------------------------------------------------------

On 28 Sep 2025, at 17:05, Rudolf Adamkovič wrote:

> Tim Hansinger [email protected] writes:
>
>> Everything's clear now. I'm travelling this weekend but will have time
>> to implement this next week.
>
> Thanks for the heads-up, Tim!
>
> As Ihor says, we are in no hurry here. :)
>
> In the meantime, enjoy the travels!
>
> Rudy
>
> "I have only made this letter longer because I have not had the time to
> make it shorter." --- Blaise Pascal, The Provincial Letters, 1657
>
> Rudolf Adamkovič [email protected] [he/him]
> http://adamkovic.org
From 48bcaef0324e553823975f6d1dffb9999c3f8c39 Mon Sep 17 00:00:00 2001
From: Tim Hansinger <[email protected]>
Date: Mon, 15 Sep 2025 17:52:15 +0200
Subject: [PATCH] ob-plantuml.el: Fix body parsing to handle @start/@end
 content

* 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 prefix to decide whether to
wrap with @startuml/@enduml tags.

Changes:
- Parse body into 5 components: prologue, start tag, inner content,
  end tag, and epilogue
- Only apply variable expansion to the inner diagram content, not
  the entire body
- Require exact matching between @start<type> and @end<type> tags
  (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 tags
- Preserve any content before the first @start tag (prologue) and
  after the last @end tag (epilogue) without modification
  (exception: trimming)

This allows for more complex PlantUML source blocks that may contain
configuration directives, comments, or multiple diagram types while
ensuring variable substitution only affects the actual diagram content.

Examples now supported:
- Content before/after diagram blocks
- Multiple diagram types (e.g. @startsalt/@endsalt, @startmindmap/@endmindmap)
- Proper handling of mismatched start/end tag pairs

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

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

diff --git a/lisp/ob-plantuml.el b/lisp/ob-plantuml.el
index 05a4f7263..48e61a741 100644
--- a/lisp/ob-plantuml.el
+++ b/lisp/ob-plantuml.el
@@ -95,20 +95,64 @@ are expected to be scalar variables."
 
 (defun org-babel-plantuml-make-body (body params)
   "Return PlantUML input string.
-
 BODY is the content of the source block and PARAMS is a property list
 of source block parameters.  This function relies on the
 `org-babel-expand-body:generic' function to extract `:var' entries
 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 pairs:
+- If a matching pair is found (e.g., @startuml/@enduml or @startsalt/@endsalt),
+  variable expansion is applied only to the content between the tags,
+  preserving any prologue and epilogue content outside the tags.
+- 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 tags after variable expansion."
+  (let* (
+         (default-start "@startuml")
+         (default-end "@enduml")
+         (start-regex "^[[:blank:]]*@start\\([a-zA-Z]+\\)[[:blank:]]*$")
+         (start-match-pos (string-match start-regex body))
+         (start-match-end (when start-match-pos
+                            (match-end 0)))
+         (start-type (when start-match-pos 
+                       (progn (string-match start-regex body start-match-pos)
+                              (match-string 1 body))))
+         (end-regex (when start-type
+                      (format "^[[:blank:]]*@end%s\\(?:[[:blank:]]*$\\)" 
start-type)))
+         (end-match-pos (when end-regex
+                          (string-match end-regex body start-match-end)))
+         (end-match-end (when end-match-pos
+                          (match-end 0)))
+         pro epi start end wrapped-body expanded-body)
+    
+    (if (and start-match-pos end-match-pos)
+        (progn
+          ;; Extract parts when matching @start/@end pair exists and trim 
whitespace
+          (setq pro (string-trim (substring body 0 start-match-pos))
+                start (string-trim (substring body start-match-pos 
start-match-end))
+                wrapped-body (string-trim (substring body start-match-end 
end-match-pos))
+                end (string-trim (substring body end-match-pos end-match-end))
+                epi (string-trim (substring body end-match-end))
+                expanded-body (org-babel-expand-body:generic wrapped-body
+                                                             params
+                                                             
(org-babel-variable-assignments:plantuml params))))
+      ;; No @start found OR @start found but no matching @end
+      (setq expanded-body (org-babel-expand-body:generic (string-trim body)
+                                                         params
+                                                         
(org-babel-variable-assignments:plantuml params))))
+    
+    (if wrapped-body
+        (string-join (remove "" (list pro
+                                      start
+                                      expanded-body
+                                      end
+                                      epi))
+                     "\n")
+      (string-join (remove "" (list default-start
+                                    expanded-body
+                                    default-end))
+                   "\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..b237be803 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-clauses-and-prologue-epilogue
 ()
+  "Test file output with input variable on BODY with @startxxx ... @endxxx 
clauses
+and prologue / epilogue comments."
+  (should
+   (string=
+    "'prologue comment
+@startuml
+!define CLASSNAME test_class
+class CLASSNAME
+@enduml
+'epilogue comment"
+    (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
+'prologue comment
+@startuml
+class CLASSNAME
+@enduml
+'epilogue comment
+#+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.0

Reply via email to