mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
18b75bd03e | |||
ecc3611bec | |||
f754160402 | |||
3a3c4d97d5 | |||
07ec4342aa | |||
096b0c5e30 | |||
32c0f3d2ec | |||
02e35e3b01 | |||
377d39bfff | |||
3d4c93a2a0 | |||
683605c813 | |||
eae1cdc00c | |||
1bbc20cfa8 | |||
de94212cdd | |||
78144d434b | |||
55526a4283 | |||
d2e933cc3e | |||
9405d014af | |||
98434919e0 | |||
7ed513294d | |||
6fe38b959b | |||
44960f189f | |||
fbe3f2909f | |||
f8f3870cf0 | |||
1bdfc25f80 | |||
109917c589 | |||
bc4b7fa409 | |||
f8dd345bd1 | |||
0a7d365e22 | |||
389bf3c7e2 | |||
ca1b02829b | |||
dd7c3fab23 | |||
15c1a46e41 | |||
22c8d1032b | |||
4e0b3fb564 | |||
697c686390 | |||
68dab59a69 | |||
2dc5d6281a | |||
eb9a9e487c | |||
fd23360100 | |||
57f63461cd | |||
d9ba961f96 | |||
169ebe6367 |
@ -11,6 +11,7 @@
|
||||
### Changed
|
||||
|
||||
- [#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
|
||||
|
||||
|
@ -47,7 +47,7 @@ Here's a sample configuration with `use-package`:
|
||||
:hook
|
||||
(after-init . org-roam-mode)
|
||||
:custom
|
||||
(org-roam-directory "/path/to/org-files/")
|
||||
(org-roam-directory (file-truename "/path/to/org-files/"))
|
||||
:bind (:map org-roam-mode-map
|
||||
(("C-c n l" . org-roam)
|
||||
("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))))
|
||||
```
|
||||
|
||||
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
|
||||
graph-related functionality. It is recommended to install PCRE-enabled ripgrep
|
||||
for better performance and extended functionality.
|
||||
|
@ -361,9 +361,12 @@ For this tutorial, create an empty directory, and set ~org-roam-directory~:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(make-directory "~/org-roam")
|
||||
(setq org-roam-directory "~/org-roam")
|
||||
(setq org-roam-directory (file-truename "~/org-roam"))
|
||||
#+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
|
||||
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:
|
||||
@ -508,6 +511,17 @@ Alternatively, Org-roam provides some functions to add or remove aliases.
|
||||
|
||||
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 are unique identifiers for nodes. These keys allow references to the key to
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -33,13 +33,16 @@
|
||||
;;;; Library Requires
|
||||
(require 'org-capture)
|
||||
(eval-when-compile
|
||||
(require 'org-roam-macs))
|
||||
(require 'org-roam-macs)
|
||||
(require 'org-macs))
|
||||
(require 'org-roam-db)
|
||||
(require 'dash)
|
||||
(require 'cl-lib)
|
||||
|
||||
;; Declarations
|
||||
(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)
|
||||
|
||||
@ -121,15 +124,20 @@ the following options:
|
||||
The file will be created, prescribed an ID, and head content will be
|
||||
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
|
||||
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
|
||||
inserted at the start of the file. The OLP (h1, h2) will be created,
|
||||
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
|
||||
properties are:
|
||||
|
||||
@ -468,7 +476,12 @@ This function is to be called in the Org-capture finalization process."
|
||||
"Finalize the `org-roam-capture' process."
|
||||
(when-let ((region (org-roam-capture--get :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)))
|
||||
(funcall (intern (concat "org-roam-capture--finalize-"
|
||||
(symbol-name (org-roam-capture--get :finalize)))))))
|
||||
@ -515,6 +528,8 @@ Return the ID of the location."
|
||||
(setq path (expand-file-name
|
||||
(string-trim (org-roam-capture--fill-template path t))
|
||||
org-roam-directory))
|
||||
(unless (file-exists-p path)
|
||||
(org-roam-capture--put :new-file path))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(widen)
|
||||
(setq p (point)))
|
||||
@ -523,6 +538,8 @@ Return the ID of the location."
|
||||
(string-trim (org-roam-capture--fill-template path t))
|
||||
org-roam-directory))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(unless (file-exists-p path)
|
||||
(org-roam-capture--put :new-file path))
|
||||
(setq p (point-min))
|
||||
(let ((m (org-roam-capture-find-or-create-olp olp)))
|
||||
(goto-char m))
|
||||
@ -531,10 +548,10 @@ Return the ID of the location."
|
||||
(setq path (expand-file-name
|
||||
(string-trim (org-roam-capture--fill-template path t))
|
||||
org-roam-directory))
|
||||
(let ((exists-p (file-exists-p path)))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(unless exists-p
|
||||
(insert (org-roam-capture--fill-template head t))))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(unless (file-exists-p path)
|
||||
(org-roam-capture--put :new-file path)
|
||||
(insert (org-roam-capture--fill-template head t)))
|
||||
(widen)
|
||||
(setq p (point-min)))
|
||||
(`(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))
|
||||
org-roam-directory))
|
||||
(widen)
|
||||
(let ((exists-p (file-exists-p path)))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(unless exists-p
|
||||
(insert (org-roam-capture--fill-template head t))))
|
||||
(set-buffer (org-capture-target-buffer path))
|
||||
(unless (file-exists-p path)
|
||||
(org-roam-capture--put :new-file path)
|
||||
(insert (org-roam-capture--fill-template head t)))
|
||||
(setq p (point-min))
|
||||
(let ((m (org-roam-capture-find-or-create-olp olp)))
|
||||
(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)
|
||||
;; first try to get ID, then try to get title/alias
|
||||
(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))))
|
||||
(save-excursion
|
||||
(goto-char p)
|
||||
(run-hooks 'org-roam-capture-new-node-hook)
|
||||
(org-id-get-create))))
|
||||
(prog1
|
||||
(org-id-get-create)
|
||||
(run-hooks 'org-roam-capture-new-node-hook)))))
|
||||
|
||||
(defun org-roam-capture-find-or-create-olp (olp)
|
||||
"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)
|
||||
(= (length org-capture-templates) 1))
|
||||
(setq keys (caar org-capture-templates)))
|
||||
(org-capture goto keys)))
|
||||
(org-capture goto keys)
|
||||
(unless goto
|
||||
(org-cycle-hide-drawers 'all))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-capture (&optional goto keys)
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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 program is free software; you can redistribute it and/or modify
|
||||
|
@ -8,7 +8,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -61,10 +61,10 @@
|
||||
:type 'hook)
|
||||
|
||||
(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")
|
||||
"#+title: %<%Y-%m-%d>\n")))
|
||||
:if-new (file+head ,(expand-file-name "%<%Y-%m-%d>.org" org-roam-dailies-directory)
|
||||
"#+title: %<%Y-%m-%d>\n")))
|
||||
"Capture templates for daily-notes in Org-roam.
|
||||
See `org-roam-capture-templates' for the template documentation."
|
||||
:group 'org-roam
|
||||
|
218
org-roam-db.el
218
org-roam-db.el
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
;;;; Library Requires
|
||||
(eval-when-compile (require 'subr-x))
|
||||
(require 'emacsql)
|
||||
(require 'emacsql-sqlite3)
|
||||
(require 'emacsql-sqlite)
|
||||
(require 'seq)
|
||||
|
||||
(eval-and-compile
|
||||
@ -79,7 +79,11 @@ value like `most-positive-fixnum'."
|
||||
:type 'int
|
||||
: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)
|
||||
"Database connection to Org-roam database.")
|
||||
@ -99,7 +103,7 @@ Performs a database upgrade when required."
|
||||
(emacsql-live-p (org-roam-db--get-connection)))
|
||||
(let ((init-db (not (file-exists-p org-roam-db-location))))
|
||||
(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)
|
||||
(puthash (expand-file-name org-roam-directory)
|
||||
conn
|
||||
@ -129,51 +133,53 @@ SQL can be either the emacsql vector representation, or a string."
|
||||
(apply #'emacsql (org-roam-db) sql args)))
|
||||
|
||||
;;;; Schemata
|
||||
;; NOTE: Foreign key somehow doesn't work! Adding a file column to every table as a workaround.
|
||||
(defconst org-roam-db--table-schemata
|
||||
'((files
|
||||
[(file :unique :primary-key)
|
||||
(hash :not-null)])
|
||||
|
||||
(nodes
|
||||
[(id :primary-key :not-null)
|
||||
(file :not-null)
|
||||
(level :not-null)
|
||||
(pos :not-null)
|
||||
todo
|
||||
priority
|
||||
(scheduled text)
|
||||
(deadline text)
|
||||
title]
|
||||
(:foreign-key [file] :references files [file] :on-delete :cascade))
|
||||
([(id :not-null :primary-key)
|
||||
(file :not-null)
|
||||
(level :not-null)
|
||||
(pos :not-null)
|
||||
todo
|
||||
priority
|
||||
(scheduled text)
|
||||
(deadline text)
|
||||
title
|
||||
properties
|
||||
olp]
|
||||
(:foreign-key [file] :references files [file] :on-delete :cascade)))
|
||||
|
||||
(aliases
|
||||
[(file :not-null)
|
||||
(node-id :not-null)
|
||||
alias]
|
||||
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade))
|
||||
([(node-id :not-null)
|
||||
alias]
|
||||
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
|
||||
|
||||
(refs
|
||||
([(file :not-null)
|
||||
(node-id :not-null)
|
||||
([(node-id :not-null)
|
||||
(ref :not-null)
|
||||
(type :not-null)]
|
||||
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
|
||||
|
||||
(tags
|
||||
[(file :not-null)
|
||||
(node-id :not-null)
|
||||
tag]
|
||||
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade))
|
||||
([(node-id :not-null)
|
||||
tag]
|
||||
(:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
|
||||
|
||||
(links
|
||||
[(file :not-null)
|
||||
(pos :not-null)
|
||||
(source :not-null)
|
||||
(dest :not-null)
|
||||
(type :not-null)
|
||||
(properties :not-null)]
|
||||
(:foreign-key [file] :references files [file] :on-delete :cascade))))
|
||||
([(pos :not-null)
|
||||
(source :not-null)
|
||||
(dest :not-null)
|
||||
(type :not-null)
|
||||
(properties :not-null)]
|
||||
(:foreign-key [source] :references nodes [id] :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)
|
||||
"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")
|
||||
(pcase-dolist (`(,table ,schema) org-roam-db--table-schemata)
|
||||
(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))))
|
||||
|
||||
(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.
|
||||
If FILE is nil, clear the current 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 ,table
|
||||
:where (= file $s1)]
|
||||
file)))
|
||||
(org-roam-db-query [:delete :from files
|
||||
:where (= file $s1)]
|
||||
file))
|
||||
|
||||
;;;;; Updating tables
|
||||
(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-let ((id (org-id-get)))
|
||||
(let* ((file (buffer-file-name (buffer-base-buffer)))
|
||||
(title (or (cadr (assoc "TITLE" (org-collect-keywords '("title"))
|
||||
#'string-equal))
|
||||
(file-relative-name file org-roam-directory)))
|
||||
(title (org-link-display-format
|
||||
(or (cadr (assoc "TITLE" (org-collect-keywords '("title"))
|
||||
#'string-equal))
|
||||
(file-relative-name file org-roam-directory))))
|
||||
(pos (point))
|
||||
(todo nil)
|
||||
(priority nil)
|
||||
@ -281,46 +289,44 @@ If UPDATE-P is non-nil, first remove the file in the database."
|
||||
(level 0)
|
||||
(aliases (org-entry-get (point) "ROAM_ALIASES"))
|
||||
(tags org-file-tags)
|
||||
(refs (org-entry-get (point) "ROAM_REFS")))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(refs (org-entry-get (point) "ROAM_REFS"))
|
||||
(properties (org-entry-properties))
|
||||
(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
|
||||
[:insert :into nodes
|
||||
[:insert :into refs
|
||||
:values $v1]
|
||||
(vector id file level pos todo priority
|
||||
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))))))))
|
||||
rows)))))))))
|
||||
|
||||
(defun org-roam-db-insert-node-data ()
|
||||
"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))
|
||||
(scheduled (org-roam-db-get-scheduled-time))
|
||||
(deadline (org-roam-db-get-deadline-time))
|
||||
(title (nth 4 heading-components)))
|
||||
(condition-case nil
|
||||
(org-roam-db-query
|
||||
[:insert :into nodes
|
||||
:values $v1]
|
||||
(vector id file level pos todo priority
|
||||
scheduled deadline title))
|
||||
(t
|
||||
(lwarn '(org-roam) :error
|
||||
"Duplicate ID %s, skipping..." id))))))
|
||||
(title (org-link-display-format (nth 4 heading-components)))
|
||||
(properties (org-entry-properties))
|
||||
(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)))))
|
||||
|
||||
(defun org-roam-db-insert-aliases ()
|
||||
"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 ()
|
||||
"Insert tags for node at point into Org-roam cache."
|
||||
(when-let ((file (buffer-file-name (buffer-base-buffer)))
|
||||
(node-id (org-id-get))
|
||||
(when-let ((node-id (org-id-get))
|
||||
(tags (org-get-tags)))
|
||||
(org-roam-db-query [:insert :into tags
|
||||
:values $v1]
|
||||
(mapcar (lambda (tag)
|
||||
(vector file node-id tag)) tags))))
|
||||
(vector node-id (substring-no-properties tag))) tags))))
|
||||
|
||||
(defun org-roam-db-insert-refs ()
|
||||
"Insert refs for node at point into Org-roam cache."
|
||||
(when-let* ((file (buffer-file-name (buffer-base-buffer)))
|
||||
(node-id (org-id-get))
|
||||
(when-let* ((node-id (org-id-get))
|
||||
(refs (org-entry-get (point) "ROAM_REFS"))
|
||||
(refs (split-string-and-unquote refs)))
|
||||
(let (rows)
|
||||
@ -376,7 +378,7 @@ If UPDATE-P is non-nil, first remove the file in the database."
|
||||
(save-match-data
|
||||
(if (string-match org-link-plain-re ref)
|
||||
(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
|
||||
"%s:%s\tInvalid ref %s, skipping..." (buffer-file-name) (point) ref))))
|
||||
(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."
|
||||
(save-excursion
|
||||
(goto-char (org-element-property :begin link))
|
||||
(let ((file (buffer-file-name (buffer-base-buffer)))
|
||||
(type (org-element-property :type link))
|
||||
(let ((type (org-element-property :type link))
|
||||
(dest (org-element-property :path link))
|
||||
(properties (list :outline (org-get-outline-path)))
|
||||
(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
|
||||
[:insert :into links
|
||||
:values $v1]
|
||||
(vector file (point) source dest type properties))))))
|
||||
(vector (point) source dest type properties))))))
|
||||
|
||||
;;;;; Fetching
|
||||
(defun org-roam-db--get-current-files ()
|
||||
@ -436,18 +437,19 @@ If FORCE, force a rebuild of the cache from scratch."
|
||||
contents-hash)
|
||||
(push file modified-files)))
|
||||
(remhash file current-files))
|
||||
(if (fboundp 'dolist-with-progress-reporter)
|
||||
(dolist-with-progress-reporter (file (hash-table-keys current-files))
|
||||
"Clearing removed files..."
|
||||
(org-roam-db-clear-file file))
|
||||
(dolist (file (hash-table-keys current-files))
|
||||
(org-roam-db-clear-file file)))
|
||||
(if (fboundp 'dolist-with-progress-reporter)
|
||||
(dolist-with-progress-reporter (file modified-files)
|
||||
"Processing modified files..."
|
||||
(org-roam-db-update-file file))
|
||||
(dolist (file modified-files)
|
||||
(org-roam-db-update-file file)))))
|
||||
(emacsql-with-transaction (org-roam-db)
|
||||
(if (fboundp 'dolist-with-progress-reporter)
|
||||
(dolist-with-progress-reporter (file (hash-table-keys current-files))
|
||||
"Clearing removed files..."
|
||||
(org-roam-db-clear-file file))
|
||||
(dolist (file (hash-table-keys current-files))
|
||||
(org-roam-db-clear-file file)))
|
||||
(if (fboundp 'dolist-with-progress-reporter)
|
||||
(dolist-with-progress-reporter (file modified-files)
|
||||
"Processing modified files..."
|
||||
(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)
|
||||
"Update Org-roam cache for FILE-PATH.
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/jethrokuan/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -212,7 +212,8 @@ If CHECKALL, run the check for all Org-roam files."
|
||||
(let ((buf (find-file-noselect f)))
|
||||
(org-roam-doctor--check buf checkers)
|
||||
(unless (memq buf existing-buffers)
|
||||
(save-buffer buf)
|
||||
(with-current-buffer buf
|
||||
(save-buffer))
|
||||
(kill-buffer buf))))))
|
||||
(org-roam-message "Linting completed."))
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -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."
|
||||
(declare (indent 2) (debug t))
|
||||
`(let* (new-buf
|
||||
(auto-mode-alist nil)
|
||||
(buf (or (and (not ,file)
|
||||
(current-buffer)) ;If FILE is nil, use current 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)
|
||||
(with-current-buffer buf
|
||||
(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))
|
||||
(unless (and new-buf (not ,keep-buf-p))
|
||||
(save-buffer)))
|
||||
|
@ -5,7 +5,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -124,11 +124,12 @@ and `:slant'."
|
||||
(defvar org-roam-current-node nil
|
||||
"The current node at point.")
|
||||
|
||||
(defcustom org-roam-mode-sections (list #'org-roam-backlinks-section
|
||||
#'org-roam-reflinks-section)
|
||||
"List of functions that insert sections for Org-roam."
|
||||
(defcustom org-roam-mode-section-functions (list #'org-roam-backlinks-section
|
||||
#'org-roam-reflinks-section)
|
||||
"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
|
||||
:type '(repeat function))
|
||||
:type 'hook)
|
||||
|
||||
;;; The mode
|
||||
(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))
|
||||
(magit-insert-section (org-roam)
|
||||
(magit-insert-heading)
|
||||
(dolist (fn org-roam-mode-sections)
|
||||
(funcall fn org-roam-current-node))))))
|
||||
(run-hook-with-args 'org-roam-mode-section-functions org-roam-current-node)))))
|
||||
|
||||
(defun org-roam-buffer ()
|
||||
"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))
|
||||
(magit-insert-section (org-roam)
|
||||
(magit-insert-heading)
|
||||
(dolist (fn org-roam-mode-sections)
|
||||
(dolist (fn org-roam-mode-section-functions)
|
||||
(funcall fn org-roam-current-node)))))))
|
||||
|
||||
(defun org-roam-buffer--redisplay ()
|
||||
@ -419,9 +419,9 @@ References from FILE are excluded."
|
||||
(let* ((titles (cons (org-roam-node-title node)
|
||||
(org-roam-node-aliases node)))
|
||||
(rg-command (concat "rg -o --vimgrep -P -i "
|
||||
(string-join (mapcar (lambda (glob) (concat "-g " glob))
|
||||
(org-roam--list-files-search-globs
|
||||
org-roam-file-extensions)) " ")
|
||||
(mapconcat (lambda (glob) (concat "-g " glob))
|
||||
(org-roam--list-files-search-globs org-roam-file-extensions)
|
||||
" ")
|
||||
(format " '\\[([^[]]++|(?R))*\\]%s' "
|
||||
(mapconcat (lambda (title)
|
||||
(format "|(\\b%s\\b)" (shell-quote-argument title)))
|
||||
|
@ -5,7 +5,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -156,20 +156,24 @@ Adapted from `s-format'."
|
||||
t t)
|
||||
(set-match-data saved-match-data))))
|
||||
|
||||
(defvar org-roam--cached-display-format nil)
|
||||
|
||||
(defun org-roam--process-display-format (format)
|
||||
"Pre-calculate minimal widths needed by the FORMAT string."
|
||||
(let* ((fields-width 0)
|
||||
(string-width
|
||||
(string-width
|
||||
(org-roam-format
|
||||
format
|
||||
(lambda (field)
|
||||
(setq fields-width
|
||||
(+ fields-width
|
||||
(string-to-number
|
||||
(or (cadr (split-string field ":"))
|
||||
"")))))))))
|
||||
(cons format (+ fields-width string-width))))
|
||||
(or org-roam--cached-display-format
|
||||
(setq org-roam--cached-display-format
|
||||
(let* ((fields-width 0)
|
||||
(string-width
|
||||
(string-width
|
||||
(org-roam-format
|
||||
format
|
||||
(lambda (field)
|
||||
(setq fields-width
|
||||
(+ fields-width
|
||||
(string-to-number
|
||||
(or (cadr (split-string field ":"))
|
||||
"")))))))))
|
||||
(cons format (+ fields-width string-width))))))
|
||||
|
||||
;;; Diagnostics
|
||||
;;;###autoload
|
||||
|
219
org-roam.el
219
org-roam.el
@ -6,7 +6,7 @@
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; 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.
|
||||
|
||||
@ -50,7 +50,8 @@
|
||||
;;;; Features
|
||||
(require 'org-roam-compat)
|
||||
(eval-when-compile
|
||||
(require 'org-roam-macs))
|
||||
(require 'org-roam-macs)
|
||||
(require 'org-macs))
|
||||
(require 'org-roam-utils)
|
||||
(require 'org-roam-mode)
|
||||
(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.
|
||||
|
||||
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)
|
||||
: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))
|
||||
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 ()
|
||||
"Return a list of buffers that are Org-roam files."
|
||||
(--filter (and (with-current-buffer it (derived-mode-p 'org-mode))
|
||||
(buffer-file-name it)
|
||||
(org-roam--org-roam-file-p (buffer-file-name it)))
|
||||
(--filter (org-roam--org-roam-buffer-p it)
|
||||
(buffer-list)))
|
||||
|
||||
(defun org-roam--get-titles ()
|
||||
@ -421,7 +431,7 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
|
||||
;;;; Nodes
|
||||
(cl-defstruct (org-roam-node (:constructor org-roam-node-create)
|
||||
(: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)
|
||||
|
||||
(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.
|
||||
This can be quite costly: avoid, unless dealing with very few
|
||||
nodes."
|
||||
(let ((node-info (car (org-roam-db-query [:select [file level pos todo priority scheduled deadline title]
|
||||
:from nodes
|
||||
:where (= id $s1)
|
||||
:limit 1]
|
||||
(org-roam-node-id node))))
|
||||
(tag-info (mapcar #'car (org-roam-db-query [:select [tag] :from tags
|
||||
:where (= node-id $s1)]
|
||||
(org-roam-node-id node))))
|
||||
(alias-info (mapcar #'car (org-roam-db-query [:select [alias] :from aliases
|
||||
(when-let ((node-info (car (org-roam-db-query [:select [file level pos todo priority
|
||||
scheduled deadline title properties olp]
|
||||
:from nodes
|
||||
:where (= id $s1)
|
||||
:limit 1]
|
||||
(org-roam-node-id node)))))
|
||||
(let ((tag-info (mapcar #'car (org-roam-db-query [:select [tag] :from tags
|
||||
:where (= node-id $s1)]
|
||||
(org-roam-node-id node))))
|
||||
(refs-info (mapcar #'car (org-roam-db-query [:select [ref] :from refs
|
||||
:where (= node-id $s1)]
|
||||
(org-roam-node-id node)))))
|
||||
(pcase-let ((`(,file ,level ,pos ,todo ,priority ,scheduled ,deadline ,title) node-info))
|
||||
(setf (org-roam-node-file node) file
|
||||
(org-roam-node-level node) level
|
||||
(org-roam-node-point node) pos
|
||||
(org-roam-node-todo node) todo
|
||||
(org-roam-node-priority node) priority
|
||||
(org-roam-node-scheduled node) scheduled
|
||||
(org-roam-node-deadline node) deadline
|
||||
(org-roam-node-title node) title
|
||||
(org-roam-node-tags node) tag-info
|
||||
(org-roam-node-refs node) refs-info
|
||||
(org-roam-node-aliases node) alias-info))
|
||||
node))
|
||||
(alias-info (mapcar #'car (org-roam-db-query [:select [alias] :from aliases
|
||||
:where (= node-id $s1)]
|
||||
(org-roam-node-id node))))
|
||||
(refs-info (mapcar #'car (org-roam-db-query [:select [ref] :from refs
|
||||
:where (= node-id $s1)]
|
||||
(org-roam-node-id node)))))
|
||||
(pcase-let ((`(,file ,level ,pos ,todo ,priority ,scheduled
|
||||
,deadline ,title ,properties ,olp) node-info))
|
||||
(setf (org-roam-node-file node) file
|
||||
(org-roam-node-level node) level
|
||||
(org-roam-node-point node) pos
|
||||
(org-roam-node-todo node) todo
|
||||
(org-roam-node-priority node) priority
|
||||
(org-roam-node-scheduled node) scheduled
|
||||
(org-roam-node-deadline node) deadline
|
||||
(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
|
||||
"${title:*} ${tags:10}"
|
||||
"${title:*} ${tags:10}"
|
||||
"Configures display formatting for Org-roam node."
|
||||
:group 'org-roam
|
||||
:type 'string)
|
||||
|
||||
(defun org-roam--tags-to-str (tags)
|
||||
"Convert list of TAGS into a string."
|
||||
(string-join
|
||||
(mapcar (lambda (s) (concat "#" s)) tags)
|
||||
" "))
|
||||
(mapconcat (lambda (s) (concat "#" s)) tags " "))
|
||||
|
||||
(defun org-roam-node--format-entry (node width)
|
||||
"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")
|
||||
field-value)
|
||||
(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)
|
||||
field-value
|
||||
(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-let ((node (magit-section-case
|
||||
(org-roam-node-section (oref it node))
|
||||
(t (when-let ((id (org-roam-id-at-point)))
|
||||
(org-roam-populate (org-roam-node-create :id id)))))))
|
||||
(t (let (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
|
||||
(when assert
|
||||
(user-error "No node at point"))))
|
||||
@ -601,8 +623,7 @@ Throw an error if multiple choices exist."
|
||||
:where (= title $s1)]
|
||||
s)
|
||||
(org-roam-db-query [:select [node-id] :from aliases
|
||||
:left :join nodes :on (= nodes:id aliases:node-id)
|
||||
:where (= aliases:node-id $s1)]
|
||||
:where (= alias $s1)]
|
||||
s)))))
|
||||
(cond
|
||||
((seq-empty-p matches)
|
||||
@ -616,19 +637,22 @@ Throw an error if multiple choices exist."
|
||||
"Return an alist for node completion.
|
||||
The car is the displayed title or alias for the node, and the cdr
|
||||
is the `org-roam-node'."
|
||||
(setq org-roam--cached-display-format nil)
|
||||
(let ((tags-table (org-roam--tags-table)))
|
||||
(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])
|
||||
(org-roam-db-query [:select [nodes:file pos alias title node-id]
|
||||
:from aliases
|
||||
:left-join nodes
|
||||
: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
|
||||
:file file
|
||||
:title alias
|
||||
:point pos
|
||||
:properties properties
|
||||
:olp olp
|
||||
:tags (gethash id tags-table)))
|
||||
(candidate-main (org-roam-node--format-entry node (1- (frame-width))))
|
||||
(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."
|
||||
(magit-insert-section section (org-roam-node-section)
|
||||
(let ((outline (if-let ((outline (plist-get properties :outline)))
|
||||
(string-join (mapcar #'org-link-display-format outline)
|
||||
" > ")
|
||||
(mapconcat #'org-link-display-format outline " > ")
|
||||
"Top")))
|
||||
(insert (concat (propertize (org-roam-node-title source-node)
|
||||
'font-lock-face 'org-roam-title)
|
||||
@ -779,41 +802,121 @@ window instead."
|
||||
(let* ((p (org-entry-get (point) prop))
|
||||
(lst (when p (split-string-and-unquote p)))
|
||||
(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)
|
||||
"Prompt to remove an item from PROP."
|
||||
(defun org-roam-remove-property (prop &optional s)
|
||||
"Remove S from property PROP.
|
||||
|
||||
If S is not specified, user is prompted to select a value."
|
||||
(let* ((p (org-entry-get (point) prop))
|
||||
(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)))
|
||||
(if 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
|
||||
(defun org-roam-alias-add (alias)
|
||||
"Add ALIAS to the node at point."
|
||||
(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 ()
|
||||
"Remove an alias from the node at point."
|
||||
(defun org-roam-alias-remove (&optional alias)
|
||||
"Remove an ALIAS from the node at point."
|
||||
(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
|
||||
(defun org-roam-ref-add (ref)
|
||||
"Add REF to the node at point."
|
||||
(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 ()
|
||||
"Remove a ref from the node at point."
|
||||
(defun org-roam-ref-remove (&optional ref)
|
||||
"Remove a REF from the node at point."
|
||||
(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 ()
|
||||
"Return an alist for ref completion.
|
||||
The car is the ref, and the cdr is the corresponding node for the ref."
|
||||
|
Reference in New Issue
Block a user