2

I am trying to set the default header parameters for org source blocks in plantuml-mode.

My goal is to generate the result file in the org attachment directory with a generic name by default, and display it as an inline image in the result drawer.

This is my first attempt:

(setq org-babel-default-header-args:plantuml
         `((:results . "file link") (:output-dir . ,(org-attach-dir-get-create)) (:file-ext . "png") (:file . "diagram.png") (:exports . "results")))

This works fine the first time, but then, the org-attach-dir string is prepended to the file path string at every code block execution.

So for example, if org-attach-dir-get-create returns "mydir/", the first time I execute the block, the :file value is mydir/diagram.png as intended. The second time I execute the block, I get mydir/mydir/diagram.png, the third time, mydir/mydir/mydir/diagram.png, and so on.

Please note that I am keeping file-ext, file, and output-dir as separated parameters in the hope of being able to override them individually if needed in specific cases.

I understood from that failure that the header-defaults are set at each block execution, so I tried to pass them all as a function:

(defun my/org-babel-plantuml-header-args ()
  "Set dynamic header arguments for PlantUML source blocks."
  (let ((output-dir (org-attach-dir-get-create)))
    `((:results . "file link")
      (:output-dir . ,output-dir)
      (:file-ext . "png")
      (:file . "diagram.png")
      (:exports . "results"))))

  (setq org-babel-default-header-args:plantuml
         (my/org-babel-plantuml-header-args))

This has exactly the same outcome. It does not make sense to me, but my tests are very stubborn at proving me wrong.

I would appreciate some help in gettng it to work, because I used some of my most advanced Elisp tricks for that one :)

=== EDIT ===

After further testing, I jumped to the wrong conclusion when blaming this buggy behavior on the dynamic setting of the output directory.

I can reproduce it every time I set both output-dir and file within the org-babel-default-header-args variable even with a static string.

Regarding my initial goal, which was to have the diagram files created in the attachment directory, and displayed in the buffer, it is not possible to achieve using the header default arguments.

As @NickD commented, any function used there to set an option is only called once upon setting the org-babel-default-header-args:plantuml variable. This approach even had other adverse effects as I am using an org config file.

I managed to achieve the same results by using a custom function in a Yasnippet snippet to build the output-dir string. It works fine, just has a bit of a cluttered header.

Sorry if this thread is a bit all over the place. If future readers are looking to achieve the same kind of thing, I would gladly share the snippet code, and the custom hook function to set the attachment tag to the appropriate org node. I am not posting it because it uses Org-roam which is a niche use case I think, and I think it is off-topic considering how the thread started.

4
  • There is no difference between the (setq org-bable-default-header-args:plantuml ...) in the first and second code block: in the first you set it to a manually constructed list, in the second you set it to the result of calling a function, but the function constructs the same list (in the sense of equal: they are not the same object, but they look identical and act identically), so the final values are the same in both cases. Does that make sense?
    – NickD
    Commented Sep 25 at 20:16
  • 1
    Thanks, but to be honest I am not sure I fully understand. I edited my post because my description of the bug I am trying to squash was not very explicit. I thought taking the org-attach-dir-get-create function out of the list would prevent it from being called at every block execution. Still in my mind, if the function is called at every block execution, it should always yield the same list, more specifically, the output-dir should not be affected in the same way by repeated block execution. Anyway, I am not set on this approach if it is not te way to go.
    – lyndhurst
    Commented Sep 25 at 20:52
  • 1
    I just re-tested after editing my comment, and it does not seem to be a problem with the dynamic setting of output-dir, setting it to a static string produces the same 'bug'. So I guess output-dir and file should not both be set or something like that. I have to test and research further...
    – lyndhurst
    Commented Sep 25 at 21:00
  • org-attach-dir-get-create is not called on every block evaluation: either way it is called once when the variable is set. So there is no difference between the methods.
    – NickD
    Commented Sep 26 at 13:16

2 Answers 2

2

Instead of setting org-babel-header-args:plantuml once-and-for-all, you can use an infile property:

:PROPERTIES:
:header-args:plantuml: :results file link :file diagram.png :output-dir (org-attach-dir-get-create) :file-ext png
:END:

* plantuml diagram

#+BEGIN_SRC plantuml
@startuml
(*) --> "First Activity"
-->[You can put also labels] "Second Activity"
--> (*)
@enduml
#+END_SRC

* Another plantuml diagram

#+BEGIN_SRC plantuml :file diagram1.png
@startuml
(*) --> "Start"
-->[You can put also labels] "Continue"
--> (*)
@enduml
#+END_SRC

Evaluating each block evaluates the call to org-attach-dir-get-create which adds an ID property to the section where the block is located and does not cause any infinite concatenation problems, even though both output-dir and file header args are defined. Note also that the second block overrides the file header arg from the property with its own.

I don't know how to do the same thing with the variable yet.

UPDATE: As mentioned in the other answer, it was not possible to "do the same thing with the variable" because of a bug in Org mode. See the EDIT in my other answer for the patch, which has now (2024-10-10) been committed to the Org mode git repo. If you install Org mode from git, it should be possible to do what you wanted by modifying the variable as you did originally.

1

Try this setting:

#+begin_src elisp :results drawer
   (setq org-babel-default-header-args:plantuml
            `((:results . "file link")
              (:file-ext . "png")
              (:file . ,(concat (org-attach-dir-get-create) "/diagram.png"))
              (:exports . "results")))
#+end_src

For some reason, it seems that org-babel-get-src-block-info concatenates the value of the :output-dir property and the value of the :file property, modifying the value of the :file property in the process. So if both are set, you get the infinite concatenation you observe.

This feels like a bug to me, but I have not investigated enough to convince me one way or the other yet.

EDIT: I submitted a bug report and patch to the Org mode ML. UPDATE: The patch has now been committed to the Org mode git repo. It should be available in an update shortly.

5
  • I did not have time to report back earlier today. As I already hinted yesterday in the comments, I reached the same conclusion that it might be a bug. As I am fairly new to Emacs, and using Doom Emacs, I would first blame it on me, then Doom, then Org, but if you can reproduce, then it is different. I updated my initial post as I understand this is the prefered method here. Thank you for your solution, I personally settled for a dynamic setting of the output-dir and a uml code block snippet for convenience. Please let me know If you reach a verdict on that suspected bug. Thanks.
    – lyndhurst
    Commented Sep 26 at 19:17
  • I actually have to go back over this, and find another way to set the directory. The function is messing with my config.org literate config file. It is trying to create a property drawer on top of the file.
    – lyndhurst
    Commented Sep 26 at 20:12
  • I do not understand fully what is happening yet, but it is not working at all actually. What happens is that an ID is created in my literate config when Doom Emacs loads, and this ID is set globally as the output directory. There is nothing dynamic about it. I have to research more and understand how others do it. Maybe I need to learn how to do it with a snippet if I want a default output directory different for each org node where I create a block.
    – lyndhurst
    Commented Sep 26 at 21:10
  • 1
    Read my last comment under your question.
    – NickD
    Commented Sep 26 at 21:28
  • You were right, I gave up on this approach. I came up with a workaround, so it is fine. Thanks.
    – lyndhurst
    Commented Sep 28 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.