1

I have a custom function written to replace the quotation mark key (") with a pair of curly double quotation marks around my cursor (“|”), bound to the " key. I have this enabled in org mode, with the exception of source code blocks using the predicate function (org-in-src-block-p). However, when I type inside a drawer, I get my curly variant of double quotes rather than the straight quotation mark. Since I mostly want straight quotes inside drawers, I attempted to find a similar predicate function for drawers, to exclude my function from drawers. However, I only found predicate functions (org-at-drawer-p, org-at-property-drawer-p) that test for being at a drawer, not for being in one.

Is there a predicate function for being in an org mode drawer?

2
  • What do you mean by *in* an org mode drawer? I thought I understood (more or less): in the drawer :FOO:\nbar\n:END:\nwhere the newlines are as indicated, I thought that if point was anywhere in that string (e.g somewhere on bar), I would be in the drawer - but your function in the answer says no in that case. So please clarify what you mean by being in the drawer. Commented Aug 10 at 2:35
  • Your understanding is correct; I only tested my function with a property drawer, in which case my function does work for properties inside the property drawer. Sorry! Commented Aug 11 at 18:27

2 Answers 2

1

Here is an example Org mode file that includes a drawer:

* Introduction :FOO: Some text. The same works at the deeper levels, with this drawer having =:drawer:= line as BEGIN, this paragraph belonging to drawer CONTENTS, =:end:= representing END, and no BLANK after. :END: Some text. * Some heading Some more text. 

I assume that you want a function that says yes when point is anywhere from the initial colon in :FOO: to the newline after the final colon in :END:. Note that the function in your answer says yes only when point is on the :FOO: line or on the :END: line: anywhere in-between, it says no. Given your stated purpose, I think that's not going to work.

The best way to resolve such questions is not with regular expressions: it is by using the org-element parser. For the details of the syntax that the parser recognizes, see Org Syntax. The parser provides a function, org-element-at-point, which parses an Org mode buffer locally and finds the smallest syntactic element around point. It also provides functions that allow you to query that element and find information about it: its type, where it begins, where it ends, its parent, etc.

In your case, you want to find out whether you are inside a drawer. Assuming that you mean what I described above, all you have to do is ask the parser to parse the element around point and tell you whether it's a drawer.

Unfortunately, there are some complications:

  • If point is on the :FOO: line or the :END: line, org-element-at-point will tell you that you are in a drawer; but if point is somewhere in-between those lines, it will tell you that you are in a paragraph whose parent is a drawer.

  • The org-element parser considers trailing whitespace as part of the syntactic element it parsed. So the empty lines after the :END: line above count as part of the drawer. However, that's not what you want in your function (at least, as I understand your requirements). Fortunately, the parser includes the number of such trailing whitespace characters in the information it returns about the element, so we can compensate.

Here is an implementation of the required function:

(defun my/org-in-drawer-p () (let* ((el (org-element-at-point)) (parent (org-element-parent-element el)) (typ (org-element-type el)) (ptyp (org-element-type parent))) (when (member 'drawer (list typ ptyp)) ;; one of them is of type `drawer', so pick the right one ;; and find its beginning, end and the number of trailing whitespace characters (let* ((drawer (if (eq typ 'drawer) el parent)) (beg (org-element-property :begin drawer)) (end (org-element-property :end drawer)) (post-blank (org-element-property :post-blank drawer))) (and (>= (point) beg) (< (point) (- end post-blank))))))) 

We get the element around point and its parent and consider their types. If neither is drawer, then the function returns nil. If either is a drawer (and only one can be: drawers cannot include drawers - see the syntax document I linked to), then we pick the one that is a drawer, find its beginning, its end and the number of trailing whitespace characters and return t if and only if point is greater than, or equal to the beginning of the drawer and less than the end minus the number of trailing whitespace characters.

3
  • 1
    This works, except for the edge case of property drawers. Org mode seems to parse them as a different token than 'drawer, so to cover that edge case, you just have to also check for 'property-drawer. Commented Aug 11 at 18:48
  • Indeed - if you just want property drawers, then your version is simpler (and faster - org-element is fairly fast nowadays, thanks to the efforts of the Org maintainer, Ihor Radchenko, but I don't think it can compete with a simple regexp match.) OTOH, I still like the generality of using the parser and, as you say, a similar function that uses property-drawer instead of `drawer' in a couple of places could easily be written to check for property drawers. Commented Aug 13 at 2:26
  • I'd rather keep the two cases separate though: if you want to check for both, (or (my/org-in-drawer-p) (my/org-in-property-drawer-p)) would do at the cost of a second parse. Combining the two in one function precludes using it for checking just one of the cases. Commented Aug 13 at 2:29
0

I don’t know if there is a org-mode predicate function for being inside a drawer, but I have written my own mimicking how org-at-drawer-p was written. I think it covers all the cases, but I’m not sure.

(defun org-in-drawer-p () "Return t if cursor is in a drawer." (save-excursion (move-beginning-of-line 1) (looking-at org-property-re))) 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.