Compare commits

...

43 Commits

Author SHA1 Message Date
18b75bd03e (feat): Hide drawer content when capturing 2021-05-12 09:16:05 +02:00
ecc3611bec Merge branch 'master' into v2 2021-05-12 09:13:11 +02:00
f754160402 (fix): Fix chronology issue between renaming notes and updating links (#1517) 2021-05-12 09:06:11 +02:00
3a3c4d97d5 (fix) setting non-blank keyword value (#1516)
`string-blank-p` returns a non-nil value if the string is blank, and
nil (which you can't compare to 0) otherwise.
2021-05-10 14:19:02 +08:00
07ec4342aa (feat) allow to specify props to remove and return them (#1515)
This is a follow up of #1420/ for v2.

Basically:

1. Allow to pass a tag/alias/ref to remove.
2. Return removed tag/alias/ref.
3. Return added tag/alias/ref to mirror (2).
2021-05-10 14:18:48 +08:00
096b0c5e30 allow capture to datetree 2021-05-08 18:16:42 +08:00
32c0f3d2ec fix org-roam-capture--goto-location (#1514)
Fix regression introduced in 7ed51329.

The function should return an ID.
2021-05-08 15:14:00 +08:00
02e35e3b01 (feat): add org-roam-graph-filetype (#1513)
Co-authored-by: Greg Coladonato <gcoladonato3@gatech.edu>
2021-05-06 19:23:46 +08:00
377d39bfff update org-roam-capture-tempates docstring (#1509)
Remove quotes before heading lists in olp examples, they do not belong
there. Quoting these lists will lead to an error.
2021-05-04 12:28:49 +08:00
3d4c93a2a0 macro: find-file non-literally, but start in fundamental-mode?
Lexically bind auto-mode-alist to nil, so emacs opens the file initially
in the global major-mode, which is traditionally fundamental-mode. cc @nobiot
2021-05-03 19:03:05 +08:00
683605c813 (fix) fix aliases lookup in org-roam-node-from-title-or-alias (#1508)
It should match by title instead of id and the left join is not needed
here as data is not used in any case.
2021-05-03 14:18:04 +08:00
eae1cdc00c try other buffer-file-coding-system fix
Previous fix resulted in slow DB caching, suspect that it doesn't apply
the perf enhancements. Maybe this works? cc @nobiot
2021-05-03 11:41:20 +08:00
1bbc20cfa8 remove primary key constraint on aliases table
this allows a node to have multiple aliases cc @myshevchuk
2021-05-03 11:37:00 +08:00
de94212cdd ensure buffer-encoding is appropriate
don't do a literal find for files, but do all the
org-inhibit-startup/delay-mode-hooks setting in the lexical context.

Addresses #1497?
2021-05-03 04:16:13 +08:00
78144d434b cache completions display format (#1505)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2021-05-03 03:52:17 +08:00
55526a4283 Fix auto save buffer in org-roam-doctor (#1493) 2021-05-03 03:36:50 +08:00
d2e933cc3e Fix auto save buffer in org-roam-doctor (#1493) 2021-05-03 03:36:09 +08:00
9405d014af fix: permanent-redisplay (#1504) (#1506)
Related to #1504, commit 109917c
It needs to change dolist for `sections` to `section-functions`
2021-05-03 02:59:41 +08:00
98434919e0 discard inherited property on headline node tags
Addresses #1494
2021-05-03 02:51:51 +08:00
7ed513294d org-roam-node-at-point: include point info
org-roam-node-at-point now returns a node with id and point if the noden
is not yet saved in the database. Fixes #1500.
2021-05-02 17:33:00 +08:00
6fe38b959b fix 2021-05-02 17:23:38 +08:00
44960f189f fix lints 2021-05-02 17:08:02 +08:00
fbe3f2909f Add org-macs where org-with-wide-buffer is called
Addresses https://github.com/org-roam/org-roam/issues/1026#issuecomment-830742020
2021-05-02 17:04:15 +08:00
f8f3870cf0 ensure binary when loading org-roam-db 2021-05-02 16:59:33 +08:00
1bdfc25f80 remove NOTE on foreign key 2021-05-02 16:43:50 +08:00
109917c589 org-roam-mode-sections: Convert to abnormal hook (#1504)
See: #1503
2021-05-02 16:35:36 +08:00
bc4b7fa409 prefer mapconcat over string-join (#1502) 2021-05-02 16:34:43 +08:00
f8dd345bd1 emacsql-sqlite3 -> emacsql-sqlite
Revert from emacsql-sqlite3 to emacsql-sqlite for foreign key support.
This makes it slightly harder to get Org-roam up and running for
Windows, but not impossible, but foreign keys makes database operations
faster and keeps the code clean.

Also, fix schemata for foreign keys on various tables, and add indices
where joins are common (org-roam-db--table-indices).
2021-05-02 16:30:43 +08:00
0a7d365e22 speed up org-roam-db-sync by wrapping db activity in a transaction (#1495)
In my case of testing with 8253 zettels (available on request if
anyone wants to test with them), this improves cleanup speed by 6x
and update speed by 2x.

Improvements will be smaller for smaller batches as this really just
removes the transaction overhead.
2021-04-30 12:51:29 +08:00
389bf3c7e2 Add properties and olp to db 2021-04-29 14:02:14 +08:00
ca1b02829b title: replace link with description on db-build 2021-04-28 13:54:12 +08:00
dd7c3fab23 Merge branch 'master' into v2
* master:
  (doc): Add file-truename to set org-roam-directory (README and documentation) (#1487)
2021-04-28 13:45:23 +08:00
15c1a46e41 (doc): Add file-truename to set org-roam-directory (README and documentation) (#1487)
* (doc): Add file-truename to set org-roam-directory

Refer this debuging Slack exchange:
https://orgroam.slack.com/archives/CV160S8EL/p1619089118195300

Not using `file-truename` to set
`org-roam-directory` can lead to an issue that is
hard to identify.

It appears that cache database may updates with
files but `org-roam-buffer` fails to find the file
as symbolic links do not resolve. This is
confusing as the table query to the table seems to
return what appears to be correct entries, but the
backlink fails to insert context around the
link (as Org-roam fails to find the file).

There have been similiar issues -- by making sure
`file-truename` is added in the documentation and
README, it is hoped to eliminate such issues to
recur.

This is probably relevant for V2.

* undo changes to texi

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2021-04-28 13:43:42 +08:00
22c8d1032b fix indent 2021-04-28 13:16:08 +08:00
4e0b3fb564 update docs 2021-04-27 23:16:27 +08:00
697c686390 remove roam_tags
See #1492. Primary reason really is that I do want tags to be exactly
like Org tags, so we can reuse some of Org's cool features related to
tags.
2021-04-27 22:51:21 +08:00
68dab59a69 delete new file if org-note-abort
Fixes #1490
2021-04-27 20:11:55 +08:00
2dc5d6281a make tag completions unique 2021-04-27 19:18:42 +08:00
eb9a9e487c add org-roam-tag-{add,remove}
also fix org-roam interactive functions to operate on node
2021-04-27 18:04:53 +08:00
fd23360100 Add back ROAM_TAGS to top-level nodes 2021-04-27 13:04:24 +08:00
57f63461cd (perf): make Org load faster on literal find 2021-04-26 23:20:06 +08:00
d9ba961f96 fix indent 2021-04-26 00:47:40 +08:00
169ebe6367 fix org-roam-dailies-capture-templates
Fixes #1489
2021-04-26 00:28:55 +08:00
15 changed files with 418 additions and 220 deletions

View File

@ -11,6 +11,7 @@
### Changed ### Changed
- [#1352](https://github.com/org-roam/org-roam/pull/1352) prefer lower-case for roam_tag and roam_alias in interactive commands - [#1352](https://github.com/org-roam/org-roam/pull/1352) prefer lower-case for roam_tag and roam_alias in interactive commands
- [#1513](https://github.com/org-roam/org-roam/pull/1513) replaced hardcoded "svg" with defcustom org-roam-graph-filetype
### Fixed ### Fixed

View File

@ -47,7 +47,7 @@ Here's a sample configuration with `use-package`:
:hook :hook
(after-init . org-roam-mode) (after-init . org-roam-mode)
:custom :custom
(org-roam-directory "/path/to/org-files/") (org-roam-directory (file-truename "/path/to/org-files/"))
:bind (:map org-roam-mode-map :bind (:map org-roam-mode-map
(("C-c n l" . org-roam) (("C-c n l" . org-roam)
("C-c n f" . org-roam-find-file) ("C-c n f" . org-roam-find-file)
@ -56,6 +56,9 @@ Here's a sample configuration with `use-package`:
(("C-c n i" . org-roam-insert)))) (("C-c n i" . org-roam-insert))))
``` ```
The `file-truename` function is only necessary when you use symbolic links
inside `org-roam-directory`: Org-roam does not resolve symbolic links.
Org-roam requires sqlite to function. Org-roam optionally uses Graphviz for Org-roam requires sqlite to function. Org-roam optionally uses Graphviz for
graph-related functionality. It is recommended to install PCRE-enabled ripgrep graph-related functionality. It is recommended to install PCRE-enabled ripgrep
for better performance and extended functionality. for better performance and extended functionality.

View File

@ -361,9 +361,12 @@ For this tutorial, create an empty directory, and set ~org-roam-directory~:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(make-directory "~/org-roam") (make-directory "~/org-roam")
(setq org-roam-directory "~/org-roam") (setq org-roam-directory (file-truename "~/org-roam"))
#+END_SRC #+END_SRC
The ~file-truename~ function is only necessary when you use symbolic links
inside ~org-roam-directory~: Org-roam does not resolve symbolic links.
Next, we setup Org-roam to run functions on file changes to maintain cache Next, we setup Org-roam to run functions on file changes to maintain cache
consistency. This is achieved by running ~M-x org-roam-setup~. To ensure that consistency. This is achieved by running ~M-x org-roam-setup~. To ensure that
Org-roam is available on startup, place this in your Emacs configuration: Org-roam is available on startup, place this in your Emacs configuration:
@ -508,6 +511,17 @@ Alternatively, Org-roam provides some functions to add or remove aliases.
Remove an alias from the node at point. Remove an alias from the node at point.
** Tags
Tags for top-level (file) nodes are pulled from the variable ~org-file-tags~,
which is set by the ~#+filetags~ keyword, as well as other tags the file may
have inherited. Tags for headline level nodes are regular Org tags.
Note that the ~#+filetags~ keyword results in tags being inherited by headers
within the file. This makes it impossible for selective tag inheritance: i.e.
either tag inheritance is turned off, or all headline nodes will inherit the
tags from the file node. This is a design compromise of Org-roam.
** Refs ** Refs
Refs are unique identifiers for nodes. These keys allow references to the key to Refs are unique identifiers for nodes. These keys allow references to the key to

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -33,13 +33,16 @@
;;;; Library Requires ;;;; Library Requires
(require 'org-capture) (require 'org-capture)
(eval-when-compile (eval-when-compile
(require 'org-roam-macs)) (require 'org-roam-macs)
(require 'org-macs))
(require 'org-roam-db) (require 'org-roam-db)
(require 'dash) (require 'dash)
(require 'cl-lib) (require 'cl-lib)
;; Declarations ;; Declarations
(declare-function org-roam-ref-add "org-roam" (ref)) (declare-function org-roam-ref-add "org-roam" (ref))
(declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction))
(declare-function org-datetree-find-month-create (d &optional keep-restriction))
(defvar org-roam-directory) (defvar org-roam-directory)
@ -121,15 +124,20 @@ the following options:
The file will be created, prescribed an ID, and head content will be The file will be created, prescribed an ID, and head content will be
inserted into the file. inserted into the file.
(file+olp \"path/to/file\" '(\"h1\" \"h2\")) (file+olp \"path/to/file\" (\"h1\" \"h2\"))
The file will be created, prescribed an ID. The OLP (h1, h2) will be The file will be created, prescribed an ID. The OLP (h1, h2) will be
created, and the point placed after. created, and the point placed after.
(file+head+olp \"path/to/file\" \"head content\" '(\"h1\" \"h2\")) (file+head+olp \"path/to/file\" \"head content\" (\"h1\" \"h2\"))
The file will be created, prescribed an ID. Head content will be The file will be created, prescribed an ID. Head content will be
inserted at the start of the file. The OLP (h1, h2) will be created, inserted at the start of the file. The OLP (h1, h2) will be created,
and the point placed after. and the point placed after.
(file+datetree \"path/to/file\" day)
The file will be created, prescribed an ID. Head content will be
inserted at the start of the file. The datetree will be created,
available options are day, week, month.
The rest of the entry is a property list of additional options. Recognized The rest of the entry is a property list of additional options. Recognized
properties are: properties are:
@ -468,7 +476,12 @@ This function is to be called in the Org-capture finalization process."
"Finalize the `org-roam-capture' process." "Finalize the `org-roam-capture' process."
(when-let ((region (org-roam-capture--get :region))) (when-let ((region (org-roam-capture--get :region)))
(org-roam-unshield-region (car region) (cdr region))) (org-roam-unshield-region (car region) (cdr region)))
(unless org-note-abort (if org-note-abort
(when-let ((new-file (org-roam-capture--get :new-file)))
(org-roam-message "Deleting file for aborted capture %s" new-file)
(when (find-buffer-visiting new-file)
(kill-buffer (find-buffer-visiting new-file)))
(delete-file new-file))
(when-let ((finalize (org-roam-capture--get :finalize))) (when-let ((finalize (org-roam-capture--get :finalize)))
(funcall (intern (concat "org-roam-capture--finalize-" (funcall (intern (concat "org-roam-capture--finalize-"
(symbol-name (org-roam-capture--get :finalize))))))) (symbol-name (org-roam-capture--get :finalize)))))))
@ -515,6 +528,8 @@ Return the ID of the location."
(setq path (expand-file-name (setq path (expand-file-name
(string-trim (org-roam-capture--fill-template path t)) (string-trim (org-roam-capture--fill-template path t))
org-roam-directory)) org-roam-directory))
(unless (file-exists-p path)
(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 (point))) (setq p (point)))
@ -523,6 +538,8 @@ Return the ID of the location."
(string-trim (org-roam-capture--fill-template path t)) (string-trim (org-roam-capture--fill-template path t))
org-roam-directory)) org-roam-directory))
(set-buffer (org-capture-target-buffer path)) (set-buffer (org-capture-target-buffer path))
(unless (file-exists-p path)
(org-roam-capture--put :new-file path))
(setq p (point-min)) (setq p (point-min))
(let ((m (org-roam-capture-find-or-create-olp olp))) (let ((m (org-roam-capture-find-or-create-olp olp)))
(goto-char m)) (goto-char m))
@ -531,10 +548,10 @@ Return the ID of the location."
(setq path (expand-file-name (setq path (expand-file-name
(string-trim (org-roam-capture--fill-template path t)) (string-trim (org-roam-capture--fill-template path t))
org-roam-directory)) org-roam-directory))
(let ((exists-p (file-exists-p path))) (set-buffer (org-capture-target-buffer path))
(set-buffer (org-capture-target-buffer path)) (unless (file-exists-p path)
(unless exists-p (org-roam-capture--put :new-file path)
(insert (org-roam-capture--fill-template head t)))) (insert (org-roam-capture--fill-template head t)))
(widen) (widen)
(setq p (point-min))) (setq p (point-min)))
(`(file+head+olp ,path ,head ,olp) (`(file+head+olp ,path ,head ,olp)
@ -542,13 +559,59 @@ Return the ID of the location."
(string-trim (org-roam-capture--fill-template path t)) (string-trim (org-roam-capture--fill-template path t))
org-roam-directory)) org-roam-directory))
(widen) (widen)
(let ((exists-p (file-exists-p path))) (set-buffer (org-capture-target-buffer path))
(set-buffer (org-capture-target-buffer path)) (unless (file-exists-p path)
(unless exists-p (org-roam-capture--put :new-file path)
(insert (org-roam-capture--fill-template head t)))) (insert (org-roam-capture--fill-template head t)))
(setq p (point-min)) (setq p (point-min))
(let ((m (org-roam-capture-find-or-create-olp olp))) (let ((m (org-roam-capture-find-or-create-olp olp)))
(goto-char m))) (goto-char m)))
(`(file+datetree ,path ,tree-type)
(setq path (expand-file-name
(string-trim (org-roam-capture--fill-template path t))
org-roam-directory))
(require 'org-datetree)
(widen)
(set-buffer (org-capture-target-buffer path))
(unless (file-exists-p path)
(org-roam-capture--put :new-file path))
(funcall
(pcase tree-type
(`week #'org-datetree-find-iso-week-create)
(`month #'org-datetree-find-month-create)
(_ #'org-datetree-find-date-create))
(calendar-gregorian-from-absolute
(cond
(org-overriding-default-time
;; Use the overriding default time.
(time-to-days org-overriding-default-time))
((org-capture-get :default-time)
(time-to-days (org-capture-get :default-time)))
((org-capture-get :time-prompt)
;; Prompt for date. Bind `org-end-time-was-given' so
;; that `org-read-date-analyze' handles the time range
;; case and returns `prompt-time' with the start value.
(let* ((org-time-was-given nil)
(org-end-time-was-given nil)
(prompt-time (org-read-date
nil t nil "Date for tree entry:")))
(org-capture-put
:default-time
(if (or org-time-was-given
(= (time-to-days prompt-time) (org-today)))
prompt-time
;; Use 00:00 when no time is given for another
;; date than today?
(apply #'encode-time 0 0
org-extend-today-until
(cl-cdddr (decode-time prompt-time)))))
(time-to-days prompt-time)))
(t
;; Current date, possibly corrected for late night
;; workers.
(org-today)))))
(org-end-of-subtree t t)
(setq p (point)))
(`(node ,title-or-id) (`(node ,title-or-id)
;; first try to get ID, then try to get title/alias ;; first try to get ID, then try to get title/alias
(let ((node (or (org-roam-node-from-id title-or-id) (let ((node (or (org-roam-node-from-id title-or-id)
@ -560,8 +623,9 @@ Return the ID of the location."
(org-end-of-subtree t t)))) (org-end-of-subtree t t))))
(save-excursion (save-excursion
(goto-char p) (goto-char p)
(run-hooks 'org-roam-capture-new-node-hook) (prog1
(org-id-get-create)))) (org-id-get-create)
(run-hooks 'org-roam-capture-new-node-hook)))))
(defun org-roam-capture-find-or-create-olp (olp) (defun org-roam-capture-find-or-create-olp (olp)
"Return a marker pointing to the entry at OLP in the current buffer. "Return a marker pointing to the entry at OLP in the current buffer.
@ -696,7 +760,9 @@ TEMPLATES is a list of org-roam templates."
(when (and (not keys) (when (and (not keys)
(= (length org-capture-templates) 1)) (= (length org-capture-templates) 1))
(setq keys (caar org-capture-templates))) (setq keys (caar org-capture-templates)))
(org-capture goto keys))) (org-capture goto keys)
(unless goto
(org-cycle-hide-drawers 'all))))
;;;###autoload ;;;###autoload
(defun org-roam-capture (&optional goto keys) (defun org-roam-capture (&optional goto keys)

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify ;; This program is free software; you can redistribute it and/or modify

View File

@ -8,7 +8,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -61,10 +61,10 @@
:type 'hook) :type 'hook)
(defcustom org-roam-dailies-capture-templates (defcustom org-roam-dailies-capture-templates
'(("d" "default" entry `(("d" "default" entry
"* %?" "* %?"
:if-new `(file+head ,(concat org-roam-dailies-directory "%<%Y-%m-%d>.org") :if-new (file+head ,(expand-file-name "%<%Y-%m-%d>.org" org-roam-dailies-directory)
"#+title: %<%Y-%m-%d>\n"))) "#+title: %<%Y-%m-%d>\n")))
"Capture templates for daily-notes in Org-roam. "Capture templates for daily-notes in Org-roam.
See `org-roam-capture-templates' for the template documentation." See `org-roam-capture-templates' for the template documentation."
:group 'org-roam :group 'org-roam

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -33,7 +33,7 @@
;;;; Library Requires ;;;; Library Requires
(eval-when-compile (require 'subr-x)) (eval-when-compile (require 'subr-x))
(require 'emacsql) (require 'emacsql)
(require 'emacsql-sqlite3) (require 'emacsql-sqlite)
(require 'seq) (require 'seq)
(eval-and-compile (eval-and-compile
@ -79,7 +79,11 @@ value like `most-positive-fixnum'."
:type 'int :type 'int
:group 'org-roam) :group 'org-roam)
(defconst org-roam-db--version 12) (defconst org-roam-db--version 15)
(defconst org-roam--sqlite-available-p
(with-demoted-errors "Org-roam initialization: %S"
(emacsql-sqlite-ensure-binary)
t))
(defvar org-roam-db--connection (make-hash-table :test #'equal) (defvar org-roam-db--connection (make-hash-table :test #'equal)
"Database connection to Org-roam database.") "Database connection to Org-roam database.")
@ -99,7 +103,7 @@ Performs a database upgrade when required."
(emacsql-live-p (org-roam-db--get-connection))) (emacsql-live-p (org-roam-db--get-connection)))
(let ((init-db (not (file-exists-p org-roam-db-location)))) (let ((init-db (not (file-exists-p org-roam-db-location))))
(make-directory (file-name-directory org-roam-db-location) t) (make-directory (file-name-directory org-roam-db-location) t)
(let ((conn (emacsql-sqlite3 org-roam-db-location))) (let ((conn (emacsql-sqlite org-roam-db-location)))
(set-process-query-on-exit-flag (emacsql-process conn) nil) (set-process-query-on-exit-flag (emacsql-process conn) nil)
(puthash (expand-file-name org-roam-directory) (puthash (expand-file-name org-roam-directory)
conn conn
@ -129,51 +133,53 @@ SQL can be either the emacsql vector representation, or a string."
(apply #'emacsql (org-roam-db) sql args))) (apply #'emacsql (org-roam-db) sql args)))
;;;; Schemata ;;;; Schemata
;; NOTE: Foreign key somehow doesn't work! Adding a file column to every table as a workaround.
(defconst org-roam-db--table-schemata (defconst org-roam-db--table-schemata
'((files '((files
[(file :unique :primary-key) [(file :unique :primary-key)
(hash :not-null)]) (hash :not-null)])
(nodes (nodes
[(id :primary-key :not-null) ([(id :not-null :primary-key)
(file :not-null) (file :not-null)
(level :not-null) (level :not-null)
(pos :not-null) (pos :not-null)
todo todo
priority priority
(scheduled text) (scheduled text)
(deadline text) (deadline text)
title] title
(:foreign-key [file] :references files [file] :on-delete :cascade)) properties
olp]
(:foreign-key [file] :references files [file] :on-delete :cascade)))
(aliases (aliases
[(file :not-null) ([(node-id :not-null)
(node-id :not-null) alias]
alias] (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade))
(refs (refs
([(file :not-null) ([(node-id :not-null)
(node-id :not-null)
(ref :not-null) (ref :not-null)
(type :not-null)] (type :not-null)]
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade))) (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
(tags (tags
[(file :not-null) ([(node-id :not-null)
(node-id :not-null) tag]
tag] (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade))
(links (links
[(file :not-null) ([(pos :not-null)
(pos :not-null) (source :not-null)
(source :not-null) (dest :not-null)
(dest :not-null) (type :not-null)
(type :not-null) (properties :not-null)]
(properties :not-null)] (:foreign-key [source] :references nodes [id] :on-delete :cascade)))))
(:foreign-key [file] :references files [file] :on-delete :cascade))))
(defconst org-roam-db--table-indices
'((alias-node-id aliases [node-id])
(refs-node-id refs [node-id])
(tags-node-id tags [node-id])))
(defun org-roam-db--init (db) (defun org-roam-db--init (db)
"Initialize database DB with the correct schema and user version." "Initialize database DB with the correct schema and user version."
@ -181,6 +187,8 @@ SQL can be either the emacsql vector representation, or a string."
(emacsql db "PRAGMA foreign_keys = ON") (emacsql db "PRAGMA foreign_keys = ON")
(pcase-dolist (`(,table ,schema) org-roam-db--table-schemata) (pcase-dolist (`(,table ,schema) org-roam-db--table-schemata)
(emacsql db [:create-table $i1 $S2] table schema)) (emacsql db [:create-table $i1 $S2] table schema))
(pcase-dolist (`(,index-name ,table ,columns) org-roam-db--table-indices)
(emacsql db [:create-index $i1 :on $i2 $S3] index-name table columns))
(emacsql db (format "PRAGMA user_version = %s" org-roam-db--version)))) (emacsql db (format "PRAGMA user_version = %s" org-roam-db--version))))
(defun org-roam-db--upgrade-maybe (db version) (defun org-roam-db--upgrade-maybe (db version)
@ -222,10 +230,9 @@ the current `org-roam-directory'."
This is equivalent to removing the node from the graph. This is equivalent to removing the node from the graph.
If FILE is nil, clear the current buffer." If FILE is nil, clear the current buffer."
(setq file (or file (buffer-file-name (buffer-base-buffer)))) (setq file (or file (buffer-file-name (buffer-base-buffer))))
(dolist (table (mapcar #'car org-roam-db--table-schemata)) (org-roam-db-query [:delete :from files
(org-roam-db-query `[:delete :from ,table :where (= file $s1)]
:where (= file $s1)] file))
file)))
;;;;; Updating tables ;;;;; Updating tables
(defun org-roam-db-insert-file () (defun org-roam-db-insert-file ()
@ -270,9 +277,10 @@ If UPDATE-P is non-nil, first remove the file in the database."
(when (= (org-outline-level) 0) (when (= (org-outline-level) 0)
(when-let ((id (org-id-get))) (when-let ((id (org-id-get)))
(let* ((file (buffer-file-name (buffer-base-buffer))) (let* ((file (buffer-file-name (buffer-base-buffer)))
(title (or (cadr (assoc "TITLE" (org-collect-keywords '("title")) (title (org-link-display-format
#'string-equal)) (or (cadr (assoc "TITLE" (org-collect-keywords '("title"))
(file-relative-name file org-roam-directory))) #'string-equal))
(file-relative-name file org-roam-directory))))
(pos (point)) (pos (point))
(todo nil) (todo nil)
(priority nil) (priority nil)
@ -281,46 +289,44 @@ If UPDATE-P is non-nil, first remove the file in the database."
(level 0) (level 0)
(aliases (org-entry-get (point) "ROAM_ALIASES")) (aliases (org-entry-get (point) "ROAM_ALIASES"))
(tags org-file-tags) (tags org-file-tags)
(refs (org-entry-get (point) "ROAM_REFS"))) (refs (org-entry-get (point) "ROAM_REFS"))
(condition-case nil (properties (org-entry-properties))
(progn (olp (org-get-outline-path)))
(org-roam-db-query
[:insert :into nodes
:values $v1]
(vector id file level pos todo priority
scheduled deadline title properties olp))
(when tags
(org-roam-db-query
[:insert :into tags
:values $v1]
(mapcar (lambda (tag)
(vector id (substring-no-properties tag)))
tags)))
(when aliases
(org-roam-db-query
[:insert :into aliases
:values $v1]
(mapcar (lambda (alias)
(vector id alias))
(split-string-and-unquote aliases))))
(when refs
(setq refs (split-string-and-unquote refs))
(let (rows)
(dolist (ref refs)
(if (string-match org-link-plain-re ref)
(progn
(push (vector id (match-string 2 ref)
(match-string 1 ref)) rows))
(lwarn '(org-roam) :warning
"%s:%s\tInvalid ref %s, skipping..."
(buffer-file-name) (point) ref)))
(when rows
(org-roam-db-query (org-roam-db-query
[:insert :into nodes [:insert :into refs
:values $v1] :values $v1]
(vector id file level pos todo priority rows)))))))))
scheduled deadline title))
(when tags
(org-roam-db-query
[:insert :into tags
:values $v1]
(mapcar (lambda (tag)
(vector file id (substring-no-properties tag)))
tags)))
(when aliases
(org-roam-db-query
[:insert :into aliases
:values $v1]
(mapcar (lambda (alias)
(vector file id alias))
(split-string-and-unquote aliases))))
(when refs
(setq refs (split-string-and-unquote refs))
(let (rows)
(dolist (ref refs)
(if (string-match org-link-plain-re ref)
(progn
(push (vector file id (match-string 2 ref)
(match-string 1 ref)) rows))
(lwarn '(org-roam) :warning
"%s:%s\tInvalid ref %s, skipping..."
(buffer-file-name) (point) ref)))
(when rows
(org-roam-db-query
[:insert :into refs
:values $v1]
rows)))))
(t
(lwarn '(org-roam) :error "Duplicate ID %s, skipping..." id))))))))
(defun org-roam-db-insert-node-data () (defun org-roam-db-insert-node-data ()
"Insert node data for headline at point into the Org-roam cache." "Insert node data for headline at point into the Org-roam cache."
@ -333,16 +339,14 @@ If UPDATE-P is non-nil, first remove the file in the database."
(level (nth 1 heading-components)) (level (nth 1 heading-components))
(scheduled (org-roam-db-get-scheduled-time)) (scheduled (org-roam-db-get-scheduled-time))
(deadline (org-roam-db-get-deadline-time)) (deadline (org-roam-db-get-deadline-time))
(title (nth 4 heading-components))) (title (org-link-display-format (nth 4 heading-components)))
(condition-case nil (properties (org-entry-properties))
(org-roam-db-query (olp (org-get-outline-path)))
[:insert :into nodes (org-roam-db-query
:values $v1] [:insert :into nodes
(vector id file level pos todo priority :values $v1]
scheduled deadline title)) (vector id file level pos todo priority
(t scheduled deadline title properties olp)))))
(lwarn '(org-roam) :error
"Duplicate ID %s, skipping..." id))))))
(defun org-roam-db-insert-aliases () (defun org-roam-db-insert-aliases ()
"Insert aliases for node at point into Org-roam cache." "Insert aliases for node at point into Org-roam cache."
@ -357,18 +361,16 @@ If UPDATE-P is non-nil, first remove the file in the database."
(defun org-roam-db-insert-tags () (defun org-roam-db-insert-tags ()
"Insert tags for node at point into Org-roam cache." "Insert tags for node at point into Org-roam cache."
(when-let ((file (buffer-file-name (buffer-base-buffer))) (when-let ((node-id (org-id-get))
(node-id (org-id-get))
(tags (org-get-tags))) (tags (org-get-tags)))
(org-roam-db-query [:insert :into tags (org-roam-db-query [:insert :into tags
:values $v1] :values $v1]
(mapcar (lambda (tag) (mapcar (lambda (tag)
(vector file node-id tag)) tags)))) (vector node-id (substring-no-properties tag))) tags))))
(defun org-roam-db-insert-refs () (defun org-roam-db-insert-refs ()
"Insert refs for node at point into Org-roam cache." "Insert refs for node at point into Org-roam cache."
(when-let* ((file (buffer-file-name (buffer-base-buffer))) (when-let* ((node-id (org-id-get))
(node-id (org-id-get))
(refs (org-entry-get (point) "ROAM_REFS")) (refs (org-entry-get (point) "ROAM_REFS"))
(refs (split-string-and-unquote refs))) (refs (split-string-and-unquote refs)))
(let (rows) (let (rows)
@ -376,7 +378,7 @@ If UPDATE-P is non-nil, first remove the file in the database."
(save-match-data (save-match-data
(if (string-match org-link-plain-re ref) (if (string-match org-link-plain-re ref)
(progn (progn
(push (vector file node-id (match-string 2 ref) (match-string 1 ref)) rows)) (push (vector node-id (match-string 2 ref) (match-string 1 ref)) rows))
(lwarn '(org-roam) :warning (lwarn '(org-roam) :warning
"%s:%s\tInvalid ref %s, skipping..." (buffer-file-name) (point) ref)))) "%s:%s\tInvalid ref %s, skipping..." (buffer-file-name) (point) ref))))
(org-roam-db-query [:insert :into refs (org-roam-db-query [:insert :into refs
@ -387,8 +389,7 @@ If UPDATE-P is non-nil, first remove the file in the database."
"Insert link data for LINK at current point into the Org-roam cache." "Insert link data for LINK at current point into the Org-roam cache."
(save-excursion (save-excursion
(goto-char (org-element-property :begin link)) (goto-char (org-element-property :begin link))
(let ((file (buffer-file-name (buffer-base-buffer))) (let ((type (org-element-property :type link))
(type (org-element-property :type link))
(dest (org-element-property :path link)) (dest (org-element-property :path link))
(properties (list :outline (org-get-outline-path))) (properties (list :outline (org-get-outline-path)))
(source (org-roam-id-at-point))) (source (org-roam-id-at-point)))
@ -396,7 +397,7 @@ If UPDATE-P is non-nil, first remove the file in the database."
(org-roam-db-query (org-roam-db-query
[:insert :into links [:insert :into links
:values $v1] :values $v1]
(vector file (point) source dest type properties)))))) (vector (point) source dest type properties))))))
;;;;; Fetching ;;;;; Fetching
(defun org-roam-db--get-current-files () (defun org-roam-db--get-current-files ()
@ -436,18 +437,19 @@ If FORCE, force a rebuild of the cache from scratch."
contents-hash) contents-hash)
(push file modified-files))) (push file modified-files)))
(remhash file current-files)) (remhash file current-files))
(if (fboundp 'dolist-with-progress-reporter) (emacsql-with-transaction (org-roam-db)
(dolist-with-progress-reporter (file (hash-table-keys current-files)) (if (fboundp 'dolist-with-progress-reporter)
"Clearing removed files..." (dolist-with-progress-reporter (file (hash-table-keys current-files))
(org-roam-db-clear-file file)) "Clearing removed files..."
(dolist (file (hash-table-keys current-files)) (org-roam-db-clear-file file))
(org-roam-db-clear-file file))) (dolist (file (hash-table-keys current-files))
(if (fboundp 'dolist-with-progress-reporter) (org-roam-db-clear-file file)))
(dolist-with-progress-reporter (file modified-files) (if (fboundp 'dolist-with-progress-reporter)
"Processing modified files..." (dolist-with-progress-reporter (file modified-files)
(org-roam-db-update-file file)) "Processing modified files..."
(dolist (file modified-files) (org-roam-db-update-file file))
(org-roam-db-update-file file))))) (dolist (file modified-files)
(org-roam-db-update-file file))))))
(defun org-roam-db-update-file (&optional file-path) (defun org-roam-db-update-file (&optional file-path)
"Update Org-roam cache for FILE-PATH. "Update Org-roam cache for FILE-PATH.

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/jethrokuan/org-roam ;; URL: https://github.com/jethrokuan/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -212,7 +212,8 @@ If CHECKALL, run the check for all Org-roam files."
(let ((buf (find-file-noselect f))) (let ((buf (find-file-noselect f)))
(org-roam-doctor--check buf checkers) (org-roam-doctor--check buf checkers)
(unless (memq buf existing-buffers) (unless (memq buf existing-buffers)
(save-buffer buf) (with-current-buffer buf
(save-buffer))
(kill-buffer buf)))))) (kill-buffer buf))))))
(org-roam-message "Linting completed.")) (org-roam-message "Linting completed."))

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -48,6 +48,7 @@ If FILE is nil, execute BODY in the current buffer.
Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited." Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited."
(declare (indent 2) (debug t)) (declare (indent 2) (debug t))
`(let* (new-buf `(let* (new-buf
(auto-mode-alist nil)
(buf (or (and (not ,file) (buf (or (and (not ,file)
(current-buffer)) ;If FILE is nil, use current buffer (current-buffer)) ;If FILE is nil, use current buffer
(find-buffer-visiting ,file) ; If FILE is already visited, find buffer (find-buffer-visiting ,file) ; If FILE is already visited, find buffer
@ -57,7 +58,10 @@ Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited."
res) res)
(with-current-buffer buf (with-current-buffer buf
(unless (equal major-mode 'org-mode) (unless (equal major-mode 'org-mode)
(delay-mode-hooks (org-mode))) (delay-mode-hooks
(let ((org-inhibit-startup t)
(org-agenda-files nil))
(org-mode))))
(setq res (progn ,@body)) (setq res (progn ,@body))
(unless (and new-buf (not ,keep-buf-p)) (unless (and new-buf (not ,keep-buf-p))
(save-buffer))) (save-buffer)))

View File

@ -5,7 +5,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -124,11 +124,12 @@ and `:slant'."
(defvar org-roam-current-node nil (defvar org-roam-current-node nil
"The current node at point.") "The current node at point.")
(defcustom org-roam-mode-sections (list #'org-roam-backlinks-section (defcustom org-roam-mode-section-functions (list #'org-roam-backlinks-section
#'org-roam-reflinks-section) #'org-roam-reflinks-section)
"List of functions that insert sections for Org-roam." "Functions which insert sections of the `org-roam-buffer'.
Each function is called with one argument, which is the current org-roam node at point."
:group 'org-roam :group 'org-roam
:type '(repeat function)) :type 'hook)
;;; The mode ;;; The mode
(defvar org-roam-mode-map (defvar org-roam-mode-map
@ -162,8 +163,7 @@ which visits the thing at point."
(org-roam-set-header-line-format (org-roam-node-title org-roam-current-node)) (org-roam-set-header-line-format (org-roam-node-title org-roam-current-node))
(magit-insert-section (org-roam) (magit-insert-section (org-roam)
(magit-insert-heading) (magit-insert-heading)
(dolist (fn org-roam-mode-sections) (run-hook-with-args 'org-roam-mode-section-functions org-roam-current-node)))))
(funcall fn org-roam-current-node))))))
(defun org-roam-buffer () (defun org-roam-buffer ()
"Launch an Org-roam buffer for the current node at point." "Launch an Org-roam buffer for the current node at point."
@ -230,7 +230,7 @@ Has no effect when `org-roam-current-node' is nil."
(org-roam-set-header-line-format (org-roam-node-title org-roam-current-node)) (org-roam-set-header-line-format (org-roam-node-title org-roam-current-node))
(magit-insert-section (org-roam) (magit-insert-section (org-roam)
(magit-insert-heading) (magit-insert-heading)
(dolist (fn org-roam-mode-sections) (dolist (fn org-roam-mode-section-functions)
(funcall fn org-roam-current-node))))))) (funcall fn org-roam-current-node)))))))
(defun org-roam-buffer--redisplay () (defun org-roam-buffer--redisplay ()
@ -419,9 +419,9 @@ References from FILE are excluded."
(let* ((titles (cons (org-roam-node-title node) (let* ((titles (cons (org-roam-node-title node)
(org-roam-node-aliases node))) (org-roam-node-aliases node)))
(rg-command (concat "rg -o --vimgrep -P -i " (rg-command (concat "rg -o --vimgrep -P -i "
(string-join (mapcar (lambda (glob) (concat "-g " glob)) (mapconcat (lambda (glob) (concat "-g " glob))
(org-roam--list-files-search-globs (org-roam--list-files-search-globs org-roam-file-extensions)
org-roam-file-extensions)) " ") " ")
(format " '\\[([^[]]++|(?R))*\\]%s' " (format " '\\[([^[]]++|(?R))*\\]%s' "
(mapconcat (lambda (title) (mapconcat (lambda (title)
(format "|(\\b%s\\b)" (shell-quote-argument title))) (format "|(\\b%s\\b)" (shell-quote-argument title)))

View File

@ -5,7 +5,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -156,20 +156,24 @@ Adapted from `s-format'."
t t) t t)
(set-match-data saved-match-data)))) (set-match-data saved-match-data))))
(defvar org-roam--cached-display-format nil)
(defun org-roam--process-display-format (format) (defun org-roam--process-display-format (format)
"Pre-calculate minimal widths needed by the FORMAT string." "Pre-calculate minimal widths needed by the FORMAT string."
(let* ((fields-width 0) (or org-roam--cached-display-format
(string-width (setq org-roam--cached-display-format
(string-width (let* ((fields-width 0)
(org-roam-format (string-width
format (string-width
(lambda (field) (org-roam-format
(setq fields-width format
(+ fields-width (lambda (field)
(string-to-number (setq fields-width
(or (cadr (split-string field ":")) (+ fields-width
""))))))))) (string-to-number
(cons format (+ fields-width string-width)))) (or (cadr (split-string field ":"))
"")))))))))
(cons format (+ fields-width string-width))))))
;;; Diagnostics ;;; Diagnostics
;;;###autoload ;;;###autoload

View File

@ -6,7 +6,7 @@
;; URL: https://github.com/org-roam/org-roam ;; URL: https://github.com/org-roam/org-roam
;; Keywords: org-mode, roam, convenience ;; Keywords: org-mode, roam, convenience
;; Version: 2.0.0 ;; Version: 2.0.0
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2") (magit-section "2.90.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "2.90.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -50,7 +50,8 @@
;;;; Features ;;;; Features
(require 'org-roam-compat) (require 'org-roam-compat)
(eval-when-compile (eval-when-compile
(require 'org-roam-macs)) (require 'org-roam-macs)
(require 'org-macs))
(require 'org-roam-utils) (require 'org-roam-utils)
(require 'org-roam-mode) (require 'org-roam-mode)
(require 'org-roam-completion) (require 'org-roam-completion)
@ -152,7 +153,8 @@ method symbol as a cons cell. For example: '(find (rg . \"/path/to/rg\"))."
) )
"Characters to trim from Unicode normalization for slug. "Characters to trim from Unicode normalization for slug.
By default, the characters are specified to remove Diacritical Marks from the Latin alphabet." By default, the characters are specified to remove Diacritical
Marks from the Latin alphabet."
:type '(repeat character) :type '(repeat character)
:group 'org-roam) :group 'org-roam)
@ -341,11 +343,19 @@ Use external shell commands if defined in `org-roam-list-files-commands'."
(puthash node-id (cons tag (gethash node-id ht)) ht)) (puthash node-id (cons tag (gethash node-id ht)) ht))
ht)) ht))
(defun org-roam--org-roam-buffer-p (&optional buffer)
"Return t if BUFFER is accessing a part of Org-roam system.
If BUFFER is not specified, use the current buffer."
(let ((buffer (or buffer (current-buffer)))
path)
(with-current-buffer buffer
(and (derived-mode-p 'org-mode)
(setq path (buffer-file-name (buffer-base-buffer)))
(org-roam--org-roam-file-p path)))))
(defun org-roam--get-roam-buffers () (defun org-roam--get-roam-buffers ()
"Return a list of buffers that are Org-roam files." "Return a list of buffers that are Org-roam files."
(--filter (and (with-current-buffer it (derived-mode-p 'org-mode)) (--filter (org-roam--org-roam-buffer-p it)
(buffer-file-name it)
(org-roam--org-roam-file-p (buffer-file-name it)))
(buffer-list))) (buffer-list)))
(defun org-roam--get-titles () (defun org-roam--get-titles ()
@ -421,7 +431,7 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
;;;; Nodes ;;;; Nodes
(cl-defstruct (org-roam-node (:constructor org-roam-node-create) (cl-defstruct (org-roam-node (:constructor org-roam-node-create)
(:copier nil)) (:copier nil))
id file level point todo priority scheduled deadline title id file level point todo priority scheduled deadline title properties olp
tags aliases refs) tags aliases refs)
(cl-defmethod org-roam-node-slug ((node org-roam-node)) (cl-defmethod org-roam-node-slug ((node org-roam-node))
@ -471,45 +481,47 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
Uses the ID, and fetches remaining details from the database. Uses the ID, and fetches remaining details from the database.
This can be quite costly: avoid, unless dealing with very few This can be quite costly: avoid, unless dealing with very few
nodes." nodes."
(let ((node-info (car (org-roam-db-query [:select [file level pos todo priority scheduled deadline title] (when-let ((node-info (car (org-roam-db-query [:select [file level pos todo priority
:from nodes scheduled deadline title properties olp]
:where (= id $s1) :from nodes
:limit 1] :where (= id $s1)
(org-roam-node-id node)))) :limit 1]
(tag-info (mapcar #'car (org-roam-db-query [:select [tag] :from tags (org-roam-node-id node)))))
:where (= node-id $s1)] (let ((tag-info (mapcar #'car (org-roam-db-query [:select [tag] :from tags
(org-roam-node-id node))))
(alias-info (mapcar #'car (org-roam-db-query [:select [alias] :from aliases
:where (= node-id $s1)] :where (= node-id $s1)]
(org-roam-node-id node)))) (org-roam-node-id node))))
(refs-info (mapcar #'car (org-roam-db-query [:select [ref] :from refs (alias-info (mapcar #'car (org-roam-db-query [:select [alias] :from aliases
:where (= node-id $s1)] :where (= node-id $s1)]
(org-roam-node-id node))))) (org-roam-node-id node))))
(pcase-let ((`(,file ,level ,pos ,todo ,priority ,scheduled ,deadline ,title) node-info)) (refs-info (mapcar #'car (org-roam-db-query [:select [ref] :from refs
(setf (org-roam-node-file node) file :where (= node-id $s1)]
(org-roam-node-level node) level (org-roam-node-id node)))))
(org-roam-node-point node) pos (pcase-let ((`(,file ,level ,pos ,todo ,priority ,scheduled
(org-roam-node-todo node) todo ,deadline ,title ,properties ,olp) node-info))
(org-roam-node-priority node) priority (setf (org-roam-node-file node) file
(org-roam-node-scheduled node) scheduled (org-roam-node-level node) level
(org-roam-node-deadline node) deadline (org-roam-node-point node) pos
(org-roam-node-title node) title (org-roam-node-todo node) todo
(org-roam-node-tags node) tag-info (org-roam-node-priority node) priority
(org-roam-node-refs node) refs-info (org-roam-node-scheduled node) scheduled
(org-roam-node-aliases node) alias-info)) (org-roam-node-deadline node) deadline
node)) (org-roam-node-title node) title
(org-roam-node-properties node) properties
(org-roam-node-olp node) olp
(org-roam-node-tags node) tag-info
(org-roam-node-refs node) refs-info
(org-roam-node-aliases node) alias-info))))
node)
(defcustom org-roam-node-display-template (defcustom org-roam-node-display-template
"${title:*} ${tags:10}" "${title:*} ${tags:10}"
"Configures display formatting for Org-roam node." "Configures display formatting for Org-roam node."
:group 'org-roam :group 'org-roam
:type 'string) :type 'string)
(defun org-roam--tags-to-str (tags) (defun org-roam--tags-to-str (tags)
"Convert list of TAGS into a string." "Convert list of TAGS into a string."
(string-join (mapconcat (lambda (s) (concat "#" s)) tags " "))
(mapcar (lambda (s) (concat "#" s)) tags)
" "))
(defun org-roam-node--format-entry (node width) (defun org-roam-node--format-entry (node width)
"Formats NODE for display in the results list. "Formats NODE for display in the results list.
@ -529,6 +541,9 @@ WIDTH is the width of the results list."
(when (and (equal field-name "file") (when (and (equal field-name "file")
field-value) field-value)
(setq field-value (file-relative-name field-value org-roam-directory))) (setq field-value (file-relative-name field-value org-roam-directory)))
(when (and (equal field-name "olp")
field-value)
(setq field-value (string-join field-value " > ")))
(if (not field-width) (if (not field-width)
field-value field-value
(setq field-width (string-to-number field-width)) (setq field-width (string-to-number field-width))
@ -556,8 +571,15 @@ WIDTH is the width of the results list."
If ASSERT, throw an error." If ASSERT, throw an error."
(if-let ((node (magit-section-case (if-let ((node (magit-section-case
(org-roam-node-section (oref it node)) (org-roam-node-section (oref it node))
(t (when-let ((id (org-roam-id-at-point))) (t (let (id)
(org-roam-populate (org-roam-node-create :id id))))))) (org-with-wide-buffer
(while (and (not (setq id (org-id-get)))
(not (bobp)))
(org-roam-up-heading-or-point-min))
(when id
(org-roam-populate
(org-roam-node-create :id id
:point (point))))))))))
node node
(when assert (when assert
(user-error "No node at point")))) (user-error "No node at point"))))
@ -601,8 +623,7 @@ Throw an error if multiple choices exist."
:where (= title $s1)] :where (= title $s1)]
s) s)
(org-roam-db-query [:select [node-id] :from aliases (org-roam-db-query [:select [node-id] :from aliases
:left :join nodes :on (= nodes:id aliases:node-id) :where (= alias $s1)]
:where (= aliases:node-id $s1)]
s))))) s)))))
(cond (cond
((seq-empty-p matches) ((seq-empty-p matches)
@ -616,19 +637,22 @@ Throw an error if multiple choices exist."
"Return an alist for node completion. "Return an alist for node completion.
The car is the displayed title or alias for the node, and the cdr The car is the displayed title or alias for the node, and the cdr
is the `org-roam-node'." is the `org-roam-node'."
(setq org-roam--cached-display-format nil)
(let ((tags-table (org-roam--tags-table))) (let ((tags-table (org-roam--tags-table)))
(cl-loop for row in (append (cl-loop for row in (append
(org-roam-db-query [:select [file pos title title id] (org-roam-db-query [:select [file pos title title id properties olp]
:from nodes]) :from nodes])
(org-roam-db-query [:select [nodes:file pos alias title node-id] (org-roam-db-query [:select [nodes:file pos alias title node-id]
:from aliases :from aliases
:left-join nodes :left-join nodes
:on (= aliases:node-id nodes:id)])) :on (= aliases:node-id nodes:id)]))
collect (pcase-let* ((`(,file ,pos ,alias ,title ,id) row) collect (pcase-let* ((`(,file ,pos ,alias ,title ,id ,properties ,olp) row)
(node (org-roam-node-create :id id (node (org-roam-node-create :id id
:file file :file file
:title alias :title alias
:point pos :point pos
:properties properties
:olp olp
:tags (gethash id tags-table))) :tags (gethash id tags-table)))
(candidate-main (org-roam-node--format-entry node (1- (frame-width)))) (candidate-main (org-roam-node--format-entry node (1- (frame-width))))
(tag-str (org-roam--tags-to-str (org-roam-node-tags node)))) (tag-str (org-roam--tags-to-str (org-roam-node-tags node))))
@ -691,8 +715,7 @@ POINT is the point in buffer for the link.
PROPERTIES contains properties about the link." PROPERTIES contains properties about the link."
(magit-insert-section section (org-roam-node-section) (magit-insert-section section (org-roam-node-section)
(let ((outline (if-let ((outline (plist-get properties :outline))) (let ((outline (if-let ((outline (plist-get properties :outline)))
(string-join (mapcar #'org-link-display-format outline) (mapconcat #'org-link-display-format outline " > ")
" > ")
"Top"))) "Top")))
(insert (concat (propertize (org-roam-node-title source-node) (insert (concat (propertize (org-roam-node-title source-node)
'font-lock-face 'org-roam-title) 'font-lock-face 'org-roam-title)
@ -779,41 +802,121 @@ window instead."
(let* ((p (org-entry-get (point) prop)) (let* ((p (org-entry-get (point) prop))
(lst (when p (split-string-and-unquote p))) (lst (when p (split-string-and-unquote p)))
(lst (if (memq s lst) lst (cons s lst)))) (lst (if (memq s lst) lst (cons s lst))))
(org-set-property prop (combine-and-quote-strings lst)))) (org-set-property prop (combine-and-quote-strings lst))
s))
(defun org-roam-remove-property (prop) (defun org-roam-remove-property (prop &optional s)
"Prompt to remove an item from PROP." "Remove S from property PROP.
If S is not specified, user is prompted to select a value."
(let* ((p (org-entry-get (point) prop)) (let* ((p (org-entry-get (point) prop))
(lst (when p (split-string-and-unquote p))) (lst (when p (split-string-and-unquote p)))
(prop-to-remove (completing-read "Remove: " lst)) (prop-to-remove (or s (completing-read "Remove: " lst)))
(lst (delete prop-to-remove lst))) (lst (delete prop-to-remove lst)))
(if lst (if lst
(org-set-property prop (combine-and-quote-strings lst)) (org-set-property prop (combine-and-quote-strings lst))
(org-delete-property prop)))) (org-delete-property prop))
prop-to-remove))
(defun org-roam-set-keyword (key value)
"Set keyword KEY to VALUE.
If the property is already set, it's value is replaced."
(org-with-point-at 1
(let ((case-fold-search t))
(if (re-search-forward (concat "^#\\+" key ":\\(.*\\)") (point-max) t)
(if (string-blank-p value)
(kill-whole-line)
(replace-match (concat " " value) 'fixedcase nil nil 1))
(while (and (not (eobp))
(looking-at "^[#:]"))
(if (save-excursion (end-of-line) (eobp))
(progn
(end-of-line)
(insert "\n"))
(forward-line)
(beginning-of-line)))
(insert "#+" key ": " value "\n")))))
;;;; Tags
(defun org-roam-tag-completions ()
"Return list of tags for completions within Org-roam."
(let ((roam-tags (mapcar #'car (org-roam-db-query [:select :distinct [tag] :from tags])))
(org-tags (cl-loop for tagg in org-tag-alist
nconc (pcase tagg
('(:newline)
nil)
(`(,tag . ,_)
(list tag))
(_ nil)))))
(seq-uniq (append roam-tags org-tags))))
(defun org-roam-tag-add (tag)
"Add TAG to the node at point."
(interactive
(list (completing-read "Tag: " (org-roam-tag-completions))))
(let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(if (= (org-outline-level) 0)
(let ((current-tags (split-string (or (cadr (assoc "FILETAGS"
(org-collect-keywords '("filetags"))))
""))))
(org-roam-set-keyword "filetags" (string-join (seq-uniq (cons tag current-tags)) " ")))
(org-set-tags (seq-uniq (cons tag (org-get-tags)))))
tag)))
(defun org-roam-tag-remove (&optional tag)
"Remove a TAG to the node at point."
(interactive)
(let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(if (= (org-outline-level) 0)
(let* ((current-tags (split-string (or (cadr (assoc "FILETAGS"
(org-collect-keywords '("filetags"))))
(user-error "No tag to remove"))))
(tag (or tag (completing-read "Tag: " current-tags))))
(org-roam-set-keyword "filetags" (string-join (delete tag current-tags) " ")))
(let* ((current-tags (or (org-get-tags)
(user-error "No tag to remove")))
(tag (completing-read "Tag: " current-tags)))
(org-set-tags (delete tag current-tags))))
tag)))
;;;; Aliases ;;;; Aliases
(defun org-roam-alias-add (alias) (defun org-roam-alias-add (alias)
"Add ALIAS to the node at point." "Add ALIAS to the node at point."
(interactive "sAlias: ") (interactive "sAlias: ")
(org-roam-add-property alias "ROAM_ALIASES")) (let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(org-roam-add-property alias "ROAM_ALIASES"))))
(defun org-roam-alias-remove () (defun org-roam-alias-remove (&optional alias)
"Remove an alias from the node at point." "Remove an ALIAS from the node at point."
(interactive) (interactive)
(org-roam-remove-property "ROAM_ALIASES")) (let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(org-roam-remove-property "ROAM_ALIASES" alias))))
;;;; Refs ;;;; Refs
(defun org-roam-ref-add (ref) (defun org-roam-ref-add (ref)
"Add REF to the node at point." "Add REF to the node at point."
(interactive "sRef: ") (interactive "sRef: ")
(org-roam-add-property ref "ROAM_REFS")) (let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(org-roam-add-property ref "ROAM_REFS"))))
(defun org-roam-ref-remove () (defun org-roam-ref-remove (&optional ref)
"Remove a ref from the node at point." "Remove a REF from the node at point."
(interactive) (interactive)
(org-roam-remove-property "ROAM_REFS")) (let ((node (org-roam-node-at-point 'assert)))
(save-excursion
(goto-char (org-roam-node-point node))
(org-roam-remove-property "ROAM_REFS" ref))))
;;;; Refs
(defun org-roam-ref--completions () (defun org-roam-ref--completions ()
"Return an alist for ref completion. "Return an alist for ref completion.
The car is the ref, and the cdr is the corresponding node for the ref." The car is the ref, and the cdr is the corresponding node for the ref."