4

I often want to define a whole bunch of variables at the top of an org file. In some cases I think it would be really nice to define all the variables in a table. So far, the best solution I've come up with is this:

#+NAME: variables
| Variable | Value      | Description |
|----------+------------+-------------|
| user1    | alice      | Manager     |
| user2    | bob        | Developer   |

#+NAME: lookup
#+begin_src emacs-lisp :var data=variables var="a var name"
  (nth 1 (assoc var data))
#+end_src

#+begin_src sh :var user=lookup(var="user1")
 echo $user
#+end_src

#+RESULTS:
: alice

That works, but the syntax :var user=lookup(var="user1") is verbose, and it gets tiresome to call lookup in every :var header argument. I'm wondering if there is a way to register each variable and value in the table as though they were defined like this:

#+NAME: user1
: alice

#+NAME: user2
: bob

That way, you could write source blocks with simple and familiar :var header arguments, like:

#+begin_src sh :var user=user1
  echo $user
#+end_src

#+RESULTS:
: alice

So I guess my question is: is there a way to "register" the variables/values in the variables table in such a way that would let me use them like :var user=user1?

Or even just an alternative way to do something similar that you find works for you?

EDIT: I perused the org source code a bit; I don't see any super easy way to do this. Maybe the best idea would be to introduce a new header argument, like #+begin_src :tblvar user=user1, which resolves variables defined in a table.

2 Answers 2

5

I don't find this any easier than what you have already shown, but here are some different approaches that use advanced features (https://orgmode.org/manual/Advanced-features.html) of naming fields in tables. You have to put the $ in the first column to name these constants. Getting these into your variable is a little verbose, but explicit at least. The org-table-get-remote-range function returns a string of the value in parentheses, so I use read to turn it into lisp, then a car in the front to get the element from the list.

#+NAME: variables
|   | Value       | Description |
|---+-------------+-------------|
| $ | user1=alice | Manager     |
|---+-------------+-------------|
| $ | user2=bob   | Developer   |

#+begin_src sh :var user=(car (read (org-table-get-remote-range "variables" "$user2")))
 echo $user
#+end_src

#+RESULTS:
: bob

If having extra functions is on the table, you might do something like this:

#+BEGIN_SRC emacs-lisp
(defmacro ot (name field)
  `(car (read (org-table-get-remote-range ,(symbol-name name) , (symbol-name field)))))
#+END_SRC

so that it is a little shorter:

#+begin_src sh :var user=(ot variables $user2))
 echo $user
#+end_src

#+RESULTS:
: bob

The final version, which may not be a good idea is to load the variables in the table to a local variable space in the buffer. To do this, you have to get the variables by going to the table, analyzing it and then using setq-local to add them to the local space.

#+BEGIN_SRC emacs-lisp
(defun org-goto-named-table (name)
  (let ((pos (catch 'pos
           (cl-loop for table in  (org-element-map (org-element-parse-buffer) 'table 'identity)
            do
            (when (string= name (org-element-property :name table))
              (throw 'pos (org-element-property :contents-begin table)))))))
    (when pos (goto-char pos))))



(defun org-load-local-table-variables (name)
  (save-excursion
    (org-goto-named-table name)
    (org-table-analyze)
    (cl-loop for (name . value) in org-table-local-parameters
         do (eval `(setq-local ,(intern name) value)))
    org-table-local-parameters))
#+END_SRC

Then you need a src block to run the function that loads them:

#+BEGIN_SRC emacs-lisp
(org-load-local-table-variables "variables") 
#+END_SRC

#+RESULTS:
: ((user2 . bob) (user1 . alice))

And finally, you can use `, notation to use those var-names in your src-blocks. I leave it to you to decide if these variables should be prefixed, e.g instead of user2, maybe variables-user2.

#+begin_src sh :var user=`,user2
echo $user
#+end_src

#+RESULTS:
: bob

You might also consider variations on this theme where instead of using setq-local, you use the returned results of the org-load-local-table-variables and do look up by alist, etc...

1
  • Amazing! I am learning a lot from this. Commented Feb 22, 2021 at 17:57
1

I use #+constants: variable=value for this and then (org-table-get-constant "variable") to get the value for src blocks etc. The advantage is that constants can be used directly in tables with $variable.

3
  • 1
    I am missing an example. Where do you use the lisp part? In a separate code block? $variable, do you use in a table? - I voted for the Q:, I would like also for the answer...
    – jaromrax
    Commented Feb 13, 2021 at 9:43
  • 1
    The lisp part can be used in src blocks to set variables, as in :var myvar=(org-table-get-constant "variable") on the #+begin_src line. In tables, the $variable can be used in any formula where you would use, e.g., a reference to an entry in the table such as @3$5.
    – éric
    Commented Feb 13, 2021 at 10:28
  • Cool idea. It's definitely nice to be able to define several variables in one line using #+CONSTANTS. Commented Feb 18, 2021 at 20:08

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.