Macros in Org-Mode
Introduction
This guide was written by me, Brandon Nolet, in the context that I’ve begun to use macros in org-mode and I’m already loving them!
What is a Macro?
A macro, in the context of the org-mode mode of the Emacs text-editor, is essentially text expansion. With a macro, a small piece of specially crafted text gets expanded into a larger snippet of text.
Here is an example from a macro I recently created:
("Mocha-definition" . "{{{Mocha-ref}}} is a feature-rich JavaScript test framework running on Node.js")
In this macro, the text “Mocha-definition” gets replaced with “Mocha is a feature-rich JavaScript test framework running on Node.js”. As you’ll notice, there’s some curly braces in the text snippet. That’s another macro, that’s defined in the same notation as above.
The triple curly braces surrounding the text to be replaced is precisely how you signal to org-mode that that’s text to be replaced. To use the macro above, I would write {{{Mocha-definition}}}
.
Defining Macros
There are two ways you can define a macro. The first way is called an inline macro definition and the second way is called a global macro definition.
Inline Macros
The defining of an inline macro looks like the following:
#+MACRO: genera-definition Genera is a RESTful to-do list web server
Every inline macro starts with the standard in-buffer specification (the pound sign followed by the plus symbol). Following that you use the word macro to say that this in-buffer setting is going to be a macro.
Then the first string of characters following the colon and space, up until the second space, in this case genera-definition, defines the identifier for the macro. Another way to think of that is that this will be the text you’ll be replacing.
Macros are written like this: {{{genera-definition}}}
Whatever is that first string in the macro definition, will go inside three enclosed curly-braces. Anything following the second space will be the text that the macro is expanded to. {{{genera-definition}}}
will expand to Genera is a RESTful to-do list web server.
This type of macro definition is placed within any org-mode file and will only be useable or accessible from within the file the macro definition is placed.
The second type of macro definition, global, is usually defined in an elisp file that’s loaded with your init file. Mine is called macros.el
with .el
being the extension for files meant to be evaluated with Emacs.
Global Macros
The following is the syntax for the global definition of macros:
(setq org-export-global-macros '(("<text-to-be-replaced>" . "<text-snippet>")))
They are called global because they can be referenced in all buffers within emacs, provided that the macro has been loaded (more on how to do that later).
setq
, the first string in our global macro definition, is standard notation in the lisp (and in elisp) programming language for assigning a variable with the syntax (setq <variable> <value>)
. org-export-global-macros
is the name of the variable we’re assigning a value to. After that are the macros we’re defining.
As you can see, org-mode macros are essentially just a bunch of variables with values assigned to them. It’s a programming paradigm.
The quote is so that when org-mode is accessing the variable, it doesn’t get evaluated into other values. Again, it’s programming paradigms.
The double parentheses might be a little confusing, but it has a purpose. When defining multiple macros, you want to be able to separate them. The inner parentheses here are the singular macro definition whereas the outer ones are the “encapsulation” of all the macros you’ll be defining.
At the innermost strings, this is the definition of a macro. The first string (always enclosed in double quotes) is the text you’ll put inside the triple curly braces. Then a period, then in double quotes the text snippet that the macro should expand into.
Above is a single definition but if you wanted to include more, here’s an example of how you would do so:
(setq org-export-global-macros '(
("macro-1" . "this is the first text snippet")
("macro-2" . "this is the second text snippet")
))
The spacing, indentation, newlines, and whitespace are all stylistic and serve no functional purpose other than to make it more legible. As you can see, the notation is pretty simple and there’s no frills here.
Arguments
Arguably the best feature of macros is that they, as a virtue of being defined in a text-editor that’s also a programming language interpreter, can accept arguments. I think this may be best described with an example, as are most things in Emacs.
[...]
("genera-definition" . "Genera is a RESTful to-do list web server")
("define" . "{{{$1-definition}}}")
[...]
By defining the define macro as another macro, using the first argument variable, $1
, as the prefix, I can use one macro to chain to another macro. The second argument would be $2
and so on. For n arguments use $n
. How I would use the define macro is the following:
{{{define(genera)}}}
The first and only argument I pass, within the parentheses, to the define macro is “genera” and so the macro turns in to {{{genera-definition}}}
and then in turn that gets replaced with “Genera is a RESTful to-do list web server”.
Note: I learned this from @[email protected] here.
Loading Global Macros
Inline macros, because of their inline nature, do not need to separately be loaded. By virtue of defining them, they are already loaded. Global macros, however, must separately be defined and loaded.
One would usually put their macros in a specific file, separate from most of the settings. When Emacs opens up, it will check inside your home folder within the ~/.emacs.d/
directory for an init.el
file. This file, if already created, would contain any settings that you want to change, automatically, at startup. There are “other locations” for the init file, but this is the one I chose. In my case, I would put my macros.el
file inside ~/.emacs.d/
Within the init.el
file you would include the following statement:
(load "~/.emacs.d/macros.el")
This is assuming your file name is macros.el
. If the file your macros are contained in is named something different or in a different location, replace the value within the double quotes as necessary. Then, every time Emacs starts up the macros listed in macros.el
will be loaded into memory.
But what if you’re defining macros as you write? Not a problem. You can load the macros while you’re using Emacs by doing the following:
- Press
M-x
(that’s Alt+x) - Type
load-file
and pressRET
(that’s theEnter
button) - Type the absolute location of your
macros.el
file (ex:~/.emacs.d/macros.el
) and press enter.
If there are no errors with your macros.el
file, it should give you the message Loading <filename> (source)...done
.
Use Cases
You might be asking what the use of org-mode macros might be and you wouldn’t be the only one. There are many different use cases and they’re certainly not limited to programming. People from all types of backgrounds including authors, bloggers, programmers, editors, and pretty much any other profession that requires lots of writing, use org-mode macros for their text editing purposes.
The reason for this is that macros allow you to increase the efficiency of your writing. Imagine that you’re writing a technical document that refers to the same snippet of text over and over again. Being able to use a smaller piece of text to expand to that larger piece every time could save you several minutes to many hours depending on the size of the text.
What about when you’re writing blog posts and you want to link to previous ones, or other web pages? Being able to refer to those pages often using a smaller piece of text may make writing feeling less tedious.
The last two use cases are similar in nature but with similar motivations.
The first one is let’s say you’re writing a novel and you know the protagonist has a hometown but you’re not sure what to call it, if anything. So instead of writing “fake hometown” and coming back to it later, you write {{{protagonist-hometown}}}
. Any time you come to writing the protagonist’s hometown, you write the same macro until you finally come up with a name. Except now you only have to define the macro for it, either inline or globally, rather than having to go back and perform a search and replace all over your 400-page novel. This specific use case was brought to my attention by @[email protected].
The second one is that you’re challenging yourself to write a DnD adventure. Writing these adventures requires a more abstract level of thinking because you have to think of the variety of situations that your players might bring upon the DnD session. So in order to make things the most abstract, instead of writing “Barrbon” or “flail” you might write {{{village}}}
or {{{murder-weapon}}}
, respectively. This way you’re leaving a lot of room for the players’ creativity. This specific use case was brought to my attention by @[email protected].
I’m sure there’s many more use cases and I’d love to learn about (and maybe add) them. Feel free to toot me about them!
Conclusion
As you can see, org-mode macros can be very powerful when taking a moment to think about how you’ll define them. Use them often and don’t be afraid to ditch old ones when you find a better way to craft your system of macros.
Editor’s Note: If there are any errors or typos in this guide, please do not hesitate to email me at linuxliaison {at} fastmail (full stop) com.
Below I’ve attached all the macros I have defined as of the writing of this post:
(setq org-export-global-macros
'(
;;;;Misc
("newline" . "src_emacs-lisp[:results raw]{'\n'}")
("define" . "{{{$1-definition}}}")
("ref" . "{{{$1-ref}}}")
("source" . "{{{$1-source}}}")
("rvl" . "reader, viewer, and/or listener")
;;;;Hugo
;;;;Node Definitions
;;;; Genera
("generadev-source" . "[[https://gitea.bnolet.me/brandon/generalist/src/branch/develop][genera(develop branch)]]")
;;;;Reference Links
;;;Node
("Promise-ref" . "[[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise][Promise]]")
("asynchronous-code-ref" . "[[https://blog.risingstack.com/node-hero-async-programming-in-node-js/][asynchronous code]]")
;;;; Chai
("Chai-ref" . "[[https://www.chaijs.com/][Chai]]")
("Chaidocs-ref" . "[[https://www.chaijs.com/guide/][here]]")
;;;; Mocha
("Mocha-ref" . "[[https://mochajs.org/][Mocha]]")
("Mochadocs-ref" . "Mocha documentation [[https://mochajs.org/#table-of-contents][here]]")
;;;Emacs/org-mode
("init-location-ref" . "[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html][$1]]")
;;;Misc
("Emacs-ref" . "[[https://www.gnu.org/software/emacs/][Emacs]]")
("org-mode-ref" . "[[https://orgmode.org/][org-mode]]")
("Emacs-lisp-ref" . "[[https://en.wikipedia.org/wiki/Emacs_Lisp][elisp]]")
("eval-ref" . "[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval.html][eval]]")
("emsenn-ref" . "[[https://tenforward.social/@emsenn][@[email protected]]]")
("DnD-ref" . "[[https://en.wikipedia.org/wiki/Dungeons_%26_Dragons][DnD]]")
("me-mastodon-ref" . "[[https://fosstodon.org/@brandon][toot me]]")
("grok-ref" . "[[https://en.wikipedia.org/wiki/Grok][grok]]")
("OS-ref" . "[[https://en.wikipedia.org/wiki/Operating_system][OS]]")
("C-lang-ref" . "[[https://en.wikipedia.org/wiki/C_(programming_language)][C-lang]")
;;;;Definitions
("Mocha-definition" . "{{{Mocha}}} is a feature-rich JavaScript test framework running on Node.js")
("Chai-definition" . "{{{Chai}}} is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.")
("genera-definition" . "Genera is a RESTful to-do list web server")
("generalist-definition" . "GeneraList is a front-end for the Genera web server")
("emacs-definition" . "Emacs is a real-time, extensible, customizable, self-documenting, text editor")
("being-verbose-definition" . "using or expressing in more words than are needed")
("being-explicit-definition" . "stating clearly and in detail, leaving no room for confusion or doubt")
("knowledge-assumption-definition" . "when one, often unreasonably, assume that the reader, listener, or viewer is in possession of a given set of knowledge")
))