Compare commits

...

11 Commits
v2.3.0 ... main

Author SHA1 Message Date
89dfaef38b Fix link replacement optimization
We are searching for a string literal for speed, so don’t treat it as a
regexp.

Fixes: fc8638759b ("feat: Limit link replacement scope")
Fixes: #2529
2025-07-01 00:28:11 -05:00
fc8638759b feat: Limit link replacement scope
Test for `roam:` link prefix to limit the number of times we need to
call `org-roam-link-replace-at-point`.
2025-06-29 19:44:00 -05:00
1958e035fc build: do not lock issues
Allow folks to revisit issues at any time if they want to restart the conversation.

See Discourse conversation: https://org-roam.discourse.group/t/org-roam-development-status-may-2025/3810/15

Amend: d099204129
2025-06-28 15:38:16 -07:00
1ea7e3077c fix: run org-roam-db-clear-file on file deletion via vc-delete-file 2025-06-26 14:54:27 -07:00
7ce95a286b release 2.3.1 2025-06-26 11:55:56 -07:00
c172951345 nit: fix indentation 2025-06-09 01:20:49 -07:00
0786f73669 fix: write unlinked references regex to a temp file
Node titles with special characters (single quotes, dollar signs, etc.)
break the ripgrep command because the regex pattern is passed through
the shell. This causes silent failures that show up as unlinked
references not being present for a given node.

This change writes the regex pattern to a temp file and uses ripgrep's
--file option instead of shell command line. `shell-quote-argument` is
replaced with `regexp-quote` since we're no longer passing through
shell. Wrapped in unwind-protect for cleanup.

Fix: #2407
Close: #2408
2025-06-09 00:39:50 -07:00
df4e903208 perf: suppress extra org features in the temp buffer
Figuring out how to indent things and folding and clock updates, etc.
consumes a great deal of time especially considering that files
are (currently) processed for /every single backlink/ even if multiple
backlinks come from the same file.

This dramatically improves the load/refresh time for the org-roam
buffer.

Fix: #2399
2025-06-07 19:58:20 -07:00
d099204129 build: tidy up old issues
To keep us from having to triage a ton of old issues/PRs that may not be
relevant anymore; this should give us a fresh(ish) start.
2025-06-07 10:27:45 -07:00
031ee63bee (fix): Use correct type specifications to suppress warnings 2025-05-27 08:58:53 -07:00
fed577f805 refactor: improve speed of retrieving and formatting nodes
The original code uses macros that when expanded use a lot memory
and are relatively slow. This removes some of that processing:

- Add a constructor to org-roam-node that does uses parameters
  by position instead or by name

- Add the option of using a function to format nodes. The current
  code uses a string template. But this template is processes
  for every node. Using a function will further improve
  performance significantly.

Simplified expected return value user's template function.

As suggested by akashpal-21, the return value of the user's template
function can be simpler.

I have refactored the code such that the function only returns
a string (potentially propertized) with the formatted node.

I have also modified the documentation of
org-roam-node-display-template with an example of the user defined
function.

Acknowledgement: akashpal-21 for the suggestion for improvement

Close: #2511
2025-05-25 00:15:21 -07:00
20 changed files with 267 additions and 94 deletions

93
.github/workflows/tidy-issues.yml vendored Normal file
View File

