2

I need help writing a function that makes Org Capture prompt me of what it should place the capture to a specified header in my tasks org file (basically I need it to search through the specified org file and show the headers I have in that file and I want to be able to actively select which header I want to capture to). I found this code from 2017 but it's not working at all in the latest version org mode v9.4.4, I removed the org-capture-overriding-marker bit of the function as it kept throwing errors, doing so made it at least run (it would give me this error) but it doesn't open a prompt at all nor does it actually do anything. Anyone have any idea how I can rewrite the function to make it function properly?

(defun org-get-target-headline (&optional targets prompt)
  "Prompt for a location in an org file and jump to it.

This is for promping for refile targets when doing captures.
Targets are selected from `org-refile-targets'. If TARGETS is
given it temporarily overrides `org-refile-targets'. PROMPT will
replace the default prompt message.

If CAPTURE-LOC is is given, capture to that location instead of
prompting."
  (let ((org-refile-targets (or targets org-refile-targets))
        (prompt (or prompt "Capture Location")))
    (if org-capture-overriding-marker
        (org-goto-marker-or-bmk org-capture-overriding-marker)
      (org-refile t nil nil prompt))))

I'm using Doom Emacs with my setup and this is the capture template:

        ("a" "School Assignments")
        ("aa" "Assignments" entry
                (file+function "~/sync/org/Assignments.org" org-get-target-headline)
                "\n*TODO %? %<%I:%M %p> - n"
                ;; ,(dw/read-file-as-string "~/Notes/Templates/Daily.org")
                :clock-in :clock-resume
                :empty-lines 1)

Update: after replacing the original template, now I'm getting this error: let: Symbol’s value as variable is void: org-capture-overriding-marker.

Update 2: Now the prompt opens up with the changes

(defun org-get-target-headline (&optional targets prompt)
  "Prompt for a location in an org file and jump to it.

This is for promping for refile targets when doing captures.
Targets are selected from `org-refile-targets'. If TARGETS is
given it temporarily overrides `org-refile-targets'. PROMPT will
replace the default prompt message.

If CAPTURE-LOC is is given, capture to that location instead of
prompting."
  (let ((org-refile-targets (or targets org-refile-targets))
        (prompt (or prompt "Capture Location")))
    (org-refile t nil nil prompt)))

But then I get this apply: Wrong number of arguments: (0 . 0), 4 error Debugger:

