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:
C-c a < < m
- Enter my search query, something like:
LAST_PRACTICED_DATE<"<2023-09-24>"-TODO="DONE"
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
:PROPERTIES:
:LAST_PRACTICED_DATE: [2023-10-01 Sun]
:END:
***** zìxíngchē
****** 自行车
**** VERIFY easy
:PROPERTIES:
:LAST_PRACTICED_DATE: [2023-10-02 Mon]
:END:
***** 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:
(save-excursion
(save-restriction
(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:
(save-excursion
(save-restriction
(if (eq buffer org-agenda-restrict)
(narrow-to-region org-agenda-restrict-begin
org-agenda-restrict-end)
(widen))
(setq rtn (org-scan-tags 'agenda
matcher
org--matcher-tags-todo-only))
(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 triedorg-tree-to-indirect-buffer
instead? I think it's much more likely to give you what you want. Or maybeorg-match-sparse-tree
instead oforg-tags-view
in your code above.org-tags-view
invoked when I pressC-c a < < m
? I thought it should be somehow trivial to do the same programmatically. Maybe it is not.org-tags-view
does not know anything about. You might want to check howorg-agenda
callsorg-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 oforg-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).org-agenda
to callorg-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 thematch
argument toorg-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!