mirror of
https://github.com/org-roam/org-roam
synced 2025-09-24 16:30:55 -05:00
(feat!)capture: change id creation to headline on entry-type capture-templates
This commit is contained in:
committed by
Dustin Farris
parent
89dfaef38b
commit
ed94524964
@@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2.3.2
|
||||||
|
- [#2056](https://github.com/org-roam/org-roam/pull/2056) capture: IDs are now created for entries in capture templates
|
||||||
|
|
||||||
## 2.3.1 (2025-06-26)
|
## 2.3.1 (2025-06-26)
|
||||||
|
|
||||||
* (fix): Use correct type specifications to suppress warnings by @okomestudio in https://github.com/org-roam/org-roam/pull/2522
|
* (fix): Use correct type specifications to suppress warnings by @okomestudio in https://github.com/org-roam/org-roam/pull/2522
|
||||||
|
@@ -339,11 +339,12 @@ In this case, interactive selection will be bypassed."
|
|||||||
(when goto (run-hooks 'org-roam-dailies-find-file-hook)))
|
(when goto (run-hooks 'org-roam-dailies-find-file-hook)))
|
||||||
|
|
||||||
(add-hook 'org-roam-capture-preface-hook #'org-roam-dailies--override-capture-time-h)
|
(add-hook 'org-roam-capture-preface-hook #'org-roam-dailies--override-capture-time-h)
|
||||||
|
|
||||||
(defun org-roam-dailies--override-capture-time-h ()
|
(defun org-roam-dailies--override-capture-time-h ()
|
||||||
"Override the `:default-time' with the time from `:override-default-time'."
|
"Override the `:default-time' with the time from `:override-default-time'."
|
||||||
(prog1 nil
|
|
||||||
(when (org-roam-capture--get :override-default-time)
|
(when (org-roam-capture--get :override-default-time)
|
||||||
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))))
|
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))
|
||||||
|
nil)
|
||||||
|
|
||||||
;;; Bindings
|
;;; Bindings
|
||||||
(defvar org-roam-dailies-map (make-sparse-keymap)
|
(defvar org-roam-dailies-map (make-sparse-keymap)
|
||||||
|
@@ -466,22 +466,21 @@ processing by `org-capture'.
|
|||||||
Note: During the capture process this function is run by
|
Note: During the capture process this function is run by
|
||||||
`org-capture-set-target-location', as a (function ...) based
|
`org-capture-set-target-location', as a (function ...) based
|
||||||
capture target."
|
capture target."
|
||||||
(let ((id (cond ((run-hook-with-args-until-success 'org-roam-capture-preface-hook))
|
(if-let ((id (run-hook-with-args-until-success 'org-roam-capture-preface-hook)))
|
||||||
(t (org-roam-capture--setup-target-location)))))
|
(org-roam-capture--put :id id)
|
||||||
(org-roam-capture--adjust-point-for-capture-type)
|
(org-roam-capture--setup-target-location))
|
||||||
(let ((template (org-capture-get :template)))
|
(let ((template (org-capture-get :template)))
|
||||||
(when (stringp template)
|
(when (stringp template)
|
||||||
(org-capture-put
|
(org-capture-put
|
||||||
:template
|
:template
|
||||||
(org-roam-capture--fill-template template))))
|
(org-roam-capture--fill-template template))))
|
||||||
(org-roam-capture--put :id id)
|
|
||||||
(org-roam-capture--put :finalize (or (org-capture-get :finalize)
|
(org-roam-capture--put :finalize (or (org-capture-get :finalize)
|
||||||
(org-roam-capture--get :finalize)))))
|
(org-roam-capture--get :finalize))))
|
||||||
|
|
||||||
(defun org-roam-capture--setup-target-location ()
|
(defun org-roam-capture--setup-target-location ()
|
||||||
"Initialize the buffer, and goto the location of the new capture.
|
"Initialize the buffer, and goto the location of the new capture."
|
||||||
Return the ID of the location."
|
(let ((target-entry-p t)
|
||||||
(let (p new-file-p)
|
p new-file-p id)
|
||||||
(pcase (org-roam-capture--get-target)
|
(pcase (org-roam-capture--get-target)
|
||||||
(`(file ,path)
|
(`(file ,path)
|
||||||
(setq path (org-roam-capture--target-truepath path)
|
(setq path (org-roam-capture--target-truepath path)
|
||||||
@@ -489,7 +488,8 @@ Return the ID of the location."
|
|||||||
(when new-file-p (org-roam-capture--put :new-file path))
|
(when new-file-p (org-roam-capture--put :new-file path))
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(widen)
|
(widen)
|
||||||
(setq p (goto-char (point-min))))
|
(setq p (goto-char (point-min))
|
||||||
|
target-entry-p nil))
|
||||||
(`(file+olp ,path ,olp)
|
(`(file+olp ,path ,olp)
|
||||||
(setq path (org-roam-capture--target-truepath path)
|
(setq path (org-roam-capture--target-truepath path)
|
||||||
new-file-p (org-roam-capture--new-file-p path))
|
new-file-p (org-roam-capture--new-file-p path))
|
||||||
@@ -507,7 +507,8 @@ Return the ID of the location."
|
|||||||
(org-roam-capture--put :new-file path)
|
(org-roam-capture--put :new-file path)
|
||||||
(insert (org-roam-capture--fill-template head 'ensure-newline)))
|
(insert (org-roam-capture--fill-template head 'ensure-newline)))
|
||||||
(widen)
|
(widen)
|
||||||
(setq p (goto-char (point-min))))
|
(setq p (goto-char (point-min))
|
||||||
|
target-entry-p nil))
|
||||||
(`(file+head+olp ,path ,head ,olp)
|
(`(file+head+olp ,path ,head ,olp)
|
||||||
(setq path (org-roam-capture--target-truepath path)
|
(setq path (org-roam-capture--target-truepath path)
|
||||||
new-file-p (org-roam-capture--new-file-p path))
|
new-file-p (org-roam-capture--new-file-p path))
|
||||||
@@ -569,17 +570,45 @@ Return the ID of the location."
|
|||||||
(user-error "No node with title or id \"%s\"" title-or-id))))
|
(user-error "No node with title or id \"%s\"" title-or-id))))
|
||||||
(set-buffer (org-capture-target-buffer (org-roam-node-file node)))
|
(set-buffer (org-capture-target-buffer (org-roam-node-file node)))
|
||||||
(goto-char (org-roam-node-point node))
|
(goto-char (org-roam-node-point node))
|
||||||
(setq p (org-roam-node-point node)))))
|
(setq p (org-roam-node-point node)
|
||||||
|
target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p))))))
|
||||||
;; Setup `org-id' for the current capture target and return it back to the
|
;; Setup `org-id' for the current capture target and return it back to the
|
||||||
;; caller.
|
;; caller.
|
||||||
|
;; Unless it's an entry type, then we want to create an ID for the entry instead
|
||||||
|
(pcase (org-capture-get :type)
|
||||||
|
('entry
|
||||||
|
(advice-add #'org-capture-place-entry :after #'org-roam-capture--create-id-for-entry)
|
||||||
|
(org-roam-capture--put :new-node-p t)
|
||||||
|
(setq id (org-roam-node-id org-roam-capture--node)))
|
||||||
|
(_
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char p)
|
(goto-char p)
|
||||||
(if-let ((id (org-entry-get p "ID")))
|
(unless (org-entry-get p "ID")
|
||||||
|
(org-roam-capture--put :new-node-p t))
|
||||||
|
(setq id (or (org-entry-get p "ID")
|
||||||
|
(org-roam-node-id org-roam-capture--node)))
|
||||||
(setf (org-roam-node-id org-roam-capture--node) id)
|
(setf (org-roam-node-id org-roam-capture--node) id)
|
||||||
(org-entry-put p "ID" (org-roam-node-id org-roam-capture--node)))
|
(org-entry-put p "ID" id))))
|
||||||
(prog1
|
(org-roam-capture--put :id id)
|
||||||
(org-id-get)
|
(org-roam-capture--put :target-entry-p target-entry-p)
|
||||||
(run-hooks 'org-roam-capture-new-node-hook)))))
|
(advice-add #'org-capture-place-template :before #'org-roam-capture--set-target-entry-p-a)
|
||||||
|
(advice-add #'org-capture-place-template :after #'org-roam-capture-run-new-node-hook-a)))
|
||||||
|
|
||||||
|
(defun org-roam-capture--set-target-entry-p-a (_)
|
||||||
|
"Correct `:target-entry-p' in Org-capture template based on `:target.'."
|
||||||
|
(org-capture-put :target-entry-p (org-roam-capture--get :target-entry-p))
|
||||||
|
(advice-remove #'org-capture-place-template #'org-roam-capture--set-target-entry-p-a))
|
||||||
|
|
||||||
|
(defun org-roam-capture-run-new-node-hook-a (_)
|
||||||
|
"Advice to run after the Org-capture template is placed."
|
||||||
|
(when (org-roam-capture--get :new-node-p)
|
||||||
|
(run-hooks 'org-roam-capture-new-node-hook))
|
||||||
|
(advice-remove #'org-capture-place-template #'org-roam-capture--place-template-a))
|
||||||
|
|
||||||
|
(defun org-roam-capture--create-id-for-entry ()
|
||||||
|
"Create the ID for the new entry."
|
||||||
|
(org-entry-put (point) "ID" (org-roam-capture--get :id))
|
||||||
|
(advice-remove #'org-capture-place-entry #'org-roam-capture--create-id-for-entry))
|
||||||
|
|
||||||
(defun org-roam-capture--get-target ()
|
(defun org-roam-capture--get-target ()
|
||||||
"Get the current capture :target for the capture template in use."
|
"Get the current capture :target for the capture template in use."
|
||||||
|
@@ -58,6 +58,193 @@
|
|||||||
(org-roam-capture--fill-template (lambda () "foo"))
|
(org-roam-capture--fill-template (lambda () "foo"))
|
||||||
:to-equal "foo")))
|
:to-equal "foo")))
|
||||||
|
|
||||||
|
(describe "org-roam-capture entry-type ID creation"
|
||||||
|
(it "creates ID for entry-type captures"
|
||||||
|
(let* ((temp-dir (make-temp-file "org-roam-test" t))
|
||||||
|
(test-file (expand-file-name "test.org" temp-dir))
|
||||||
|
(org-roam-directory temp-dir)
|
||||||
|
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
|
||||||
|
(org-roam-capture--info (make-hash-table :test 'equal))
|
||||||
|
capture-id)
|
||||||
|
(unwind-protect
|
||||||
|
(progn
|
||||||
|
;; Create initial file content
|
||||||
|
(with-temp-file test-file
|
||||||
|
(insert "#+TITLE: Test File\n\n* Parent Heading\n:PROPERTIES:\n:ID: parent-id\n:END:\n"))
|
||||||
|
|
||||||
|
;; Mock org-capture context and get-target
|
||||||
|
(cl-letf* (((symbol-function 'org-capture-get)
|
||||||
|
(lambda (prop)
|
||||||
|
(pcase prop
|
||||||
|
(:type 'entry)
|
||||||
|
(_ nil))))
|
||||||
|
((symbol-function 'org-roam-capture--get-target)
|
||||||
|
(lambda () `(file ,test-file))))
|
||||||
|
|
||||||
|
;; Call the setup function
|
||||||
|
(with-current-buffer (find-file-noselect test-file)
|
||||||
|
(org-roam-capture--setup-target-location)
|
||||||
|
(setq capture-id (org-roam-capture--get :id)))
|
||||||
|
|
||||||
|
;; Verify ID was created and stored for entry type
|
||||||
|
(expect capture-id :not :to-be nil)
|
||||||
|
(expect (org-roam-capture--get :new-node-p) :to-be t)))
|
||||||
|
(delete-directory temp-dir t))))
|
||||||
|
|
||||||
|
(it "creates ID at target for non-entry-type captures"
|
||||||
|
(let* ((temp-dir (make-temp-file "org-roam-test" t))
|
||||||
|
(test-file (expand-file-name "test-plain.org" temp-dir))
|
||||||
|
(org-roam-directory temp-dir)
|
||||||
|
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
|
||||||
|
(org-roam-capture--info (make-hash-table :test 'equal))
|
||||||
|
capture-id)
|
||||||
|
(unwind-protect
|
||||||
|
(progn
|
||||||
|
;; Create initial empty file
|
||||||
|
(with-temp-file test-file
|
||||||
|
(insert "#+TITLE: Test\n"))
|
||||||
|
|
||||||
|
;; Mock org-capture context for plain type
|
||||||
|
(cl-letf* (((symbol-function 'org-capture-get)
|
||||||
|
(lambda (prop)
|
||||||
|
(pcase prop
|
||||||
|
(:type 'plain)
|
||||||
|
(_ nil))))
|
||||||
|
((symbol-function 'org-roam-capture--get-target)
|
||||||
|
(lambda () `(file ,test-file))))
|
||||||
|
|
||||||
|
;; Call the setup function
|
||||||
|
(with-current-buffer (find-file-noselect test-file)
|
||||||
|
(org-roam-capture--setup-target-location)
|
||||||
|
(setq capture-id (org-roam-capture--get :id)))
|
||||||
|
|
||||||
|
;; For non-entry types, ID should be created at the target location
|
||||||
|
(expect capture-id :not :to-be nil)
|
||||||
|
(expect (org-roam-capture--get :new-node-p) :to-be t)))
|
||||||
|
(delete-directory temp-dir t)))))
|
||||||
|
|
||||||
|
(describe "org-roam-capture advice functions"
|
||||||
|
:var ((org-roam-capture--info))
|
||||||
|
|
||||||
|
(before-each
|
||||||
|
(setq org-roam-capture--info (make-hash-table :test 'equal)))
|
||||||
|
|
||||||
|
(it "org-roam-capture--create-id-for-entry creates and removes advice"
|
||||||
|
(cl-letf* ((entry-id nil)
|
||||||
|
((symbol-function 'org-entry-put)
|
||||||
|
(lambda (pom prop val)
|
||||||
|
(when (string= prop "ID")
|
||||||
|
(setq entry-id val))))
|
||||||
|
((symbol-function 'org-roam-capture--get)
|
||||||
|
(lambda (prop)
|
||||||
|
(if (eq prop :id)
|
||||||
|
"test-id-123"
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
;; Add the advice
|
||||||
|
(advice-add #'org-capture-place-entry :after #'org-roam-capture--create-id-for-entry)
|
||||||
|
|
||||||
|
;; Call the function
|
||||||
|
(org-roam-capture--create-id-for-entry)
|
||||||
|
|
||||||
|
;; Verify ID was set and advice removed
|
||||||
|
(expect entry-id :to-equal "test-id-123")
|
||||||
|
(expect (advice-member-p #'org-roam-capture--create-id-for-entry
|
||||||
|
#'org-capture-place-entry)
|
||||||
|
:to-be nil)))
|
||||||
|
|
||||||
|
(it "org-roam-capture--set-target-entry-p-a sets and removes advice"
|
||||||
|
(cl-letf* ((captured-value nil)
|
||||||
|
((symbol-function 'org-capture-put)
|
||||||
|
(lambda (prop val)
|
||||||
|
(when (eq prop :target-entry-p)
|
||||||
|
(setq captured-value val))))
|
||||||
|
((symbol-function 'org-roam-capture--get)
|
||||||
|
(lambda (prop)
|
||||||
|
(if (eq prop :target-entry-p)
|
||||||
|
t
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
;; Add the advice
|
||||||
|
(advice-add #'org-capture-place-template :before #'org-roam-capture--set-target-entry-p-a)
|
||||||
|
|
||||||
|
;; Call the function
|
||||||
|
(org-roam-capture--set-target-entry-p-a nil)
|
||||||
|
|
||||||
|
;; Verify value was set and advice removed
|
||||||
|
(expect captured-value :to-be t)
|
||||||
|
(expect (advice-member-p #'org-roam-capture--set-target-entry-p-a
|
||||||
|
#'org-capture-place-template)
|
||||||
|
:to-be nil)))
|
||||||
|
|
||||||
|
(it "org-roam-capture-run-new-node-hook-a runs hook when new node"
|
||||||
|
(let ((hook-ran nil))
|
||||||
|
(cl-letf* (((symbol-function 'org-roam-capture--get)
|
||||||
|
(lambda (prop)
|
||||||
|
(if (eq prop :new-node-p)
|
||||||
|
t
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
;; Add test hook
|
||||||
|
(add-hook 'org-roam-capture-new-node-hook
|
||||||
|
(lambda () (setq hook-ran t)))
|
||||||
|
|
||||||
|
;; Add the advice
|
||||||
|
(advice-add #'org-capture-place-template :after #'org-roam-capture-run-new-node-hook-a)
|
||||||
|
|
||||||
|
;; Call the function
|
||||||
|
(org-roam-capture-run-new-node-hook-a nil)
|
||||||
|
|
||||||
|
;; Verify hook ran
|
||||||
|
(expect hook-ran :to-be t)
|
||||||
|
|
||||||
|
;; Clean up
|
||||||
|
(remove-hook 'org-roam-capture-new-node-hook
|
||||||
|
(lambda () (setq hook-ran t)))))))
|
||||||
|
|
||||||
|
(describe "org-roam-capture target-entry-p detection"
|
||||||
|
(it "detects entry target for file+olp"
|
||||||
|
(let* ((temp-dir (make-temp-file "org-roam-test" t))
|
||||||
|
(test-file (expand-file-name "test-olp.org" temp-dir))
|
||||||
|
(org-roam-directory temp-dir)
|
||||||
|
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
|
||||||
|
(org-roam-capture--info (make-hash-table :test 'equal)))
|
||||||
|
(unwind-protect
|
||||||
|
(progn
|
||||||
|
(with-temp-file test-file
|
||||||
|
(insert "* Level 1\n** Level 2\n"))
|
||||||
|
|
||||||
|
(cl-letf* (((symbol-function 'org-roam-capture--get-target)
|
||||||
|
(lambda () `(file+olp ,test-file ("Level 1" "Level 2"))))
|
||||||
|
((symbol-function 'org-capture-get)
|
||||||
|
(lambda (prop) nil)))
|
||||||
|
|
||||||
|
(with-current-buffer (find-file-noselect test-file)
|
||||||
|
(org-roam-capture--setup-target-location)
|
||||||
|
(expect (org-roam-capture--get :target-entry-p) :to-be t))))
|
||||||
|
(delete-directory temp-dir t))))
|
||||||
|
|
||||||
|
(it "detects non-entry target for file"
|
||||||
|
(let* ((temp-dir (make-temp-file "org-roam-test" t))
|
||||||
|
(test-file (expand-file-name "test-file.org" temp-dir))
|
||||||
|
(org-roam-directory temp-dir)
|
||||||
|
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
|
||||||
|
(org-roam-capture--info (make-hash-table :test 'equal)))
|
||||||
|
(unwind-protect
|
||||||
|
(progn
|
||||||
|
(with-temp-file test-file
|
||||||
|
(insert "#+TITLE: Test\n"))
|
||||||
|
|
||||||
|
(cl-letf* (((symbol-function 'org-roam-capture--get-target)
|
||||||
|
(lambda () `(file ,test-file)))
|
||||||
|
((symbol-function 'org-capture-get)
|
||||||
|
(lambda (prop) nil)))
|
||||||
|
|
||||||
|
(with-current-buffer (find-file-noselect test-file)
|
||||||
|
(org-roam-capture--setup-target-location)
|
||||||
|
(expect (org-roam-capture--get :target-entry-p) :to-be nil))))
|
||||||
|
(delete-directory temp-dir t)))))
|
||||||
|
|
||||||
(provide 'test-org-roam-capture)
|
(provide 'test-org-roam-capture)
|
||||||
|
|
||||||
;;; test-org-roam-capture.el ends here
|
;;; test-org-roam-capture.el ends here
|
||||||
|
Reference in New Issue
Block a user