Whoops, I forgot to include in my diff the small test I added. I've attached the full diff here.
-- Best, Kristoffer
diff --git a/lisp/org-capture.el b/lisp/org-capture.el index 6d395406cf..f418e9fba9 100644 --- a/lisp/org-capture.el +++ b/lisp/org-capture.el @@ -205,24 +205,36 @@ (file+headline <file-spec> \"node headline\") (file+headline <file-spec> function-returning-string) (file+headline <file-spec> symbol-containing-string) - Fast configuration if the target heading is unique in the file + Fast configuration if the target heading is unique in + the file. If function-returning-list-of-strings or + symbol-containing-list-of-strings return the symbol + \\='file,the entry will be inserted at the end of + <file-spec> on top level. (file+olp <file-spec> \"Level 1 heading\" \"Level 2\" ...) (file+olp <file-spec> function-returning-list-of-strings) (file+olp <file-spec> symbol-containing-list-of-strings) - For non-unique headings, the full outline path is safer + For non-unique headings, the full outline path is + safer. If function-returning-list-of-strings or + symbol-containing-list-of-strings return the symbol + \\='file,the entry will be inserted at the end of + <file-spec> on top level. (file+regexp <file-spec> \"regexp to find location\") File to the entry matching regexp + (file+olp+datetree <file-spec>) (file+olp+datetree <file-spec> \"Level 1 heading\" ...) (file+olp+datetree <file-spec> function-returning-list-of-strings) (file+olp+datetree <file-spec> symbol-containing-list-of-strings) Will create a heading in a date tree for today's date. - If no heading is given, the tree will be on top level. - To prompt for date instead of using TODAY, use the - :time-prompt property. To create a week-tree, use the - :tree-type property. + The tree will be inserted at the end of <file-spec> on + top level if no heading is given or if + function-returning-list-of-strings or + symbol-containing-list-of-strings return the symbol + \\='file. To prompt for date instead of using TODAY, + use the :time-prompt property. To create a week-tree, + use the :tree-type property. (file+function <file-spec> function-finding-location) A function to find the right location in the file @@ -1055,18 +1067,28 @@ (widen) (goto-char (point-min)) (setq headline (org-capture-expand-headline headline)) - (if (re-search-forward (format org-complex-heading-regexp-format - (regexp-quote headline)) - nil t) - (forward-line 0) - (goto-char (point-max)) - (unless (bolp) (insert "\n")) - (insert "* " headline "\n") - (forward-line -1))) + (pcase headline + ('file (goto-char (point-max))) + ((pred stringp) + (re-search-forward (format org-complex-heading-regexp-format + (regexp-quote headline)) + nil t) + (forward-line 0)) + (_ + (goto-char (point-max)) + (unless (bolp) (insert "\n")) + (insert "* " headline "\n") + (forward-line -1)))) (`(file+olp ,path . ,(and outline-path (guard outline-path))) (let* ((expanded-file-path (org-capture-expand-file path)) - (m (org-find-olp (cons expanded-file-path - (apply #'org-capture-expand-olp expanded-file-path outline-path))))) + (expanded-olp (apply #'org-capture-expand-olp expanded-file-path outline-path)) + ;; If expanded-olp is 'file, then create the datetree + ;; at the end of the file specified by + ;; expanded-file-path + (m (if (not (eq expanded-olp 'file)) + (org-find-olp (cons expanded-file-path expanded-olp)) + (set-buffer (org-capture-target-buffer expanded-file-path)) + (save-restriction (widen) (point-max-marker))))) (set-buffer (marker-buffer m)) (org-capture-put-target-region-and-position) (widen) @@ -1086,12 +1108,16 @@ (setq target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p))))) (`(file+olp+datetree ,path . ,outline-path) - (let ((m (if outline-path - (let ((expanded-file-path (org-capture-expand-file path))) - (org-find-olp (cons expanded-file-path - (apply #'org-capture-expand-olp expanded-file-path outline-path)))) - (set-buffer (org-capture-target-buffer path)) - (point-marker)))) + (let* ((expanded-file-path (org-capture-expand-file path)) + (expanded-olp (apply #'org-capture-expand-olp expanded-file-path outline-path)) + ;; If expanded-olp is 'file, then create the datetree + ;; at the end of the file specified by + ;; expanded-file-path + (file-level-datetree-p (eq expanded-olp 'file)) + (m (if (not file-level-datetree-p) + (org-find-olp (cons expanded-file-path expanded-olp)) + (set-buffer (org-capture-target-buffer expanded-file-path)) + (save-restriction (widen) (point-max-marker))))) (set-buffer (marker-buffer m)) (org-capture-put-target-region-and-position) (widen) @@ -1152,7 +1178,7 @@ (org-today)))) ;; the following is the keep-restriction argument for ;; org-datetree-find-date-create - (when outline-path 'subtree-at-point)))) + (unless file-level-datetree-p 'subtree-at-point)))) (`(file+function ,path ,(and function (pred functionp))) (set-buffer (org-capture-target-buffer path)) (org-capture-put-target-region-and-position) @@ -1188,9 +1214,18 @@ (defun org-capture-expand-headline (headline) "Expand functions, symbols and headline names for HEADLINE. +Return a string representing a headline. + When HEADLINE is a function, call it. When it is a variable, return its value. When it is a string, return it. In any other case, signal -an error." +an error. + +This function may also return the symbol \\='file if HEADLINE, when it +is a function or variable, returns that symbol. Such cases will be +interpreted by `org-capture-set-target-location' as a request to target +the file directly (rather than a specific headline). This behavior +allows users to dynamically set a template\\='s target location to a +specific headline or the top-level of a file." (let* ((final-headline (cond ((stringp headline) headline) ((functionp headline) (funcall headline)) ((and (symbolp headline) (boundp headline)) @@ -1201,12 +1236,22 @@ (defun org-capture-expand-olp (file &rest olp) "Expand functions, symbols and outline paths in FILE for OLP. +Return a list of strings representing an outline path (OLP) in FILE. + When OLP is a function, call it with no arguments while the current buffer is the FILE-visiting buffer. When it is a variable, return its -value. When it is a list of string, return it. In any other case, -signal an error." +value. When it is a list of strings, return it. In any other case, +signal an error. + +This function may also return the symbol \\='file if OLP, when it is a +function or variable, returns that symbol. Such cases will be +interpreted by `org-capture-set-target-location' as a request to target +the file directly (rather than a specific outline path). This behavior +allows users to dynamically set a template\\='s target location to a +specific outline path or the top-level of a file." (let* ((first (car olp)) - (final-olp (cond ((not (memq nil (mapcar #'stringp olp))) olp) + (final-olp (cond ((equal olp '(nil)) 'file) + ((not (memq nil (mapcar #'stringp olp))) olp) ((and (not (cdr olp)) (functionp first)) (with-current-buffer (find-file-noselect file) (funcall first))) diff --git a/testing/lisp/test-org-capture.el b/testing/lisp/test-org-capture.el index 7673347ed0..fe6af5a6c4 100644 --- a/testing/lisp/test-org-capture.el +++ b/testing/lisp/test-org-capture.el @@ -1019,6 +1019,15 @@ (unwind-protect (org-capture-expand-olp file "A" "B" "C") (delete-file file))))) + ;; `org-capture-expand-olp' should return 'file if the outline path + ;; is nil + (should + (equal + 'file + (let ((file (make-temp-file "org-test"))) + (unwind-protect + (org-capture-expand-olp file nil) + (delete-file file))))) ;; The current buffer during the funcall of the lambda is the temporary ;; test file. (should