0

Lets say for example that I have this todos.org:

* README
* Emacs Wayland
* Another Headline

How do I capture after README, like this:

* README
* New Entry
* Emacs Wayland
* Another Headline

I have tried file+regex and file+function but both capture as a child level 2. I would like to capture as level 1.

(add-to-list 'org-capture-templates
                 '("k"          ; key
                   "Test"      ; name
                   entry        ; type
                   ;;(file+regexp (lambda () (concat  org-directory "agenda/todos.org")) "README")
                   (file+function (lambda () (concat  org-directory "agenda/todos.org"))(lambda () (goto-char (point-min)) (re-search-forward "\\(README\\)" nil t 1)))
                   "* %?" :unnarrowed t :prepend nil :kill-buffer t))
1
  • Is the README headline empty or does it have contents (possibly a whole subtree)? If it has contents, hhe regex solutions would break that structure.
    – NickD
    Apr 23, 2023 at 2:49

3 Answers 3

0

The entry type creates a child of the target entry. You may want to use the plain type instead.

2
  • Thanks that worked, added outline-next-heading and it works like a charm. :D
    – Rick
    Apr 22, 2023 at 22:42
  • The entry type with a file target creates top-level headlines.
    – NickD
    Apr 23, 2023 at 2:56
1

Based on Nick's answer with some modifications. You can also send integer arguments to org-move-subtree-down to move it further down.

(defun my/org-move-subtree-down ()
"Helper function for org captures"
  (goto-char (point-min))
  (outline-next-heading)
  (org-move-subtree-down))

Uncomment :before-finalize to use the latest org version which provides this.

(setq my/capture-top-level 
        '("a" "Top level"
              entry
              (file "~/Documents/org/agenda/todos.org")
              "* %^{titles}\n\n%?"
              :prepend t
              :unnarrowed t
              ;;:before-finalize my/org-move-subtree-down)
        ))