Debugger entered--Lisp error: (wrong-number-of-arguments (0 . 0) 4)
  org-save-all-org-buffers(t nil nil "Capture Location")
  apply(org-save-all-org-buffers (t nil nil "Capture Location"))
  org-refile(t nil nil "Capture Location")
  (let ((org-refile-targets (or targets org-refile-targets)) (prompt (or prompt "Capture Location"))) (org-refile t nil nil prompt))
  org-get-target-headline()
  org-capture-set-target-location(nil)
  #f(compiled-function (&optional goto keys) "Capture something.\n\\<org-capture-mode-map>\nThis will let you select a template from `org-capture-templates', and\nthen file the newly captured information.  The text is immediately\ninserted at the target location, and an indirect buffer is shown where\nyou can edit it.  Pressing `\\[org-capture-finalize]' brings you back to the previous\nstate of Emacs, so that you can continue your work.\n\nWhen called interactively with a `\\[universal-argument]' prefix argument GOTO, don't\ncapture anything, just go to the file/headline where the selected\ntemplate stores its notes.\n\nWith a `\\[universal-argument] \\[universal-argument]' prefix argument, go to the last note stored.\n\nWhen called with a `C-0' (zero) prefix, insert a template at point.\n\nWhen called with a `C-1' (one) prefix, force prompting for a date when\na datetree entry is made.\n\nELisp programs can set KEYS to a string associated with a template\nin `org-capture-templates'.  In this case, interactive selection\nwill be bypassed.\n\nIf `org-capture-use-agenda-date' is non-nil, capturing from the\nagenda will use the date at point as the default date.  Then, a\n`C-1' prefix will tell the capture process to use the HH:MM time\nof the day at point (if any) or the current HH:MM time." (interactive "P") #<bytecode 0x1567eda85bd5>)(nil)
  apply(#f(compiled-function (&optional goto keys) "Capture something.\n\\<org-capture-mode-map>\nThis will let you select a template from `org-capture-templates', and\nthen file the newly captured information.  The text is immediately\ninserted at the target location, and an indirect buffer is shown where\nyou can edit it.  Pressing `\\[org-capture-finalize]' brings you back to the previous\nstate of Emacs, so that you can continue your work.\n\nWhen called interactively with a `\\[universal-argument]' prefix argument GOTO, don't\ncapture anything, just go to the file/headline where the selected\ntemplate stores its notes.\n\nWith a `\\[universal-argument] \\[universal-argument]' prefix argument, go to the last note stored.\n\nWhen called with a `C-0' (zero) prefix, insert a template at point.\n\nWhen called with a `C-1' (one) prefix, force prompting for a date when\na datetree entry is made.\n\nELisp programs can set KEYS to a string associated with a template\nin `org-capture-templates'.  In this case, interactive selection\nwill be bypassed.\n\nIf `org-capture-use-agenda-date' is non-nil, capturing from the\nagenda will use the date at point as the default date.  Then, a\n`C-1' prefix will tell the capture process to use the HH:MM time\nof the day at point (if any) or the current HH:MM time." (interactive "P") #<bytecode 0x1567eda85bd5>) nil)
  (let ((+file-templates-inhibit t)) (apply fn args))
  +file-templates-inhibit-in-org-capture-a(#f(compiled-function (&optional goto keys) "Capture something.\n\\<org-capture-mode-map>\nThis will let you select a template from `org-capture-templates', and\nthen file the newly captured information.  The text is immediately\ninserted at the target location, and an indirect buffer is shown where\nyou can edit it.  Pressing `\\[org-capture-finalize]' brings you back to the previous\nstate of Emacs, so that you can continue your work.\n\nWhen called interactively with a `\\[universal-argument]' prefix argument GOTO, don't\ncapture anything, just go to the file/headline where the selected\ntemplate stores its notes.\n\nWith a `\\[universal-argument] \\[universal-argument]' prefix argument, go to the last note stored.\n\nWhen called with a `C-0' (zero) prefix, insert a template at point.\n\nWhen called with a `C-1' (one) prefix, force prompting for a date when\na datetree entry is made.\n\nELisp programs can set KEYS to a string associated with a template\nin `org-capture-templates'.  In this case, interactive selection\nwill be bypassed.\n\nIf `org-capture-use-agenda-date' is non-nil, capturing from the\nagenda will use the date at point as the default date.  Then, a\n`C-1' prefix will tell the capture process to use the HH:MM time\nof the day at point (if any) or the current HH:MM time." (interactive "P") #<bytecode 0x1567eda85bd5>) nil)
  apply(+file-templates-inhibit-in-org-capture-a #f(compiled-function (&optional goto keys) "Capture something.\n\\<org-capture-mode-map>\nThis will let you select a template from `org-capture-templates', and\nthen file the newly captured information.  The text is immediately\ninserted at the target location, and an indirect buffer is shown where\nyou can edit it.  Pressing `\\[org-capture-finalize]' brings you back to the previous\nstate of Emacs, so that you can continue your work.\n\nWhen called interactively with a `\\[universal-argument]' prefix argument GOTO, don't\ncapture anything, just go to the file/headline where the selected\ntemplate stores its notes.\n\nWith a `\\[universal-argument] \\[universal-argument]' prefix argument, go to the last note stored.\n\nWhen called with a `C-0' (zero) prefix, insert a template at point.\n\nWhen called with a `C-1' (one) prefix, force prompting for a date when\na datetree entry is made.\n\nELisp programs can set KEYS to a string associated with a template\nin `org-capture-templates'.  In this case, interactive selection\nwill be bypassed.\n\nIf `org-capture-use-agenda-date' is non-nil, capturing from the\nagenda will use the date at point as the default date.  Then, a\n`C-1' prefix will tell the capture process to use the HH:MM time\nof the day at point (if any) or the current HH:MM time." (interactive "P") #<bytecode 0x1567eda85bd5>) nil)
  org-capture(nil)
  #<subr funcall-interactively>(org-capture nil)
  apply(#<subr funcall-interactively> (org-capture nil))
  funcall-interactively(org-capture nil)
  #<subr call-interactively>(org-capture record nil)
  apply(#<subr call-interactively> (org-capture record nil))
  explain-pause--wrap-call-interactively(#<subr call-interactively> org-capture record nil)
  apply(explain-pause--wrap-call-interactively #<subr call-interactively> (org-capture record nil))
  call-interactively(org-capture record nil)
  command-execute(org-capture record)
  execute-extended-command(nil "org-capture" nil)
  #<subr funcall-interactively>(execute-extended-command nil "org-capture" nil)
  apply(#<subr funcall-interactively> (execute-extended-command nil "org-capture" nil))
  funcall-interactively(execute-extended-command nil "org-capture" nil)
  #<subr call-interactively>(execute-extended-command nil nil)
  apply(#<subr call-interactively> (execute-extended-command nil nil))
  explain-pause--wrap-call-interactively(#<subr call-interactively> execute-extended-command nil nil)
  apply(explain-pause--wrap-call-interactively #<subr call-interactively> (execute-extended-command nil nil))
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)
4
  • You need to call this function from a capture entry in the variable org-capture-templates. Have you done that? What does your capture template look like? It should look something like the file+function one in the link you provided.
    – NickD
    Commented Mar 24, 2022 at 19:05
  • Yeah I have, but I get an error saying org-get-target-headline: "let" bindings can have only one value-form: org-refile-targets, (or targets org-refile-targets), (prompt (or prompt "Capture Location")) and this is what my capture template looks like: ("aa" "Assignments" entry (file+function "~/sync/org/Assignments.org" org-get-target-headline) "\n*TODO %? %<%I:%M %p> - n" ;; ,(dw/read-file-as-string "~/Notes/Templates/Daily.org") clock-in :clock-resume :empty-lines 1)
    – Nova Leary
    Commented Mar 24, 2022 at 19:13
  • Please add all relevant information to the question by clicking the Edit button under the question - not in a comment.
    – NickD
    Commented Mar 24, 2022 at 19:32
  • 1
    Ah, my apologies! I just went and added the the information to the question
    – Nova Leary
    Commented Mar 24, 2022 at 19:37

2 Answers 2

1

To include the function in the template, you just use the name of the function:

        ("a" "School Assignments")
        ("aa" "Assignments" entry
                (file+function "~/sync/org/Assignments.org" org-get-target-headline)
                "\n*TODO %? %<%I:%M %p> - n"
                ;; ,(dw/read-file-as-string "~/Notes/Templates/Daily.org")
                :clock-in :clock-resume
                :empty-lines 1)

The expression (org-get-target-headline) is a function call.

EDIT: The org-get-target-headline function is from an old post. Unfortunately, the variable org-capture-overriding-marker does not exist any longer, so you'll have to edit the function to get rid of it (probably as you did previously). My (untested) suggestion would be to simplify it to this (I've elided the beginning of the function which stays the same):

  ...
  (let ((org-refile-targets (or targets org-refile-targets))
        (prompt (or prompt "Capture Location")))
    (org-refile t nil nil prompt)))
5
  • Huh.. now I'm getting a different error after replacing what I have with your corrected version, will update my question with relevant information
    – Nova Leary
    Commented Mar 24, 2022 at 20:06
  • 1
    That variable does not exist any longer. I edited the answer with a suggestion.
    – NickD
    Commented Mar 24, 2022 at 21:11
  • Hey that actually made the prompt open up! but then I get a apply: Wrong number of arguments: (0 . 0), 4 error, gonna add the information of that into the question
    – Nova Leary
    Commented Mar 24, 2022 at 21:52
  • 1
    At this point, the original question has been answered. I suggest you ask another question with the new problem, referring back to this question for context.
    – NickD
    Commented Mar 24, 2022 at 23:01
  • 1
    Thank you for your help! I just went and made a new question :)
    – Nova Leary
    Commented Mar 24, 2022 at 23:40
0

file-function with a custom function using org-refile will crate a weird behavior, instead for org-mode version 9.x you can use do:

    (defun my/org-ask-headline-target (&optional prompt targets)
      (let* ((loc-prompt (or prompt "Headline"))
            ;; number of max headline level to display
            (org-refile-targets (or targets '((nil :maxlevel . 2)))) 
            (hd (condition-case nil
                   (car (org-refile-get-location loc-prompt nil t))
                   (error (car org-refile-history)))))
        (goto-char (point-min))
        (outline-next-heading)
        (if (re-search-forward
             (format org-complex-heading-regexp-format (regexp-quote hd))
             nil t)
          (goto-char (point-at-bol))
        (goto-char (point-max)))))

in this version works fine with :empty-lines, :append etc.

(setq org-capture-templates `("t" "Todo" entry
  (file+function "/path/to/todo.org" "My Headline" my/org-ask-headline-target)
  "* TODO %?"))

In version >= 9.5.5 C-c C-w + :refile-targets have the same result and you can define the targets per template definition, e.g:

(setq org-capture-templates `("t" "Todo" entry
  (file+headline "/path/to/todo.org" "My Headline")
  "* TODO %?" :refile-targets ((nil :maxlevel . 2))))

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.