3

In my org mode file, I have defined some header-args.

In an emacs-lisp code I use org-entry-get-with-inheritanceto retrieve them. By example

(org-entry-get-with-inheritance "header-args:julia")

returns me this string:

":exports code :tangle-mode (identity #o555) :mkdirp yes :eval never :comments link :tangle yes :tangle ../../test/QuadraticSolvers/runtests.jl"

Now I want to get the value associated to the symbol ":tangle" (here ../../test/QuadraticSolvers/runtests.jl)

This can be done with a regex, but I guess it already exists an org-mode function for this task.

Do you know this function ?

2

2 Answers 2

3

If you are calling your program while your cursor is in the source block of interest, you can use org-babel-get-src-block-info to get all of the info. You can then get the alist of header args with (caddr (org-babel-get-src-block-info)) and use assq to get the :tangle entry:

(assq :tangle (caddr (org-babel-get-src-block-info)))

That should take care of all headers, whether they are attached to the source block or are defined as properties.

You could combine this with org-babel-find-named-block to do what you have described in your answer:

(defun my1-get-babel-tangled-file (name)
   (save-excursion
       (goto-char (org-babel-find-named-block name))
       (cdr (assq :tangle (caddr (org-babel-get-src-block-info))))))

Error handling is non-existent but the basic idea is sound, I think.

But this requires that you name your source blocks:

#+name: foo
#+begin_src julia
...
#+end_src

However, that is always a good idea IMO.

2

Thanks to NickD and Drew for their help & advice

I finally found a solution to my problem. Even if it is much longer than expected (meaning that there is certainly a shorter/more elegant solution)

The function that helped me is org-babel-parse-header-arguments. It parses the returned string from org-entry-get-with-inheritance and return an alist :

(org-babel-parse-header-arguments (org-entry-get-with-inheritance "header-args:julia"))

gives

((:exports . "code") (:tangle-mode . 365) (:mkdirp . "yes") (:eval . "never") (:comments . "link") (:tangle . "yes") (:tangle . "../../test/QuadraticSolvers/runtests.jl"))

Then I had to modify the rassq-delete-all as it uses eq, and not equal as required for strings.

I also had to manage the fact that the same :tangle symbol can be used several times with "yes", "no" or with the filename I am looking for.

Here is my code

(defun my1--assoc-delete-all-yes-no-cdr (alist)
  "Check ALIST `car', return `t' if its  `cdr' is `equal' to \"yes\" or \"no\""
  (let ((tocheck (cdr (car alist))))
    (or (equal tocheck "yes") (equal tocheck "no"))))

(defun my1-assoc-delete-all-yes-no-cdr (alist)
  "Delete from ALIST all elements whose cdr is `equal' to \"yes\" or \"no\"
Return the modified alist.
(inspired from `rassq-delete-all')"
  (while (my1--assoc-delete-all-yes-no-cdr alist)
    (setq alist (cdr alist)))
  (let ((tail alist) tail-cdr)
    (while (setq tail-cdr (cdr tail))
      (if (my1--assoc-delete-all-yes-no-cdr tail-cdr)
      (setcdr tail (cdr tail-cdr))
    (setq tail tail-cdr))))
  alist)

(defun my1--get-babel-header-argument-as-alist (lang symbol)
  "Return babel header argument as an alist
Example (my1--get-babel-header-argument-as-alist \"julia\" :tangle)."
  (let ((header-arg-lang (concat "header-args" ":" lang)))
    (assoc-delete-all
     symbol
     (org-babel-parse-header-arguments
      (org-entry-get-with-inheritance header-arg-lang)) (lambda (a b) (not (equal a b))))))

(defun my1-get-babel-tangled-file (lang)
  "Return tangled file from header-args (if any) for the given LANG
Exemple: (my1-get-babel-tangled-file \"julia\")"
  (cdr
   (car
    (my1-assoc-delete-all-yes-no-cdr
     (my1--get-babel-header-argument-as-alist lang :tangle)))))

Here is a demo, with more context. I use literate programming and tangle a lot of pieces of code. At some point (for unit tests by example), I want a generic script to do something with the tangled code. That is the reason why I wanted to programmatically retrieve the current :tangle.

* Demo
:PROPERTIES:
:header-args:julia: :tangle yes :tangle julia_code.jl :exports code :tangle-mode (identity #o555) :mkdirp yes :eval never :comments link 
:END:

#+begin_src julia
  # my julia code
#+end_src

Literate programming here... :)

#+begin_src julia
  println("hello world...")
#+end_src

Now do something with my julia code

#+begin_src sh :var file=(my1-get-babel-tangled-file "julia")"
echo "Do something with $file"
#+end_src

#+RESULTS:
: Do something with julia_code.jl
7
  • 1
    Note that :tangle yes :tangle /path/to/file is equivalent to :tangle /path/to/file: the last :tangle wins.
    – NickD
    Dec 1, 2022 at 18:00
  • @NickD thanks for the clarification. It is “unfortunate” that the last tangle wins but the assoc function returns the first occurrence. Maybe reversing the alist is an idea ? Dec 1, 2022 at 18:03
  • 1
    The advantage of org-babel-get-src-blockinfo is that it already has done all the appropriate reductions: you will only have one :tangle in its output, the operative one.
    – NickD
    Dec 1, 2022 at 18:11
  • I do agree but how to use it in my context ? I will dig a little more, I'm not very happy with my current solution, which is certainly over complex. Dec 1, 2022 at 18:17
  • 1
    It is not a "lesser evil": it is a "greater good" :-)
    – NickD
    Dec 1, 2022 at 18:46

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.