From 8f8ccf67979444db2f642f3a606a2f0193b54377 Mon Sep 17 00:00:00 2001 From: Jethro Kuan Date: Tue, 17 Mar 2020 17:06:49 +0800 Subject: [PATCH] (feat): clean-ups for org-roam-capture system (#315) This introduces 2 user-facing changes: 1. If a capture process is aborted, the file is not created if it doesn't yet exist 2. If a capture process is aborted, org-roam-insert will not insert a link --- org-roam.el | 132 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 54 deletions(-) diff --git a/org-roam.el b/org-roam.el index f7a8ff7..0bb5527 100644 --- a/org-roam.el +++ b/org-roam.el @@ -735,6 +735,9 @@ applies. inserted on initial creation (added only once). This is where insertion of any note metadata should go.") +(defvar org-roam--capture-insert-link-plist nil + "Dynamically scoped variable used to insert the org link after org-capture.") + (defun org-roam--fill-template (str &optional info) "Expands the template STR, returning the string. This is an extension of org-capture's template expansion. @@ -751,6 +754,16 @@ Next, it expands the remaining template string using (completing-read (format "%s: " key ) nil))) nil) (org-capture-fill-template))) +(defun org-roam--capture-save-file-maybe-h () + "This function is saves the file if the original value of +:no-save is not t and `org-note-abort' is not t. It is added to +`org-capture-after-finalize-hook'." + (when (and (not (org-capture-get :orig-no-save)) + (not org-note-abort)) + (with-current-buffer (org-capture-get :buffer) + (save-buffer))) + (remove-hook 'org-capture-after-finalize-hook #'org-roam--capture-save-file-maybe-h)) + (defun org-roam--capture-new-file () "Return the path to the new file during an Org-roam capture. @@ -761,7 +774,18 @@ If the file path already exists, it throw an error. Else, to insert the header content in the file, org-capture template is prepended with the `:head' portion of the Org-roam -capture template." +capture template. + +To prevent the creation of a new file if the capture process is +aborted, we do the following: + +1. Save the original value of the capture template's :no-save. + +2. Set the capture template's :no-save to t. + +3. Add a function on `org-capture-after-finalize-hook' that saves +the file if the original value of :no-save is not t and +`org-note-abort' is not t." (let* ((name-templ (or (org-capture-get :file-name) org-roam--capture-file-name-default)) (new-id (s-trim (org-roam--fill-template @@ -770,12 +794,14 @@ capture template." (file-path (org-roam--file-path-from-id new-id))) (when (file-exists-p file-path) (error (format "File exists at %s, aborting" file-path))) + (org-capture-put :orig-no-save (org-capture-get :no-save)) (org-capture-put :template (concat (or (org-capture-get :head) org-roam--capture-header-default) (org-capture-get :template)) - :type 'plain) + :type 'plain + :no-save t) file-path)) (defun org-roam--expand-capture-template () @@ -801,7 +827,7 @@ This function is used solely in Org-roam's capture templates: see ('title (let ((file-path (org-roam--capture-new-file))) (org-roam--expand-capture-template) - (setq org-roam--capture-file-path file-path) + (org-capture-put :roam-file-path file-path) (set-buffer (org-capture-target-buffer file-path)) (widen) (goto-char (point-max)))) @@ -811,7 +837,7 @@ This function is used solely in Org-roam's capture templates: see (file-path (or (cdr (assoc ref completions)) (org-roam--capture-new-file)))) (org-roam--expand-capture-template) - (setq org-roam--capture-file-path file-path) + (org-capture-put :roam-file-path file-path) (set-buffer (org-capture-target-buffer file-path)) (widen) (goto-char (point-max)))) @@ -822,23 +848,31 @@ This function is used solely in Org-roam's capture templates: see The templates are defined at `org-roam-capture-templates'. The GOTO and KEYS argument have the same functionality as `org-capture'." - (let ((org-capture-templates org-roam-capture-templates) - file-path) + (let ((org-capture-templates org-roam-capture-templates)) (when (= (length org-capture-templates) 1) (setq keys (caar org-capture-templates))) (org-capture goto keys))) ;;; Interactive Commands ;;;; org-roam-insert -(defvar org-roam--capture-insert-point nil - "The point to jump to after the call to `org-roam-insert'.") - (defun org-roam--format-link-title (title) "Retur the link title, given the file TITLE." (if (functionp org-roam-link-title-format) (funcall org-roam-link-title-format title) (format org-roam-link-title-format title))) +(defun org-roam--format-link (target description) + "Formats an org link for a given file TARGET and link DESCRIPTION." + (let* ((here (-> (or (buffer-base-buffer) + (current-buffer)) + (buffer-file-name) + (file-truename) + (file-name-directory)))) + (format "[[%s][%s]]" + (concat "file:" + (file-relative-name target here)) + description))) + (defun org-roam-insert (prefix) "Find an Org-roam file, and insert a relative org link to it at point. If PREFIX, downcase the title before insertion." @@ -856,49 +890,40 @@ If PREFIX, downcase the title before insertion." (title (org-roam--completing-read "File: " completions :initial-input region-text)) (region-or-title (or region-text title)) - (target-file-path (cdr (assoc title completions))) (current-file-path (-> (or (buffer-base-buffer) - (current-buffer)) - (buffer-file-name) - (file-truename) - (file-name-directory))) - (buf (current-buffer)) - (p (point-marker))) - (unless (and target-file-path - (file-exists-p target-file-path)) + (current-buffer)) + (buffer-file-name) + (file-truename) + (file-name-directory))) + (target-file-path (cdr (assoc title completions))) + (link-description (org-roam--format-link-title (if prefix + (downcase region-or-title) + region-or-title)))) + (if (and target-file-path + (file-exists-p target-file-path)) + (progn + (when region ;; Remove previously selected text. + (delete-region (car region) (cdr region))) + (insert (org-roam--format-link target-file-path link-description))) (let* ((org-roam--capture-info (list (cons 'title title) (cons 'slug (org-roam--title-to-slug title)))) (org-roam--capture-context 'title)) - (org-roam-capture) - (setq target-file-path org-roam--capture-file-path) - (setq org-roam--capture-file-path nil))) - (with-current-buffer buf - (when region ;; Remove previously selected text. - (delete-region (car region) (cdr region))) - (let ((link-location (concat "file:" - (file-relative-name target-file-path - current-file-path))) - (description (org-roam--format-link-title (if prefix - (downcase region-or-title) - region-or-title)))) - (goto-char p) - (insert (format "[[%s][%s]]" - link-location - description)) - (setq org-roam--capture-insert-point (point)))) - (add-hook 'org-capture-after-finalize-hook #'org-roam--capture-advance-point))) + (add-hook 'org-capture-after-finalize-hook #'org-roam--capture-insert-link-h) + (setq org-roam--capture-insert-link-plist (list :region region + :link-description link-description)) + (org-roam-capture))))) -(defun org-roam--capture-advance-point () - "Advances the point if it is updated. - -We need this function because typically `org-capture' prevents the -point from being advanced, whereas when a link is inserted, the -point moves some characters forward. This is added as a hook to -`org-capture-after-finalize-hook'." - (when org-roam--capture-insert-point - (goto-char org-roam--capture-insert-point) - (setq org-roam--capture-insert-point nil)) - (remove-hook 'org-capture-after-finalize-hook #'org-roam--capture-advance-point)) +(defun org-roam--capture-insert-link-h () + "Inserts the link into the original buffer, after the capture process is done. +This is added as a hook to `org-capture-after-finalize-hook'." + (when (and (not org-note-abort) + org-roam--capture-insert-link-plist) + (when-let ((region (plist-get org-roam--capture-insert-link-plist :region))) ;; Remove previously selected text. + (delete-region (car region) (cdr region))) + (insert (org-roam--format-link (org-capture-get :roam-file-path) + (plist-get org-roam--capture-insert-link-plist :link-description))) + (setq org-roam--capture-insert-link-plist nil)) + (remove-hook 'org-capture-after-finalize-hook #'org-roam--capture-insert-link-h)) ;;;; org-roam-find-file (defun org-roam--get-title-path-completions () @@ -915,13 +940,12 @@ point moves some characters forward. This is added as a hook to file-path) res)))) res)) -(defun org-roam--capture-find-file () +(defun org-roam--capture-find-file-h () "Opens the newly created template file. This is added as a hook to `org-capture-after-finalize-hook'." - (when org-roam--capture-file-path - (find-file org-roam--capture-file-path) - (setq org-roam--capture-file-path nil)) - (remove-hook 'org-capture-after-finalize-hook #'org-roam--capture-find-file)) + (when-let ((file-path (org-capture-get :roam-file-path))) + (find-file file-path)) + (remove-hook 'org-capture-after-finalize-hook #'org-roam--capture-find-file-h)) (defun org-roam-find-file (&optional initial-prompt) "Find and open an Org-roam file. @@ -936,8 +960,8 @@ INITIAL-PROMPT is the initial title prompt." (let* ((org-roam--capture-info (list (cons 'title title) (cons 'slug (org-roam--title-to-slug title)))) (org-roam--capture-context 'title)) - (org-roam-capture) - (add-hook 'org-capture-after-finalize-hook #'org-roam--capture-find-file))))) + (add-hook 'org-capture-after-finalize-hook #'org-roam--capture-find-file-h) + (org-roam-capture))))) ;;;; org-roam-find-ref (defun org-roam--get-ref-path-completions ()