3

I'm in the process of writing a LaTeX class using org-babel for the literate programming. In several places of the file .org file, I have to insert the release date of this class, at least in the tangled README.md file like this:

Release
-------
2021-05-10 v1.0.0-alpha

and in the tangled .cls source file of the class, like this:

\ProvidesExplPackage{my-nice-class}
{2021-05-10}
{v1.0.0-alpha}
{
  Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
}

Because I'm likely to forget to update this date before the actual release, I'd like it to be automatically updated each time the .org file is saved (in the same spirit as Inserting and Updating Copyrights).

Is it possible?

Edit

In order to make clearer my question, here is a minimal example of my org-mode file:

#+TITLE:     My Nice Class
#+LANGUAGE:  en
#+OPTIONS:   num:t toc:nil

#+LaTeX_CLASS: article
#+LaTeX_CLASS_OPTIONS: [11pt,english]

#+LaTeX_HEADER: \usepackage{babel}

#+PROPERTY: header-args :padline no :tangle my-nice-class.cls :exports both :noweb yes :eval always

#+begin_src markdown :tangle README.md :exports none
  my-nice-class
  =============================================================

  About
  -------
  This class is mine and nice!

  Release
  -------
  2021-05-10 v1.0.0-alpha
#+end_src

#+begin_src latex
\ProvidesExplClass{my-nice-class}
{2021-05-10}
{v1.0.0-alpha}
{
  A nice class
}
#+end_src
3
  • Are you tanglfing or exporting? The title says one thing, the body another. How exactly are you going from the Org mode file to the README.md and the .cls file? And how is that process related to saving the Org mode file? Would you be better off by using a Makefile to produce the artifacts and incorporate into the Makefile an extra step of template expansion (using something like jinja2)?
    – NickD
    May 10, 2021 at 14:46
  • 1
    @NickD Sorry for having been unclear. I'll edit my question to make it clearer. May 11, 2021 at 6:32
  • Thank you! The example makes things much clearer. I don't know off the top of my head the best way to deal with this, but it seems clear that @Tyler's macro answer will need some modifications before it can apply (maybe just passing variables to the source blocks might work? I don't know for sure). EDIT: I see he has updated his answer already, while I was asleep :-)
    – NickD
    May 11, 2021 at 13:54

1 Answer 1

3

Update: noweb references

I misunderstood your question. My original solution works for text outside of code blocks, when you asked for updating values inside code blocks. To accomplish this, we can use (org) Noweb Reference Syntax. This allows us to insert the value of one code block in another. Using your example, this looks like:

#+TITLE:     My Nice Class
#+LaTeX_CLASS: article
#+LaTeX_CLASS_OPTIONS: [11pt,english]
#+LaTeX_HEADER: \usepackage{babel}
#+PROPERTY: header-args :padline no :exports both :noweb yes :eval always

#+name: release-date
#+begin_src elisp :exports none
2021-05-10
#+end_src

#+name: release-version
#+begin_src elisp :exports none
v1.0.0-alpha
#+end_src

#+begin_src markdown :tangle README.md :exports none :noweb yes
  my-nice-class
  =============================================================

  About
  -------
  This class is mine and nice!

  Release
  -------
  <<release-date>> <<release-version>>
#+end_src

#+begin_src latex :noweb yes :tangle my-nice-class.cls
\ProvidesExplClass{my-nice-class}
{<<release-date>>}
{<<release-version>>}
{
  A nice class
}
#+end_src

You don't need to use elisp source blocks, any language will do. The key thing is the block needs to return the value you want inserted elsewhere. The blocks need to have names, which you can then reference with the <<<block-name>>> syntax.

Tangling this file with org-babel-tangle produces two code files.

my-nice-class.cls:

\ProvidesExplClass{my-nice-class}
{2021-05-10}
{v1.0.0-alpha}
{
  A nice class
}

And README.md:

my-nice-class
=============================================================

About
-------
This class is mine and nice!

Release
-------
2021-05-10 v1.0.0-alpha

I don't think there's a way to capture macro values, like date, in noweb references. You could use before-save-hook to call a custom function that searches for the reference-date block and updates the contents to accomplish this.

Original answer: org macros

I think this is one of the main use-cases for (org) Macro Replacement. In your case, you could do something like this:

#+MACRO: RELEASE 2021-05-10 v1.0.0-alpha

* Release
{{{RELEASE}}}

The macro is replaced every time you export your file. Use the {{{RELEASE}}} string anywhere you need it in your document. There are also ways to automatically generate dates, either the date the file was exported, or the DATE keyword from the org file header. See the manual page for more details.

Auto-updating on save

I use the following code to automatically update the DATE of my org-mode file:

(defun tws-org-set-time-file-property (property &optional anywhere pos)
  "Set the time file PROPERTY in the preamble.
When ANYWHERE is non-nil, search beyond the preamble.
If the position of the file PROPERTY has already been computed,
it can be passed in POS.

https://github.com/zaeph/.emacs.d/blob/615ac37be6bd78c37e967fdb43d28897a4116583/lisp/zp-org.el#L194"
  (when-let ((pos (or pos
                      (tws-org-find-time-file-property property))))
    (save-excursion
      (goto-char pos)
      (if (looking-at-p " ")
          (forward-char)
        (insert " "))
      (delete-region (point) (line-end-position))
      (let* ((now (format-time-string "[%Y-%m-%d %a %H:%M]")))
        (insert now)))))

(defun tws-org-set-date ()
  "Update the LAST_MODIFIED file property in the preamble.
https://github.com/zaeph/.emacs.d/blob/615ac37be6bd78c37e967fdb43d28897a4116583/lisp/zp-org.el#L212"
  (when (and (derived-mode-p 'org-mode)
             (buffer-modified-p))
    (tws-org-set-time-file-property "DATE")))

(add-hook 'before-save-hook 'tws-org-set-date nil t)

This will automatically update the #+DATE: header in your file after each save. That means you can use the {{{date}}} macro in your document anywhere you need to to update to the last-saved date.

8
  • Do macros get expanded when tangling a file? I'm not sure whether the OP needs that, but his mention of tangling has me confused.
    – NickD
    May 10, 2021 at 15:59
  • @NickD no, this only works for non-code parts of the document. You can use noweb references to insert the same bit of code in multiple source blocks, if that's necessary here. I think it isn't, but you're right, it's a little unclear from the question.
    – Tyler
    May 10, 2021 at 16:16
  • 1
    @Tyler Sorry for my not enough clear question. I hope it is clearer now and, indeed, what I want for the date is to update it in the code parts of the document. Could you elaborate your idea about noweb? May 11, 2021 at 6:50
  • 2
    @ctietze you can use code in your named code block to generate the current date, or any other value you want. However, this code is evaluated on export, not on save. If you want a value that is updated every time you save the file, a before-save-hook is likely the simplest way to go
    – Tyler
    May 13, 2021 at 15:20
  • 1
    With your example, the tangled my-nice-class.cls file contained spurious 2021-05-10 v1.0.0-alpha at its beginning. May 19, 2021 at 13:51

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.