@ -0,0 +1,93 @@
name: 'Manage stale/dormant issues and PRs'
on:
schedule:
- cron: '0 1 * * *' # Daily at 1 AM UTC
workflow_dispatch: # Allow this to be run manually
inputs:
dry_run:
description: 'Dry run: Show what would happen without making changes'
required: false
default: 'false'
type: boolean
jobs:
stale:
runs-on: ubuntu-latest
steps:
# Handle stale issues/PRs
- name: Mark/close stale issues and PRs
uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Dry run setting
debug-only: ${{ github.event.inputs.dry_run == 'true' }}
# Messages
stale-issue-message: |
📅 **Stale Issue Notice**
This issue has been automatically marked as stale because it has not had recent activity for **6 months**.
**⏰ This issue will be closed in 2 weeks** if no further activity occurs.
**To keep this issue open:**
- Comment on this issue
- Reference it in a commit or PR
- Add new information or updates
Thank you for your contributions to org-roam! 🙏
close-issue-message: |
🔒 **Issue Automatically Closed**
This issue was automatically closed due to **6 months of inactivity** followed by 2 weeks notice.
**To reopen:**
- If still relevant, comment below and we'll reopen
- Or create a new issue with updated information
If this issue is not reopened in 2 weeks, it will be locked.
This helps keep our issue tracker focused and manageable.
stale-pr-message: |
📅 **Stale Pull Request Notice**
This pull request has been automatically marked as stale because it has not had recent activity for **6 months**.
**⏰ This PR will be closed in 2 weeks** if no further activity occurs.
**To keep this PR open:**
- Push new commits
- Comment with updates
- Rebase on latest main branch
Thank you for your contributions to org-roam! 🙏
close-pr-message: |
🔒 **Pull Request Automatically Closed**
This pull request was automatically closed due to **6 months of inactivity** followed by 2 weeks notice.
# Timing (6 months + 2 weeks)
days-before-stale: 182 # ~6 months
days-before-close: 14 # 2 weeks notice
# Performance
operations-per-run: 1000
# Show dry run summary
- name: Dry run summary
if: github.event.inputs.dry_run == 'true'
run: |
echo "🧪 DRY RUN COMPLETED"
echo "This was a dry run - no actual changes were made."
echo "Check the action logs above to see what would have happened."
echo ""
echo "To run for real:"
echo "1. Go to Actions tab"
echo "2. Click 'Run workflow'"
echo "3. Leave 'Dry run mode' unchecked"
echo "4. Click 'Run workflow'"

View File

@ -1,5 +1,10 @@
# Changelog # Changelog
## 2.3.1 (2025-06-26)
* (fix): Use correct type specifications to suppress warnings by @okomestudio in https://github.com/org-roam/org-roam/pull/2522
* perf: suppress extra org features in the temp buffer by @dustinfarris in https://github.com/org-roam/org-roam/pull/2524
## 2.3.0 (2025-05-24) ## 2.3.0 (2025-05-24)
* (perf)node-read: filter before map to candidate by @russmatney in https://github.com/org-roam/org-roam/pull/2168 * (perf)node-read: filter before map to candidate by @russmatney in https://github.com/org-roam/org-roam/pull/2168

View File

@ -8,13 +8,13 @@
#+texinfo_dir_category: Emacs #+texinfo_dir_category: Emacs
#+texinfo_dir_title: Org-roam: (org-roam). #+texinfo_dir_title: Org-roam: (org-roam).
#+texinfo_dir_desc: Roam Research for Emacs. #+texinfo_dir_desc: Roam Research for Emacs.
#+subtitle: for version 2.3.0 #+subtitle: for version 2.3.1
#+options: H:4 num:3 toc:nil creator:t ':t #+options: H:4 num:3 toc:nil creator:t ':t
#+property: header-args :eval never #+property: header-args :eval never
#+texinfo: @noindent #+texinfo: @noindent
This manual is for Org-roam version 2.3.0. This manual is for Org-roam version 2.3.1.
#+BEGIN_QUOTE #+BEGIN_QUOTE
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@gmail.com> Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>

View File

@ -31,7 +31,7 @@ General Public License for more details.
@finalout @finalout
@titlepage @titlepage
@title Org-roam User Manual @title Org-roam User Manual
@subtitle for version 2.3.0 @subtitle for version 2.3.1
@author Jethro Kuan @author Jethro Kuan
@page @page
@vskip 0pt plus 1filll @vskip 0pt plus 1filll
@ -44,7 +44,7 @@ General Public License for more details.
@noindent @noindent
This manual is for Org-roam version 2.3.0. This manual is for Org-roam version 2.3.1.
@quotation @quotation
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com> Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com>

