diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index b809ee1c2..a3695e54c 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -199,7 +199,31 @@ :desc "Arbitrary date" "d" #'org-roam-dailies-find-date :desc "Today" "t" #'org-roam-dailies-find-today :desc "Tomorrow" "m" #'org-roam-dailies-find-tomorrow - :desc "Yesterday" "y" #'org-roam-dailies-find-yesterday)))) + :desc "Yesterday" "y" #'org-roam-dailies-find-yesterday))) + (:when (featurep! :lang org +roam2) + (:prefix ("r" . "roam") + :desc "Open random node" "a" #'org-roam-node-random + :desc "Find node" "f" #'org-roam-node-find + :desc "Find ref" "F" #'org-roam-ref-find + :desc "Show graph" "g" #'org-roam-graph + :desc "Insert node" "i" #'org-roam-node-insert + :desc "Capture to node" "n" #'org-roam-capture + :desc "Toggle roam buffer" "r" #'org-roam-buffer-toggle + :desc "Launch roam buffer" "R" #'org-roam-buffer-display-dedicated + :desc "Sync database" "s" #'org-roam-db-sync + (: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)))) ;;; o --- open "o" nil ; we need to unbind it first as Org claims this prefix diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index a7bfcd4de..ae707b883 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -555,6 +555,31 @@ :desc "Tomorrow" "m" #'org-roam-dailies-find-tomorrow :desc "Yesterday" "y" #'org-roam-dailies-find-yesterday))) + (:when (featurep! :lang org +roam2) + (:prefix ("r" . "roam") + :desc "Open random node" "a" #'org-roam-node-random + :desc "Find node" "f" #'org-roam-node-find + :desc "Find ref" "F" #'org-roam-ref-find + :desc "Show graph" "g" #'org-roam-graph + :desc "Insert node" "i" #'org-roam-node-insert + :desc "Capture to node" "n" #'org-roam-capture + :desc "Toggle roam buffer" "r" #'org-roam-buffer-toggle + :desc "Launch roam buffer" "R" #'org-roam-buffer-display-dedicated + :desc "Sync database" "s" #'org-roam-db-sync + (: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))) + (:when (featurep! :lang org +journal) (:prefix ("j" . "journal") :desc "New Entry" "j" #'org-journal-new-entry diff --git a/modules/lang/org/README.org b/modules/lang/org/README.org index ace6d4bb2..dd5823444 100644 --- a/modules/lang/org/README.org +++ b/modules/lang/org/README.org @@ -19,6 +19,10 @@ - [[#configuration][Configuration]] - [[#changing-org-directory][Changing ~org-directory~]] - [[#changing-org-noter-notes-search-path][Changing ~org-noter-notes-search-path~]] +- [[#troubleshooting][Troubleshooting]] + - [[#org-roam][=org-roam=]] + - [[#should-i-go-with-roam-v1-or-roam2-v2][Should I go with =+roam= (v1) or =+roam2= (v2)?]] + - [[#migrating-your-existing-files-from-v1-roam-to-v2-roam2][Migrating your existing files from v1 (=+roam=) to v2 (=+roam2=)]] * Description This module adds org-mode support to Doom Emacs, along with a number of @@ -73,8 +77,10 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode + =+pretty= Enables pretty unicode symbols for bullets and priorities, and better syntax highlighting for latex. Keep in mind: this can be expensive. If org becomes too slow, it'd be wise to disable this flag. -+ =+roam= Enables org-roam integration. This requires ~sqlite3~ to be installed - on your system. ++ =+roam= Enables integration with [[https://github.com/org-roam/org-roam-v1][org-roam v1]]. This requires ~sqlite3~ to be + installed on your system. Incompatible with =+roam2=. ++ =+roam2= Enables integration with [[https://github.com/org-roam/org-roam][org-roam v2]]. This requires ~sqlite3~ to be + installed on your system. Incompatible with =+roam=. ** Plugins + [[https://github.com/hniksic/emacs-htmlize][htmlize]] @@ -128,7 +134,9 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode + [[https://github.com/integral-dw/org-superstar-mode][org-superstar]] + [[https://github.com/harrybournis/org-fancy-priorities][org-fancy-priorities]] + =+roam= - + [[https://github.com/org-roam/org-roam][org-roam]] + + [[https://github.com/org-roam/org-roam-v1][org-roam]] (v1) ++ =+roam2= + - [[https://github.com/org-roam/org-roam/tree/v2][org-roam]] (v2) + =+noter= + [[https://github.com/weirdNox/org-noter][org-noter]] @@ -180,7 +188,7 @@ esoteric features: its dependencies are met, e.g. install the =ruby= executable for ruby support. To use ~jupyter kernels~ you need the =+jupyter= flag, the associated kernel as well as the ~jupyter~ program. -+ =org-roam= (with the =+roam= flag) requires =sqlite3= to be installed. ++ =org-roam= (with =+roam= or =+roam2= flag) requires =sqlite3= to be installed. ** MacOS #+BEGIN_SRC sh @@ -253,3 +261,44 @@ To modify ~org-noter-notes-search-path~ set: ;; ~/.doom.d/config.el (setq org-noter-notes-search-path '("~/notes/path/")) #+END_SRC + +* Troubleshooting +** =org-roam= +*** Should I go with =+roam= (v1) or =+roam2= (v2)? +Long story short: if you're new to =org-roam= and haven't used it, then you +should go with =+roam2=; if you already have an ~org-roam-directory~ with the v1 +files in it, then you can keep use =+roam= for a time being. + +V1 isn't actively maintained anymore and is now basically EOL. This means that +the feature disparity between the both will continue to grow, while its existing +bugs and problems won't be addressed, at least by the main maintainers. V2 can +be considered as a complete rewrite of the package so it comes with a lot of +breaking changes. + +While v1 won't be actively maintained anymore, it still will be available in +Doom for a while, at least until there will be a reliable tool that will migrate +your data from v1 to v2. + +To learn more about v2 you can use the next resources: +- [[https://github.com/org-roam/org-roam/blob/master/doc/org-roam.org][Org-roam v2 Official Manual]] +- [[https://github.com/org-roam/org-roam/wiki/Hitchhiker's-Rough-Guide-to-Org-roam-V2][Hitchhiker's Rough Guide to Org roam V2]] +- [[https://blog.jethro.dev/posts/org_roam_v2/][Releasing Org-roam v2 - Jethro Kuan's blog]] +- [[https://org-roam.discourse.group/t/org-roam-major-redesign/1198][Thread about the redesign from Org-Roam Discourse]] + +*** Migrating your existing files from v1 (=+roam=) to v2 (=+roam2=) +V2 comes with a migration wizard for v1 users. It's new, which means issues can +appear during the migration process. Because of that, *don't forget to backup* +your ~org-roam-directory~ before attempting to migrate. + +In order to migrate from v1 to v2 using Doom follow the next steps: +1. Enable =+roam2= flag (and disable =+roam= if it was previously enabled) in + your =init.el=. +2. Ensure your ~org-roam-directory~ points to a directory with your v1 files. +3. Run =doom sync -u= in your shell. +4. Restart Emacs (if it was previously opened) and run ~org-roam-migrate-wizard~ + command (=M-x org-roam-migrate-wizard RET=). The wizard will automatically + attempt to backup your previous ~org-roam-directory~ to =org-roam.bak=, but + just in case backup it yourself too. +4. After the wizard is done you should be good to go. Verify the integrity of + your data and whether it did everything as expected. In case of failure + [[https://github.com/org-roam/org-roam/issues][report]] your issue. diff --git a/modules/lang/org/autoload/contrib-roam2.el b/modules/lang/org/autoload/contrib-roam2.el new file mode 100644 index 000000000..72b6d3d9e --- /dev/null +++ b/modules/lang/org/autoload/contrib-roam2.el @@ -0,0 +1,58 @@ +;;; lang/org/autoload/contrib-roam2.el -*- lexical-binding: t; -*- +;;;###if (featurep! +roam2) + +;;; Custom node accessors +;;;###autoload (autoload 'org-roam-node-doom-filetitle "lang/org/autoload/contrib-roam2" nil t) +(cl-defmethod org-roam-node-doom-filetitle ((node org-roam-node)) + "Return the value of \"#+title:\" (if any) from file that NODE resides in. +If there's no file-level title in the file, return empty string." + (or (if (= (org-roam-node-level node) 0) + (org-roam-node-title node) + (org-roam-get-keyword "TITLE" (org-roam-node-file node))) + "")) + +;;;###autoload (autoload 'org-roam-node-doom-hierarchy "lang/org/autoload/contrib-roam2" nil t) +(cl-defmethod org-roam-node-doom-hierarchy ((node org-roam-node)) + "Return hierarchy for NODE, constructed of its file title, OLP and direct title. +If some elements are missing, they will be stripped out." + (let ((title (org-roam-node-title node)) + (olp (org-roam-node-olp node)) + (level (org-roam-node-level node)) + (filetitle (org-roam-node-doom-filetitle node)) + (separator (propertize " > " 'face 'shadow))) + (cl-case level + ;; node is a top-level file + (0 filetitle) + ;; node is a level 1 heading + (1 (concat (propertize filetitle 'face '(shadow italic)) + separator title)) + ;; node is a heading with an arbitrary outline path + (t (concat (propertize filetitle 'face '(shadow italic)) + separator (propertize (string-join olp " > ") 'face '(shadow italic)) + separator title))))) + +;;;###autoload (autoload 'org-roam-node-doom-subdirs "lang/org/autoload/contrib-roam2" nil t) +(cl-defmethod org-roam-node-doom-subdirs ((node org-roam-node)) + "Return subdirectories of `org-roam-directory' in which NODE resides in. +If there's none, return an empty string." + (if-let ((dirs (thread-first node + (org-roam-node-file) + (file-relative-name org-roam-directory) + (file-name-directory)))) + dirs + "")) + +;;;###autoload (autoload 'org-roam-node-doom-tags "lang/org/autoload/contrib-roam2" nil t) +(cl-defmethod org-roam-node-doom-tags ((node org-roam-node)) + "Return tags formatted in the same way how they appear in org files. +Treat subdirectories as tags too. If there's no elements to build +the tags of, return an empty string." + (let ((tags (org-roam-node-tags node)) + (subdirs (org-roam-node-doom-subdirs node))) + (when tags + (setq tags (propertize (concat (mapconcat (lambda (s) (concat ":" s)) tags nil) ":") + 'face 'shadow))) + (unless (string-empty-p subdirs) + (setq subdirs (propertize (concat ":" (replace-regexp-in-string "/\\|\\\\" ":" subdirs)) + 'face '(shadow italic)))) + (replace-regexp-in-string ":+" (propertize ":" 'face 'shadow) (concat subdirs tags)))) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 76a176f59..9f0507188 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -732,7 +732,7 @@ between the two." "f" #'org-footnote-new "h" #'org-toggle-heading "i" #'org-toggle-item - "I" #'org-toggle-inline-images + "I" #'org-id-get-create "n" #'org-store-link "o" #'org-set-property "q" #'org-set-tags-command diff --git a/modules/lang/org/contrib/roam2.el b/modules/lang/org/contrib/roam2.el new file mode 100644 index 000000000..3e4389ebd --- /dev/null +++ b/modules/lang/org/contrib/roam2.el @@ -0,0 +1,184 @@ +;;; 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 (orig-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 orig-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) diff --git a/modules/lang/org/doctor.el b/modules/lang/org/doctor.el index c4c672d24..d96e1c721 100644 --- a/modules/lang/org/doctor.el +++ b/modules/lang/org/doctor.el @@ -7,7 +7,9 @@ (when (featurep! +roam) (unless (executable-find "sqlite3") - (warn! "Couldn't find the sqlite3 executable. org-roam will not work.")) + (warn! "Couldn't find the sqlite3 executable. org-roam will not work."))) +(when (or (featurep! +roam) + (featurep! +roam2)) (unless (executable-find "dot") (warn! "Couldn't find the dot executable (from graphviz). org-roam will not be able to generate graph visualizations."))) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 5da2374f5..cc02eda5a 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -76,8 +76,13 @@ :recipe (:host github :repo "hakimel/reveal.js" :files ("css" "dist" "js" "plugin")) :pin "b18f12d964ef80bd9ffb061aae48ff4c15fb43ad")) -(when (featurep! +roam) - (package! org-roam :pin "756f6215b672e267f986a3d6e494f5309825b91a")) +(cond + ((featurep! +roam) + (package! org-roam + :recipe (:host github :repo "org-roam/org-roam-v1") + :pin "946a879a4a18756a0508afba1e0b0fe070c6a8b4")) + ((featurep! +roam2) + (package! org-roam :pin "028c95a011395d01ff9b5217dc365f23187bc26c"))) ;;; Babel (package! ob-async :pin "9aac486073f5c356ada20e716571be33a350a982")