diff --git a/doc/org-roam.org b/doc/org-roam.org index f4bdf94..a754951 100644 --- a/doc/org-roam.org +++ b/doc/org-roam.org @@ -847,7 +847,9 @@ of the template are similar to ~org-capture~ templates. automatically chooses this template for you. 2. The template is given a description of ~"default"~. 3. ~plain~ text is inserted. Other options include Org headings via - ~entry~. + ~entry~. Note that ~entry~ type captures create regular Org headings + without IDs by default. To create an Org-roam node with an ID, use + the ~:entry-node t~ option. 4. Notice that the ~target~ that's usually in Org-capture templates is missing here. 5. ~"%?"~ is the template inserted on each call to ~org-roam-capture-~. @@ -1221,6 +1223,10 @@ Here is a sane default configuration: "#+title: %<%Y-%m-%d>\n")))) #+end_src +Note: The ~entry~ type capture above creates a regular Org heading without an +ID. If you want each daily entry to be an Org-roam node with its own ID, add +~:entry-node t~ to the template. + See [[*The Templating System][The Templating System]] for creating new templates. *** Usage diff --git a/doc/org-roam.texi b/doc/org-roam.texi index 1238a7a..d8830d8 100644 --- a/doc/org-roam.texi +++ b/doc/org-roam.texi @@ -1219,7 +1219,9 @@ automatically chooses this template for you. The template is given a description of @code{"default"}. @item @code{plain} text is inserted. Other options include Org headings via -@code{entry}. +@code{entry}. Note that @code{entry} type captures create regular Org headings +without IDs by default. To create an Org-roam node with an ID, use +the @code{:entry-node t} option. @item Notice that the @code{target} that's usually in Org-capture templates is missing here. @@ -1703,6 +1705,10 @@ Here is a sane default configuration: "#+title: %<%Y-%m-%d>\n")))) @end lisp +Note: The @code{entry} type capture above creates a regular Org heading without an +ID@. If you want each daily entry to be an Org-roam node with its own ID, add +@code{:entry-node t} to the template. + See @ref{The Templating System} for creating new templates. @node Usage @@ -2421,5 +2427,5 @@ When GOTO is non-nil, go the note without creating an entry." @printindex vr -Emacs 30.1 (Org mode 9.7.29) +Emacs 30.2 (Org mode 9.7.34) @bye diff --git a/org-roam-capture.el b/org-roam-capture.el index 00e96b7..d3c2b2d 100644 --- a/org-roam-capture.el +++ b/org-roam-capture.el @@ -63,9 +63,11 @@ description A short string describing the template, which will be shown during selection. type The type of entry. Valid types are: - entry an Org node, with a headline. Will be filed - as the child of the target entry or as a - top level entry. Its default template is: + entry an Org heading. Will be filed as the child + of the target entry or as a top level entry. + Use :entry-node t to create an ID for this + heading, making it an org-roam node. + Its default template is: \"* %?\n %a\" item a plain list item, will be placed in the first plain list at the target location. @@ -135,6 +137,11 @@ The following options are supported for the :target property: The rest of the entry is a property list of additional options. Recognized properties are: + :entry-node When set to t for entry-type captures, creates an ID for + the captured entry heading. When nil or not specified, + no ID is created for the entry. Only applies to templates + with type 'entry'. + :prepend Normally newly captured information will be appended at the target location (last child, last table line, last list item...). Setting this property will @@ -390,7 +397,7 @@ This variable is populated dynamically, and is only non-nil during the Org-roam capture process.") (defconst org-roam-capture--template-keywords (list :target :id :link-description :call-location - :region) + :region :entry-node) "Keywords used in `org-roam-capture-templates' specific to Org-roam.") ;;; Main entry point @@ -578,12 +585,13 @@ capture target." 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 ;; caller. - ;; Unless it's an entry type, then we want to create an ID for the entry instead + ;; For entry type: only create ID if :entry-node is t (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))) + (when (org-roam-capture--get :entry-node) + (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 (goto-char p) @@ -610,7 +618,7 @@ capture target." (advice-remove #'org-capture-place-template #'org-roam-capture-run-new-node-hook-a)) (defun org-roam-capture--create-id-for-entry () - "Create the ID for the new entry." + "Create the ID for the new entry heading." (org-entry-put (point) "ID" (org-roam-capture--get :id)) (advice-remove #'org-capture-place-entry #'org-roam-capture--create-id-for-entry)) diff --git a/tests/test-org-roam-capture.el b/tests/test-org-roam-capture.el index 6c16b31..fb6a4ce 100644 --- a/tests/test-org-roam-capture.el +++ b/tests/test-org-roam-capture.el @@ -58,38 +58,76 @@ (org-roam-capture--fill-template (lambda () "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")) +(describe "org-roam-capture :entry-node option" + :var ((temp-dir) (org-roam-directory) (org-roam-db-location)) - ;; 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)))) + (before-each + (setq temp-dir (make-temp-file "org-roam-test" t)) + (setq org-roam-directory temp-dir) + (setq org-roam-db-location (expand-file-name "org-roam.db" temp-dir)) + (org-roam-db-sync)) - ;; 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))) + (after-each + (delete-directory temp-dir t)) - ;; 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 "does not create ID for entry-type capture without :entry-node option" + (let* ((test-file (expand-file-name "test-parent.org" temp-dir)) + (org-roam-capture-templates + '(("t" "test" entry "* ${title}" + :target (file "test-parent.org") + :unnarrowed t)))) + + ;; Create parent file with an existing heading + (with-temp-file test-file + (insert "#+TITLE: Parent File\n\n* Existing Heading\n")) + + ;; Mock the node selection to return a new node + (cl-letf (((symbol-function 'org-roam-node-read) + (lambda (&rest _) + (org-roam-node-create :title "New Entry Without ID")))) + + (org-roam-capture) + + ;; Finalize the capture + (org-capture-finalize)) + + ;; Verify the captured entry exists but has no ID + (with-temp-buffer + (insert-file-contents test-file) + (goto-char (point-min)) + (re-search-forward "^\\* New Entry Without ID$") + (let ((has-id (org-entry-get (point) "ID"))) + (expect has-id :to-be nil))))) + + (it "creates ID for entry-type capture with :entry-node t option" + (let* ((test-file (expand-file-name "test-parent-with-id.org" temp-dir)) + (org-roam-capture-templates + '(("t" "test" entry "* ${title}" + :target (file "test-parent-with-id.org") + :entry-node t + :unnarrowed t)))) + + ;; Create parent file with an existing heading + (with-temp-file test-file + (insert "#+TITLE: Parent File\n\n* Existing Heading\n")) + + ;; Mock the node selection to return a new node + (cl-letf (((symbol-function 'org-roam-node-read) + (lambda (&rest _) + (org-roam-node-create :title "New Entry With ID")))) + + (org-roam-capture) + + ;; Finalize the capture + (org-capture-finalize)) + + ;; Verify the captured entry has an ID + (with-temp-buffer + (insert-file-contents test-file) + (goto-char (point-min)) + (re-search-forward "^\\* New Entry With ID$") + (let ((has-id (org-entry-get (point) "ID"))) + (expect has-id :not :to-be nil))))) (it "creates ID at target for non-entry-type captures" (let* ((temp-dir (make-temp-file "org-roam-test" t))