32

I draft documents in org-mode and export them to LaTeX, HTML, etc. as needed. I frequently use the tag :no_export: whenever I don't want a certain subtree to show up in the output.

What I also want is want is for the export to completely ignore certain subtree headings, but not to ignore then content of the subtree under that heading. In other words, certain subtrees should be exported as if their content were just a continuation of the parent, instead of having its own subtree.

I'm ideally imagining a tag like :no_title:. I want my org-mode outline to look more or less the way I am used to. But I would consider other approaches with similar results.

To illustrate, here is a MWE of a document drafted in org-mode.

* Chapter 1
** Section 1
*** Paragraph 1      :no_title:
First paragraph of text in the section.
*** Paragraph 2      :no_title:
Second paragraph of text in the section.
* Chapter 2
** Section 1
*** Subsection 1
**** Paragraph 1      :no_title:
First paragraph of text in the subsection.
**** Paragraph 2      :no_title:
Second paragraph of text in the subsection

When I export that currently, I get something like this:

1 Chapter 1

1.1 Section 1

1.1.1 Paragraph 1 :no_title:

First paragraph of text in the section.

1.1.2 Paragraph 2 :no_title:

Second paragraph of text in the section.

2 Chapter 2

2.1 Section 1

2.1.1 Subsection 1

◊ 2.1.1.1 Paragraph 1 :no_title:

First paragraph of text in the subsection.

But what I want is more like this:

1 Chapter 1

1.1 Section 1

First paragraph of text in the section.

Second paragraph of text in the section.

2 Chapter 2

2.1 Section 1

2.1.1 Subsection 1

First paragraph of text in the subsection.

Brian Z
  • 2,863
  • 16
  • 22

2 Answers2

34

