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