View File

@ -7,7 +7,7 @@
;; Leo Vivier <leo.vivier+dev@gmail.com> ;; Leo Vivier <leo.vivier+dev@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org-roam "2.1")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org-roam "2.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -4,7 +4,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1")) ;; Package-Requires: ((emacs "26.1"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -655,12 +655,14 @@ database, see `org-roam-db-sync' command."
(add-hook 'kill-emacs-hook #'org-roam-db--close-all) (add-hook 'kill-emacs-hook #'org-roam-db--close-all)
(advice-add #'rename-file :after #'org-roam-db-autosync--rename-file-a) (advice-add #'rename-file :after #'org-roam-db-autosync--rename-file-a)
(advice-add #'delete-file :before #'org-roam-db-autosync--delete-file-a) (advice-add #'delete-file :before #'org-roam-db-autosync--delete-file-a)
(advice-add #'vc-delete-file :around #'org-roam-db-autosync--vc-delete-file-a)
(org-roam-db-sync)) (org-roam-db-sync))
(t (t
(remove-hook 'find-file-hook #'org-roam-db-autosync--setup-file-h) (remove-hook 'find-file-hook #'org-roam-db-autosync--setup-file-h)
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all) (remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
(advice-remove #'rename-file #'org-roam-db-autosync--rename-file-a) (advice-remove #'rename-file #'org-roam-db-autosync--rename-file-a)
(advice-remove #'delete-file #'org-roam-db-autosync--delete-file-a) (advice-remove #'delete-file #'org-roam-db-autosync--delete-file-a)
(advice-remove #'vc-delete-file #'org-roam-db-autosync--vc-delete-file-a)
(org-roam-db--close-all) (org-roam-db--close-all)
;; Disable local hooks for all org-roam buffers ;; Disable local hooks for all org-roam buffers
(dolist (buf (org-roam-buffer-list)) (dolist (buf (org-roam-buffer-list))
@ -688,6 +690,17 @@ FILE is removed from the database."
(org-roam-file-p file)) (org-roam-file-p file))
(org-roam-db-clear-file (expand-file-name file)))) (org-roam-db-clear-file (expand-file-name file))))
(defun org-roam-db-autosync--vc-delete-file-a (fun file)
"Maintain cache consistency on file deletion by FUN.
FILE is removed from the database."
(let ((org-roam-file-p (and (not (auto-save-file-name-p file))
(not (backup-file-name-p file))
(org-roam-file-p file))))
(apply fun `(,file))
(when (and org-roam-file-p
(not (file-exists-p file)))
(org-roam-db-clear-file (expand-file-name file)))))
(defun org-roam-db-autosync--rename-file-a (old-file new-file-or-dir &rest _args) (defun org-roam-db-autosync--rename-file-a (old-file new-file-or-dir &rest _args)
"Maintain cache consistency of file rename. "Maintain cache consistency of file rename.
OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added." OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -658,17 +658,26 @@ This is the ROW within FILE."
(end-of-line) (end-of-line)
(point))))) (point)))))
(defun org-roam-unlinked-references--rg-command (titles) (defun org-roam-unlinked-references--rg-command (titles temp-file)
"Return the ripgrep command searching for TITLES." "Return the ripgrep command searching for TITLES using TEMP-FILE for pattern.
This avoids shell escaping issues by writing the pattern to a file instead
of passing it directly through the shell command line."
;; Write pattern to temp file to avoid shell escaping issues with quotes,
;; spaces, and other special characters in titles
(with-temp-file temp-file
(insert "\\[([^[]]++|(?R))*\\]"
(mapconcat (lambda (title)
;; Use regexp-quote instead of shell-quote-argument
;; since we're writing a regex pattern, not a shell argument
(format "|(\\b%s\\b)" (regexp-quote title)))
titles "")))
(concat "rg --follow --only-matching --vimgrep --pcre2 --ignore-case " (concat "rg --follow --only-matching --vimgrep --pcre2 --ignore-case "
(mapconcat (lambda (glob) (concat "--glob " glob)) (mapconcat (lambda (glob) (concat "--glob " glob))
(org-roam--list-files-search-globs org-roam-file-extensions) (org-roam--list-files-search-globs org-roam-file-extensions)
" ") " ")
(format " '\\[([^[]]++|(?R))*\\]%s' " " --file " (shell-quote-argument temp-file) " "
(mapconcat (lambda (title) (shell-quote-argument (expand-file-name org-roam-directory))))
(format "|(\\b%s\\b)" (shell-quote-argument title)))
titles ""))
(shell-quote-argument org-roam-directory)))
(defun org-roam-unlinked-references-section (node) (defun org-roam-unlinked-references-section (node)
"The unlinked references section for NODE. "The unlinked references section for NODE.
@ -679,33 +688,39 @@ References from FILE are excluded."
(shell-command-to-string "rg --pcre2-version")))) (shell-command-to-string "rg --pcre2-version"))))
(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 (org-roam-unlinked-references--rg-command titles)) ;; Create temp file for the regex pattern
(results (split-string (shell-command-to-string rg-command) "\n")) (temp-file (make-temp-file "org-roam-rg-pattern-"))
f row col match) (rg-command (org-roam-unlinked-references--rg-command titles temp-file)))
(magit-insert-section (unlinked-references) ;; Use unwind-protect to ensure temp file cleanup even if errors occur
(magit-insert-heading "Unlinked References:") (unwind-protect
(dolist (line results) (let* ((results (split-string (shell-command-to-string rg-command) "\n"))
(save-match-data f row col match)
(when (string-match org-roam-unlinked-references-result-re line) (magit-insert-section (unlinked-references)
(setq f (match-string 1 line) (magit-insert-heading "Unlinked References:")
row (string-to-number (match-string 2 line)) (dolist (line results)
col (string-to-number (match-string 3 line)) (save-match-data
match (match-string 4 line)) (when (string-match org-roam-unlinked-references-result-re line)
(when (and match (setq f (match-string 1 line)
(not (file-equal-p (org-roam-node-file node) f)) row (string-to-number (match-string 2 line))
(member (downcase match) (mapcar #'downcase titles))) col (string-to-number (match-string 3 line))
(magit-insert-section section (org-roam-grep-section) match (match-string 4 line))
(oset section file f) (when (and match
(oset section row row) (not (file-equal-p (org-roam-node-file node) f))
(oset section col col) (member (downcase match) (mapcar #'downcase titles)))
(insert (propertize (format "%s:%s:%s" (magit-insert-section section (org-roam-grep-section)
(truncate-string-to-width (file-name-base f) 15 nil nil t) (oset section file f)
row col) 'font-lock-face 'org-roam-dim) (oset section row row)
" " (oset section col col)
(org-roam-fontify-like-in-org-mode (insert (propertize (format "%s:%s:%s"
(org-roam-unlinked-references-preview-line f row)) (truncate-string-to-width (file-name-base f) 15 nil nil t)
"\n")))))) row col) 'font-lock-face 'org-roam-dim)
(insert ?\n))))) " "
(org-roam-fontify-like-in-org-mode
(org-roam-unlinked-references-preview-line f row))
"\n"))))))
(insert ?\n)))
;; Clean up temp file - this runs even if an error occurs above
(delete-file temp-file)))))
(provide 'org-roam-mode) (provide 'org-roam-mode)
;;; org-roam-mode.el ends here ;;; org-roam-mode.el ends here

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -39,6 +39,16 @@
;;;; Completing-read ;;;; Completing-read
(defcustom org-roam-node-display-template "${title}" (defcustom org-roam-node-display-template "${title}"
"Configures display formatting for Org-roam node. "Configures display formatting for Org-roam node.
If it is a function, it will be called to format a node.
Its result is expected to be a string (potentially with
embedded properties).
If it is a string and it will be used as described in org-roam
(see org-roam-node-display-template)
When it is a string, the following processing is done:
Patterns of form \"${field-name:length}\" are interpolated based Patterns of form \"${field-name:length}\" are interpolated based
on the current node. on the current node.
@ -64,9 +74,23 @@ as many characters as possible and will be aligned accordingly.
A closure can also be assigned to this variable in which case the A closure can also be assigned to this variable in which case the
closure is evaluated and the return value is used as the closure is evaluated and the return value is used as the
template. The closure must evaluate to a valid template string." template. The closure must evaluate to a valid template string.
When org-roam-node-display-template is a function, the function is
expected to return a string, potentially propertized. For example, the
following function shows the title and base filename of the node:
\(defun my--org-roam-format (node)
\"formats the node\"
(format \"%-40s %s\"
(if (org-roam-node-title node)
(propertize (org-roam-node-title node) 'face 'org-todo)
\"\")
(file-name-nondirectory (org-roam-node-file node))))
\q(setq org-roam-node-display-template 'my--org-roam-format)"
:group 'org-roam :group 'org-roam
:type '(string function)) :type '(choice string function))
(defcustom org-roam-node-annotation-function #'org-roam-node-read--annotation (defcustom org-roam-node-annotation-function #'org-roam-node-read--annotation
"This function used to attach annotations for `org-roam-node-read'. "This function used to attach annotations for `org-roam-node-read'.
@ -90,7 +114,7 @@ argument, an `org-roam-node', and return a string.
If a string is provided, it is a template string expanded by If a string is provided, it is a template string expanded by
`org-roam-node--format-entry'." `org-roam-node--format-entry'."
:group 'org-roam :group 'org-roam
:type '(string function)) :type '(choice string function))
(defcustom org-roam-node-template-prefixes (defcustom org-roam-node-template-prefixes
'(("tags" . "#") '(("tags" . "#")
@ -143,6 +167,10 @@ This path is relative to `org-roam-directory'."
:group 'org-roam :group 'org-roam
:type 'string) :type 'string)
(defvar org-roam-link-type "roam"
"Link type for org-roam nodes.
Replaced by `id' automatically when `org-roam-link-auto-replace' is non-nil.")
(defvar org-roam-node-history nil (defvar org-roam-node-history nil
"Minibuffer history of nodes.") "Minibuffer history of nodes.")
@ -151,6 +179,11 @@ This path is relative to `org-roam-directory'."
;;; Definition ;;; Definition
(cl-defstruct (org-roam-node (:constructor org-roam-node-create) (cl-defstruct (org-roam-node (:constructor org-roam-node-create)
(:constructor org-roam-node-create-from-db
(title aliases ; 2
id file file-title level todo ; 5
point priority scheduled deadline properties ;;5
olp file-atime file-mtime tags refs)) ;;5
(:copier nil)) (:copier nil))
"A heading or top level file with an assigned ID property." "A heading or top level file with an assigned ID property."
file file-title file-hash file-atime file-mtime file file-title file-hash file-atime file-mtime
@ -352,23 +385,27 @@ nodes."
(defun org-roam-node-list () (defun org-roam-node-list ()
"Return all nodes stored in the database as a list of `org-roam-node's." "Return all nodes stored in the database as a list of `org-roam-node's."
(let ((rows (org-roam-db-query (let ((rows (org-roam-db-query
"SELECT "
SELECT
title,
aliases,
id, id,
file, file,
filetitle, filetitle,
\"level\", \"level\",
todo, todo,
pos, pos,
priority , priority ,
scheduled , scheduled ,
deadline , deadline ,
title,
properties , properties ,
olp, olp,
atime, atime,
mtime, mtime,
'(' || group_concat(tags, ' ') || ')' as tags, '(' || group_concat(tags, ' ') || ')' as tags,
aliases,
refs refs
FROM FROM
( (
@ -417,32 +454,20 @@ FROM
LEFT JOIN refs ON refs.node_id = nodes.id LEFT JOIN refs ON refs.node_id = nodes.id
GROUP BY nodes.id, tags.tag, aliases.alias ) GROUP BY nodes.id, tags.tag, aliases.alias )
GROUP BY id, tags ) GROUP BY id, tags )
GROUP BY id"))) GROUP BY id
(cl-loop for row in rows ")))
append (pcase-let* ((`( (mapcan
,id ,file ,file-title ,level ,todo ,pos ,priority ,scheduled ,deadline (lambda (row)
,title ,properties ,olp ,atime ,mtime ,tags ,aliases ,refs) (let (
row) (all-titles (cons (car row) (nth 1 row)))
(all-titles (cons title aliases))) )
(mapcar (lambda (temp-title) (mapcar (lambda (temp-title)
(org-roam-node-create :id id (apply 'org-roam-node-create-from-db (cons temp-title (cdr row))))
:file file all-titles)
:file-title file-title ))
:file-atime atime rows)
:file-mtime mtime )
:level level )
:point pos
:todo todo
:priority priority
:scheduled scheduled
:deadline deadline
:title temp-title
:aliases aliases
:properties properties
:olp olp
:tags tags
:refs refs))
all-titles)))))
;;;; Finders ;;;; Finders
(defun org-roam-node-marker (node) (defun org-roam-node-marker (node)
@ -556,6 +581,25 @@ PROMPT is a string to show at the beginning of the mini-buffer, defaulting to \"
(or (cdr (assoc node nodes)) (or (cdr (assoc node nodes))
(org-roam-node-create :title node)))) (org-roam-node-create :title node))))
(defun org-roam--format-nodes-using-template (nodes)
"Formats NODES using org-roam template features.
Uses org-roam--node-display-template."
(let (
(wTemplate (org-roam-node--process-display-format org-roam-node-display-template))
)
(mapcar (lambda (node)
(org-roam-node-read--to-candidate node wTemplate)) nodes))
)
(defun org-roam--format-nodes-using-function (nodes)
"Formats NODES using the function org-roam-node-display-template."
(mapcar (lambda (node)
(cons
(propertize (funcall org-roam-node-display-template node) 'node node)
node))
nodes)
)
(defun org-roam-node-read--completions (&optional filter-fn sort-fn) (defun org-roam-node-read--completions (&optional filter-fn sort-fn)
"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
@ -565,15 +609,17 @@ and when nil is returned the node will be filtered out.
SORT-FN is a function to sort nodes. See `org-roam-node-read-sort-by-file-mtime' SORT-FN is a function to sort nodes. See `org-roam-node-read-sort-by-file-mtime'
for an example sort function. for an example sort function.
The displayed title is formatted according to `org-roam-node-display-template'." The displayed title is formatted according to `org-roam-node-display-template'."
(let* ((template (org-roam-node--process-display-format org-roam-node-display-template)) (let* (
(nodes (org-roam-node-list)) (nodes (org-roam-node-list))
(nodes (if filter-fn (nodes (if filter-fn
(cl-remove-if-not (cl-remove-if-not
(lambda (n) (funcall filter-fn n)) (lambda (n) (funcall filter-fn n))
nodes) nodes)
nodes)) nodes))
(nodes (mapcar (lambda (node) (nodes (if (functionp org-roam-node-display-template)
(org-roam-node-read--to-candidate node template)) nodes)) (org-roam--format-nodes-using-function nodes)
(org-roam--format-nodes-using-template nodes)))
(sort-fn (or sort-fn (sort-fn (or sort-fn
(when org-roam-node-default-sort (when org-roam-node-default-sort
(intern (concat "org-roam-node-read-sort-by-" (intern (concat "org-roam-node-read-sort-by-"
@ -726,7 +772,7 @@ The INFO, if provided, is passed to the underlying `org-roam-capture-'."
(deactivate-mark))) (deactivate-mark)))
;;;;; [roam:] link ;;;;; [roam:] link
(org-link-set-parameters "roam" :follow #'org-roam-link-follow-link) (org-link-set-parameters org-roam-link-type :follow #'org-roam-link-follow-link)
(defun org-roam-link-follow-link (title-or-alias) (defun org-roam-link-follow-link (title-or-alias)
"Navigate \"roam:\" link to find and open the node with TITLE-OR-ALIAS. "Navigate \"roam:\" link to find and open the node with TITLE-OR-ALIAS.
Assumes that the cursor was put where the link is." Assumes that the cursor was put where the link is."
@ -755,7 +801,7 @@ Assumes that the cursor was put where the link is."
node) node)
(goto-char (org-element-property :begin link)) (goto-char (org-element-property :begin link))
(when (and (org-in-regexp org-link-any-re 1) (when (and (org-in-regexp org-link-any-re 1)
(string-equal type "roam") (string-equal type org-roam-link-type)
(setq node (save-match-data (org-roam-node-from-title-or-alias path)))) (setq node (save-match-data (org-roam-node-from-title-or-alias path))))
(replace-match (org-link-make-string (replace-match (org-link-make-string
(concat "id:" (org-roam-node-id node)) (concat "id:" (org-roam-node-id node))
@ -765,7 +811,7 @@ Assumes that the cursor was put where the link is."
"Replace all \"roam:\" links in buffer with \"id:\" links." "Replace all \"roam:\" links in buffer with \"id:\" links."
(interactive) (interactive)
(org-with-point-at 1 (org-with-point-at 1
(while (re-search-forward org-link-bracket-re nil t) (while (search-forward (concat "[[" org-roam-link-type ":") nil t)
(org-roam-link-replace-at-point)))) (org-roam-link-replace-at-point))))
(add-hook 'org-roam-find-file-hook #'org-roam--replace-roam-links-on-save-h) (add-hook 'org-roam-find-file-hook #'org-roam--replace-roam-links-on-save-h)

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -163,7 +163,8 @@ If FILE, set `default-directory' to FILE's directory and insert its contents."
(let ((current-org-roam-directory (make-symbol "current-org-roam-directory"))) (let ((current-org-roam-directory (make-symbol "current-org-roam-directory")))
`(let ((,current-org-roam-directory org-roam-directory)) `(let ((,current-org-roam-directory org-roam-directory))
(with-temp-buffer (with-temp-buffer
(let ((org-roam-directory ,current-org-roam-directory)) (let ((org-roam-directory ,current-org-roam-directory)
(org-inhibit-startup t))
(delay-mode-hooks (org-mode)) (delay-mode-hooks (org-mode))
(when ,file (when ,file
(insert-file-contents ,file) (insert-file-contents ,file)

View File

@ -5,7 +5,7 @@
;; Author: Jethro Kuan <jethrokuan95@gmail.com> ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; 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.3.0 ;; Version: 2.3.1
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0"))
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.

View File

@ -30,9 +30,9 @@
(setq org-roam-directory "/tmp/org roam")) (setq org-roam-directory "/tmp/org roam"))
(it "returns the correct rg command for unlinked references" (it "returns the correct rg command for unlinked references"
(expect (org-roam-unlinked-references--rg-command '("foo" "bar")) (expect (org-roam-unlinked-references--rg-command '("foo" "bar") "/tmp/regex")
:to-equal :to-equal
"rg --follow --only-matching --vimgrep --pcre2 --ignore-case --glob \"*.org\" --glob \"*.org.gpg\" --glob \"*.org.age\" '\\[([^[]]++|(?R))*\\]|(\\bfoo\\b)|(\\bbar\\b)' /tmp/org\\ roam"))) "rg --follow --only-matching --vimgrep --pcre2 --ignore-case --glob \"*.org\" --glob \"*.org.gpg\" --glob \"*.org.age\" --file /tmp/regex /tmp/org\\ roam")))
(provide 'test-org-roam-mode) (provide 'test-org-roam-mode)