(add-to-list 'org-capture-templates my/capture-top-level)

Only use this if you're not using :before-finalize, more of a hack but it works. Match any file name in which you want to run this (e.g., todos.org).

(add-hook 'org-capture-before-finalize-hook
          (lambda ()
            (let ((buff (replace-regexp-in-string "CAPTURE-" "" (buffer-name))))
              (when (string-match-p "todos.org" buff) (my/org-move-subtree-down)))))

Here's a slightly more idiomatic function to add to org-capture-before-finalize-hook - this is meant for older Org mode where the :before-finalize template property is not available.

While org-capture is running, it accumulates information about the capture in a dynamic variable org-capture-plist. One of the properties on that list is :target which is a list like this: (file "/home/rick/Documents/org/agenda/todos.org") - just like the entry in the template except that the path is fully expanded. So that can be used to get the filename, without having to munge the buffer name of the capture buffer:

(defun my/before-finalize-func ()
  (let ((fname (cadr (plist-get org-capture-plist :target))))
     (when (and fname (string= fname (expand-file-name "~/Documents/org/agenda/todos.org"))
        (my/org-move-subtree-down))))

(add-hook 'org-capture-before-finalize-hook #'my/before-finalize-func)

Untested and may be missing some error checking.

9
  • One other promising avenue of investigation I think is to use org-refile instead of org-move-subtree-down. I was going to try to do that next, but I'm feeling a bit burnt out ATM, so I'll take a break before attempting that :-)
    – NickD
    Apr 25, 2023 at 3:04
  • Yea that's a possibility. You know what's funny that after all this I can't use :before-finalize because I'm getting constant errors:Warning (org-element-cache): org-element--cache: Org parser error in capture-org.el::8508. Resetting. The error was: (error "rx ‘**’ range error"). If I want to use it I need to figure out in what commit they added :before-finailize then figure out how to configure straight to that :commit and hope for no errors. My org built-in is 9.5.5 the latest is 9.7-pre which gives me errors but gives me access to :begin-finalize.
    – Rick
    Apr 25, 2023 at 3:19
  • I've led you on a merry chase, haven't I? Sorry about that. The cache has caused problems in the past, but @yantar92 (the chief maintainer IIUC, but he is not active on Emacs SE) has improved it substantially and with current upstream, there have been no reports of breakage AFAIK. I know that he is very interested in getting such bugs squashed so I'd encourage you to reach out with the problems you encounter on the Org mode ML.
    – NickD
    Apr 25, 2023 at 3:34
  • I'll have to do that. I think it may be that the package maintainers are not up to date with the latest org or something wrong in my config that broke with the org update. Who knows, eventually I need to figure that out if I want to update my org package and use the new keys in org-capture-templates.
    – Rick
    Apr 25, 2023 at 3:41
  • I added a slightly more idiomatic function for the normal hook at the end. Feel free to revert/delete/modify as you see fit.
    – NickD
    Apr 25, 2023 at 3:53
0

I am assuming that the README section has contents, not just a headline. I'm also assuming that you want the new entry to be the second top-level headline, following the README section.

Here's a capture template that does that:

    (defun my/org-move-subtree-down ()
      (goto-char (point-min))
      (outline-next-heading)
      (org-move-subtree-down))

    (setq my/capture-top-level 
            '("z" "Top level"
                  entry
                  (file "/path/to/foo.org")
                  "* %?"
                  :prepend t
                  :before-finalize my/org-move-subtree-down))
    (add-to-list 'org-capture-templates my/capture-top-level)

The entry type with the file target creates top-level headlines, the :prepend t makes sure that the new entry goes to the top of the file and the :before-finalize makes sure that it is moved down past the next subtree at the same level.

Regex solutions are inadequate for dealing with a README section with contents: they would break the structure.


EDIT: As the OP points out in a comment,the template-specific :after-finalize hook was introduced in Org mode 9.6 and is not available in previous versions of Org mode. Something similar could be done in earlier versions by using the org-capture-after-finalize-hook but that is run after every capture, so the hook function would have to check which capture is running the hook and decide whether to do something or not. Although I haven't tried it, I think it's possible but it's certainly much more awkward than the template-specific hook.


EDIT: I had :after-finalize earlier but as the OP points out in the comments below, that is wrong (and I have now fixed it): it has to be :before-finalize so that it happens in the capture buffer; :after-finalize does not happen in the file where the capture ends up as I erroneously thought, it happens in the (probably completely unrelated) buffer where org-capture was invoked. My bad.

16
  • Thanks Nick. The after-finalize don't work, I think that's a config problem on my end. Works fine with M-X. The problem with your suggestion is it would work for my purpose but if I had README or WHATEVER at line 500, i cant call :after-finalize several times. file+olp would be a better solution or file+function ?
    – Rick
    Apr 23, 2023 at 20:50
  • :after-finalize should work with emacs -q (i.e. without any configuration on your part other than the template above). Once you get that working, you can replace org-move-subtree-down with a different function that does whatever you want. The point is that the place that a regexp finds is (probably) not a good place to put a subtree (it would break the structure). file+olp is meant for captures that go further down in the hierarchy and you need to specify the full "path": AFAIK, it does not accommodate top-level captures.
    – NickD
    Apr 23, 2023 at 23:00
  • 1
    I figured out what the problem was. I had to update to the latest org version which has the key after-finalize but even then I get this error Cannot move past superior level or buffer limit.
    – Rick
    Apr 23, 2023 at 23:45
  • 1
    Updated with straight, was using built in, hopefully no more random errors from latest org. I tried your template that you provided on a new org file. All I did was change the key to 1 and file to ~/nick.org.
    – Rick
    Apr 24, 2023 at 4:09
  • 1
    I used :before-finalize, as I mentioned previously. So no need for my hacky add-hook but it's there if I need to downgrade org. I take care of that with string-match-p. Put inside my/org-move-subtree-down the function (message "Inside %s" (buffer-name)), it will show that :after-finalize is not in the captured file. What does it show on your end? I added an answer.
    – Rick
    Apr 24, 2023 at 20:13

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.