mirror of
https://github.com/org-roam/org-roam
synced 2025-08-11 13:07:24 -05:00
Compare commits
27 Commits
v2.0.0
...
feat/paren
Author | SHA1 | Date | |
---|---|---|---|
d7e1bd49a5 | |||
9acd982332 | |||
028c95a011 | |||
d1e3a5d9be | |||
8e89bad945 | |||
9c10a3c04c | |||
1aba91eacd | |||
2fe233ffa0 | |||
d9015cb931 | |||
7d5b7e6185 | |||
3ea433c09d | |||
e5c735c86a | |||
23605546d8 | |||
f50e30dd51 | |||
a529b20a81 | |||
6cbd4ad3e8 | |||
e997c017de | |||
63450e9eaf | |||
da02453ab1 | |||
bbf1d97eb0 | |||
7d9fcf5288 | |||
d0be7f3b2a | |||
363dca1765 | |||
4c5a041556 | |||
5e42d854c1 | |||
681873759d | |||
2168490d5a |
@ -86,6 +86,7 @@ it has not already been addressed on [GitHub][issues] or on
|
|||||||
- [Jethro Kuan](https://braindump.jethro.dev/)
|
- [Jethro Kuan](https://braindump.jethro.dev/)
|
||||||
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
|
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
|
||||||
- [Alexey Shmalko](https://braindump.rasen.dev/)
|
- [Alexey Shmalko](https://braindump.rasen.dev/)
|
||||||
|
- [Sidharth Arya](https://sidhartharya.github.io/braindump/index.html)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
145
doc/org-roam.org
145
doc/org-roam.org
@ -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
|
*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
|
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
|
~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.
|
thoughts, to be processed later into permanent notes.
|
||||||
|
|
||||||
*Permanent notes*
|
*Permanent notes*
|
||||||
@ -198,19 +198,6 @@ using:
|
|||||||
M-x package-install RET org-roam RET
|
M-x package-install RET org-roam RET
|
||||||
#+END_EXAMPLE
|
#+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
|
|
||||||
using Apt:
|
|
||||||
|
|
||||||
#+BEGIN_SRC bash
|
|
||||||
apt-get install elpa-org-roam
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
Org-roam will then be autoloaded into Emacs.
|
|
||||||
|
|
||||||
** Installing from Source
|
** Installing from Source
|
||||||
|
|
||||||
You may install Org-roam directly from the repository on [[https://github.com/org-roam/org-roam][GitHub]] if you like.
|
You may install Org-roam directly from the repository on [[https://github.com/org-roam/org-roam][GitHub]] if you like.
|
||||||
@ -248,7 +235,8 @@ dependencies that it requires. These include:
|
|||||||
- s
|
- s
|
||||||
- org
|
- org
|
||||||
- emacsql
|
- emacsql
|
||||||
- emacsql-sqlite3
|
- emacsql-sqlite
|
||||||
|
- magit-section
|
||||||
|
|
||||||
You can install this manually as well, or get the latest version from MELPA. You
|
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.
|
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 +279,43 @@ file:
|
|||||||
install-info /path/to/my/info/files/org-roam.info /path/to/my/info/files/dir
|
install-info /path/to/my/info/files/org-roam.info /path/to/my/info/files/dir
|
||||||
#+end_src
|
#+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~).
|
~emacsql-sqlite~ requires a C compiler (e.g. ~gcc~ or ~clang~) to be present in
|
||||||
Please ensure that ~sqlite3~ is installed appropriately on your operating
|
your computer. How to install a C compiler depends on the OS that you use.
|
||||||
system. You can verify that this is the case by executing [fn:2]:
|
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
- For Windows:
|
||||||
(executable-find "sqlite3")
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
If you have ~sqlite3~ installed, and ~executable-find~ still reports ~nil~, then
|
There are various ways to install one, depending on how you have installed
|
||||||
the path to the executable is not a member of the Emacs variable ~exec-path~.
|
Emacs. If you use Emacs within a Cygwin or MinGW environment, then you should
|
||||||
Rectify this by manually adding the path within your Emacs configuration:
|
install a compiler using their respective package manager.
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
If you have installed your Emacs from the [[https://www.gnu.org/software/emacs/][GNU Emacs website]], then the easiest way
|
||||||
(add-to-list 'exec-path "path/to/sqlite3")
|
is to use [[https://www.msys2.org/][MSYS2]] as at the time of this writing:
|
||||||
#+END_SRC
|
|
||||||
|
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
|
* Getting Started
|
||||||
** The Org-roam Node
|
** The Org-roam Node
|
||||||
@ -376,7 +384,7 @@ Org-roam is available on startup, place this in your Emacs configuration:
|
|||||||
(org-roam-setup)
|
(org-roam-setup)
|
||||||
#+end_src
|
#+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
|
take a while the first time, but subsequent builds are often instantaneous
|
||||||
because they only reprocess modified files.
|
because they only reprocess modified files.
|
||||||
|
|
||||||
@ -439,19 +447,15 @@ Org-roam provides (see [[id:70083bfd-d1e3-42b9-bf83-5b05708791c0][Completion]]).
|
|||||||
|
|
||||||
Org-roam provides the Org-roam buffer: an interface to view relationships with
|
Org-roam provides the Org-roam buffer: an interface to view relationships with
|
||||||
other notes (backlinks, reference links, unlinked references etc.). There are
|
other notes (backlinks, reference links, unlinked references etc.). There are
|
||||||
two main functions to use here:
|
two main commands to use here:
|
||||||
|
|
||||||
- ~org-roam-buffer~: Launch an Org-roam buffer for the current node at point.
|
|
||||||
- ~org-roam-buffer-toggle~: Launch an Org-roam buffer that tracks the node
|
- ~org-roam-buffer-toggle~: Launch an Org-roam buffer that tracks the node
|
||||||
currently at point. This means that the content of the buffer changes as the
|
currently at point. This means that the content of the buffer changes as the
|
||||||
point is moved, if necessary.
|
point is moved, if necessary.
|
||||||
|
- ~org-roam-buffer-display-dedicated~: Launch an Org-roam buffer for a specific
|
||||||
Use ~org-roam-buffer-toggle~ when you want wish for the Org-roam buffer to
|
node without visiting its file. Unlike ~org-roam-buffer-toggle~ you can have
|
||||||
buffer, call ~M-x org-roam-buffer~.
|
multiple such buffers and their content won't be automatically replaced with a
|
||||||
|
new node at point.
|
||||||
- Function: org-roam-buffer
|
|
||||||
|
|
||||||
Launch an Org-roam buffer for the current node at point.
|
|
||||||
|
|
||||||
To bring up a buffer that tracks the current node at point, call ~M-x
|
To bring up a buffer that tracks the current node at point, call ~M-x
|
||||||
org-roam-buffer-toggle~.
|
org-roam-buffer-toggle~.
|
||||||
@ -460,6 +464,13 @@ org-roam-buffer-toggle~.
|
|||||||
|
|
||||||
Toggle display of the ~org-roam-buffer~.
|
Toggle display of the ~org-roam-buffer~.
|
||||||
|
|
||||||
|
To bring up a buffer that's dedicated for a specific node, call ~M-x
|
||||||
|
org-roam-buffer-display-dedicated~.
|
||||||
|
|
||||||
|
- Function: org-roam-buffer-display-dedicated
|
||||||
|
|
||||||
|
Launch node dedicated Org-roam buffer without visiting the node itself.
|
||||||
|
|
||||||
** Navigating the Org-roam Buffer
|
** Navigating the Org-roam Buffer
|
||||||
|
|
||||||
The Org-roam buffer uses ~magit-section~, making the typical ~magit-section~
|
The Org-roam buffer uses ~magit-section~, making the typical ~magit-section~
|
||||||
@ -478,14 +489,14 @@ section-specific commands such as ~org-roam-node-visit~.
|
|||||||
There are currently 3 provided widget types:
|
There are currently 3 provided widget types:
|
||||||
|
|
||||||
- Backlinks :: View (preview of) nodes that link to this node
|
- 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
|
- Unlinked references :: View nodes that contain text that match the nodes
|
||||||
title/alias but are not linked
|
title/alias but are not linked
|
||||||
|
|
||||||
To configure what sections are displayed in the buffer, set ~org-roam-mode-sections~.
|
To configure what sections are displayed in the buffer, set ~org-roam-mode-section-functions~.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(setq org-roam-mode-sections
|
(setq org-roam-mode-section-functions
|
||||||
(list #'org-roam-backlinks-section
|
(list #'org-roam-backlinks-section
|
||||||
#'org-roam-reflinks-section
|
#'org-roam-reflinks-section
|
||||||
;; #'org-roam-unlinked-references-section
|
;; #'org-roam-unlinked-references-section
|
||||||
@ -501,11 +512,11 @@ the user. The author's recommended configuration is as follows:
|
|||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(add-to-list 'display-buffer-alist
|
(add-to-list 'display-buffer-alist
|
||||||
'(("\\*org-roam\\*"
|
'("\\*org-roam\\*"
|
||||||
(display-buffer-in-direction)
|
(display-buffer-in-direction)
|
||||||
(direction . right)
|
(direction . right)
|
||||||
(window-width . 0.33)
|
(window-width . 0.33)
|
||||||
(window-height . fit-window-to-buffer))))
|
(window-height . fit-window-to-buffer)))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
Crucially, the window is a regular window (not a side-window), and this allows
|
Crucially, the window is a regular window (not a side-window), and this allows
|
||||||
@ -515,6 +526,20 @@ for predictable navigation:
|
|||||||
Org-roam buffer.
|
Org-roam buffer.
|
||||||
- ~C-u RET~ navigates to thing-at-point in the other window.
|
- ~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
|
** TODO Styling the Org-roam buffer
|
||||||
* Node Properties
|
* Node Properties
|
||||||
** Standard Org properties
|
** Standard Org properties
|
||||||
@ -587,7 +612,7 @@ With the above example, if another node links to https://www.google.com/, it
|
|||||||
will show up as a “reference backlink”.
|
will show up as a “reference backlink”.
|
||||||
|
|
||||||
These keys also come in useful for when taking website notes, using the
|
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
|
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
|
multiple papers in a series to share the same note, or an article has a citation
|
||||||
@ -664,6 +689,10 @@ extension in your Org-roam capture templates. For example:
|
|||||||
:unnarrowed t)))
|
:unnarrowed t)))
|
||||||
#+end_src
|
#+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 Protocol
|
||||||
|
|
||||||
Org-roam provides extensions for capturing content from external applications
|
Org-roam provides extensions for capturing content from external applications
|
||||||
@ -838,7 +867,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
|
~org-roam-capture-ref-templates~ (see [[*The Templating System][The Templating System]]). These templates
|
||||||
should contain a ~#+roam_key: ${ref}~ in it.
|
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
|
Org-roam extends the ~org-capture~ system, providing a smoother note-taking
|
||||||
experience. However, these extensions mean Org-roam capture templates are
|
experience. However, these extensions mean Org-roam capture templates are
|
||||||
@ -902,7 +931,7 @@ strings. ~${foo}~'s substitution is performed as follows:
|
|||||||
|
|
||||||
Org-roam provides basic graphing capabilities to explore interconnections
|
Org-roam provides basic graphing capabilities to explore interconnections
|
||||||
between notes, in ~org-roam-graph~. This is done by performing SQL queries and
|
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]].
|
Protocol]].
|
||||||
|
|
||||||
The entry point to graph creation is ~org-roam-graph~.
|
The entry point to graph creation is ~org-roam-graph~.
|
||||||
@ -971,9 +1000,12 @@ for customizable options.
|
|||||||
Example: ~'(("dir" . "back"))~
|
Example: ~'(("dir" . "back"))~
|
||||||
|
|
||||||
* Org-roam Dailies
|
* Org-roam Dailies
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 4eae8552-95e1-4e4a-b7b7-2c53433730ea
|
||||||
|
:END:
|
||||||
|
|
||||||
Org-roam provides journaling capabilities akin to
|
Org-roam provides journaling capabilities akin to
|
||||||
[[#org-journal][Org-journal]] with ~org-roam-dailies~.
|
Org-journal with ~org-roam-dailies~.
|
||||||
|
|
||||||
** Configuration
|
** Configuration
|
||||||
|
|
||||||
@ -1011,7 +1043,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.
|
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.
|
Find the daily note for today, creating it if necessary.
|
||||||
|
|
||||||
@ -1023,7 +1055,7 @@ There are variants of those commands for ~-yesterday~ and ~-tomorrow~:
|
|||||||
|
|
||||||
With numeric argument ~n~, use the daily note ~n~ days in the past.
|
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.
|
With numeric argument N, use the daily-note N days in the future.
|
||||||
|
|
||||||
@ -1038,7 +1070,7 @@ There are also commands which allow you to use Emacs’s ~calendar~ to find the
|
|||||||
With a 'C-u' prefix or when ~goto~ is non-nil, go the note without
|
With a 'C-u' prefix or when ~goto~ is non-nil, go the note without
|
||||||
creating an entry.
|
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.
|
Find the daily note for a date using the calendar, creating it if necessary.
|
||||||
|
|
||||||
@ -1048,11 +1080,11 @@ There are also commands which allow you to use Emacs’s ~calendar~ to find the
|
|||||||
|
|
||||||
Find and open ~org-roam-dailies-directory~.
|
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.
|
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.
|
When in an daily-note, find the next one.
|
||||||
* Performance Optimization
|
* Performance Optimization
|
||||||
@ -1128,7 +1160,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
|
[[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
|
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.
|
journal entries from the ideas you would write on a scratchpad.
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@ -1234,6 +1266,13 @@ are the solutions:
|
|||||||
set ~ivy-use-selectable-prompt~ to ~t~, so that "bar" is now selectable.
|
set ~ivy-use-selectable-prompt~ to ~t~, so that "bar" is now selectable.
|
||||||
- Helm :: Org-roam should provide a selectable "[?] bar" candidate at the top of
|
- Helm :: Org-roam should provide a selectable "[?] bar" candidate at the top of
|
||||||
the candidate list.
|
the candidate list.
|
||||||
|
** How can I stop Org-roam from creating IDs everywhere?
|
||||||
|
|
||||||
|
Other than the interactive commands that Org-roam provides, Org-roam does not
|
||||||
|
create IDs everywhere. If you are noticing that IDs are being created even when
|
||||||
|
you don't want them to be (e.g. when tangling an Org file), check the value you
|
||||||
|
have set for ~org-id-link-to-org-use-id~: setting it to ~'create-if-interactive~
|
||||||
|
is a popular option.
|
||||||
|
|
||||||
* Migrating from Org-roam v1
|
* Migrating from Org-roam v1
|
||||||
|
|
||||||
|
2360
doc/org-roam.texi
2360
doc/org-roam.texi
File diff suppressed because it is too large
Load Diff
@ -525,6 +525,12 @@ also run Org-capture's template expansion."
|
|||||||
(when-let ((ref (plist-get org-roam-capture--info :ref)))
|
(when-let ((ref (plist-get org-roam-capture--info :ref)))
|
||||||
(org-roam-ref-add ref)))
|
(org-roam-ref-add ref)))
|
||||||
|
|
||||||
|
(defun org-roam-capture--create-parent-directory (path)
|
||||||
|
"Create the parent directory for PATH."
|
||||||
|
(make-directory
|
||||||
|
(file-name-directory path)
|
||||||
|
'parents))
|
||||||
|
|
||||||
(defun org-roam-capture--goto-location ()
|
(defun org-roam-capture--goto-location ()
|
||||||
"Initialize the buffer, and goto the location of the new capture.
|
"Initialize the buffer, and goto the location of the new capture.
|
||||||
Return the ID of the location."
|
Return the ID of the location."
|
||||||
@ -535,6 +541,7 @@ Return the ID of the location."
|
|||||||
(setq path (expand-file-name
|
(setq path (expand-file-name
|
||||||
(string-trim (org-roam-capture--fill-template path t))
|
(string-trim (org-roam-capture--fill-template path t))
|
||||||
org-roam-directory))
|
org-roam-directory))
|
||||||
|
(org-roam-capture--create-parent-directory path)
|
||||||
(unless (file-exists-p path)
|
(unless (file-exists-p path)
|
||||||
(org-roam-capture--put :new-file path))
|
(org-roam-capture--put :new-file path))
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
@ -544,6 +551,7 @@ Return the ID of the location."
|
|||||||
(setq path (expand-file-name
|
(setq path (expand-file-name
|
||||||
(string-trim (org-roam-capture--fill-template path t))
|
(string-trim (org-roam-capture--fill-template path t))
|
||||||
org-roam-directory))
|
org-roam-directory))
|
||||||
|
(org-roam-capture--create-parent-directory path)
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(unless (file-exists-p path)
|
(unless (file-exists-p path)
|
||||||
(org-roam-capture--put :new-file path))
|
(org-roam-capture--put :new-file path))
|
||||||
@ -555,6 +563,7 @@ Return the ID of the location."
|
|||||||
(setq path (expand-file-name
|
(setq path (expand-file-name
|
||||||
(string-trim (org-roam-capture--fill-template path t))
|
(string-trim (org-roam-capture--fill-template path t))
|
||||||
org-roam-directory))
|
org-roam-directory))
|
||||||
|
(org-roam-capture--create-parent-directory path)
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(unless (file-exists-p path)
|
(unless (file-exists-p path)
|
||||||
(org-roam-capture--put :new-file path)
|
(org-roam-capture--put :new-file path)
|
||||||
@ -565,6 +574,7 @@ Return the ID of the location."
|
|||||||
(setq path (expand-file-name
|
(setq path (expand-file-name
|
||||||
(string-trim (org-roam-capture--fill-template path t))
|
(string-trim (org-roam-capture--fill-template path t))
|
||||||
org-roam-directory))
|
org-roam-directory))
|
||||||
|
(org-roam-capture--create-parent-directory path)
|
||||||
(widen)
|
(widen)
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(unless (file-exists-p path)
|
(unless (file-exists-p path)
|
||||||
@ -577,6 +587,7 @@ Return the ID of the location."
|
|||||||
(setq path (expand-file-name
|
(setq path (expand-file-name
|
||||||
(string-trim (org-roam-capture--fill-template path t))
|
(string-trim (org-roam-capture--fill-template path t))
|
||||||
org-roam-directory))
|
org-roam-directory))
|
||||||
|
(org-roam-capture--create-parent-directory path)
|
||||||
(require 'org-datetree)
|
(require 'org-datetree)
|
||||||
(widen)
|
(widen)
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
|
@ -33,7 +33,103 @@
|
|||||||
;;; Code:
|
;;; Code:
|
||||||
;;;; Library Requires
|
;;;; 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)
|
;;; Obsolete aliases (remove after next major release)
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-roam-current-node
|
||||||
|
'org-roam-buffer-current-node "org-roam 2.0")
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-roam-current-directory
|
||||||
|
'org-roam-buffer-current-directory "org-roam 2.0")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-roam-buffer-render
|
||||||
|
'org-roam-buffer-render-contents "org-roam 2.0")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-roam-buffer
|
||||||
|
'org-roam-buffer-display-dedicated "org-roam 2.0")
|
||||||
|
|
||||||
|
(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
|
;;; Obsolete functions
|
||||||
|
|
||||||
(provide 'org-roam-compat)
|
(provide 'org-roam-compat)
|
||||||
|
@ -67,6 +67,8 @@ This path is relative to `org-roam-directory'."
|
|||||||
:if-new (file+head "%<%Y-%m-%d>.org"
|
:if-new (file+head "%<%Y-%m-%d>.org"
|
||||||
"#+title: %<%Y-%m-%d>\n")))
|
"#+title: %<%Y-%m-%d>\n")))
|
||||||
"Capture templates for daily-notes in Org-roam.
|
"Capture templates for daily-notes in Org-roam.
|
||||||
|
Note that for daily files to show up in the calendar, they have to be of format
|
||||||
|
\"org-time-string.org\".
|
||||||
See `org-roam-capture-templates' for the template documentation."
|
See `org-roam-capture-templates' for the template documentation."
|
||||||
:group 'org-roam
|
:group 'org-roam
|
||||||
:type '(repeat
|
:type '(repeat
|
||||||
@ -213,16 +215,17 @@ future."
|
|||||||
(org-roam-dailies-capture-tomorrow (- n) t))
|
(org-roam-dailies-capture-tomorrow (- n) t))
|
||||||
|
|
||||||
;;; Calendar
|
;;; Calendar
|
||||||
(defun org-roam-dailies-calendar--file-to-date (&optional file)
|
(defun org-roam-dailies-calendar--file-to-date (file)
|
||||||
"Convert FILE to date.
|
"Convert FILE to date.
|
||||||
Return (MONTH DAY YEAR)."
|
Return (MONTH DAY YEAR) or nil if not an Org time-string."
|
||||||
(let ((file (or file
|
(condition-case nil
|
||||||
(buffer-base-buffer (buffer-file-name)))))
|
(progn
|
||||||
(cl-destructuring-bind (_ _ _ d m y _ _ _)
|
(cl-destructuring-bind (_ _ _ d m y _ _ _)
|
||||||
(org-parse-time-string
|
(org-parse-time-string
|
||||||
(file-name-sans-extension
|
(file-name-sans-extension
|
||||||
(file-name-nondirectory file)))
|
(file-name-nondirectory file)))
|
||||||
(list m d y))))
|
(list m d y)))
|
||||||
|
(t nil)))
|
||||||
|
|
||||||
(defun org-roam-dailies-calendar--date-to-time (date)
|
(defun org-roam-dailies-calendar--date-to-time (date)
|
||||||
"Convert DATE as returned from then calendar (MONTH DAY YEAR) to a time."
|
"Convert DATE as returned from then calendar (MONTH DAY YEAR) to a time."
|
||||||
@ -231,8 +234,9 @@ Return (MONTH DAY YEAR)."
|
|||||||
(defun org-roam-dailies-calendar-mark-entries ()
|
(defun org-roam-dailies-calendar-mark-entries ()
|
||||||
"Mark days in the calendar for which a daily-note is present."
|
"Mark days in the calendar for which a daily-note is present."
|
||||||
(when (file-exists-p (expand-file-name org-roam-dailies-directory org-roam-directory))
|
(when (file-exists-p (expand-file-name org-roam-dailies-directory org-roam-directory))
|
||||||
(dolist (date (mapcar #'org-roam-dailies-calendar--file-to-date
|
(dolist (date (remove nil
|
||||||
(org-roam-dailies--list-files)))
|
(mapcar #'org-roam-dailies-calendar--file-to-date
|
||||||
|
(org-roam-dailies--list-files))))
|
||||||
(when (calendar-date-is-visible-p date)
|
(when (calendar-date-is-visible-p date)
|
||||||
(calendar-mark-visible-date date 'org-roam-dailies-calendar-note)))))
|
(calendar-mark-visible-date date 'org-roam-dailies-calendar-note)))))
|
||||||
|
|
||||||
@ -327,25 +331,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 "v") #'org-roam-dailies-capture-date)
|
||||||
(define-key org-roam-dailies-map (kbd ".") #'org-roam-dailies-find-directory)
|
(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)
|
(provide 'org-roam-dailies)
|
||||||
|
|
||||||
;;; org-roam-dailies.el ends here
|
;;; org-roam-dailies.el ends here
|
||||||
|
@ -422,14 +422,23 @@ If UPDATE-P is non-nil, first remove the file in the database."
|
|||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (org-element-property :begin link))
|
(goto-char (org-element-property :begin link))
|
||||||
(let ((type (org-element-property :type link))
|
(let ((type (org-element-property :type link))
|
||||||
(dest (org-element-property :path link))
|
(path (org-element-property :path link))
|
||||||
(properties (list :outline (org-get-outline-path)))
|
(properties (list :outline (org-get-outline-path)))
|
||||||
(source (org-roam-id-at-point)))
|
(source (org-roam-id-at-point)))
|
||||||
(when source
|
;; For Org-ref links, we need to split the path into the cite keys
|
||||||
|
(when (and (boundp 'org-ref-cite-types)
|
||||||
|
(fboundp 'org-ref-split-and-strip-string)
|
||||||
|
(member type org-ref-cite-types))
|
||||||
|
(setq path (org-ref-split-and-strip-string path)))
|
||||||
|
(unless (listp path)
|
||||||
|
(setq path (list path)))
|
||||||
|
(when (and source path)
|
||||||
(org-roam-db-query
|
(org-roam-db-query
|
||||||
[:insert :into links
|
[:insert :into links
|
||||||
:values $v1]
|
:values $v1]
|
||||||
(vector (point) source dest type properties))))))
|
(mapcar (lambda (p)
|
||||||
|
(vector (point) source p type properties))
|
||||||
|
path))))))
|
||||||
|
|
||||||
;;;;; Fetching
|
;;;;; Fetching
|
||||||
(defun org-roam-db--get-current-files ()
|
(defun org-roam-db--get-current-files ()
|
||||||
|
@ -51,7 +51,12 @@ for an overview of the major changes.
|
|||||||
|
|
||||||
Notes taken in v1 are incompatible with v1, but you can upgrade
|
Notes taken in v1 are incompatible with v1, but you can upgrade
|
||||||
them to the v2 format via a simple command. To migrate your
|
them to the v2 format via a simple command. To migrate your
|
||||||
notes, run:
|
notes, first make sure you're on at least Org 9.4 (check with
|
||||||
|
C-h v org-version) and set your org-roam-directory to your notes:
|
||||||
|
|
||||||
|
(setq org-roam-directory \"path/to/org/files\")
|
||||||
|
|
||||||
|
then, run:
|
||||||
|
|
||||||
M-x org-roam-migrate-wizard
|
M-x org-roam-migrate-wizard
|
||||||
|
|
||||||
@ -83,10 +88,14 @@ This will take a while. Are you sure you want to do this?")
|
|||||||
(message "Backing up files to %s" backup-dir)
|
(message "Backing up files to %s" backup-dir)
|
||||||
(copy-directory org-roam-directory backup-dir))
|
(copy-directory org-roam-directory backup-dir))
|
||||||
|
|
||||||
|
;; Upgrade database to v2
|
||||||
|
(org-roam-db-sync 'force)
|
||||||
|
|
||||||
;; Convert v1 to v2
|
;; Convert v1 to v2
|
||||||
(dolist (f (org-roam--list-all-files))
|
(dolist (f (org-roam--list-all-files))
|
||||||
(org-roam-with-file f nil
|
(org-roam-with-file f nil
|
||||||
(org-roam-migrate-v1-to-v2)))
|
(org-roam-migrate-v1-to-v2)))
|
||||||
|
|
||||||
;; Rebuild cache
|
;; Rebuild cache
|
||||||
(org-roam-db-sync 'force)
|
(org-roam-db-sync 'force)
|
||||||
|
|
||||||
@ -115,18 +124,22 @@ This will take a while. Are you sure you want to do this?")
|
|||||||
;; Replace roam_alias into properties drawer roam_aliases
|
;; Replace roam_alias into properties drawer roam_aliases
|
||||||
(when-let* ((aliases (mapcan #'split-string-and-unquote
|
(when-let* ((aliases (mapcan #'split-string-and-unquote
|
||||||
(cdar (org-collect-keywords '("roam_alias"))))))
|
(cdar (org-collect-keywords '("roam_alias"))))))
|
||||||
|
(dolist (alias aliases)
|
||||||
|
(org-roam-alias-add alias)))
|
||||||
(let ((case-fold-search t))
|
(let ((case-fold-search t))
|
||||||
(org-with-point-at 1
|
(org-with-point-at 1
|
||||||
(dolist (alias aliases)
|
|
||||||
(org-roam-alias-add alias))
|
|
||||||
(while (re-search-forward "^#\\+roam_alias:" (point-max) t)
|
(while (re-search-forward "^#\\+roam_alias:" (point-max) t)
|
||||||
(beginning-of-line)
|
(beginning-of-line)
|
||||||
(kill-line 1)))))
|
(kill-line 1))))
|
||||||
|
|
||||||
;; Replace #+roam_tags into #+filetags
|
;; Replace #+roam_tags into #+filetags
|
||||||
(org-with-point-at 1
|
(org-with-point-at 1
|
||||||
(let* ((roam-tags (org-roam-migrate-get-prop-list "ROAM_TAGS"))
|
(let* ((roam-tags (org-roam-migrate-get-prop-list "ROAM_TAGS"))
|
||||||
(file-tags (org-roam-migrate-get-prop-list "FILETAGS"))
|
(file-tags (cl-mapcan (lambda (value)
|
||||||
|
(cl-mapcan
|
||||||
|
(lambda (k) (org-split-string k ":"))
|
||||||
|
(split-string value)))
|
||||||
|
(org-roam-migrate-get-prop-list "FILETAGS")))
|
||||||
(tags (append roam-tags file-tags))
|
(tags (append roam-tags file-tags))
|
||||||
(tags (seq-map (lambda (tag)
|
(tags (seq-map (lambda (tag)
|
||||||
(replace-regexp-in-string
|
(replace-regexp-in-string
|
||||||
@ -135,7 +148,7 @@ This will take a while. Are you sure you want to do this?")
|
|||||||
tag)) tags))
|
tag)) tags))
|
||||||
(tags (seq-uniq tags)))
|
(tags (seq-uniq tags)))
|
||||||
(when tags
|
(when tags
|
||||||
(org-roam-migrate-prop-set "filetags" (string-join tags " "))))
|
(org-roam-migrate-prop-set "filetags" (org-make-tag-string tags))))
|
||||||
(let ((case-fold-search t))
|
(let ((case-fold-search t))
|
||||||
(org-with-point-at 1
|
(org-with-point-at 1
|
||||||
(while (re-search-forward "^#\\+roam_tags:" (point-max) t)
|
(while (re-search-forward "^#\\+roam_tags:" (point-max) t)
|
||||||
@ -186,7 +199,8 @@ If the property is already set, replace its value."
|
|||||||
:where (= file $s1)
|
:where (= file $s1)
|
||||||
:and (= level 0)] path))))
|
:and (= level 0)] path))))
|
||||||
(set-match-data mdata)
|
(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)
|
||||||
|
desc) nil t)))))))
|
||||||
|
|
||||||
(provide 'org-roam-migrate)
|
(provide 'org-roam-migrate)
|
||||||
;;; org-roam-migrate.el ends here
|
;;; org-roam-migrate.el ends here
|
||||||
|
179
org-roam-mode.el
179
org-roam-mode.el
@ -120,16 +120,26 @@ and `:slant'."
|
|||||||
:group 'org-roam-faces)
|
:group 'org-roam-faces)
|
||||||
|
|
||||||
;;; Variables
|
;;; Variables
|
||||||
(defvar org-roam-current-node nil
|
(defvar org-roam-buffer-current-node nil
|
||||||
"The current node at point.")
|
"The node for which an `org-roam-mode' based buffer displays its contents.
|
||||||
|
This set both, locally and globally. Normally the local value is
|
||||||
|
only set in the `org-roam-mode' based buffers, while the global
|
||||||
|
value shows the current node in the persistent `org-roam-buffer'.")
|
||||||
|
|
||||||
(defvar org-roam-current-directory nil
|
(put 'org-roam-buffer-current-node 'permanent-local t)
|
||||||
"The `org-roam-directory' value for the current node.")
|
|
||||||
|
(defvar org-roam-buffer-current-directory nil
|
||||||
|
"The `org-roam-directory' value of `org-roam-buffer-current-node'.
|
||||||
|
Set both, locally and globally in the same way as `org-roam-buffer-current-node'.")
|
||||||
|
|
||||||
|
(put 'org-roam-buffer-current-directory 'permanent-local t)
|
||||||
|
|
||||||
(defcustom org-roam-mode-section-functions (list #'org-roam-backlinks-section
|
(defcustom org-roam-mode-section-functions (list #'org-roam-backlinks-section
|
||||||
#'org-roam-reflinks-section)
|
#'org-roam-reflinks-section)
|
||||||
"Functions which insert sections of the `org-roam-buffer'.
|
"Functions that insert sections in the `org-roam-mode' based buffers.
|
||||||
Each function is called with one argument, which is the current org-roam node at point."
|
Each function is called with one argument, which is an
|
||||||
|
`org-roam-node' for which the buffer will be constructed for.
|
||||||
|
Normally this node is `org-roam-buffer-current-node'."
|
||||||
:group 'org-roam
|
:group 'org-roam
|
||||||
:type 'hook)
|
:type 'hook)
|
||||||
|
|
||||||
@ -139,7 +149,7 @@ Each function is called with one argument, which is the current org-roam node at
|
|||||||
(set-keymap-parent map magit-section-mode-map)
|
(set-keymap-parent map magit-section-mode-map)
|
||||||
(define-key map [C-return] 'org-roam-visit-thing)
|
(define-key map [C-return] 'org-roam-visit-thing)
|
||||||
(define-key map (kbd "C-m") 'org-roam-visit-thing)
|
(define-key map (kbd "C-m") 'org-roam-visit-thing)
|
||||||
(define-key map [remap revert-buffer] 'org-roam-buffer-render)
|
(define-key map [remap revert-buffer] 'org-roam-buffer-refresh)
|
||||||
map)
|
map)
|
||||||
"Parent keymap for all keymaps of modes derived from `org-roam-mode'.")
|
"Parent keymap for all keymaps of modes derived from `org-roam-mode'.")
|
||||||
|
|
||||||
@ -156,54 +166,85 @@ which visits the thing at point."
|
|||||||
(interactive)
|
(interactive)
|
||||||
(user-error "There is no thing at point that could be visited"))
|
(user-error "There is no thing at point that could be visited"))
|
||||||
|
|
||||||
(defun org-roam-buffer-render ()
|
(defun org-roam-buffer-refresh ()
|
||||||
"Render the current node at point."
|
"Refresh the contents of the currently selected Org-roam buffer."
|
||||||
(interactive)
|
(interactive)
|
||||||
(when (derived-mode-p 'org-roam-mode)
|
(cl-assert (derived-mode-p 'org-roam-mode))
|
||||||
|
(save-excursion (org-roam-buffer-render-contents)))
|
||||||
|
|
||||||
|
(defun org-roam-buffer-render-contents ()
|
||||||
|
"Recompute and render the contents of an Org-roam buffer.
|
||||||
|
Assumes that the current buffer is an `org-roam-mode' based
|
||||||
|
buffer."
|
||||||
(let ((inhibit-read-only t))
|
(let ((inhibit-read-only t))
|
||||||
(erase-buffer)
|
(erase-buffer)
|
||||||
(setq-local default-directory org-roam-current-directory)
|
(org-roam-mode)
|
||||||
(setq-local org-roam-directory org-roam-current-directory)
|
(setq-local default-directory org-roam-buffer-current-directory)
|
||||||
(org-roam-set-header-line-format (org-roam-node-title org-roam-current-node))
|
(setq-local org-roam-directory org-roam-buffer-current-directory)
|
||||||
|
(org-roam-set-header-line-format
|
||||||
|
(org-roam-node-title org-roam-buffer-current-node))
|
||||||
(magit-insert-section (org-roam)
|
(magit-insert-section (org-roam)
|
||||||
(magit-insert-heading)
|
(magit-insert-heading)
|
||||||
(run-hook-with-args 'org-roam-mode-section-functions org-roam-current-node)))))
|
(run-hook-with-args 'org-roam-mode-section-functions org-roam-buffer-current-node))
|
||||||
|
(goto-char 0)))
|
||||||
|
|
||||||
(defun org-roam-buffer ()
|
;;; Dedicated buffer
|
||||||
"Launch an Org-roam buffer for the current node at point."
|
;;;###autoload
|
||||||
(interactive)
|
(defun org-roam-buffer-display-dedicated (node)
|
||||||
(if-let ((node (org-roam-node-at-point))
|
"Launch NODE dedicated Org-roam buffer.
|
||||||
(source-org-roam-directory org-roam-directory))
|
Unlike the persistent `org-roam-buffer', the contents of this
|
||||||
(progn
|
buffer won't be automatically changed and will be held in place.
|
||||||
(let ((buffer (get-buffer-create
|
|
||||||
(concat "org-roam: "
|
In interactive calls prompt to select NODE, unless called with
|
||||||
(file-relative-name (buffer-file-name) org-roam-directory)))))
|
`universal-argument', in which case NODE will be set to
|
||||||
|
`org-roam-node-at-point'."
|
||||||
|
(interactive
|
||||||
|
(list (if current-prefix-arg
|
||||||
|
(org-roam-node-at-point 'assert)
|
||||||
|
(org-roam-node-read nil nil nil 'require-match))))
|
||||||
|
(let ((buffer (get-buffer-create (org-roam-buffer--dedicated-name node))))
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(org-roam-mode)
|
(setq-local org-roam-buffer-current-node node)
|
||||||
(setq-local org-roam-current-node node)
|
(setq-local org-roam-buffer-current-directory org-roam-directory)
|
||||||
(setq-local org-roam-current-directory source-org-roam-directory)
|
(org-roam-buffer-render-contents))
|
||||||
(org-roam-buffer-render))
|
(display-buffer buffer)))
|
||||||
(switch-to-buffer-other-window buffer)))
|
|
||||||
(user-error "No node at point")))
|
(defun org-roam-buffer--dedicated-name (node)
|
||||||
|
"Construct buffer name for NODE dedicated Org-roam buffer."
|
||||||
|
(let ((title (org-roam-node-title node))
|
||||||
|
(filename (file-relative-name (org-roam-node-file node) org-roam-directory)))
|
||||||
|
(format "*org-roam: %s<%s>*" title filename)))
|
||||||
|
|
||||||
|
(defun org-roam-buffer-dedicated-p (&optional buffer)
|
||||||
|
"Return t if an Org-roam BUFFER is a node dedicated one.
|
||||||
|
See `org-roam-buffer-display-dedicated' for more details.
|
||||||
|
If BUFFER is nil, default it to `current-buffer'."
|
||||||
|
(or buffer (setq buffer (current-buffer)))
|
||||||
|
(string-match-p (concat "^" (regexp-quote "*org-roam: "))
|
||||||
|
(buffer-name buffer)))
|
||||||
|
|
||||||
;;; Persistent buffer
|
;;; Persistent buffer
|
||||||
(defvar org-roam-buffer "*org-roam*"
|
(defvar org-roam-buffer "*org-roam*"
|
||||||
"The persistent Org-roam buffer name.")
|
"The persistent Org-roam buffer name. Must be surround with \"*\".
|
||||||
|
The content inside of this buffer will be automatically updated
|
||||||
|
to the nearest node at point that comes from the current buffer.
|
||||||
|
To toggle its display use `org-roam-buffer-toggle' command.")
|
||||||
|
|
||||||
(defun org-roam-buffer--post-command-h ()
|
(defun org-roam-buffer-toggle ()
|
||||||
"Reconstructs the Org-roam buffer.
|
"Toggle display of the persistent `org-roam-buffer'."
|
||||||
This needs to be quick or infrequent, because this is run at
|
(interactive)
|
||||||
`post-command-hook'. If REDISPLAY, force an update of
|
(pcase (org-roam-buffer--visibility)
|
||||||
the Org-roam buffer."
|
('visible
|
||||||
(when (get-buffer-window org-roam-buffer)
|
(progn
|
||||||
(when-let ((node (org-roam-node-at-point)))
|
(delete-window (get-buffer-window org-roam-buffer))
|
||||||
(unless (equal node org-roam-current-node)
|
(remove-hook 'post-command-hook #'org-roam-buffer--redisplay-h)))
|
||||||
(setq org-roam-current-node node)
|
((or 'exists 'none)
|
||||||
(setq org-roam-current-directory org-roam-directory)
|
(progn
|
||||||
|
(display-buffer (get-buffer-create org-roam-buffer))
|
||||||
(org-roam-buffer-persistent-redisplay)))))
|
(org-roam-buffer-persistent-redisplay)))))
|
||||||
|
|
||||||
(define-inline org-roam-buffer--visibility ()
|
(define-inline org-roam-buffer--visibility ()
|
||||||
"Return whether the current visibility state of the org-roam buffer.
|
"Return the current visibility state of the persistent `org-roam-buffer'.
|
||||||
Valid states are 'visible, 'exists and 'none."
|
Valid states are 'visible, 'exists and 'none."
|
||||||
(declare (side-effect-free t))
|
(declare (side-effect-free t))
|
||||||
(inline-quote
|
(inline-quote
|
||||||
@ -212,42 +253,34 @@ Valid states are 'visible, 'exists and 'none."
|
|||||||
((get-buffer org-roam-buffer) 'exists)
|
((get-buffer org-roam-buffer) 'exists)
|
||||||
(t 'none))))
|
(t 'none))))
|
||||||
|
|
||||||
(defun org-roam-buffer-toggle ()
|
(defun org-roam-buffer--persistent-cleanup-h ()
|
||||||
"Toggle display of the Org-roam buffer."
|
"Clean-up global state thats dedicated for the persistent `org-roam-buffer'."
|
||||||
(interactive)
|
(setq-default org-roam-buffer-current-node nil
|
||||||
(pcase (org-roam-buffer--visibility)
|
org-roam-buffer-current-directory nil))
|
||||||
('visible
|
|
||||||
(progn
|
|
||||||
(delete-window (get-buffer-window org-roam-buffer))
|
|
||||||
(remove-hook 'post-command-hook #'org-roam-buffer--post-command-h)))
|
|
||||||
((or 'exists 'none)
|
|
||||||
(progn
|
|
||||||
(setq org-roam-current-node (org-roam-node-at-point)
|
|
||||||
org-roam-current-directory org-roam-directory)
|
|
||||||
(display-buffer (get-buffer-create org-roam-buffer))
|
|
||||||
(org-roam-buffer-persistent-redisplay)))))
|
|
||||||
|
|
||||||
(defun org-roam-buffer-persistent-redisplay ()
|
(defun org-roam-buffer-persistent-redisplay ()
|
||||||
"Recompute contents of the persistent Org-roam buffer.
|
"Recompute contents of the persistent `org-roam-buffer'.
|
||||||
Has no effect when `org-roam-current-node' is nil."
|
Has no effect when there's no `org-roam-node-at-point'."
|
||||||
(when org-roam-current-node
|
(when-let ((node (org-roam-node-at-point)))
|
||||||
|
(unless (equal node org-roam-buffer-current-node)
|
||||||
|
(setq org-roam-buffer-current-node node
|
||||||
|
org-roam-buffer-current-directory org-roam-directory)
|
||||||
(with-current-buffer (get-buffer-create org-roam-buffer)
|
(with-current-buffer (get-buffer-create org-roam-buffer)
|
||||||
(let ((inhibit-read-only t))
|
(org-roam-buffer-render-contents)
|
||||||
(erase-buffer)
|
(add-hook 'kill-buffer-hook #'org-roam-buffer--persistent-cleanup-h nil t)))))
|
||||||
(org-roam-mode)
|
|
||||||
(setq-local default-directory org-roam-current-directory)
|
|
||||||
(setq-local org-roam-directory org-roam-current-directory)
|
|
||||||
(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-section-functions)
|
|
||||||
(funcall fn org-roam-current-node)))))))
|
|
||||||
|
|
||||||
(defun org-roam-buffer--redisplay ()
|
(defun org-roam-buffer--redisplay-h ()
|
||||||
"."
|
"Reconstruct the persistent `org-roam-buffer'.
|
||||||
(add-hook 'post-command-hook #'org-roam-buffer--post-command-h nil t))
|
This needs to be quick or infrequent, because this designed to
|
||||||
|
run at `post-command-hook'."
|
||||||
|
(and (get-buffer-window org-roam-buffer)
|
||||||
|
(org-roam-buffer-persistent-redisplay)))
|
||||||
|
|
||||||
(add-hook 'org-roam-find-file-hook #'org-roam-buffer--redisplay)
|
(defun org-roam-buffer--setup-redisplay-h ()
|
||||||
|
"Setup automatic redisplay of the persistent `org-roam-buffer'."
|
||||||
|
(add-hook 'post-command-hook #'org-roam-buffer--redisplay-h nil t))
|
||||||
|
|
||||||
|
(add-hook 'org-roam-find-file-hook #'org-roam-buffer--setup-redisplay-h)
|
||||||
|
|
||||||
;;; Sections
|
;;; Sections
|
||||||
;;;; Backlinks
|
;;;; Backlinks
|
||||||
@ -410,7 +443,7 @@ If ROW, move to the row, and if COL move to the COL."
|
|||||||
"Return the preview line from FILE.
|
"Return the preview line from FILE.
|
||||||
This is the ROW within FILE."
|
This is the ROW within FILE."
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert-file-contents-literally file)
|
(insert-file-contents file)
|
||||||
(forward-line (1- row))
|
(forward-line (1- row))
|
||||||
(buffer-substring-no-properties
|
(buffer-substring-no-properties
|
||||||
(save-excursion
|
(save-excursion
|
||||||
|
285
org-roam.el
285
org-roam.el
@ -167,12 +167,13 @@ Like `file-name-extension', but does not strip version number."
|
|||||||
"Return t if FILE is part of Org-roam system, nil otherwise.
|
"Return t if FILE is part of Org-roam system, nil otherwise.
|
||||||
If FILE is not specified, use the current buffer's file-path."
|
If FILE is not specified, use the current buffer's file-path."
|
||||||
(let* ((path (or file (buffer-file-name (buffer-base-buffer))))
|
(let* ((path (or file (buffer-file-name (buffer-base-buffer))))
|
||||||
(ext (org-roam--file-name-extension path))
|
(ext (when path (org-roam--file-name-extension path)))
|
||||||
(ext (if (string= ext "gpg")
|
(ext (if (string= ext "gpg")
|
||||||
(org-roam--file-name-extension (file-name-sans-extension path))
|
(org-roam--file-name-extension (file-name-sans-extension path))
|
||||||
ext)))
|
ext)))
|
||||||
(save-match-data
|
(save-match-data
|
||||||
(and
|
(and
|
||||||
|
path
|
||||||
(member ext org-roam-file-extensions)
|
(member ext org-roam-file-extensions)
|
||||||
(not (and org-roam-file-exclude-regexp
|
(not (and org-roam-file-exclude-regexp
|
||||||
(string-match-p org-roam-file-exclude-regexp path)))
|
(string-match-p org-roam-file-exclude-regexp path)))
|
||||||
@ -216,67 +217,6 @@ E.g. (\".org\") => (\"*.org\" \"*.org.gpg\")"
|
|||||||
(command (s-join " " `(,executable "-L" ,dir "-type f \\(" ,names "\\)"))))
|
(command (s-join " " `(,executable "-L" ,dir "-type f \\(" ,names "\\)"))))
|
||||||
(org-roam--shell-command-files command)))
|
(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)
|
(defun org-roam--list-files-elisp (dir)
|
||||||
"Return all Org-roam files located recursively within DIR, using elisp."
|
"Return all Org-roam files located recursively within DIR, using elisp."
|
||||||
(let ((regex (concat "\\.\\(?:"(mapconcat
|
(let ((regex (concat "\\.\\(?:"(mapconcat
|
||||||
@ -352,7 +292,9 @@ If BUFFER is not specified, use the current buffer."
|
|||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-roam-setup ()
|
(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)
|
(interactive)
|
||||||
(add-hook 'find-file-hook #'org-roam--file-setup)
|
(add-hook 'find-file-hook #'org-roam--file-setup)
|
||||||
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||||
@ -361,7 +303,10 @@ If BUFFER is not specified, use the current buffer."
|
|||||||
(org-roam-db-sync))
|
(org-roam-db-sync))
|
||||||
|
|
||||||
(defun org-roam-teardown ()
|
(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)
|
(interactive)
|
||||||
(remove-hook 'find-file-hook #'org-roam--file-setup)
|
(remove-hook 'find-file-hook #'org-roam--file-setup)
|
||||||
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||||
@ -407,6 +352,7 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
|
|||||||
;;;; Nodes
|
;;;; Nodes
|
||||||
(cl-defstruct (org-roam-node (:constructor org-roam-node-create)
|
(cl-defstruct (org-roam-node (:constructor org-roam-node-create)
|
||||||
(:copier nil))
|
(:copier nil))
|
||||||
|
"A heading or top level file with an assigned ID property."
|
||||||
file file-hash file-atime file-mtime
|
file file-hash file-atime file-mtime
|
||||||
id level point todo priority scheduled deadline title properties olp
|
id level point todo priority scheduled deadline title properties olp
|
||||||
tags aliases refs)
|
tags aliases refs)
|
||||||
@ -456,24 +402,26 @@ OLD-FILE is cleared from the database, and NEW-FILE-OR-DIR is added."
|
|||||||
(set-keymap-parent map org-roam-mode-map)
|
(set-keymap-parent map org-roam-mode-map)
|
||||||
(define-key map [remap org-roam-visit-thing] 'org-roam-node-visit)
|
(define-key map [remap org-roam-visit-thing] 'org-roam-node-visit)
|
||||||
map)
|
map)
|
||||||
"Keymap for Org-roam node sections.")
|
"Keymap for `org-roam-node-section's.")
|
||||||
|
|
||||||
(defclass org-roam-node-section (magit-section)
|
(defclass org-roam-node-section (magit-section)
|
||||||
((keymap :initform 'org-roam-node-map)
|
((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
|
(defvar org-roam-preview-map
|
||||||
(let ((map (make-sparse-keymap)))
|
(let ((map (make-sparse-keymap)))
|
||||||
(set-keymap-parent map org-roam-mode-map)
|
(set-keymap-parent map org-roam-mode-map)
|
||||||
(define-key map [remap org-roam-visit-thing] 'org-roam-preview-visit)
|
(define-key map [remap org-roam-visit-thing] 'org-roam-preview-visit)
|
||||||
map)
|
map)
|
||||||
"Keymap for Org-roam preview.")
|
"Keymap for `org-roam-preview-section's.")
|
||||||
|
|
||||||
(defclass org-roam-preview-section (magit-section)
|
(defclass org-roam-preview-section (magit-section)
|
||||||
((keymap :initform 'org-roam-preview-map)
|
((keymap :initform 'org-roam-preview-map)
|
||||||
(file :initform nil)
|
(file :initform nil)
|
||||||
(begin :initform nil)
|
(point :initform nil))
|
||||||
(end :initform nil)))
|
"A `magit-section' used by `org-roam-mode' to contain preview content.
|
||||||
|
The preview content comes from FILE, and the link as at POINT.")
|
||||||
|
|
||||||
(cl-defmethod org-roam-populate ((node org-roam-node))
|
(cl-defmethod org-roam-populate ((node org-roam-node))
|
||||||
"Populate NODE from database.
|
"Populate NODE from database.
|
||||||
@ -521,11 +469,27 @@ nodes."
|
|||||||
"${title:*} ${tags:10}"
|
"${title:*} ${tags:10}"
|
||||||
"Configures display formatting for Org-roam node.
|
"Configures display formatting for Org-roam node.
|
||||||
Patterns of form \"${field-name:length}\" are interpolated based
|
Patterns of form \"${field-name:length}\" are interpolated based
|
||||||
on the current node. \"field-name\" is replaced with the
|
on the current node.
|
||||||
corresponding value of the field of the current node. \"length\"
|
|
||||||
specifies how many characters are used to display the value of
|
Each \"field-name\" is replaced with the return value of each
|
||||||
the field. A \"length\" of \"*\" specifies that as many
|
corresponding accessor function for `org-roam-node', e.g.
|
||||||
characters as possible should be used."
|
\"${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
|
:group 'org-roam
|
||||||
:type 'string)
|
:type 'string)
|
||||||
|
|
||||||
@ -565,17 +529,87 @@ Uses `org-roam-node-display-template' to format the entry."
|
|||||||
(- width (cdr fmt)))
|
(- width (cdr fmt)))
|
||||||
0 ?\s)))))))
|
0 ?\s)))))))
|
||||||
|
|
||||||
(defun org-roam-node-preview (file point)
|
(defun org-roam-get-preview (file point)
|
||||||
"Get preview content for FILE at POINT."
|
"Get preview content for FILE at POINT."
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(org-roam-with-temp-buffer file
|
(org-roam-with-temp-buffer file
|
||||||
(goto-char point)
|
(goto-char point)
|
||||||
(let* ((elem (org-element-at-point))
|
(let ((elem (org-element-at-point)))
|
||||||
(begin (org-element-property :begin elem))
|
;; We want the parent element always
|
||||||
|
(while (org-element-property :parent elem)
|
||||||
|
(setq elem (org-element-property :parent elem)))
|
||||||
|
(pcase (car elem)
|
||||||
|
('headline ; show subtree
|
||||||
|
(org-roam-headline-get-preview-text (point-marker) most-positive-fixnum))
|
||||||
|
(_
|
||||||
|
(let ((begin (org-element-property :begin elem))
|
||||||
(end (org-element-property :end elem)))
|
(end (org-element-property :end elem)))
|
||||||
(list begin end
|
|
||||||
(or (string-trim (buffer-substring-no-properties begin end))
|
(or (string-trim (buffer-substring-no-properties begin end))
|
||||||
(org-element-property :raw-value elem)))))))
|
(org-element-property :raw-value elem)))))))))
|
||||||
|
|
||||||
|
(defun org-roam-headline-get-preview-text (marker n-lines &optional indent)
|
||||||
|
"Extract entry text from MARKER, at most N-LINES lines.
|
||||||
|
This will ignore drawers etc, just get the text.
|
||||||
|
If INDENT is given, prefix every line with this string."
|
||||||
|
(let (txt drawer-re kwd-time-re ind)
|
||||||
|
(save-excursion
|
||||||
|
(with-current-buffer (marker-buffer marker)
|
||||||
|
(if (not (derived-mode-p 'org-mode))
|
||||||
|
(setq txt "")
|
||||||
|
(org-with-wide-buffer
|
||||||
|
(goto-char marker)
|
||||||
|
(end-of-line 1)
|
||||||
|
(setq txt (buffer-substring
|
||||||
|
(min (1+ (point)) (point-max))
|
||||||
|
(progn (outline-next-heading) (point))))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert txt)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (org-activate-links (point-max))
|
||||||
|
(goto-char (match-end 0)))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (re-search-forward org-link-bracket-re (point-max) t)
|
||||||
|
(set-text-properties (match-beginning 0) (match-end 0)
|
||||||
|
nil))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (re-search-forward org-drawer-regexp nil t)
|
||||||
|
(delete-region
|
||||||
|
(match-beginning 0)
|
||||||
|
(progn (re-search-forward
|
||||||
|
"^[ \t]*:END:.*\n?" nil 'move)
|
||||||
|
(point))))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(goto-char (point-max))
|
||||||
|
(skip-chars-backward " \t\n")
|
||||||
|
(when (looking-at "[ \t\n]+\\'") (replace-match ""))
|
||||||
|
|
||||||
|
;; find and remove min common indentation
|
||||||
|
(goto-char (point-min))
|
||||||
|
(untabify (point-min) (point-max))
|
||||||
|
(setq ind (current-indentation))
|
||||||
|
(while (not (eobp))
|
||||||
|
(unless (looking-at "[ \t]*$")
|
||||||
|
(setq ind (min ind (current-indentation))))
|
||||||
|
(beginning-of-line 2))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (not (eobp))
|
||||||
|
(unless (looking-at "[ \t]*$")
|
||||||
|
(move-to-column ind)
|
||||||
|
(delete-region (point-at-bol) (point)))
|
||||||
|
(beginning-of-line 2))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when indent
|
||||||
|
(while (and (not (eobp)) (re-search-forward "^" nil t))
|
||||||
|
(replace-match indent t t)))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (looking-at "[ \t]*\n") (replace-match ""))
|
||||||
|
(goto-char (point-max))
|
||||||
|
(when (> (org-current-line)
|
||||||
|
n-lines)
|
||||||
|
(org-goto-line (1+ n-lines))
|
||||||
|
(backward-char 1))
|
||||||
|
(setq txt (buffer-substring (point-min) (point))))))))
|
||||||
|
(list (point-min) (point) txt)))
|
||||||
|
|
||||||
(defun org-roam-node-at-point (&optional assert)
|
(defun org-roam-node-at-point (&optional assert)
|
||||||
"Return the node at point.
|
"Return the node at point.
|
||||||
@ -609,7 +643,7 @@ populated."
|
|||||||
buf))
|
buf))
|
||||||
|
|
||||||
(defun org-roam-node-visit (node &optional other-window)
|
(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
|
Display the buffer in the selected window. With a prefix
|
||||||
argument OTHER-WINDOW display the buffer in another window
|
argument OTHER-WINDOW display the buffer in another window
|
||||||
@ -649,7 +683,7 @@ Throw an error if multiple choices exist."
|
|||||||
(user-error "Multiple nodes exist with title or alias \"%s\"" s)))))
|
(user-error "Multiple nodes exist with title or alias \"%s\"" s)))))
|
||||||
|
|
||||||
(defun org-roam-node-list ()
|
(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
|
(let ((rows (org-roam-db-query
|
||||||
"SELECT
|
"SELECT
|
||||||
id,
|
id,
|
||||||
@ -821,8 +855,8 @@ Returns empty string for annotations."
|
|||||||
"Visit FILE at POINT.
|
"Visit FILE at POINT.
|
||||||
With prefix argument OTHER-WINDOW, visit the olp in another
|
With prefix argument OTHER-WINDOW, visit the olp in another
|
||||||
window instead."
|
window instead."
|
||||||
(interactive (list (org-roam-file-at-point t)
|
(interactive (list (org-roam-file-at-point 'assert)
|
||||||
(oref (magit-current-section) begin)
|
(oref (magit-current-section) point)
|
||||||
current-prefix-arg))
|
current-prefix-arg))
|
||||||
(let ((buf (find-file-noselect file)))
|
(let ((buf (find-file-noselect file)))
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
@ -833,10 +867,25 @@ window instead."
|
|||||||
#'pop-to-buffer-same-window) buf)))
|
#'pop-to-buffer-same-window) buf)))
|
||||||
|
|
||||||
(cl-defun org-roam-node-insert-section (&key source-node point properties)
|
(cl-defun org-roam-node-insert-section (&key source-node point properties)
|
||||||
"Insert section for NODE.
|
"Insert section for a link from SOURCE-NODE to some other node.
|
||||||
SOURCE-NODE is the source node.
|
|
||||||
POINT is the point in buffer for the link.
|
SOURCE-NODE is an `org-roam-node' that links or references some
|
||||||
PROPERTIES contains properties about the link."
|
other node. Normally the other node is
|
||||||
|
`org-roam-buffer-current-node'.
|
||||||
|
|
||||||
|
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)
|
(magit-insert-section section (org-roam-node-section)
|
||||||
(let ((outline (if-let ((outline (plist-get properties :outline)))
|
(let ((outline (if-let ((outline (plist-get properties :outline)))
|
||||||
(mapconcat #'org-link-display-format outline " > ")
|
(mapconcat #'org-link-display-format outline " > ")
|
||||||
@ -848,12 +897,11 @@ PROPERTIES contains properties about the link."
|
|||||||
(magit-insert-heading)
|
(magit-insert-heading)
|
||||||
(oset section node source-node)
|
(oset section node source-node)
|
||||||
(magit-insert-section section (org-roam-preview-section)
|
(magit-insert-section section (org-roam-preview-section)
|
||||||
(pcase-let ((`(,begin ,end ,s) (org-roam-node-preview (org-roam-node-file source-node)
|
(insert (org-roam-fontify-like-in-org-mode
|
||||||
point)))
|
(org-roam-get-preview (org-roam-node-file source-node) point))
|
||||||
(insert (org-roam-fontify-like-in-org-mode s) "\n")
|
"\n")
|
||||||
(oset section file (org-roam-node-file source-node))
|
(oset section file (org-roam-node-file source-node))
|
||||||
(oset section begin begin)
|
(oset section point point)
|
||||||
(oset section end end))
|
|
||||||
(insert ?\n))))
|
(insert ?\n))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
@ -873,9 +921,7 @@ If OTHER-WINDOW, visit the NODE in another window."
|
|||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-roam-node-insert (&optional filter-fn)
|
(defun org-roam-node-insert (&optional filter-fn)
|
||||||
"Find an Org-roam file, and insert a relative org link to it at point.
|
"Find an Org-roam node and insert (where the point is) an \"id:\" link to it.
|
||||||
Return selected file if it exists.
|
|
||||||
If LOWERCASE is non-nil, downcase the link description.
|
|
||||||
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
|
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."
|
and when nil is returned the node will be filtered out."
|
||||||
(interactive)
|
(interactive)
|
||||||
@ -912,7 +958,7 @@ and when nil is returned the node will be filtered out."
|
|||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-roam-node-random (&optional other-window)
|
(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
|
With prefix argument OTHER-WINDOW, visit the node in another
|
||||||
window instead."
|
window instead."
|
||||||
(interactive current-prefix-arg)
|
(interactive current-prefix-arg)
|
||||||
@ -923,22 +969,24 @@ window instead."
|
|||||||
other-window)))
|
other-window)))
|
||||||
|
|
||||||
;;;; Properties
|
;;;; Properties
|
||||||
(defun org-roam-add-property (s prop)
|
(defun org-roam-add-property (val prop)
|
||||||
"Add S to property 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))
|
(let* ((p (org-entry-get (point) prop))
|
||||||
(lst (when p (split-string-and-unquote p)))
|
(lst (when p (split-string-and-unquote p)))
|
||||||
(lst (if (memq s lst) lst (cons s lst)))
|
(lst (if (memq val lst) lst (cons val lst)))
|
||||||
(lst (seq-uniq lst)))
|
(lst (seq-uniq lst)))
|
||||||
(org-set-property prop (combine-and-quote-strings lst))
|
(org-set-property prop (combine-and-quote-strings lst))
|
||||||
s))
|
val))
|
||||||
|
|
||||||
(defun org-roam-remove-property (prop &optional s)
|
(defun org-roam-remove-property (prop &optional val)
|
||||||
"Remove S from property PROP.
|
"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))
|
(let* ((p (org-entry-get (point) prop))
|
||||||
(lst (when p (split-string-and-unquote p)))
|
(lst (when p (split-string-and-unquote p)))
|
||||||
(prop-to-remove (or s (completing-read "Remove: " lst)))
|
(prop-to-remove (or val (completing-read "Remove: " lst)))
|
||||||
(lst (delete prop-to-remove lst)))
|
(lst (delete prop-to-remove lst)))
|
||||||
(if lst
|
(if lst
|
||||||
(org-set-property prop (combine-and-quote-strings lst))
|
(org-set-property prop (combine-and-quote-strings lst))
|
||||||
@ -1093,9 +1141,7 @@ REF is assumed to be a propertized string."
|
|||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-roam-ref-find (&optional initial-input filter-fn)
|
(defun org-roam-ref-find (&optional initial-input filter-fn)
|
||||||
"Find and open and Org-roam file from REF if it exists.
|
"Find and open an Org-roam node that's dedicated to a specific ref.
|
||||||
REF should be the value of '#+roam_key:' without any
|
|
||||||
type-information (e.g. 'cite:').
|
|
||||||
INITIAL-INPUT is the initial input to the prompt.
|
INITIAL-INPUT is the initial input to the prompt.
|
||||||
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
|
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."
|
and when nil is returned the node will be filtered out."
|
||||||
@ -1106,7 +1152,7 @@ and when nil is returned the node will be filtered out."
|
|||||||
|
|
||||||
;;;; roam: link
|
;;;; roam: link
|
||||||
(defcustom org-roam-link-auto-replace t
|
(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
|
:group 'org-roam
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
@ -1114,7 +1160,7 @@ and when nil is returned the node will be filtered out."
|
|||||||
(org-link-set-parameters "roam" :follow #'org-roam-link-follow-link)
|
(org-link-set-parameters "roam" :follow #'org-roam-link-follow-link)
|
||||||
|
|
||||||
(defun org-roam-link-replace-at-point (&optional 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-excursion
|
||||||
(save-match-data
|
(save-match-data
|
||||||
(let* ((link (or link (org-element-context)))
|
(let* ((link (or link (org-element-context)))
|
||||||
@ -1143,23 +1189,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)
|
(add-hook 'org-roam-find-file-hook #'org-roam--replace-roam-links-on-save-h)
|
||||||
|
|
||||||
(defun org-roam-link-follow-link (path)
|
(defun org-roam-link-follow-link (title-or-alias)
|
||||||
"Org-roam's roam: link navigation with description PATH.
|
"Navigate \"roam:\" link to find and open the node with TITLE-OR-ALIAS.
|
||||||
This function is called by Org when following links of the type
|
Assumes that the cursor was put where the link is."
|
||||||
`roam'. While the path is passed, assume that the cursor is on
|
(if-let ((node (org-roam-node-from-title-or-alias title-or-alias)))
|
||||||
the link."
|
|
||||||
(if-let ((node (org-roam-node-from-title-or-alias path)))
|
|
||||||
(progn
|
(progn
|
||||||
(when org-roam-link-auto-replace
|
(when org-roam-link-auto-replace
|
||||||
(org-roam-link-replace-at-point))
|
(org-roam-link-replace-at-point))
|
||||||
(org-id-goto (org-roam-node-id node)))
|
(org-id-goto (org-roam-node-id node)))
|
||||||
(org-roam-capture-
|
(org-roam-capture-
|
||||||
:node (org-roam-node-create :title path)
|
:node (org-roam-node-create :title title-or-alias)
|
||||||
:props '(:finalize find-file))))
|
:props '(:finalize find-file))))
|
||||||
|
|
||||||
(defun org-roam-open-id-at-point ()
|
(defun org-roam-open-id-at-point ()
|
||||||
"Navigates to the ID at point.
|
"Try to navigate \"id:\" link to find and visit node with an assigned ID.
|
||||||
To be added to `org-open-at-point-functions'."
|
Assumes that the cursor was put where the link is."
|
||||||
(let* ((context (org-element-context))
|
(let* ((context (org-element-context))
|
||||||
(type (org-element-property :type context))
|
(type (org-element-property :type context))
|
||||||
(id (org-element-property :path context)))
|
(id (org-element-property :path context)))
|
||||||
@ -1173,7 +1217,7 @@ To be added to `org-open-at-point-functions'."
|
|||||||
(t nil))))))
|
(t nil))))))
|
||||||
|
|
||||||
(defun org-roam-open-id-with-org-roam-db-h ()
|
(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-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)
|
(add-hook 'org-roam-find-file-hook #'org-roam-open-id-with-org-roam-db-h)
|
||||||
@ -1200,7 +1244,8 @@ Any top level properties drawers are incorporated into the new heading."
|
|||||||
(org-roam--file-keyword-kill "FILETAGS")))
|
(org-roam--file-keyword-kill "FILETAGS")))
|
||||||
|
|
||||||
(defun org-roam-refile ()
|
(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)
|
(interactive)
|
||||||
(let* ((regionp (org-region-active-p))
|
(let* ((regionp (org-region-active-p))
|
||||||
(region-start (and regionp (region-beginning)))
|
(region-start (and regionp (region-beginning)))
|
||||||
|
Reference in New Issue
Block a user