I've looked through the manual but can't seem to find the answer. Is it possible to suppress the TOC when exporting to ascii, but have it export to html?
1 Answer
EDIT: Simpler answer
This answer is really along the same lines that the answer in the executive summary follows, but it is a bit more streamlined to make it easier for a new Emacs user to use. The Org mode file will not need the code blocks because we are not going to advise the function. Instead, we are going to change the global value of the org-export-options-alist
variable in the init file. And since we are making changes to the init file, we are going to add the macro definition to it as well, so that it will always be available for every Org mode file.
That means that the only thing needed in the Org mode file to take advantage of this capability is the modified #+OPTIONS
line:
#+OPTIONS: toc:{{{if-ascii-else(nil, t)}}}
* section
foo
* bar
bar
The modifications to the init file are: 1) to modify the global value of the org-export-options-alist
value and 2) to add the definition of the if-ascii-else
macro.
The modification of org-export-options-alist
is bog-standard: we need to do it after ox.el[c]
is loaded, since the variable is defined in that file, but that's what eval-after-load
is for:
(eval-after-load 'ox
(setq org-export-options-alist
(cons '(:options "OPTIONS" nil nil parse)
org-export-options-alist)))
This modifies the global value of org-export-options-alist
but the effect on export is the same as in the "executive summary" answer.
Adding the macro similarly requires some code to be executed after the appropriate file is loaded:
(eval-after-load 'ox
(add-to-list 'org-export-global-macros
'("if-ascii-else" . "(eval (if (org-export-derived-backend-p org-export-current-backend 'ascii) $1 $2))"))
org-export-global-macros
is a list of macros available globally. See its doc string with C-h v org-export-global-macros
.
After adding these two snippets to your init file, restart Emacs so they take effect and export normally: C-c C-e t A
e.g
The advantage here is that you can copy these two snippets of code into your init file and run with it, without understanding what they do, but I hope you will study the rest of the answer and get at least a glimpse of how to do these things in the future.
[Original answer starts here.]
[This was harder than it should be, although there might be a better way to do it than what I provide below. I suggest you wait a few days to see if somebody comes up with a better answer.]
Executive Summary
Here's an Org mode file that will produce a TOC only if you are not exporting to ascii
:
#+MACRO: if-ascii-else (eval (if (org-export-derived-backend-p org-export-current-backend 'ascii) $1 $2))
#+OPTIONS: toc:{{{if-ascii-else(nil, t)}}}
Options should be {{{keyword(OPTIONS)}}}
* section
foo
* bar
bar
* Code :noexport:
#+name: advice-add
#+begin_src elisp
(defun my/org-export-dispatch (orig-func &optional arg)
(let ((org-export-options-alist (cons '(:options "OPTIONS" nil nil parse) org-export-options-alist)))
(funcall orig-func arg)))
(advice-add 'org-export-dispatch :around #'my/org-export-dispatch)
#+end_src
#+name: advice-remove
#+begin_src elisp
(advice-remove 'org-export-dispatch #'my/org-export-dispatch)
#+end_src
Before you press C-e
to to start the export dispatcher, you will have to press C-c C-c
on the first code block (the one named advice-add
). You can check that the advice has been added with C-h f org-export-dispatch
and checking the last line: it should say This function has :around advice: ‘my/org-export-dispatch’.
Then, you should be able to export normally: C-c C-e t A
e.g to export to an ascii
buffer or C-c C-e h o
to export to an HTML file and open it.
Afterwards, you can remove the advice by pressing C-c C-c
on the second code block (the one name advice-remove
) to put everything back to normal. You can also leave the advice in force, assuming it causes no problems - you can even add it to your init file, so that it will be available in the future, after you have tested it thoroughly. You will have to make sure that org-export-dispatch
is defined already before you can add the advice to it, either by doing a (require 'ox)
or by doing the advice-add
within an eval-after-load
. See this answer for some details on a similar problem.
All the gory details
The idea is to use a macro to set the toc
option to either nil
or t
, depending on whether the export backend is (derived from) ascii
or not. The following macro can do that:
#+MACRO: if-ascii-else (eval (if (org-export-derived-backend-p org-export-current-backend 'ascii) $1 $2))
#+OPTIONS: toc:{{{if-ascii-else(nil, t)}}}
Options should be {{{keyword(OPTIONS)}}}
This is a modified version of a similar macro that I cribbed from @fniessen's collection.
If you add the above to your file and try exporting, everything works, except that you still get a TOC even in ascii export. It turns out that macros are expanded in certain keywords, but not in the #+OPTIONS
keyword. I added the last line for debugging, to make sure that the replacement does happen properly (it does) in normal context. It just does not happen where it would matter.
It turns out the the org-export-as
function calls the macro processor as follows:
...
(org-macro-replace-all org-macro-templates parsed-keywords)
...
where parsed-keywords
is the list of keywords where macro expansion will happen (see the doc string of org-macro-replace-all
with C-h f org-macro-replace-all
).
So I modified that line to
...
(org-macro-replace-all org-macro-templates (cons "OPTIONS" parsed-keywords))
...
and lo and behold! the exported output did not contain the TOC.
The trick now is to do the above without modifying the code explicitly. There are probably multiple ways to do that, but I figured that if I could change whatever variable was used to calculate parsed-keywords
in org-export-dispatch
, maybe I could let
-bind the variable in an advice of the function.
The parsed-keywords
variable is let-bound
through the following code in org-export-as
:
...
(parsed-keywords
(delq nil
(mapcar (lambda (o) (and (eq (nth 4 o) 'parse) (nth 1 o)))
(append (org-export-get-all-options backend)
org-export-options-alist))))
...
which basically constructs a list of keyword names out of org-export-options-alist
. Looking at the latter with C-h v org-export-options-alist
shows that "OPTIONS" is indeed not included in it. org-export-options-alist
is not a simple list of names however, so the modification is a bit more complicated:
(let ((org-export-options-alist (cons '(:options "OPTIONS" nil nil parse) org-export-options-alist)))
(org-export-dispatch))
We cons an entry onto the front of org-export-options-alist
, let
-bind the result to org-export-options-alist
and call org-export-dispatch
with that let
-binding in place. And... it works: if you go to your Org mode buffer and use M-:
with the above expression, instead of using C-e
which just calls org-export-dispatch
, you can use t A
e.g. to export to a buffer and you get no TOC.
We can now use the advice
mechanism to add the above modification to org-export-dispatch
. I like adding a Code
section to the Org file itself (with a noexport
tag, so that it is ignored by the exporter) and add code blocks that do the modifications I want:
* Code :noexport
#+name: advice-add
#+begin_src elisp
(defun my/org-export-dispatch (orig-func &optional arg)
(let ((org-export-options-alist (cons '(:options "OPTIONS" nil nil parse) org-export-options-alist)))
(funcall orig-func arg)))
(advice-add 'org-export-dispatch :around #'my/org-export-dispatch)
#+end_src
Pressing C-c C-c
on the code block executes it, thereby defining the my/org-export-dispatch
function and then adding it as an around
advice to org-export-dispatch
. The my/org-export-dispatch
function let
-binds the org-export-options-alist
just as we did manually earlier, before calling the original function that was advised, i.e. org-export-dispatch
.
I hope the step-by-step helps but you might have to read it a couple of times. In the meantime, the executive summary should suffice.