Compare commits

...

12 Commits

Author SHA1 Message Date
f5b8144c31 migration: catch error and restore files
In case of migration error, restore original files, and print a message.
2021-07-19 01:46:16 +08:00
e997c017de (docs): adjusted configuration examples for backlinks buffer (#1610)
- Fixed the original example's extra parenthesis, which broke the alist.
 - Added a second example for configuring the buffer as a side window.
2021-07-19 01:31:34 +08:00
63450e9eaf migration: proper fix for file-link replacement (#1609) 2021-07-18 23:23:04 +08:00
da02453ab1 (fix): migration: ensure empty roam_alias is removed (#1608)
* (fix): migration: ensure empty roam_alias is removed

Addresses #1596

* fix lint
2021-07-18 23:08:59 +08:00
bbf1d97eb0 (refactor): move all the backward compatibility related code to org-roam-compat.el (#1595) 2021-07-18 22:50:45 +08:00
7d9fcf5288 (docs) Replace mention of org-roam-db-build-cache with org-roam-db-sync (#1606) 2021-07-18 21:43:34 +08:00
d0be7f3b2a (fix): allow migration for desc with backslash (#1604)
Fixes #1602
2021-07-18 19:51:05 +08:00
363dca1765 (doc): add my braindump example (Sidharth Arya) (#1599) 2021-07-18 19:35:18 +08:00
4c5a041556 (docs): add note on encryption (#1597) 2021-07-18 14:55:32 +08:00
5e42d854c1 (doc): Add section for C Compiler (#1593)
* (doc): Add section for C Compiler

In the Post-Installation Tasks section, I suggest to add a sub-section for the C
compiler requirement for emacsql-sqlite. I have put an explanation for Windows
-- I have tried to make it as succinct as I can without losing some fine points
that I believe important for users to avoid confusion.

I have just removed MSYS2 and the PATH on my Windows machine and repeated the
process as I described it here to confirm that it works.

Since this new part is distinct from the existing paragraphs for SQLite, I also
suggest a sub-section heading for them -- I used "SQLite" as its title.

Please feel feel to change any part as you see fit.

It might be also useful if macOS users added their parts. I believe XCode is the
easiest way to install a C compiler (I believe it would be clang for recent
XCode versions) or perhaps brew. I don't use macOS so I will refrain from adding
this part..

I did not add .texi file to this commit -- last time, I believe you had to
revise the texi file that my machine generated anyway. Let me know if I should
follow up with a texi file.

* remove reference to emacsql-sqlite3

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2021-07-18 14:39:30 +08:00
681873759d Refactor some docstring and parameters in org-roam.el (#1594)
This mostly goes over docstrings, updating them in places where it was
outdated and adding them to places where they didn't exist or were
existed as placeholders.
2021-07-18 14:22:45 +08:00
2168490d5a bump docs for Org-roam v2 (#1591) 2021-07-17 22:54:31 +08:00
7 changed files with 1383 additions and 1397 deletions

View File

@ -86,6 +86,7 @@ it has not already been addressed on [GitHub][issues] or on
- [Jethro Kuan](https://braindump.jethro.dev/)
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
- [Alexey Shmalko](https://braindump.rasen.dev/)
- [Sidharth Arya](https://sidhartharya.github.io/braindump/index.html)
## Contributing

View File

@ -134,7 +134,7 @@ A slip-box requires a method for quickly capturing ideas. These are called
*fleeting notes*: they are simple reminders of information or ideas that will
need to be processed later on, or trashed. This is typically accomplished using
~org-capture~ (see info:org#Capture), or using Org-roam's daily notes
functionality (see [[*Daily-notes][Daily-notes]]). This provides a central inbox for collecting
functionality (see [[id:4eae8552-95e1-4e4a-b7b7-2c53433730ea][Org-roam Dailies]]). This provides a central inbox for collecting
thoughts, to be processed later into permanent notes.
*Permanent notes*
@ -198,8 +198,6 @@ using:
M-x package-install RET org-roam RET
#+END_EXAMPLE
Now see [[*Post-Installation Tasks][Post-Installation Tasks]].
** Installing from Apt
Users of Debian 11 or later or Ubuntu 20.10 or later can simply install Org-roam
@ -248,7 +246,7 @@ dependencies that it requires. These include:
- s
- org
- emacsql
- emacsql-sqlite3
- emacsql-sqlite
You can install this manually as well, or get the latest version from MELPA. You
may wish to use [[https://github.com/jwiegley/use-package][use-package]], [[https://github.com/raxod502/straight.el][straight.el]] to help manage this.
@ -291,23 +289,43 @@ file:
install-info /path/to/my/info/files/org-roam.info /path/to/my/info/files/dir
#+end_src
** Post-Installation Tasks
** Installation Troubleshooting
*** C Compiler
Org-roam relies on an Emacs package called ~emacsql~ and ~emacsql-sqlite~ to
work with the ~sqlite~ database. Both of them should be installed automatically
in your Emacs environment as a prerequisite for Org-roam when you install it.
Org-roam requires ~sqlite3~ to be locatable by Emacs (i.e. on ~exec-path~).
Please ensure that ~sqlite3~ is installed appropriately on your operating
system. You can verify that this is the case by executing [fn:2]:
~emacsql-sqlite~ requires a C compiler (e.g. ~gcc~ or ~clang~) to be present in
your computer. How to install a C compiler depends on the OS that you use.
#+BEGIN_SRC emacs-lisp
(executable-find "sqlite3")
#+END_SRC
- For Windows:
If you have ~sqlite3~ installed, and ~executable-find~ still reports ~nil~, then
the path to the executable is not a member of the Emacs variable ~exec-path~.
Rectify this by manually adding the path within your Emacs configuration:
There are various ways to install one, depending on how you have installed
Emacs. If you use Emacs within a Cygwin or MinGW environment, then you should
install a compiler using their respective package manager.
#+BEGIN_SRC emacs-lisp
(add-to-list 'exec-path "path/to/sqlite3")
#+END_SRC
If you have installed your Emacs from the [[https://www.gnu.org/software/emacs/][GNU Emacs website]], then the easiest way
is to use [[https://www.msys2.org/][MSYS2]] as at the time of this writing:
1. Use the installer in the official website and install MSYS2
2. Run MSYS2
3. In the command-line tool, type the following and answer "Y" to proceed:
#+BEGIN_SRC bash
pacman -S gcc
#+END_SRC
Note that you do not need to manually set the PATH for MSYS2; the
installer automatically takes care of it for you.
4. Open Emacs and call ~M-x org-roam-setup~
This will automatically start compiling ~emacsql-sqlite~; you should see a
message in minibuffer. It may take a while until compilation completes. Once
complete, you should see a new file ~emacsql-sqlite.exe~ created in a subfolder
named ~sqlite~ under ~emacsql-sqlite~ installation folder. It's typically in
your Emacs configuration folder like this:
~/.config/emacs/elpa/emacsql-sqlite-20190727.1710/sqlite~
* Getting Started
** The Org-roam Node
@ -376,7 +394,7 @@ Org-roam is available on startup, place this in your Emacs configuration:
(org-roam-setup)
#+end_src
To build the cache manually, run ~M-x org-roam-db-build-cache~. Cache builds may
To build the cache manually, run ~M-x org-roam-db-sync~. Cache builds may
take a while the first time, but subsequent builds are often instantaneous
because they only reprocess modified files.
@ -478,7 +496,7 @@ section-specific commands such as ~org-roam-node-visit~.
There are currently 3 provided widget types:
- Backlinks :: View (preview of) nodes that link to this node
- Reference Links :: Nodes that reference this node (see [[id:57c1f991-be38-4fab-b27d-60227047f3b7][Refs]])
- Reference Links :: Nodes that reference this node (see [[*Refs][Refs]])
- Unlinked references :: View nodes that contain text that match the nodes
title/alias but are not linked
@ -501,11 +519,11 @@ the user. The author's recommended configuration is as follows:
#+begin_src emacs-lisp
(add-to-list 'display-buffer-alist
'(("\\*org-roam\\*"
(display-buffer-in-direction)
(direction . right)
(window-width . 0.33)
(window-height . fit-window-to-buffer))))
'("\\*org-roam\\*"
(display-buffer-in-direction)
(direction . right)
(window-width . 0.33)
(window-height . fit-window-to-buffer)))
#+end_src
Crucially, the window is a regular window (not a side-window), and this allows
@ -515,6 +533,20 @@ for predictable navigation:
Org-roam buffer.
- ~C-u RET~ navigates to thing-at-point in the other window.
For users that prefer using a side-window for the org-roam buffer, the following
example configuration should provide a good starting point:
#+begin_src emacs-lisp
(add-to-list 'display-buffer-alist
'("\\*org-roam\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 0)
(window-width . 0.33)
(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t)))))
#+end_src
** TODO Styling the Org-roam buffer
* Node Properties
** Standard Org properties
@ -587,7 +619,7 @@ With the above example, if another node links to https://www.google.com/, it
will show up as a “reference backlink”.
These keys also come in useful for when taking website notes, using the
~roam-ref~ protocol (see [[*Roam Protocol][Roam Protocol]]).
~roam-ref~ protocol (see [[*Org-roam Protocol][Roam Protocol]]).
You may assign multiple refs to a single node, for example when you want
multiple papers in a series to share the same note, or an article has a citation
@ -664,6 +696,10 @@ extension in your Org-roam capture templates. For example:
:unnarrowed t)))
#+end_src
Note that the Org-roam database stores metadata information in plain-text
(headline text, for example), so if this information is private to you then you
should also ensure the database is encrypted.
* Org-roam Protocol
Org-roam provides extensions for capturing content from external applications
@ -838,7 +874,7 @@ where ~template~ is the template key for a template in
~org-roam-capture-ref-templates~ (see [[*The Templating System][The Templating System]]). These templates
should contain a ~#+roam_key: ${ref}~ in it.
* The Org-roam Templating System
* The Templating System
Org-roam extends the ~org-capture~ system, providing a smoother note-taking
experience. However, these extensions mean Org-roam capture templates are
@ -902,7 +938,7 @@ strings. ~${foo}~'s substitution is performed as follows:
Org-roam provides basic graphing capabilities to explore interconnections
between notes, in ~org-roam-graph~. This is done by performing SQL queries and
generating images using [[https://graphviz.org/][Graphviz]]. The graph can also be navigated: see [[*Roam Protocol][Roam
generating images using [[https://graphviz.org/][Graphviz]]. The graph can also be navigated: see [[*Org-roam Protocol][Roam
Protocol]].
The entry point to graph creation is ~org-roam-graph~.
@ -971,9 +1007,12 @@ for customizable options.
Example: ~'(("dir" . "back"))~
* Org-roam Dailies
:PROPERTIES:
:ID: 4eae8552-95e1-4e4a-b7b7-2c53433730ea
:END:
Org-roam provides journaling capabilities akin to
[[#org-journal][Org-journal]] with ~org-roam-dailies~.
Org-journal with ~org-roam-dailies~.
** Configuration
@ -1011,7 +1050,7 @@ See [[*The Templating System][The Templating System]] for creating new templates
When ~goto~ is non-nil, go to the note without creating an entry.
- Function: ~org-roam-dailies-find-today~
- Function: ~org-roam-dailies-goto-today~
Find the daily note for today, creating it if necessary.
@ -1023,7 +1062,7 @@ There are variants of those commands for ~-yesterday~ and ~-tomorrow~:
With numeric argument ~n~, use the daily note ~n~ days in the past.
- Function: ~org-roam-dailies-find-yesterday~
- Function: ~org-roam-dailies-goto-yesterday~
With numeric argument N, use the daily-note N days in the future.
@ -1038,7 +1077,7 @@ There are also commands which allow you to use Emacss ~calendar~ to find the
With a 'C-u' prefix or when ~goto~ is non-nil, go the note without
creating an entry.
- Function: ~org-roam-dailies-find-date~
- Function: ~org-roam-dailies-goto-date~
Find the daily note for a date using the calendar, creating it if necessary.
@ -1048,11 +1087,11 @@ There are also commands which allow you to use Emacss ~calendar~ to find the
Find and open ~org-roam-dailies-directory~.
- Function: ~org-roam-dailies-find-previous-note~
- Function: ~org-roam-dailies-goto-previous-note~
When in an daily-note, find the previous one.
- Function: ~org-roam-dailies-find-next-note~
- Function: ~org-roam-dailies-goto-next-note~
When in an daily-note, find the next one.
* Performance Optimization
@ -1128,7 +1167,7 @@ The Deft interface can slow down quickly when the number of files get huge.
[[https://github.com/bastibe/org-journal][Org-journal]] provides journaling capabilities to Org-mode. A lot of its
functionalities have been incorporated into Org-roam under the name
[[*Daily-notes][~org-roam-dailies~]]. It remains a good tool if you want to isolate your verbose
[[*Org-roam Dailies][~org-roam-dailies~]]. It remains a good tool if you want to isolate your verbose
journal entries from the ideas you would write on a scratchpad.
#+BEGIN_SRC emacs-lisp

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,90 @@
;;; Code:
;;;; Library Requires
;;; Backports
;; REVIEW Remove when 26.x support is dropped. This is exact the same as
;; `directory-files-recursively' from Emacs 26, but with FOLLOW-SYMLINKS
;; parameter from Emacs 27.
(defun org-roam--directory-files-recursively (dir regexp
&optional include-directories predicate
follow-symlinks)
"Return list of all files under directory DIR whose names match REGEXP.
This function works recursively. Files are returned in \"depth
first\" order, and files from each directory are sorted in
alphabetical order. Each file name appears in the returned list
in its absolute form.
By default, the returned list excludes directories, but if
optional argument INCLUDE-DIRECTORIES is non-nil, they are
included.
PREDICATE can be either nil (which means that all subdirectories
of DIR are descended into), t (which means that subdirectories that
can't be read are ignored), or a function (which is called with
the name of each subdirectory, and should return non-nil if the
subdirectory is to be descended into).
If FOLLOW-SYMLINKS is non-nil, symbolic links that point to
directories are followed. Note that this can lead to infinite
recursion."
(let* ((result nil)
(files nil)
(dir (directory-file-name dir))
;; When DIR is "/", remote file names like "/method:" could
;; also be offered. We shall suppress them.
(tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
(dolist (file (sort (file-name-all-completions "" dir)
'string<))
(unless (member file '("./" "../"))
(if (directory-name-p file)
(let* ((leaf (substring file 0 (1- (length file))))
(full-file (concat dir "/" leaf)))
;; Don't follow symlinks to other directories.
(when (and (or (not (file-symlink-p full-file))
(and (file-symlink-p full-file)
follow-symlinks))
;; Allow filtering subdirectories.
(or (eq predicate nil)
(eq predicate t)
(funcall predicate full-file)))
(let ((sub-files
(if (eq predicate t)
(condition-case nil
(org-roam--directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks)
(file-error nil))
(org-roam--directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks))))
(setq result (nconc result sub-files))))
(when (and include-directories
(string-match regexp leaf))
(setq result (nconc result (list full-file)))))
(when (string-match regexp file)
(push (concat dir "/" file) files)))))
(nconc result (nreverse files))))
;;; Obsolete aliases (remove after next major release)
(define-obsolete-function-alias
'org-roam-dailies-find-today
'org-roam-dailies-goto-today "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-yesterday
'org-roam-dailies-goto-yesterday "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-tomorrow
'org-roam-dailies-goto-tomorrow "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-next-note
'org-roam-dailies-goto-next-note "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-previous-note
'org-roam-dailies-goto-previous-note "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-date
'org-roam-dailies-goto-date "org-roam 2.0")
;;; Obsolete functions
(provide 'org-roam-compat)

View File

@ -327,25 +327,6 @@ negative, find note N days in the future."
(define-key org-roam-dailies-map (kbd "v") #'org-roam-dailies-capture-date)
(define-key org-roam-dailies-map (kbd ".") #'org-roam-dailies-find-directory)
(define-obsolete-function-alias
'org-roam-dailies-find-today
'org-roam-dailies-goto-today "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-yesterday
'org-roam-dailies-goto-yesterday "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-tomorrow
'org-roam-dailies-goto-tomorrow "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-next-note
'org-roam-dailies-goto-next-note "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-previous-note
'org-roam-dailies-goto-previous-note "org-roam 2.0")
(define-obsolete-function-alias
'org-roam-dailies-find-date
'org-roam-dailies-goto-date "org-roam 2.0")
(provide 'org-roam-dailies)
;;; org-roam-dailies.el ends here

View File

@ -78,23 +78,31 @@ To your init file.
(when (yes-or-no-p "Org-roam will now convert all your notes from v1 to v2.
This will take a while. Are you sure you want to do this?")
;; Back up notes
(let ((backup-dir (expand-file-name "org-roam.bak"
(file-name-directory (directory-file-name org-roam-directory)))))
(let* ((parent-dir (f-parent org-roam-directory))
(backup-dir (expand-file-name "org-roam.bak" parent-dir))
(debug-dir (expand-file-name "org-roam.debug" parent-dir)))
(message "Backing up files to %s" backup-dir)
(copy-directory org-roam-directory backup-dir))
(condition-case err
(progn
;; Convert v1 to v2
(dolist (f (org-roam--list-all-files))
(org-roam-with-file f nil
(org-roam-migrate-v1-to-v2)))
;; Rebuild cache
(org-roam-db-sync 'force)
;; Convert v1 to v2
(dolist (f (org-roam--list-all-files))
(org-roam-with-file f nil
(org-roam-migrate-v1-to-v2)))
;; Rebuild cache
(org-roam-db-sync 'force)
;;Replace all file links with ID links
(dolist (f (org-roam--list-all-files))
(org-roam-with-file f nil
(org-roam-migrate-replace-file-links-with-id)
(save-buffer)))))
;;Replace all file links with ID links
(dolist (f (org-roam--list-all-files))
(org-roam-with-file f nil
(org-roam-migrate-replace-file-links-with-id)
(save-buffer))))
(t
(rename-file org-roam-directory debug-dir)
(rename-file backup-dir org-roam-directory)
(lwarn 'org-roam :warning (format "The migration wizard failed with error:\n%s\n%s"
(error-message-string err)
"Your files have been restored, consider filing an issue.\n"))))))
(defun org-roam-migrate-v1-to-v2 ()
"Convert the current buffer to v2 format."
@ -115,13 +123,13 @@ This will take a while. Are you sure you want to do this?")
;; Replace roam_alias into properties drawer roam_aliases
(when-let* ((aliases (mapcan #'split-string-and-unquote
(cdar (org-collect-keywords '("roam_alias"))))))
(let ((case-fold-search t))
(org-with-point-at 1
(dolist (alias aliases)
(org-roam-alias-add alias))
(while (re-search-forward "^#\\+roam_alias:" (point-max) t)
(beginning-of-line)
(kill-line 1)))))
(dolist (alias aliases)
(org-roam-alias-add alias)))
(let ((case-fold-search t))
(org-with-point-at 1
(while (re-search-forward "^#\\+roam_alias:" (point-max) t)
(beginning-of-line)
(kill-line 1))))
;; Replace #+roam_tags into #+filetags
(org-with-point-at 1
@ -186,7 +194,7 @@ If the property is already set, replace its value."
:where (= file $s1)
:and (= level 0)] path))))
(set-match-data mdata)
(replace-match (org-link-make-string (concat "id:" node-id) desc))))))))
(replace-match (org-link-make-string (concat "id:" node-id)) nil t)))))))
(provide 'org-roam-migrate)
;;; org-roam-migrate.el ends here

View File

@ -216,67 +216,6 @@ E.g. (\".org\") => (\"*.org\" \"*.org.gpg\")"
(command (s-join " " `(,executable "-L" ,dir "-type f \\(" ,names "\\)"))))
(org-roam--shell-command-files command)))
;; Emacs 26 does not have FOLLOW-SYMLINKS in `directory-files-recursively'
(defun org-roam--directory-files-recursively (dir regexp
&optional include-directories predicate
follow-symlinks)
"Return list of all files under directory DIR whose names match REGEXP.
This function works recursively. Files are returned in \"depth
first\" order, and files from each directory are sorted in
alphabetical order. Each file name appears in the returned list
in its absolute form.
By default, the returned list excludes directories, but if
optional argument INCLUDE-DIRECTORIES is non-nil, they are
included.
PREDICATE can be either nil (which means that all subdirectories
of DIR are descended into), t (which means that subdirectories that
can't be read are ignored), or a function (which is called with
the name of each subdirectory, and should return non-nil if the
subdirectory is to be descended into).
If FOLLOW-SYMLINKS is non-nil, symbolic links that point to
directories are followed. Note that this can lead to infinite
recursion."
(let* ((result nil)
(files nil)
(dir (directory-file-name dir))
;; When DIR is "/", remote file names like "/method:" could
;; also be offered. We shall suppress them.
(tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
(dolist (file (sort (file-name-all-completions "" dir)
'string<))
(unless (member file '("./" "../"))
(if (directory-name-p file)
(let* ((leaf (substring file 0 (1- (length file))))
(full-file (concat dir "/" leaf)))
;; Don't follow symlinks to other directories.
(when (and (or (not (file-symlink-p full-file))
(and (file-symlink-p full-file)
follow-symlinks))
;; Allow filtering subdirectories.
(or (eq predicate nil)
(eq predicate t)
(funcall predicate full-file)))
(let ((sub-files
(if (eq predicate t)
(condition-case nil
(org-roam--directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks)
(file-error nil))
(org-roam--directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks))))
(setq result (nconc result sub-files))))
(when (and include-directories
(string-match regexp leaf))
(setq result (nconc result (list full-file)))))
(when (string-match regexp file)
(push (concat dir "/" file) files)))))
(nconc result (nreverse files))))
(defun org-roam--list-files-elisp (dir)
"Return all Org-roam files located recursively within DIR, using elisp."
(let ((regex (concat "\\.\\(?:"(mapconcat
@ -352,7 +291,9 @@ If BUFFER is not specified, use the current buffer."
;;;###autoload
(defun org-roam-setup ()
"Setup Org-roam."
"Setup Org-roam and initialize its database.
This will install the needed hooks and advices to keep everything
in sync with the connected databases."
(interactive)
(add-hook 'find-file-hook #'org-roam--file-setup)
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
@ -361,7 +302,10 @@ If BUFFER is not specified, use the current buffer."
(org-roam-db-sync))
(defun org-roam-teardown ()
"Teardown Org-roam."
"Teardown Org-roam to completely disable it.
This will remove all the hooks and advices installed by
`org-roam-setup' and close all the database connections made by
Org-roam."
(interactive)
(remove-hook 'find-file-hook #'org-roam--file-setup)
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
@ -407,6 +351,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))
"A heading or top level file with an assigned ID property."
file file-hash file-atime file-mtime
id level point todo priority scheduled deadline title properties olp
tags aliases refs)
@ -446,7 +391,7 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
(replace-regexp-in-string (car pair) (cdr pair) title)))
(let* ((pairs `(("[^[:alnum:][:digit:]]" . "_") ;; convert anything not alphanumeric
("__*" . "_") ;; remove sequential underscores
("^_" . "") ;; remove starting underscore
("^_" . "") ;; remove starting underscore
("_$" . ""))) ;; remove ending underscore
(slug (-reduce-from #'cl-replace (strip-nonspacing-marks title) pairs)))
(downcase slug)))))
@ -456,24 +401,28 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
(set-keymap-parent map org-roam-mode-map)
(define-key map [remap org-roam-visit-thing] 'org-roam-node-visit)
map)
"Keymap for Org-roam node sections.")
"Keymap for `org-roam-node-section's.")
(defclass org-roam-node-section (magit-section)
((keymap :initform 'org-roam-node-map)
(node :initform nil)))
(node :initform nil))
"A `magit-section' used by `org-roam-mode' to contain heading for NODE.")
(defvar org-roam-preview-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map org-roam-mode-map)
(define-key map [remap org-roam-visit-thing] 'org-roam-preview-visit)
map)
"Keymap for Org-roam preview.")
"Keymap for `org-roam-preview-section's.")
(defclass org-roam-preview-section (magit-section)
((keymap :initform 'org-roam-preview-map)
(file :initform nil)
(begin :initform nil)
(end :initform nil)))
(end :initform nil))
"A `magit-section' used by `org-roam-mode' to contain preview content.
The preview content comes from FILE, between the next locations:
BEGIN and END.")
(cl-defmethod org-roam-populate ((node org-roam-node))
"Populate NODE from database.
@ -521,11 +470,27 @@ nodes."
"${title:*} ${tags:10}"
"Configures display formatting for Org-roam node.
Patterns of form \"${field-name:length}\" are interpolated based
on the current node. \"field-name\" is replaced with the
corresponding value of the field of the current node. \"length\"
specifies how many characters are used to display the value of
the field. A \"length\" of \"*\" specifies that as many
characters as possible should be used."
on the current node.
Each \"field-name\" is replaced with the return value of each
corresponding accessor function for `org-roam-node', e.g.
\"${title}\" will be interpolated by the result of
`org-roam-node-title'. You can also define custom accessors using
`cl-defmethod'. For example, you can define:
(cl-defmethod org-roam-node-my-title ((node org-roam-node))
(concat \"My \" (org-roam-node-title node)))
and then reference it here or in the capture templates as
\"${my-title}\".
\"length\" is an optional specifier and declares how many
characters can be used to display the value of the corresponding
field. If it's not specified, the field will be inserted as is,
i.e. it won't be aligned nor trimmed. If it's an integer, the
field will be aligned accordingly and all the exceeding
characters will be trimmed out. If it's \"*\", the field will use
as many characters as possible and will be aligned accordingly."
:group 'org-roam
:type 'string)
@ -609,7 +574,7 @@ populated."
buf))
(defun org-roam-node-visit (node &optional other-window)
"From the buffer, visit NODE.
"From the current buffer, visit NODE.
Display the buffer in the selected window. With a prefix
argument OTHER-WINDOW display the buffer in another window
@ -649,7 +614,7 @@ Throw an error if multiple choices exist."
(user-error "Multiple nodes exist with title or alias \"%s\"" s)))))
(defun org-roam-node-list ()
"Return a list of all nodes."
"Return all nodes stored in the database as a list of `org-roam-node's."
(let ((rows (org-roam-db-query
"SELECT
id,
@ -833,10 +798,25 @@ window instead."
#'pop-to-buffer-same-window) buf)))
(cl-defun org-roam-node-insert-section (&key source-node point properties)
"Insert section for NODE.
SOURCE-NODE is the source node.
POINT is the point in buffer for the link.
PROPERTIES contains properties about the link."
"Insert section for a link from SOURCE-NODE to some other node.
SOURCE-NODE is an `org-roam-node' that links or references some
other node. Normally the other node is `org-roam-current-node'
that set in `org-roam-buffer'.
POINT is the position in SOURCE-NODE's file where the link is
located.
PROPERTIES (a plist) contains additional information about the
link.
This section is made out of the next 2 `magit-section's:
1. `org-roam-node-section' for a heading that describes
SOURCE-NODE.
2. `org-roam-preview-section' for a preview content that comes
from SOURCE-NODE's file for the link (that references the
other node) at POINT."
(magit-insert-section section (org-roam-node-section)
(let ((outline (if-let ((outline (plist-get properties :outline)))
(mapconcat #'org-link-display-format outline " > ")
@ -873,9 +853,7 @@ If OTHER-WINDOW, visit the NODE in another window."
;;;###autoload
(defun org-roam-node-insert (&optional filter-fn)
"Find an Org-roam file, and insert a relative org link to it at point.
Return selected file if it exists.
If LOWERCASE is non-nil, downcase the link description.
"Find an Org-roam node and insert (where the point is) an \"id:\" link to it.
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
and when nil is returned the node will be filtered out."
(interactive)
@ -912,7 +890,7 @@ and when nil is returned the node will be filtered out."
;;;###autoload
(defun org-roam-node-random (&optional other-window)
"Find a random Org-roam node.
"Find and open a random Org-roam node.
With prefix argument OTHER-WINDOW, visit the node in another
window instead."
(interactive current-prefix-arg)
@ -923,22 +901,24 @@ window instead."
other-window)))
;;;; Properties
(defun org-roam-add-property (s prop)
"Add S to property PROP."
(defun org-roam-add-property (val prop)
"Add VAL value to PROP property for the node at point.
Both, VAL and PROP are strings."
(let* ((p (org-entry-get (point) prop))
(lst (when p (split-string-and-unquote p)))
(lst (if (memq s lst) lst (cons s lst)))
(lst (if (memq val lst) lst (cons val lst)))
(lst (seq-uniq lst)))
(org-set-property prop (combine-and-quote-strings lst))
s))
val))
(defun org-roam-remove-property (prop &optional s)
"Remove S from property PROP.
(defun org-roam-remove-property (prop &optional val)
"Remove VAL value from PROP property for the node at point.
Both VAL and PROP are strings.
If S is not specified, user is prompted to select a value."
If VAL 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 (or s (completing-read "Remove: " lst)))
(prop-to-remove (or val (completing-read "Remove: " lst)))
(lst (delete prop-to-remove lst)))
(if lst
(org-set-property prop (combine-and-quote-strings lst))
@ -1093,9 +1073,7 @@ REF is assumed to be a propertized string."
;;;###autoload
(defun org-roam-ref-find (&optional initial-input filter-fn)
"Find and open and Org-roam file from REF if it exists.
REF should be the value of '#+roam_key:' without any
type-information (e.g. 'cite:').
"Find and open an Org-roam node that's dedicated to a specific ref.
INITIAL-INPUT is the initial input to the prompt.
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
and when nil is returned the node will be filtered out."
@ -1106,7 +1084,7 @@ and when nil is returned the node will be filtered out."
;;;; roam: link
(defcustom org-roam-link-auto-replace t
"When non-nil, replace Org-roam's roam links with file or id links whenever possible."
"If non-nil, replace \"roam:\" links to existing nodes with \"id:\" links."
:group 'org-roam
:type 'boolean)
@ -1114,7 +1092,7 @@ and when nil is returned the node will be filtered out."
(org-link-set-parameters "roam" :follow #'org-roam-link-follow-link)
(defun org-roam-link-replace-at-point (&optional link)
"Replace the roam: LINK at point with an id link."
"Replace \"roam:\" LINK at point with an \"id:\" link."
(save-excursion
(save-match-data
(let* ((link (or link (org-element-context)))
@ -1143,23 +1121,21 @@ and when nil is returned the node will be filtered out."
(add-hook 'org-roam-find-file-hook #'org-roam--replace-roam-links-on-save-h)
(defun org-roam-link-follow-link (path)
"Org-roam's roam: link navigation with description PATH.
This function is called by Org when following links of the type
`roam'. While the path is passed, assume that the cursor is on
the link."
(if-let ((node (org-roam-node-from-title-or-alias path)))
(defun org-roam-link-follow-link (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."
(if-let ((node (org-roam-node-from-title-or-alias title-or-alias)))
(progn
(when org-roam-link-auto-replace
(org-roam-link-replace-at-point))
(org-id-goto (org-roam-node-id node)))
(org-roam-capture-
:node (org-roam-node-create :title path)
:node (org-roam-node-create :title title-or-alias)
:props '(:finalize find-file))))
(defun org-roam-open-id-at-point ()
"Navigates to the ID at point.
To be added to `org-open-at-point-functions'."
"Try to navigate \"id:\" link to find and visit node with an assigned ID.
Assumes that the cursor was put where the link is."
(let* ((context (org-element-context))
(type (org-element-property :type context))
(id (org-element-property :path context)))
@ -1173,7 +1149,7 @@ To be added to `org-open-at-point-functions'."
(t nil))))))
(defun org-roam-open-id-with-org-roam-db-h ()
"."
"Try to open \"id:\" links at point by querying them to the database."
(add-hook 'org-open-at-point-functions #'org-roam-open-id-at-point nil t))
(add-hook 'org-roam-find-file-hook #'org-roam-open-id-with-org-roam-db-h)
@ -1200,7 +1176,8 @@ Any top level properties drawers are incorporated into the new heading."
(org-roam--file-keyword-kill "FILETAGS")))
(defun org-roam-refile ()
"Refile to node."
"Refile node at point to an Org-roam node.
If region is active, then use it instead of the node at point."
(interactive)
(let* ((regionp (org-region-active-p))
(region-start (and regionp (region-beginning)))