;;; lang/org/contrib/roam2.el -*- lexical-binding: t; -*- ;;;###if (featurep! +roam2) (defvar +org-roam-open-buffer-on-find-file t "If non-nil, open the org-roam buffer when opening an org roam file.") (defvar +org-roam-link-to-org-use-id 'create-if-interactive "`org-roam-directory' local value for `org-id-link-to-org-use-id'. It's not recommended to set this to nil in order for other parts of org-mode to properly utilize ID links.") ;; ;;; Packages (use-package! org-roam :hook (org-load . +org-init-roam-h) :preface ;; Set this to nil so we can later detect if the user has set custom values ;; for these variables. If not, default values will be set in the :config ;; section. (defvar org-roam-directory nil) (defvar org-roam-db-location nil) :init (doom-load-packages-incrementally '(ansi-color dash f rx seq magit-section emacsql emacsql-sqlite)) ;; Don't display warning message dedicated for v1 users. Need to be set early. (setq org-roam-v2-ack t) (defadvice! +org-roam-suppress-sqlite-build-a (fn &rest args) "Suppress automatic building of sqlite3 binary when loading `org-roam'. This is a blocking operation that can take a while to complete and better be deferred when there will be an actual demand for the database. See `+org-init-roam-h' for the launch process." :around #'emacsql-sqlite-ensure-binary (if (not (boundp 'org-roam-db-version)) (apply fn args) (advice-remove #'emacsql-sqlite-ensure-binary #'+org-roam-suppress-sqlite-build-a) nil)) :config (defun +org-init-roam-h () "Setup `org-roam' but don't immediately initialize its database. Instead, initialize it when it will be actually needed." (letf! ((#'org-roam-db-sync #'ignore)) (org-roam-setup)) (defadvice! +org-roam-try-init-db-a (&rest _) "Try to initialize org-roam database at the last possible safe moment. In case of failure, fail gracefully." :before #'org-roam-db-query (message "Initializing org-roam database...") (let ((run-cleanup-p t)) (unwind-protect ;; Try to build the binary if it doesn't exist. In case of failure ;; this will error, run the cleanup and exit, and in case of success ;; this will return nil and sync the database. (setq run-cleanup-p (emacsql-sqlite-ensure-binary)) (when run-cleanup-p (setq org-roam--sqlite-available-p nil) (org-roam-teardown) (message (concat "EmacSQL failied to build SQLite binary for org-roam; " "see *Compile-Log* buffer for details.\n" "To try reinitialize org-roam, run \"M-x org-roam-setup\""))))) (advice-remove 'org-roam-db-query #'+org-roam-try-init-db-a) (org-roam-db-sync))) (setq org-roam-directory (thread-first (or org-roam-directory "roam") (expand-file-name org-directory) (file-truename) (file-name-as-directory)) org-roam-db-location (or org-roam-db-location (concat doom-etc-dir "org-roam.db")) org-roam-node-display-template "${doom-hierarchy:*} ${doom-tags:45}" org-roam-completion-everywhere t org-roam-mode-section-functions #'(org-roam-backlinks-section org-roam-reflinks-section)) (setq-hook! 'org-roam-find-file-hook org-id-link-to-org-use-id +org-roam-link-to-org-use-id) ;; Normally, the org-roam buffer doesn't open until you explicitly call ;; `org-roam'. If `+org-roam-open-buffer-on-find-file' is non-nil, the ;; org-roam buffer will be opened for you whenever you visit a file in ;; `org-roam-directory'. (add-hook! 'org-roam-find-file-hook :append (defun +org-roam-open-with-buffer-maybe-h () (and +org-roam-open-buffer-on-find-file (not org-roam-capture--node) ; don't proc for capture buffers (not (eq 'visible (org-roam-buffer--visibility))) (org-roam-buffer-toggle)))) (set-popup-rules! `((,(regexp-quote org-roam-buffer) ; persistent org-roam buffer :side right :width .33 :height .5 :ttl nil :modeline nil :quit nil :slot 1) ("^\\*org-roam: " ; node dedicated org-roam buffer :side right :width .33 :height .5 :ttl nil :modeline nil :quit nil :slot 2))) (add-hook 'org-roam-mode-hook #'turn-on-visual-line-mode) (map! (:map org-mode-map :localleader :prefix ("m" . "org-roam") "D" #'org-roam-demote-entire-buffer "f" #'org-roam-node-find "F" #'org-roam-ref-find "g" #'org-roam-graph "i" #'org-roam-node-insert "I" #'org-id-get-create "m" #'org-roam-buffer-toggle "M" #'org-roam-buffer-display-dedicated "n" #'org-roam-capture "r" #'org-roam-refile "R" #'org-roam-link-replace-all (:prefix ("d" . "by date") :desc "Goto previous note" "b" #'org-roam-dailies-goto-previous-note :desc "Goto date" "d" #'org-roam-dailies-goto-date :desc "Capture date" "D" #'org-roam-dailies-capture-date :desc "Goto next note" "f" #'org-roam-dailies-goto-next-note :desc "Goto tomorrow" "m" #'org-roam-dailies-goto-tomorrow :desc "Capture tomorrow" "M" #'org-roam-dailies-capture-tomorrow :desc "Capture today" "n" #'org-roam-dailies-capture-today :desc "Goto today" "t" #'org-roam-dailies-goto-today :desc "Capture today" "T" #'org-roam-dailies-capture-today :desc "Goto yesterday" "y" #'org-roam-dailies-goto-yesterday :desc "Capture yesterday" "Y" #'org-roam-dailies-capture-yesterday :desc "Find directory" "-" #'org-roam-dailies-find-directory) (:prefix ("o" . "node properties") "a" #'org-roam-alias-add "A" #'org-roam-alias-remove "t" #'org-roam-tag-add "T" #'org-roam-tag-remove "r" #'org-roam-ref-add "R" #'org-roam-ref-remove))) (when (featurep! :editor evil +everywhere) (add-hook! 'org-roam-mode-hook (defun +org-roam-detach-magit-section-mode-map-h () "Detach `magit-section-mode-map' from `org-roam-mode-map'. Inheriting its keymaps introduces a lot of conflicts in `org-roam-mode' based buffers, where Evil and leader keybindings will become completely overridden. This is because `magit-section' uses 'keymap text-property to attach section-unique keymaps, which has a higher level of precedence than `emulation-mode-map-alists'. Note: We do this each time through the hook, because otherwise sections seems to ignore the detachment." (set-keymap-parent org-roam-mode-map nil))) (map! :map org-roam-mode-map :nv "]" #'magit-section-forward-sibling :nv "[" #'magit-section-backward-sibling :nv "gj" #'magit-section-forward-sibling :nv "gk" #'magit-section-backward-sibling :nv "gr" #'revert-buffer :nv "gR" #'revert-buffer :nv "z1" #'magit-section-show-level-1 :nv "z2" #'magit-section-show-level-2 :nv "z3" #'magit-section-show-level-3 :nv "z4" #'magit-section-show-level-4 :nv "za" #'magit-section-toggle :nv "zc" #'magit-section-hide :nv "zC" #'magit-section-hide-children :nv "zo" #'magit-section-show :nv "zO" #'magit-section-show-children :nv "zr" #'magit-section-show-level-4-all :nv "C-j" #'magit-section-forward :nv "C-k" #'magit-section-backward :g "M-p" #'magit-section-backward-sibling :g "M-n" #'magit-section-forward-sibling :g [tab] #'magit-section-toggle :g [C-tab] #'magit-section-cycle :g [backtab] #'magit-section-cycle-global))) ;; Since the org module lazy loads org-protocol (waits until an org URL is ;; detected), we can safely chain `org-roam-protocol' to it. (use-package! org-roam-protocol :after org-protocol)