The function org-remove-empty-drawer-at
will do that. C-h v org-remove-empty-drawer-at
says:
(org-remove-empty-drawer-at POS)
Remove an empty drawer at position POS.
POS may also be a marker.
As long as your cursor is somewhere "in" the drawer, (org-remove-empty-drawer-at (point))
will work. So for a drawer like this:
:EMPTY:
:END:
Next para
your cursor (or the POS argument) can be anywhere from the initial :
to one before the N
of Next para
.
I found it using completion: C-h f org--drawer TAB
gave me 32 choices and, with a quick scan, I was able to zero-in to it fairly easily.
BTW, your idea about using the org-element
parser was correct: look at the source code of the function (by clicking on the filename at the end of the first line of the doc string when you use C-h f
). That is always a useful exercise if you want to learn more and go deeeper into programming these things.
Response to comment: What does POS mean?
POS
is the conventional name for an argument that should be a buffer position. Every character in a buffer has a position: an integer from 1 to the length of the buffer (in characters). You can move your cursor and do C-x =
to find out what position the cursor is on.
Read the Point
section in the Emacs manual with C-h i g(emacs) RET i point RET
for more info.
You can programmatically change point
by calling (goto-char POS)
where POS is any value from 1 to the length of the buffer.
Another way to change point
is by searching in the buffer for something: if the search succeeds, point
will be either just after (if you search forwards) or just before (if you search backwards) the first match.
In general, the second method (searching) is much more common than the first method (jumping to a given point directly), but both can come in handy. For example, when I want to test a function which is supposed to be called with point
at the leftmost start of a headline (a common situation when you are using org-map-entries
e.g.), then I set up an Org mode file like this:
...
** A second level headline
<stuff>
* Code
First position the cursor on the leftmost asterisk above and do
`C-x =` to get the value of `point` - let us says it is 37. Then I
write two code blocks like this:
#+begin_src elisp :results drawer
(defun my-function (...)
"This function assumes that it is called with `point`
at the beginning of a headline. It does something."
...)
#+end_src
#+begin_src elisp :results drawer
(save-excursion
(goto-char 37)
(my-function)
#+end_src
I do C-c C-c
on the first block to define the function and then C-c C-c
on the second block to test it: the goto-char
guarantees that when the function is called, point
will be at the beginning of a headline (the headline at position 37 in the file) and the precondition on the function is satisfied. The function will do something (which will probably be wrong), so I iterate by changing the definition of the function, doing C-c C-c
on the first block to redefine it with the changes and then C-c C-c
on the second block again to retest. Lather-rinse-repeat until I'm satisfied.
But searching will be much more common. For example, if you know the name of the drawer, you can do something like this:
...
;; Search for `:<name>:` - it's more specific than just `<name>'.
;; You want to specify `bound` to be the end of this entry
;; (that's just another buffer position). That will stop
;; the search when it reaches that point, since you don't want
;; to find a drawer of the same name in some later entry.
(if (search-forward (concat ":" drawer-name ":") bound t)
(org-remove-empty-drawer-at (point)))
If the search succeeds, point
will be just after the match, so it is going to be "inside" the drawer and you can call org-remove-empty-drawer-at
at that point: if the drawer is indeed empty, it will be deleted, otherwise nothing will happen. If you want to make sure that point
will not be disturbed by this shenanigan, you wrap the whole thing in a (save-excursion ...)
form. All these manipulations are very common, so they will become second-nature very quickly.
This sideways excursion has grown too long already, but I did want to mention a couple of other things:
POM
stands for position-or-marker
: markers are a data structure holding both a buffer and a position within that buffer, so they allow you to go to a particular place in another buffer. Many functions are polymorphic in that sense: they can take as argument either a simple position (with the buffer implicitly specified to be the current buffer) or a marker - they can then go do their thing either at the specified position of the current buffer or they can use marker-buffer
to get a buffer and marker-position
to get the position in that buffer to go do their thing.
The org-element
parser allows you to parse an Org mode file "locally" and get information about the element there. You can then use the parse tree that the parser generates to get useful information. For example, if point
is currently at the beginning of a headline (on the leftmost asterisk), you can use org-element-at-point
to generate the parse tree for that element and use org-element-property
to retrieve information from the parse tree - e.g. the position of the end of the section that starts with that headline, so you can limit a search (as in the example above). A typical code example goes like this:
...
(let* ((headline (org-element-at-point))
(end (org-element-property :contents-end headline)))
;; check to make sure that we are *really* at a headline
(when (eq (org-element-type headline) 'headline)
(if (search (concat ":" drawer-name ":") end t)
;; we found it
(org-remove-empty-drawer-at (point)))))
...
Hope this helps, but there is no substitute for reading code and then trying things out: that's how you gain experience.
org-remove-empty-drawer-at
? You have to give it a position or a marker.(org-remove-empty-drawer-at (point))
. The function is well named I am surprised I missed it while searching through callables. I cannot mark a comment as an accepted answer though...