I am looking for a way to programmatically search for headings in the current subtree. More specifically, I have a vocabulary file, in which I store words I need to learn. Each word is a subtree in itself and has the property LAST_PRACTICED_DATE.

Interactively I would usually do the following:

  1. C-c a < < m
  2. Enter my search query, something like: LAST_PRACTICED_DATE<"<2023-09-24>"-TODO="DONE"
  3. Ret

And I would get the vocabulary that I did not yet mark "done" and that I practiced before some date, according to the property (for which I have a custom procedure for updating, which I mapped to C-x l, l for "last").

Here is an example of what a part of my document looks like:

* HSK 3                                                                         :hsk_3:3:

** well known                                                                  :wellknown:

*** Words

**** DONE bike / bicycle
:LAST_PRACTICED_DATE: [2023-10-01 Sun]
***** zìxíngchē
****** 自行车

**** VERIFY easy
:LAST_PRACTICED_DATE: [2023-10-02 Mon]
***** róngyì
****** 容易

** somewhat known                                                              :somewhatknown:

(more words here!)

** not known                                                                   :notknown:

(more words here!)

(VERIFY is a TODO status. I specified it at the top of the file.)

Now I would like to put all of that into an elisp source block instead.

Here is what I have come up with so far:

    (org-previous-visible-heading 1)
    (let ((elem (org-element-at-point)))
      (let ((start (org-element-property :begin elem))
            (end (org-element-property :end elem)))
        (message "start at: " (number-to-string start))
        (message "end at: " (number-to-string end))
        (narrow-to-region start end)
        (org-tags-view nil "LAST_PRACTICED_DATE<\"<2023-10-01 Sun>\"-TODO=\"DONE\"")))))

This I put in an elisp source block directly under the heading well known, so that it should narrow the search to that subtree.

However, when I run that, the search is not narrowed to the subtree, but instead searches the whole document or at least a higher level subtree instead and shows results from somewhat known or not known. Also maybe noteworthy is, that the calls to (message ...) result in only the text but no numbers being printed in the *Messages* buffer. But I don't know how to inspect the start and end values in other ways or how to print the whole elem.

I thought this code would work, because I found some snippet in the code of org-tags-view itself:

          (if (eq buffer org-agenda-restrict)
              (narrow-to-region org-agenda-restrict-begin
          (setq rtn (org-scan-tags 'agenda
          (setq rtnall (append rtnall rtn))))

So I thought narrowing before matching would do the trick.

It seems I am doing something wrong though.

How can I implement this procedure?

  • org-tags-view's doc string says: Show all headlines for all ‘org-agenda-files’ matching a TAGS criterion. so unless you restrict things further (to the current file, as well as to the current subtree), it's not going to give you what you want. Have you tried org-tree-to-indirect-buffer instead? I think it's much more likely to give you what you want. Or maybe org-match-sparse-tree instead of org-tags-view in your code above.
    – NickD
    Oct 5, 2023 at 4:16
  • @NickD Thanks for the pointers. Isn't org-tags-view invoked when I press C-c a < < m? I thought it should be somehow trivial to do the same programmatically. Maybe it is not. Oct 5, 2023 at 22:40
  • 1
    It is not: setting the restriction involves some voodoo that org-tags-view does not know anything about. You might want to check how org-agenda calls org-tags-view and how the restriction is set before it does that (there are global variables involved, including one that is attached as a property to the property list of org-agenda-files, which makes it a bit tricky to understand - and I don't quite understand it well enough to provide an answer at this point).
    – NickD
    Oct 10, 2023 at 15:12
  • 1
    I thought that you might be able to use org-agenda to call org-tags-view, since it knows how to set the resriction - see emacs.stackexchange.com/a/64606/14825 - but the problem there is that there is no way to pass the match argument to org-tags-view that way: you can only type it in interactively. So the two requirements (set the restriction and specify the match programatically) require a stretch that the code is not quite capable of making. Interesting problem!
    – NickD
    Oct 10, 2023 at 15:17
  • @NickD Thanks for that research. Guess I opened up a pretty deep rabit hole. Oct 10, 2023 at 23:44


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.