This may be a preferred way (due to its simplicity) of accomplishing your goal -- it seems to have gotten buried in the comments of another answer:

  1. Add the following to your .emacs file:

     (require 'ox-extra)
     (ox-extras-activate '(ignore-headlines))
    
  2. Use the ignore tag on headlines you'd like to have ignored (while not ignoring their content)

NOTE: if you are using ELPA to install org-mode, you must add the org-mode repository to your list of ELPA repositories. See here for more details. After this is done, refresh the package list (M-x package-refresh-contents) and install the org-plus-contrib package (e.g. with M-x package-install). Don't forget to get rid of any old org-mode installations.

NickD
  • 27,023
  • 3
  • 23
  • 42
Mark
  • 828
  • 10
  • 15
  • 2
    this is the correct answer. – rasmus Oct 28 '15 at 22:02
  • I accepted this as the answer even though it will not work for me. I tried loading org-extra.el but some other function was still missing (I am stuck with Org 8.2.4). – Brian Z Oct 30 '15 at 02:30
  • I can't find the package ox-extra ... – Toothrot Oct 15 '16 at 09:41
  • 1
    @Toothrot it's in the `contrib/lisp` folder -- see http://orgmode.org/cgit.cgi/org-mode.git/tree/contrib/lisp/ox-extra.el -- in your emacs config file, you may need to add a line like `(add-to-list 'load-path "/path/to/contrib/lisp")`. – Mark Oct 17 '16 at 18:21
  • Note that this only words in org 8.3+. And you can't just grab ox-extra.el, which has org 8.3+ dependencies. You'll get errors on export about not being able to find necessary files. – Dodgie Jan 06 '17 at 05:00
  • @Mark I don't have this file anywhere, and I am using org 9.1.9 – Guilherme Salomé Aug 07 '18 at 20:06
  • @GuilhermeSalomé here it is: https://code.orgmode.org/bzg/org-mode/src/release_9.1.9/contrib/lisp/ox-extra.el -- if you don't see it anywhere, maybe you are installing a package that has contrib split off from core emacs lisp files. – Mark Aug 23 '18 at 18:39
  • It would be good if it were possible to exclude all headings without a :noignore: tag instead. – Toothrot Sep 03 '18 at 19:10
  • I see that there is no new paragraph inserted if using the `:ignore:` tag with this solution. Is this intentional? – gdkrmr Dec 13 '18 at 10:15
  • In case people are looking for the repository, org-contrib has been moved to [its own repository](https://git.sr.ht/~bzg/org-contrib) since the time this answer was written. – Mark Aug 26 '22 at 18:46
20

Yes, this is entirely possible. Add the following code to your init-file:

(defun org-remove-headlines (backend)
  "Remove headlines with :no_title: tag."
  (org-map-entries (lambda () (delete-region (point-at-bol) (point-at-eol)))
                   "no_title"))

(add-hook 'org-export-before-processing-hook #'org-remove-headlines)

Acknowledgments

This question has been asked on StackOverflow before (with a different focus and wording, which is probably why you weren't able to locate it when you searched the internet before posting here). The code above is a simplified version of the code in the accepted answer; it works for the new exporter introduced in org-mode 8.0 and is backend-agnostic.

Update

As discussed in the comments, the code above causes contents of entries tagged with :no_title: to disappear if they are preceded by an entry that is tagged to be excluded from the exported document. For example, exporting the following document will produce a document with a title and TOC, but no content:

#+EXCLUDE_TAGS: no_export

* No export                          :no_export:
  This is not meant for you to see.
* No title                           :no_title:
  You should see this.

The reason for this is that org-mode removes the headline tagged with :no_title: before it removes the entry whose headline is tagged with :no_export:. As a result, when it gets around to removing the no_export entry, it treats contents of the no_title entry as belonging to the preceding no_export entry. This makes perfect sense because there is no structural element (heading) that separates the contents of the original entries anymore.

To solve this problem, we can transfer responsibility for removing no_export entries to org-remove-headlines. This way, we make sure all no_export entries are gone before headlines are removed from no_title entries:

(defun org-remove-headlines (backend)
  "Remove headlines with :no_title: tag."
  (org-map-entries (lambda () (let ((beg (point)))
                                (outline-next-visible-heading 1)
                                (backward-char)
                                (delete-region beg (point))))
                   "no_export" tree)
  (org-map-entries (lambda () (delete-region (point-at-bol) (point-at-eol)))
                   "no_title"))

(add-hook 'org-export-before-processing-hook #'org-remove-headlines)
itsjeyd
  • 14,586
  • 3
  • 58
  • 87
  • I've been using this a lot, so thanks! But I noticed a problem... If a heading tagged with `no_export` heading comes right before one tagged with `no_title`, then the contents of the second heading are also not exported. – Brian Z Apr 07 '15 at 08:33
  • @BrianZ You're welcome! Are the `no_export` and `no_title` headings siblings (i.e., do they have the same number of leading stars)? If not, which of the two headings has more stars? – itsjeyd Apr 07 '15 at 19:47
  • They are siblings. An export of this toy example has no content at all for me (just title, table of contents etc.) http://pastebin.com/KaiK9qJm – Brian Z Apr 08 '15 at 02:35
  • @BrianZ OK, I think I know what's going on: `org-mode` removes the *headline* tagged with `:no_title:` *before* it removes the entry whose headline is tagged with `:no_export:`. As a result, when it gets around to removing the `no_export` entry, it treats contents of the `no_title` entry as belonging to the preceding `no_export` entry. This makes perfect sense because there is no structural element (heading) that separates the contents of the original entries anymore. (contd.) – itsjeyd Apr 08 '15 at 07:26
  • @BrianZ To work around this I tried removing `org-remove-headlines` from `org-export-before-processing-hook` and adding it to a number of different hooks that are run during the export process. Unfortunately, none of them seem to run at the right time for this specific use case. But there *is* another solution: By transferring responsibility for removing `no_export` entries to `org-remove-headlines`, we can make sure all `no_export` entries are gone *before* headlines are removed from `no_title` entries (see the updates to my original answer). – itsjeyd Apr 08 '15 at 07:38
  • Looks good... If there are no more comments here from me soon, then its working well :) – Brian Z Apr 08 '15 at 09:09
  • I think I'm seeing the exact reverse problem with the new approach. That is, `:no_title:` no longer works if it comes right after a `no_export`. Try this example: https://gist.githubusercontent.com/dowcet/855494937ac91c0b81c9/raw/5af666ab60331d93bcd21b85e33ba3c10b63f768/no_title.org – Brian Z Apr 11 '15 at 07:56
  • @BrianZ I can't reproduce that behavior, sorry. Did you try with `emacs -Q`? – itsjeyd Apr 11 '15 at 14:40
  • When I try that, at first I noticed that the `:no_title:` tag simply wasn't doing anything at all, until I tried evaluating the line `(add-hook 'org-export-before-processing-hook #'org-remove-headlines)` as well. Should that be necessary? It gave me some warnings like `Warning: defvar ignored because org-agenda-archives-mode is let-bound`, but otherwise, it worked after that. Not with my regular init though, so I'll have to try and figure out what is interfering. – Brian Z Apr 11 '15 at 16:21
  • 1
    @BrianZ By itself, `org-remove-headlines` is just a function; it won't help you much if it's never called from anywhere. So yes, you still need to evaluate the line `(add-hook 'org-export-before-processing-hook #'org-remove-headlines)` to enable the correct behavior. I probably should have been explicit about that in the edits to my original answer. As for the warnings you're seeing: I don't get those with vanilla Emacs 24.4 and version 8.2.10 of Org Mode. To get rid of the warning you mention you could try to `(require 'org-agenda)` before evaluating the `add-hook` line. – itsjeyd Apr 11 '15 at 18:42
  • @BrianZ Hi, I am having issues with this peace of code for the latest version of org-mode release_8.3.1-239-gd01560. It complains that the variable `tree` is undefined. However running org-mode 8.2.10 which is the default version does the unexpected. It would be really appreciated if you could give some advice here. The code snippet is really useful. – Sparx Sep 15 '15 at 12:29
  • The above should say "does the expected", which is to not export the headline when the no_title tag is there. For some reason I could not edit the comment. – Sparx Sep 15 '15 at 12:38
  • I've narrowed down the change happening between commit `da83bf5` and `b9b500a` for me in the org git source tree, the new one (with the error) being the former. Please let me know if I'm totally out of bounds here. At least for me loading up an emacs -q and an org-mode for a specific version generates different results and I'm not sure it's a bug or just a syntax change the documentation does not specify. `C-h f RET org-map-entries` is not helping. – Sparx Sep 15 '15 at 22:07
  • Maybe @itsjeyd can help? – Brian Z Sep 16 '15 at 12:19
  • 2
    This feature is also implemented as org contrib. Ref: http://orgmode.org/cgit.cgi/org-mode.git/plain/contrib/lisp/ox-extra.el – rudolfo.christ Oct 08 '15 at 08:47
  • @rudolfo.christ, the link is broken. – Toothrot Sep 03 '18 at 19:08
  • This exports the subtree properties, which are normally not exported. – Toothrot Sep 03 '18 at 19:19