Compare commits

...

62 Commits

Author SHA1 Message Date
9065f6a999 (fix) make roam headline link completion robust (#1473)
Fixes  #1472
2021-04-16 13:02:48 +08:00
997ddcbf4b (docs): update org-roam dir-locals
use absolute path instead of relative path to prevent errors
2021-04-11 14:50:11 +08:00
2d58651699 optimize filename normalize (#1460)
* Normalize slug using ucs-normalize-NFC-string after remove Unicode spacing mark
* Add org-roam-slug--preserve-chars-from-normalization for Unicode Normalization
* Add org-roam-slug-trim-chars instead of org-roam-slug--preserve-chars-from-normalization
2021-04-05 21:35:58 +08:00
8ad57b1218 (fix): support %i in org-roam-protocol (#1445) 2021-03-08 12:57:38 +08:00
0b964ca428 (fix): fix org-roam buffer insert out-of-order (#1448) 2021-03-06 18:01:48 +08:00
643b98eeb3 (fix): dailies: avoid assuming value of org-roam-dailies-directory (#1426) 2021-03-06 10:51:29 +08:00
b0fd12647b (fix): dailies: prevent inclusion of non-org-roam files (#1409)
Fixes #1398
2021-01-28 21:41:42 +08:00
fde40dc1c4 (fix) parsing of missing props (#1406)
Followup from #1404
2021-01-26 14:45:19 +08:00
96b0a52273 (fix) inconsistency between props writing and reading (alias, tags) (#1404)
Fixes #1403
2021-01-26 11:09:19 +08:00
aa52b65a4a (feat): add org-roam-file-completion-tag-position (#1396) 2021-01-23 18:09:52 +08:00
2a1c73c0a3 (docs): clarify behavior of capture functions (#1393) 2021-01-20 21:55:17 +08:00
16c7a7bd93 (doc): update mac protocol instructions (#1390)
use native script editor, rather than Platypus.
2021-01-18 23:02:53 +08:00
1b3a0abd36 (feat): add org-roam-buffer-preview-function (#1388)
Instead of storing preview content in the database, now provide a
function to fetch these on the fly. This paves the way for future
improvements (e.g. showing more lines)
2021-01-17 02:52:39 +08:00
06e5814898 (fix): org-roam-dailies: fix false warning on new file (#1387)
Fixes #1358
2021-01-16 21:06:50 +08:00
cc2572e48b (doc): add my braindump example (Alexey Shmalko) (#1384)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2021-01-16 20:44:15 +08:00
05deb64d85 (doc) Update brew install script (#1386)
The API for Homebrew Cask changed (https://brew.sh/)
2021-01-16 16:58:13 +08:00
f10fbad386 (feat): allow org-roam-buffer-position to be a function (#1385)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2021-01-16 13:43:00 +08:00
05a9bc44f2 (fix): fix org-roam-capture--get-point (#1376) 2021-01-11 19:38:02 +08:00
3fb4e21adf (fix): fix org-roam-protocol--open-ref creating new files (#1375)
We need to split the ref into its type/file before querying the db for a
match. Throw a warning when `org-roam--capture-new-file` tries to
recreate an existing file.
2021-01-10 23:05:45 +08:00
62bba9755c (fix): keep id backlinks on filename change (#1367) 2021-01-10 16:01:15 +08:00
78a371cdc4 (fix): fix headline completions erroring (#1374)
updates org-roam-with-file to accept nil FILE arg, to operate on current
buffer. Fixes org-roam-link--get-headlines to kill buffer if not
obtaining markers.
2021-01-09 17:49:36 +08:00
15d864a500 (feat): add useful warning message to org-roam-dailies-find-next-note (#1365)
If the user creates a new daily via `org-roam-dailies-find-today`, they
will get a new buffer that hasn't yet been saved to disk. If they then try
to navigate to the previous daily via
`org-roam-dailies-find-previous-note`, they will get the error:

    `Wrong type argument: number-or-marker-p, nil`

This is because the value of the local variable `position` is set only if
the current buffer exists as a file in the dailies directory. It doesn't
until the user explicitly saves it.

That's not a big problem, but the solution is not obvious from the error
message. I added a check that will provide the user with a more informative
error message, so they know how to fix the issue:

    "Can't find current note file - have you saved it yet?"
2021-01-01 12:48:20 +08:00
65c0f0dc8c (feat): use org-link-display-format in org-roam-insert (#1356)
So that, like org-store-link, the computed description does not
contain the links that were captured.
2020-12-25 13:43:41 +08:00
48e195dd82 (refactor): refactor org-roam-capture (#1355) 2020-12-22 21:57:02 +08:00
777f6d23ec (feat): Support file-property drawers (#1353)
* (feat): Support file-property drawers

Add support for file-property drawers in property extraction. This means
the following is now supported:

:PROPERTIES:
:ROAM_ALIAS: alias
:ROAM_TAGS: tag1 tag2
:END:
2020-12-19 23:59:40 +08:00
8f1cf7b449 (fix): set-global-prop: prefer lowercase (#1352)
Prefer lower-case version of Org properties. Fixed bug where
adding/deleting props will alter the original case of the Org property.

Addresses #1342
2020-12-19 19:23:27 +08:00
3ce6e299d4 (doc) Update README (#1351) 2020-12-19 17:53:25 +08:00
ecf515f650 (doc): Add footnote for tab completion (#1348)
Follow on to #1345.

The discussion in the Slack thread further clarified what was exactly
confusing for a beginner reader of this Getting Started. When a user is using
the built-in completion (no ivy, no ido), then `org-roam-find-file` does not
immediately show any files -- instead, the user has to press TAB. This is not
explicitly mentioned.
2020-12-19 16:42:43 +08:00
43831c5819 (fix): allow link captures (#1347)
Previously we had set `org-capture-link-is-already-stored` to `t` in
org-roam captures, because org-roam-protocol stores links. This
prevented regular captures from utilizing the %a element in the
templates.

Instead, only set `org-capture-link-is-already-stored` in the
org-roam-protocol capture command.

Fixes #1341.
2020-12-16 21:12:21 +08:00
4d63f99fe8 (doc): Add async update to Getting Started (#1345) 2020-12-16 20:58:13 +08:00
57cfb3dbb7 (fix): index file: prevent malformed absolute path (#1346)
When `org-roam-directory` doesn't end with trailing slash and
`org-roam-index-file` contains relative filename, absolute path
to org-roam index file returned by `org-roam--get-index-path` is
malformed and invalid.

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-12-16 20:31:32 +08:00
9c23218553 (docs): fix docstring for org-roam--get-title-path-completions (#1344) 2020-12-15 20:26:43 +08:00
7ad32e8395 (feat): make org-roam-buffer-update interactive (#1343)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-12-15 20:04:48 +08:00
d87dd011aa (fix): preserve existing description in a roam: link on replace (#1327)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-12-15 19:47:51 +08:00
f2976fa3be (fix): close files intelligently after modifying (#1330)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-12-15 18:33:55 +08:00
8aa793b021 (docs): org-roam-doctor: fix docs for prefix arg (#1337) 2020-12-12 14:34:04 +08:00
be95b42d32 (docs): fix link to org-capture (#1329)
Required capitalization.
2020-12-01 12:42:33 +08:00
cff1168ac1 (doc): fix link to manual (#1328) 2020-11-28 19:44:15 +08:00
06d0db736a (internal): remove error-checking for olp (#1326)
The `org-roam-capture-find-or-create-olp` function will throw the same
error, so we don't have to explicitly catch and throw.
2020-11-24 21:09:28 +08:00
fb0662efe7 (fix): uniqify title and tag extraction (#1325)
Remove duplicates from title and tag extraction, arising from
multiple (or the same) sources.

Fixes #1324
2020-11-24 20:00:49 +08:00
9ca5461a2f (fix): fix mode toggle not re-enabling buffer-local hooks (#1321) 2020-11-23 19:59:30 +08:00
33805c3ff5 (fix): don't set default-directory if no file is passed (#1318)
This fixes calls like (org-roam--with-temp-buffer nil ...). Fixes #1310, #1316.
2020-11-21 16:42:50 +08:00
3a4ff76508 (fix): dailies: fix capture changing window configuration (#1314)
`*-captures` commands now maintain window configuration, while the
`*-find` commands change to the file.
2020-11-21 01:27:03 +08:00
bf41352c1c (fix): fix title change not triggering rename if new title contains old (#1315)
title

Fixes #1303
2020-11-20 21:32:36 +08:00
1b598a4618 (fix): dailies: misc fixes (#1309)
Fixes #1293
2020-11-19 23:00:33 +08:00
ab34dd138d (fix): fix rename file corrupting database (#1308)
The rename file advice is passed relative file names: e.g. "foo.org" ->
"bar.org". This was not accounted for, and paths in the Org-roam
database are supposed to be absolute paths. This caused the storing of
relative paths in the Org-roam database, which were then never purged.

Fixes #1304
2020-11-19 22:26:59 +08:00
b17cc3b1e3 (internal): org-roam-db--update-maybe -> org-roam-db--upgrade-maybe (#1307) 2020-11-19 21:55:07 +08:00
f9b1e53894 (fix): do not display buffer if it is not processed in db (#1302)
Finding an Org-roam file that does not exist yet triggers the find-file
hook, resulting in an Org-roam buffer update, attempting to show the
file. However, if the file does not yet exist, attempts to show the file
will not work, since the database does not contain the relevant
information yet.

This bug has always been present, but was only recently made visible by
the code cleanups in #1284.

Now, we ensure that the file has been processed by the database before
attempting to re-display the buffer.

Fixes #1297
2020-11-18 20:24:21 +08:00
dbed2bcf5d (fix): set default-directory in org-roam-with-temp-buffer (#1300)
This ensures that relative paths in keywords such as `#+setupfile:` and
`#+include:` work when building cache etc.
2020-11-18 18:33:57 +08:00
060a29c91d (fix): use correct type for org-roam-db-update-method (#1295)
Fixes #1294.
2020-11-18 18:17:00 +08:00
8efec080e0 (ci): fix ci test flow (#1301) 2020-11-18 18:07:05 +08:00
61e01430e0 (perf): improve database update on 'immediate (#1285) 2020-11-16 21:27:23 +08:00
d70198bba9 (fix): fix cache updates on org-id creation in non-existing files (#1288)
closes #1287
2020-11-16 10:38:58 +08:00
face683e00 (internal): remove org-roam--get-title-or-slug function (#1284)
Every file has a title when saved into the database, so this function is
redundant in most cases (e.g. in org-roam-buffer, where backlinks are
fetched from the database).
2020-11-15 19:44:36 +08:00
baf0dd9d00 (doc): document tag completions (#1282) 2020-11-15 14:36:49 +08:00
a9fd6c0fc7 (fix): fix idle-timer not created on org-roam-mode (#1281)
Fixes #1280
2020-11-15 14:21:03 +08:00
6502874576 (doc): document completion-at-point (#1279) 2020-11-15 04:43:57 +08:00
8401784cd2 (doc): document org-roam-tag-sources (#1274) 2020-11-14 21:12:08 +08:00
6dc316c450 (doc): fix menu-comment styling (#1273) 2020-11-14 14:17:08 +08:00
48ef3fee11 (doc): document org-roam-title-sources (#1271) 2020-11-13 22:55:52 +08:00
16c520068b (doc): Fix typo (#1272)
The `olp` option in the lab notes example was swapped.
2020-11-13 14:22:58 +01:00
b1608bf869 (feat): capture: create OLP if does not exist (#1270)
Remove the requirement that the OLP already exists in the captured file.
Also, make OLP available beyond dailies functionality.
2020-11-13 16:29:00 +08:00
17 changed files with 1188 additions and 748 deletions

View File

@ -45,10 +45,13 @@ jobs:
- uses: actions/checkout@v2
- name: Initialize sandbox
- name: Create Sandbox Directory
run: |
SANDBOX_DIR=$(mktemp -d) || exit 1
echo ::set-env name=SANDBOX_DIR::$SANDBOX_DIR
echo "SANDBOX_DIR=$SANDBOX_DIR" >> $GITHUB_ENV
- name: Initialize Sandbox
run: |
./makem.sh -vv --sandbox $SANDBOX_DIR --install-deps --install-linters
# The "all" rule is not used, because it treats compilation warnings

View File

@ -1,4 +1,26 @@
# Changelog
## 1.2.4 (TBD)
### Added
- [#1396](https://github.com/org-roam/org-roam/pull/1396) add option to choose between prepending, appending, and omitting `roam_tags` in file completion
- [#1270](https://github.com/org-roam/org-roam/pull/1270) capture: create OLP if it does not exist. Removes need for OLP setup in `:head`.
- [#1353](https://github.com/org-roam/org-roam/pull/1353) support file-level property drawers
### Changed
- [#1352](https://github.com/org-roam/org-roam/pull/1352) prefer lower-case for roam_tag and roam_alias in interactive commands
### Fixed
- [#1281](https://github.com/org-roam/org-roam/pull/1281) fixed idle-timer not instantiated on `org-roam-mode`
- [#1308](https://github.com/org-roam/org-roam/pull/1308) fixed file renames corrupting database
- [#1325](https://github.com/org-roam/org-roam/pull/1325) make titles and tags extracted unique per note
- [#1327](https://github.com/org-roam/org-roam/pull/1327) preserve existing link description during automatic replacement
- [#1346](https://github.com/org-roam/org-roam/pull/1346) prevent malformed path to `org-roam-index-file`
- [#1347](https://github.com/org-roam/org-roam/pull/1347) allow use of `%a` element in regular Org-roam captures
- [#1352](https://github.com/org-roam/org-roam/pull/1352) fixed org-roam-{tag/alias}-{add/delete} altering the original case of the Org property
- [#1374](https://github.com/org-roam/org-roam/pull/1374) fix headline completions erroring out
- [#1375](https://github.com/org-roam/org-roam/pull/1375) fix org-roam-protocol to use existing ref file
- [#1403](https://github.com/org-roam/org-roam/issues/1403) fixed inconsistency between how we write and read props like alias and tags
- [#1409](https://github.com/org-roam/org-roam/issues/1398) prevent inclusion of non-org-roam files in `org-roam-dailies--list-files`
## 1.2.3 (13-11-2020)
@ -15,7 +37,7 @@ Org-roam-dailies has also been revamped to include new features, see [this video
- [#1264](https://github.com/org-roam/org-roam/pull/1264) add `org-roam-db-update-method` to control when the cache is rebuilt.
### Changed
- [#1264](https://github.com/org-roam/org-roam/pull/1264) renamed `org-roam-update-db-idle-seconds` to `org-roam-db-idle-idle-seconds`
- [#1264](https://github.com/org-roam/org-roam/pull/1264) renamed `org-roam-update-db-idle-seconds` to `org-roam-db-idle-idle-seconds`
### Fixed
- [#1074](https://github.com/org-roam/org-roam/issues/1074) fix `org-roam--extract-links` to handle content boundaries.
@ -59,7 +81,7 @@ This change requires you to set `org-roam-directory` to the resolved path of a f
- [#974](https://github.com/org-roam/org-roam/pull/974) Protect region targeted by `org-roam-insert`
- [#994](https://github.com/org-roam/org-roam/pull/994) Simplify org-roam-store-link
- [#1062](https://github.com/org-roam/org-roam/pull/1062) Variable `org-roam-completions-everywhere` allows for completions everywhere from word at point
- [#910](https://github.com/org-roam/org-roam/pull/910), [#1105](https://github.com/org-roam/org-roam/pull/1105) Support fuzzy links of the form [[roam:Title]], [[roam:*Headline]] and [[roam:Title*Headline]]
- [#910](https://github.com/org-roam/org-roam/pull/910), [#1105](https://github.com/org-roam/org-roam/pull/1105) Support fuzzy links of the form `[[roam:Title]]`, `[[roam:*Headline]]` and `[[roam:Title*Headline]]`
### Bugfixes

113
README.md
View File

@ -1,47 +1,35 @@
[![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
[![GitHub Release](https://img.shields.io/github/v/release/org-roam/org-roam)](https://img.shields.io/github/v/release/org-roam/org-roam)
[![MELPA](https://melpa.org/packages/org-roam-badge.svg)](https://melpa.org/#/org-roam)
# Org-roam [![GitHub Release][release-badge]][release] [![MELPA][melpa-badge]][melpa] [![License GPL 3][gpl3-badge]][gpl3]
<img src="https://www.orgroam.com/img/logo.svg" align="right" alt="Org-roam Logo" width="240">
## Synopsis
Org-roam is a plain-text knowledge management system. It brings some of
[Roam's][roamresearch] more powerful features into the [Org-mode][org]
ecosystem.
> **NOTE:** Org-roam builds upon Emacs and Org-mode, both of which are intricate
> tools that require time investment for mastery. This makes Org-roam less
> friendly for beginners, but extremely powerful for those familiar with the
> ecosystem, or willing to invest effort in it.
Org-roam borrows principles from the Zettelkasten method, providing a solution
for non-hierarchical note-taking. It should also work as a plug-and-play
solution for anyone already using Org-mode for their personal wiki.
Org-roam is a [Roam][roamresearch] replica built on top of the
all-powerful [Org-mode][org].
- **Private and Secure**: Edit your personal wiki completely offline, entirely
in your control. Encrypt your notes with GPG. Take lasting notes in
plain-text.
- **Networked Thought**: Connect notes and thoughts together with ease using
backlinks. Discover surprising and previously unseen connections in your notes
with the built-in graph visualization.
- **Extensible and Powerful**: Leverage Emacs' fantastic text-editing interface,
and the mature Emacs and Org-mode ecosystem of packages.
- **Free and Open Source**: Org-roam is licensed under the GNU General Public
License version 3 or later.
Org-roam is a solution for effortless non-hierarchical note-taking
with Org-mode. With Org-roam, notes flow naturally, making note-taking
fun and easy. Org-roam should also work as a plug-and-play solution
for anyone already using Org-mode for their personal wiki.
Org-roam aims to implement the core features of Roam, leveraging the
mature ecosystem around Org-mode where possible. Eventually, we hope
to further introduce features enabled by the Emacs ecosystem.
[@technovangelist](https://github.com/technovangelist/) has produced a video
describing Org-roam and the concepts behind it:
[![Making Connections in your Notes](http://img.youtube.com/vi/Lg61ocfxk3c/0.jpg)](http://www.youtube.com/watch?v=Lg61ocfxk3c "Making Connections in your Notes")
Important links:
<p align="center">
<img src="https://www.orgroam.com/img/screenshot.png" alt="Org-roam Screenshot" width="738">
</p>
- **[Documentation][docs]**
- **[Discourse][discourse]**
- **[Slack][slack]**
## A Preview
Here's a screencast of Org-roam. The `org-roam-buffer` (window on the
right) shows backlinks for the active Org-roam buffer (window on the
left), as well as the surrounding content in the backlink file. The
database is built once, and updated incrementally. The graph is
generated from the link structure, and can be used to navigate to the
respective files.
![img](doc/images/org-roam-graph.gif)
- **[Frequently Asked Questions][faq]**
- **[Changelog](CHANGELOG.md)**
## Installation
@ -51,7 +39,7 @@ You can install `org-roam` using `package.el`:
M-x package-install RET org-roam RET
```
Here's a sample configuration with using `use-package`:
Here's a sample configuration with `use-package`:
```emacs-lisp
(use-package org-roam
@ -69,43 +57,30 @@ Here's a sample configuration with using `use-package`:
(("C-c n I" . org-roam-insert-immediate))))
```
`org-roam-graph` by default expects to find the `dot` executable
from the `graphviz` package in the `exec-path`.
Ensure `graphviz` is installed and found if you want to use this
feature or customize your configuration for `org-roam-graph` to use a
different tool.
For more detailed installation and configuration instructions (including for
Doom and Spacemacs users), please see [the
documentation][docs].
## Frequently-asked Questions
Q: How do I create a note whose title already matches one of the candidates (e.g. creating `bar` when `barricade` already exists)?
A: With `ivy`, you need to press `C-M-j` to use the current input instead of the nearest candidate. (Source: [`ivy`s
FAQ](https://github.com/abo-abo/swiper#frequently-asked-questions))
Org-roam requires sqlite to function. Org-roam optionally uses Graphviz for
graph-related functionality. It is recommended to install PCRE-enabled ripgrep
for better performance and extended functionality.
## Getting Help
Before creating a new topic/issue, please be mindful of our time and ensure
that it has not already been addressed on
[GitHub][issues] or on
Before creating a new topic/issue, please be mindful of our time and ensure that
it has not already been addressed on [GitHub][issues] or on
[Discourse][discourse].
- If you are new to Emacs and have problem setting up Org-roam, please ask your question on [Slack, channel #how-do-i][slack].
- For quick questions, please ask them on [Slack, channel #troubleshooting][slack].
- If something is not working as it should, or if you would like to suggest a new feature, please [create a new issue][issues].
- If you have questions about your workflow with the slip-box method, please find a relevant topic on [Discourse][discourse], or create a new one.
- If you are new to Emacs and have problem setting up Org-roam, please ask your
question on [Slack, channel #how-do-i][slack].
- For quick questions, please ask them on [Slack, channel
#troubleshooting][slack].
- If something is not working as it should, or if you would like to suggest a
new feature, please [create a new issue][issues].
- If you have questions about your workflow with the slip-box method, please
find a relevant topic on [Discourse][discourse], or create a new one.
## Knowledge Bases using Org-roam
- [Jethro Kuan](https://braindump.jethro.dev/)
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
## Changelog
A changelog is being maintained [here](CHANGELOG.md)
- [Alexey Shmalko](https://braindump.rasen.dev/)
## Contributing
@ -116,12 +91,18 @@ request. Please also see [CONTRIBUTING.md](.github/CONTRIBUTING.md).
## License
Copyright © Jethro Kuan and contributors. Distributed under the GNU
General Public License, Version 3
General Public License, Version 3.
[roamresearch]: https://www.roamresearch.com/
[org]: https://orgmode.org/
[badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg
[gpl3-badge]: https://img.shields.io/badge/license-GPL_3-green.svg
[gpl3]: http://www.gnu.org/licenses/gpl-3.0.txt
[melpa-badge]: https://melpa.org/packages/org-roam-badge.svg
[melpa]: https://melpa.org/#/org-roam
[release-badge]: https://img.shields.io/github/v/release/org-roam/org-roam
[release]: https://github.com/org-roam/org-roam/releases
[docs]: https://www.orgroam.com/manual.html
[discourse]: https://org-roam.discourse.group/
[slack]: https://join.slack.com/t/orgroam/shared_invite/zt-deoqamys-043YQ~s5Tay3iJ5QRI~Lxg
[issues]: https://github.com/org-roam/org-roam/issues
[faq]: https://www.orgroam.com/manual.html#FAQ

View File

@ -40,6 +40,15 @@ table {
width: 100%;
}
pre.menu-comment {
background: none;
border: none;
font-family: sans-serif;
padding: 0;
margin: 0;
font-size: 100%;
}
thead {
border-bottom: 1px solid var(--border);
}

View File

@ -137,7 +137,7 @@ using a powerful templating system.
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
~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
thoughts, to be processed later into permanent notes.
@ -358,9 +358,23 @@ nothing right now, since there are no notes in the directory. Entering the title
of the note you wish to create, and pressing ~RET~ should begin the note
creation process. This process uses ~org-capture~'s templating system, and can
be customized (see [[*The Templating System][The Templating System]]). Using the default template, pressing
~C-c C-c~ finishes the note capture. Running ~M-x org-roam-find-file~ again
should show the note you have created, and selecting that entry will bring you
to that note.
~C-c C-c~ finishes the note capture.
By default, Org-roam updates the cache asynchronously in the background to
avoid getting in the way of writing. Org-roam queues updates to the files,
waits for you to be idle for 2 seconds, and then automatically triggers
updating the cache. After the cache has been updated, running ~M-x
org-roam-find-file~ again should show the note you have created, and selecting
that entry will bring you to that note [fn:1]. One can customize the waiting
time by setting ~org-roam-db-update-idle-seconds~; or change the cache update
to be triggered immediately after buffer save by setting
~org-roam-db-update-method~ to ~'immediate~.
For experienced ~org-capture~ users, the behavior of ~M-x org-roam-find-file~
may seem unfamiliar: after finishing a capture with ~C-c C-c~, you are returned
not to the original buffer from which you called ~M-x org-roam-find-file~, but
to a buffer pointing to the note you just created. For the usual ~org-capture~
behavior you can call ~M-x org-roam-capture~ instead of ~M-x org-roam-find-file~.
Org-roam makes it easy to create notes, and link them together. To link notes
together, we call ~M-x org-roam-insert~. This brings up a prompt with a list of
@ -379,19 +393,29 @@ as nodes, and links between them as edges. The generated graph can be used to
navigate to the files, but this requires some additional setup (see [[*Roam
Protocol][Roam Protocol]]).
* Anatomy of an Org-roam File
* Files
:PROPERTIES:
:ID: 3edec3e6-8e26-4a43-8a0a-bf204268bbb3
:END:
The bulk of Org-roam's functionality is built on top of vanilla Org-mode.
However, to support additional functionality, Org-roam adds several
Org-roam-specific keywords.
In Org-roam, notes typically consist of multiple files, where each file is a
zettel.
** Titles
While the bulk of Org-roam's functionality is built on top of vanilla Org-mode,
Org-roam adds several Org-roam-specific keywords to support additional
functionality.
To easily find a note, a title needs to be prescribed to a note. A note can have
many titles: this allows a note to be referred to by different names, which is
especially useful for topics or concepts with acronyms. For example, for a note
like "World War 2", it may be desirable to also refer to it using the acronym
"WWII".
This section explains the important components of a file, and the extensions to
Org-mode.
** File Titles
To easily find a note, a title needs to be prescribed to a note.
A note can have many titles: this allows a note to be referred to by different
names, which is especially useful for topics or concepts with acronyms. For
example, for a note like "World War 2", it may be desirable to also refer to it
using the acronym "WWII".
Org-roam calls ~org-roam--extract-titles~ to extract titles. It uses the
variable ~org-roam-title-sources~, to control how the titles are extracted. The
@ -417,50 +441,114 @@ Take for example the following org file:
| ~'headline~ | '("Headline") |
| ~'alias~ | '("WWII" "World War II") |
One can freely control which extraction methods to use by customizing
~org-roam-title-sources~: see the doc-string for the variable for more
information. If all methods of title extraction return no results, the file-name
is used in place of the titles for completions.
If no title is provided, Org-roam defaults to using the file-path.
If you wish to add your own title extraction method, you may push a symbol
~'foo~ into ~org-roam-title-sources~, and define a
~org-roam--extract-titles-foo~ which accepts no arguments. See
~org-roam--extract-titles-title~ for an example.
*** Customizing Title Extraction
** Tags
To control how Org-roam extracts titles, customize ~org-roam-title-sources~. If
all methods of title extraction return no results, the file-name is used as the
note's title.
- User Option: org-roam-title-sources
The list of sources from which to retrieve a note title.
Each element in the list is either:
1. a symbol -- this symbol corresponds to a title retrieval function, which
returns the list of titles for the current buffer
2. a list of symbols -- symbols in the list are treated as with (1). The
return value of this list is the first symbol in the list returning a
non-nil value.
The return results of the root list are concatenated.
For example the setting: '((title headline) alias) means the following:
1. Return the 'title + 'alias, if the title of current buffer is non-empty;
2. Or return 'headline + 'alias otherwise.
The currently supported symbols are:
~'title~
The ~#+title~ property of org file.
~'alias~
The ~#+roam_alias~ property of the org file, using
space-delimited strings.
~'headline~
The first headline in the org file.
Adding your own title extraction method requires two steps. First, define a
method ~(defun org-roam--extract-titles-foo () ...)~, where ~foo~ a
self-prescribed name for the title extraction method. This method takes no
arguments, and returns a list of strings (titles). Finally, push the symbol
~foo~ into ~org-roam-title-sources~. You may need to rebuild the cache from
scratch to re-process all files to pick up the new titles.
** File Tags
Tags are used as meta-data for files: they facilitate interactions with notes
where titles are insufficient. For example, tags allow for categorization of
notes: differentiating between bibliographical and structure notes during
interactive commands.
Org-roam calls ~org-roam--extract-tags~ to extract tags from files. It uses the
variable ~org-roam-tag-sources~, to control how tags are extracted. The tag
extraction methods supported are:
By default, tags are extracted from the ~#+roam_tags~ property. To add
additional extraction methods, see [[id:c986edba-9498-4af1-b033-c94b733d42c8][Customizing Tag Extraction]].
1. ~'prop~: This extracts tags from the ~#+roam_tags~ property. Tags are space
delimited, and can be multi-word using double quotes.
2. ~'all-directories~: All sub-directories relative to ~org-roam-directory~ are
extracted as tags. That is, if a file is located at relative path
~foo/bar/file.org~, the file will have tags ~foo~ and ~bar~.
3. ~'last-directory~: Extracts the last directory relative to
~org-roam-directory~ as the tag. That is, if a file is located at relative
path ~foo/bar/file.org~, the file will have tag ~bar~.
4. ~'first-directory~: Extracts the first directory relative to
~org-roam-directory~ as the tag. That is, if a file is located at relative
path ~foo/bar/file.org~, the file will have tag ~foo~.
*** Customizing Tag Extraction
:PROPERTIES:
:ID: c986edba-9498-4af1-b033-c94b733d42c8
:END:
Org-roam calls ~org-roam--extract-tags~ to extract tags from files. The variable
~org-roam-tag-sources~, to control how tags are extracted.
- User Option: org-roam-tag-sources
Sources to obtain tags from.
It should be a list of symbols representing any of the following extraction
methods:
~'prop~
Extract tags from the ~#+roam_tags~ property.
Tags are space delimited.
Tags may contain spaces if they are double-quoted.
e.g. ~#+roam_tags: TAG "tag with spaces"~
~'vanilla~
Extract vanilla org-mode tags, including ~#+FILETAGS~ and
inherited tags.
~'all-directories~
Extract sub-directories relative to ~org-roam-directory~.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tags "foo" and "bar".
~'last-directory~
Extract the last directory relative to `org-roam-directory'.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tag \"bar\".
~'first-directory~
Extract the first directory relative to ~org-roam-directory~.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tag "foo"
By default, only the ~'prop~ extraction method is enabled. To enable the other
extraction methods, you may modify ~org-roam-tag-sources~:
extraction methods, you may modify ~org-roam-tag-sources~, for example:
#+BEGIN_SRC emacs-lisp
(setq org-roam-tag-sources '(prop last-directory))
#+END_SRC
If you wish to add your own tag extraction method, you may push a symbol ~'foo~
into ~org-roam-tag-sources~, and define a ~org-roam--extract-tags-foo~ which
accepts the absolute file path as its argument. See
~org-roam--extract-tags-prop~ for an example.
Adding your own tag extraction method requires two steps. First, define a method
~(defun org-roam--extract-tags-foo (file) ...)~, where ~foo~ a self-prescribed
name for the tag extraction method. This method takes the file path as an
argument, and returns a list of strings (titles). Finally, push the symbol ~foo~
into ~org-roam-tag-sources~. You may need to rebuild the cache from scratch to
re-process all files to pick up the new tags.
** File Refs
@ -492,6 +580,7 @@ These keys also come in useful for when taking website notes, using the
You may assign multiple refs to a single file, for example when you want
multiple papers in a series to share the same note, or an article has a citation
key and a URL at the same time.
* The Templating System
Rather than creating blank files on ~org-roam-insert~ and ~org-roam-find-file~,
@ -711,23 +800,6 @@ registers this link type, and interprets the path as follows:
- ~[[roam:title*headline]]~ :: links to the headline "headline" in the Org-roam
file with title or alias "title"
~roam~ links support auto-completion via ~completion-at-point~: simply call
~completion-at-point~ within a roam link. Users of ~company-mode~ may want to
prepend ~company-capf~ to the beginning of variable ~company-backends~.
To easily insert ~roam~ links, one may wish to use a package like [[https://github.com/emacsorphanage/key-chord/][key-chord]]. In
the following example, typing "[[" will insert a stub ~roam~ link:
#+BEGIN_SRC emacs-lisp
(key-chord-define org-mode-map "[[" #'my/insert-roam-link)
(defun my/insert-roam-link ()
"Inserts an Org-roam link."
(interactive)
(insert "[[roam:]]")
(backward-char 2))
#+END_SRC
- User Option: org-roam-link-title-format
To distinguish between org-roam links and regular links, one may choose to use
@ -736,12 +808,6 @@ the following example, typing "[[" will insert a stub ~roam~ link:
If your version of Org is at least ~9.2~, consider styling the link differently,
by customizing the ~org-roam-link~, and ~org-roam-link-current~ faces.
- User Option: org-roam-completion-ignore-case
When non-nil, the ~roam~ link completions are ignore case. For example,
calling ~completion-at-point~ within ~[[roam:fo]]~ will present a completion
for a file with title "Foo". Defaults to ~t~.
- User Option: org-roam-link-auto-replace
When non-nil, ~roam~ links will be replaced with ~file~ or ~id~ links when
@ -749,6 +815,70 @@ the following example, typing "[[" will insert a stub ~roam~ link:
desirable to maintain compatibility with vanilla Org, but resolved links are
harder to edit. Defaults to ~t~.
* Completions
Completions for Org-roam are provided via ~completion-at-point~. Completion
suggestions are implemented as separate functions. Org-roam installs all
functions in ~org-roam-completion-functions~ to ~completion-at-point-functions~.
- Variable: org-roam-completion-functions
The list of functions to be used with ~completion-at-point~.
- User Option: org-roam-completion-ignore-case
When non-nil, the ~roam~ link completions are ignore case. For example,
calling ~completion-at-point~ within ~[[roam:fo]]~ will present a completion
for a file with title "Foo". Defaults to ~t~.
To use the completions from Org-roam with ~company-mode~, prepend ~company-capf~
to variable ~company-backends~.
** Link Completion
~roam~ links support auto-completion via ~completion-at-point~: simply call
~M-x completion-at-point~ within a roam link. That is, where the ~|~ character
represents the cursor:
- ~[[|]]~: completes for a file title
- ~[[roam:]]~: completes for a file title
- ~[[*|]]~: completes for a headline within this file
- ~[[foo*|]]~: completes a headline within the file with title "foo"
- ~[[roam:foo*|]]~ completes a headline within the file with title "foo"
Completions account for the current input. For example, for ~[[f|]]~, the
completions (by default) only show for files with titles that start with "f".
- Function: org-roam-link-complete-at-point
Do appropriate completion for the link at point.
*** Link Completions Everywhere
Org-roam is able to provide completions from the current word at point, enabling
as-you-type link completions. However, this is disabled by default: the author
believes that linking should be a deliberate action and linking should be
performed with great care.
Setting ~org-roam-completion-everywhere~ to ~t~ will enable word-at-point
completions.
- User Option: org-roam-completion-everywhere
If non-nil, provide completions from the current word at point. That is, in
the scenario ~this is a sent|~, calling ~completion-at-point~ will show
completions for titles that begin with "sent".
** Tag Completion
Org-roam facilitates the insertion of existing tags via ~completion-at-point~.
That is, suppose you have notes with tags "foo", and "bar". Now, in a note, if
you're on a line beginning with ~#+roam_tags:~, completions for these will
appear as-you-type if they match.
This functionality is implemented in ~org-roam-complete-tags-at-point~.
* Navigating Around
** Index File
@ -882,7 +1012,7 @@ One may want to exclude certain files to declutter the graph.
This setting excludes all files whose path contain "private" or "dailies".
* Org-roam Completion System
* Minibuffer Completion
Org-roam allows customization of which minibuffer completion system to use for
its interactive commands. The default setting uses Emacs' standard
@ -963,41 +1093,40 @@ make the new policy take effect.
See [[https://www.chromium.org/administrators/linux-quick-start][here]] for more info on the ~/etc/opt/chrome/policies/managed~ directory and
[[https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExternalProtocolDialogShowAlwaysOpenCheckbox][here]] for information on the ~ExternalProtocolDialogShowAlwaysOpenCheckbox~ policy.
For MacOS, one solution is to use [[https://github.com/sveinbjornt/Platypus][Platypus]]. Here are the instructions for
setting up with Platypus and Chrome:
For MacOS, we need to create our own application.
1. Install and launch Platypus (with [[https://brew.sh/][Homebrew]]):
1. Launch Script Editor
2. Use the following script, paying attention to the path to ~emacsclient~:
#+BEGIN_SRC bash
brew cask install platypus
#+END_SRC
#+begin_src emacs-lisp
on open location this_URL
set EC to "/usr/local/bin/emacsclient --no-wait "
set filePath to quoted form of this_URL
do shell script EC & filePath
tell application "Emacs" to activate
end open location
#+end_src
2. Create a script ~launch_emacs.sh~:
3. Save the script in ~/Applications/OrgProtocolClient.app~, changing the script type to
"Application", rather than "Script".
4. Edit ~/Applications/OrgProtocolClient.app/Contents/Info.plist~, adding the
following before the last ~</dict>~ tag:
#+BEGIN_SRC bash
#!/usr/bin/env bash
/usr/local/bin/emacsclient --no-wait $1
#+END_SRC
#+begin_src text
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>org-protocol handler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>org-protocol</string>
</array>
</dict>
</array>
#+end_src
3. Create a Platypus app with the following settings:
| Setting | Value |
|--------------------------------+---------------------------|
| App Name | "OrgProtocol" |
| Script Type | "env" · "/usr/bin/env" |
| Script Path | "path/to/launch-emacs.sh" |
| Interface | None |
| Accept dropped items | true |
| Remain running after execution | false |
Inside ~Settings~:
| Setting | Value |
|--------------------------------+----------------|
| Accept dropped files | true |
| Register as URI scheme handler | true |
| Protocol | "org-protocol" |
5. Save the file, and run the ~OrgProtocolClient.app~ to register the protocol.
To disable the "confirm" prompt in Chrome, you can also make Chrome
show a checkbox to tick, so that the ~OrgProtocol~ app will be used
@ -1051,7 +1180,7 @@ in the generated graph.
** The roam-ref protocol
This protocol finds or creates a new note with a given ~roam_key~ (see [[*Anatomy of an Org-roam File][Anatomy of an Org-roam File]]):
This protocol finds or creates a new note with a given ~roam_key~ (see [[id:3edec3e6-8e26-4a43-8a0a-bf204268bbb3][Files]]):
[[file:images/roam-ref.gif]]
@ -1123,20 +1252,19 @@ specifying the outline-path to a heading:
#'org-roam-capture--get-point
"* %?"
:file-name "daily/%<%Y-%m-%d>"
:head "#+title: %<%Y-%m-%d>\n\n* Lab notes\n* Journal"
:olp ("Journal"))
:head "#+title: %<%Y-%m-%d>\n"
:olp ("Lab notes"))
("j" "journal" entry
#'org-roam-capture--get-point
"* %?"
:file-name "daily/%<%Y-%m-%d>"
:head "#+title: %<%Y-%m-%d>\n\n* Lab notes\n* Journal"
:olp ("Lab notes"))))
:head "#+title: %<%Y-%m-%d>\n"
:olp ("Journal"))))
#+end_src
The template ~l~ will put its notes under the heading Lab notes, and the
template ~j~ will put its notes under the heading Journal. When you use
~:olp~, make sure that the headings are present in ~:head~.
template ~j~ will put its notes under the heading Journal.
** Capturing and finding daily-notes
@ -1199,7 +1327,7 @@ You can navigate between daily-notes:
Org-roam provides a utility for diagnosing and repairing problematic files via
~org-roam-doctor~. By default, ~org-roam-doctor~ runs the check on the current
Org-roam file. To run the check only for the current file, run ~C-u M-x
Org-roam file. To run the check only for all Org-roam files, run ~C-u M-x
org-roam-doctor~, but note that this may take some time.
- Function: org-roam-doctor &optional this-buffer
@ -1356,8 +1484,8 @@ using [[https://github.com/raxod502/el-patch][el-patch]]:
#+BEGIN_SRC emacs-lisp
(use-package el-patch
:straight (:host github
:repo "raxod502/el-patch"
:branch "develop"))
:repo "raxod502/el-patch"
:branch "develop"))
(eval-when-compile
(require 'el-patch))
@ -1376,7 +1504,7 @@ using [[https://github.com/raxod502/el-patch][el-patch]]:
(if begin
(funcall deft-parse-title-function
(substring contents begin (match-end 0))))))
(org-roam--get-title-or-slug file))))
(org-roam-db--get-title file))))
#+END_SRC
The Deft interface can slow down quickly when the number of files get huge.
@ -1494,8 +1622,8 @@ variable using directory-local variables. This is what ~.dir-locals.el~ may
contain:
#+BEGIN_SRC emacs-lisp
((nil . ((org-roam-directory . ".")
(org-roam-db-location . "./org-roam.db"))))
((nil . ((org-roam-directory . (expand-file-name "."))
(org-roam-db-location . (expand-file-name "./org-roam.db")))))
#+END_SRC
All files within that directory will be treated as their own separate set of
@ -1544,6 +1672,9 @@ are the solutions:
:END:
* Footnotes
[fn:1] Depending on your completion framework, you may need to press TAB to
see the list.
[fn:roam] To understand more about Roam, a collection of links are available in [[*Note-taking Workflows][Note-taking Workflows]].
# Local Variables:

View File

@ -67,14 +67,15 @@ General Public License for more details.
* A Brief Introduction to the Zettelkasten Method::
* Installation::
* Getting Started::
* Anatomy of an Org-roam File::
* Files::
* The Templating System::
* Concepts and Configuration::
* Inserting Links::
* Completions::
* Navigating Around::
* Encryption::
* Graphing::
* Org-roam Completion System::
* Minibuffer Completion::
* Roam Protocol::
* Daily-notes::
* Diagnosing and Repairing Files::
@ -102,12 +103,20 @@ Installation
* Installing from the Git Repository::
* Post-Installation Tasks::
Anatomy of an Org-roam File
Files
* Titles::
* Tags::
* File Titles::
* File Tags::
* File Refs::
File Titles
* Customizing Title Extraction::
File Tags
* Customizing Tag Extraction::
The Templating System
* Template Walkthrough::
@ -121,6 +130,15 @@ Concepts and Configuration
* Org-roam Faces::
* The Database::
Completions
* Link Completion::
* Tag Completion::
Link Completion
* Link Completions Everywhere::
Navigating Around
* Index File::
@ -306,7 +324,7 @@ using a powerful templating system.
A slip-box requires a method for quickly capturing ideas. These are called
@strong{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
@code{org-capture} (see @ref{capture,,,org,}), or using Org-roam's daily notes
@code{org-capture} (see @ref{Capture,,,org,}), or using Org-roam's daily notes
functionality (see @ref{Daily-notes}). This provides a central inbox for collecting
thoughts, to be processed later into permanent notes.
@ -567,9 +585,24 @@ nothing right now, since there are no notes in the directory. Entering the title
of the note you wish to create, and pressing @code{RET} should begin the note
creation process. This process uses @code{org-capture}'s templating system, and can
be customized (see @ref{The Templating System}). Using the default template, pressing
@code{C-c C-c} finishes the note capture. Running @code{M-x org-roam-find-file} again
should show the note you have created, and selecting that entry will bring you
to that note.
@code{C-c C-c} finishes the note capture.
By default, Org-roam updates the cache asynchronously in the background to
avoid getting in the way of writing. Org-roam queues updates to the files,
waits for you to be idle for 2 seconds, and then automatically triggers
updating the cache. After the cache has been updated, running @code{M-x
org-roam-find-file} again should show the note you have created, and selecting
that entry will bring you to that note @footnote{Depending on your completion framework, you may need to press TAB to
see the list.}. One can customize the waiting
time by setting @code{org-roam-db-update-idle-seconds}; or change the cache update
to be triggered immediately after buffer save by setting
@code{org-roam-db-update-method} to @code{'immediate}.
For experienced @code{org-capture} users, the behavior of @code{M-x org-roam-find-file}
may seem unfamiliar: after finishing a capture with @code{C-c C-c}, you are returned
not to the original buffer from which you called @code{M-x org-roam-find-file}, but
to a buffer pointing to the note you just created. For the usual @code{org-capture}
behavior you can call @code{M-x org-roam-capture} instead of @code{M-x org-roam-find-file}.
Org-roam makes it easy to create notes, and link them together. To link notes
together, we call @code{M-x org-roam-insert}. This brings up a prompt with a list of
@ -587,27 +620,34 @@ provides graphing capabilities, using Graphviz. It generates graphs with notes
as nodes, and links between them as edges. The generated graph can be used to
navigate to the files, but this requires some additional setup (see @ref{Roam Protocol}).
@node Anatomy of an Org-roam File
@chapter Anatomy of an Org-roam File
@node Files
@chapter Files
The bulk of Org-roam's functionality is built on top of vanilla Org-mode.
However, to support additional functionality, Org-roam adds several
Org-roam-specific keywords.
In Org-roam, notes typically consist of multiple files, where each file is a
zettel.
While the bulk of Org-roam's functionality is built on top of vanilla Org-mode,
Org-roam adds several Org-roam-specific keywords to support additional
functionality.
This section explains the important components of a file, and the extensions to
Org-mode.
@menu
* Titles::
* Tags::
* File Titles::
* File Tags::
* File Refs::
@end menu
@node Titles
@section Titles
@node File Titles
@section File Titles
To easily find a note, a title needs to be prescribed to a note. A note can have
many titles: this allows a note to be referred to by different names, which is
especially useful for topics or concepts with acronyms. For example, for a note
like ``World War 2'', it may be desirable to also refer to it using the acronym
``WWII''.
To easily find a note, a title needs to be prescribed to a note.
A note can have many titles: this allows a note to be referred to by different
names, which is especially useful for topics or concepts with acronyms. For
example, for a note like ``World War 2'', it may be desirable to also refer to it
using the acronym ``WWII''.
Org-roam calls @code{org-roam--extract-titles} to extract titles. It uses the
variable @code{org-roam-title-sources}, to control how the titles are extracted. The
@ -645,60 +685,135 @@ Take for example the following org file:
@tab '(``WWII'' ``World War II'')
@end multitable
One can freely control which extraction methods to use by customizing
@code{org-roam-title-sources}: see the doc-string for the variable for more
information. If all methods of title extraction return no results, the file-name
is used in place of the titles for completions.
If no title is provided, Org-roam defaults to using the file-path.
If you wish to add your own title extraction method, you may push a symbol
@code{'foo} into @code{org-roam-title-sources}, and define a
@code{org-roam--extract-titles-foo} which accepts no arguments. See
@code{org-roam--extract-titles-title} for an example.
@menu
* Customizing Title Extraction::
@end menu
@node Tags
@section Tags
@node Customizing Title Extraction
@subsection Customizing Title Extraction
To control how Org-roam extracts titles, customize @code{org-roam-title-sources}. If
all methods of title extraction return no results, the file-name is used as the
note's title.
@defopt org-roam-title-sources
The list of sources from which to retrieve a note title.
Each element in the list is either:
@end defopt
@itemize
@item
a symbol -- this symbol corresponds to a title retrieval function, which
returns the list of titles for the current buffer
@itemize
@item
a list of symbols -- symbols in the list are treated as with (1). The
return value of this list is the first symbol in the list returning a
non-nil value.
@end itemize
The return results of the root list are concatenated.
For example the setting: '((title headline) alias) means the following:
@itemize
@item
Return the 'title + 'alias, if the title of current buffer is non-empty;
@item
Or return 'headline + 'alias otherwise.
@end itemize
The currently supported symbols are:
@code{'title}
The @code{#+title} property of org file.
@code{'alias}
The @code{#+roam_alias} property of the org file, using
space-delimited strings.
@code{'headline}
The first headline in the org file.
@end itemize
Adding your own title extraction method requires two steps. First, define a
method @code{(defun org-roam--extract-titles-foo () ...)}, where @code{foo} a
self-prescribed name for the title extraction method. This method takes no
arguments, and returns a list of strings (titles). Finally, push the symbol
@code{foo} into @code{org-roam-title-sources}. You may need to rebuild the cache from
scratch to re-process all files to pick up the new titles.
@node File Tags
@section File Tags
Tags are used as meta-data for files: they facilitate interactions with notes
where titles are insufficient. For example, tags allow for categorization of
notes: differentiating between bibliographical and structure notes during
interactive commands.
Org-roam calls @code{org-roam--extract-tags} to extract tags from files. It uses the
variable @code{org-roam-tag-sources}, to control how tags are extracted. The tag
extraction methods supported are:
By default, tags are extracted from the @code{#+roam_tags} property. To add
additional extraction methods, see @ref{Customizing Tag Extraction}.
@itemize
@item
@code{'prop}: This extracts tags from the @code{#+roam_tags} property. Tags are space
delimited, and can be multi-word using double quotes.
@menu
* Customizing Tag Extraction::
@end menu
@item
@code{'all-directories}: All sub-directories relative to @code{org-roam-directory} are
extracted as tags. That is, if a file is located at relative path
@code{foo/bar/file.org}, the file will have tags @code{foo} and @code{bar}.
@node Customizing Tag Extraction
@subsection Customizing Tag Extraction
@item
@code{'last-directory}: Extracts the last directory relative to
@code{org-roam-directory} as the tag. That is, if a file is located at relative
path @code{foo/bar/file.org}, the file will have tag @code{bar}.
Org-roam calls @code{org-roam--extract-tags} to extract tags from files. The variable
@code{org-roam-tag-sources}, to control how tags are extracted.
@item
@code{'first-directory}: Extracts the first directory relative to
@code{org-roam-directory} as the tag. That is, if a file is located at relative
path @code{foo/bar/file.org}, the file will have tag @code{foo}.
@end itemize
@defopt org-roam-tag-sources
@end defopt
Sources to obtain tags from.
It should be a list of symbols representing any of the following extraction
methods:
@code{'prop}
Extract tags from the @code{#+roam_tags} property.
Tags are space delimited.
Tags may contain spaces if they are double-quoted.
e.g. @code{#+roam_tags: TAG "tag with spaces"}
@code{'vanilla}
Extract vanilla org-mode tags, including @code{#+FILETAGS} and
inherited tags.
@code{'all-directories}
Extract sub-directories relative to @code{org-roam-directory}.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tags ``foo'' and ``bar''.
@code{'last-directory}
Extract the last directory relative to `org-roam-directory'.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tag \``bar\''.
@code{'first-directory}
Extract the first directory relative to @code{org-roam-directory}.
That is, if a file is located at relative path foo/bar/file.org,
the file will have tag ``foo''
By default, only the @code{'prop} extraction method is enabled. To enable the other
extraction methods, you may modify @code{org-roam-tag-sources}:
extraction methods, you may modify @code{org-roam-tag-sources}, for example:
@lisp
(setq org-roam-tag-sources '(prop last-directory))
@end lisp
If you wish to add your own tag extraction method, you may push a symbol @code{'foo}
into @code{org-roam-tag-sources}, and define a @code{org-roam--extract-tags-foo} which
accepts the absolute file path as its argument. See
@code{org-roam--extract-tags-prop} for an example.
Adding your own tag extraction method requires two steps. First, define a method
@code{(defun org-roam--extract-tags-foo (file) ...)}, where @code{foo} a self-prescribed
name for the tag extraction method. This method takes the file path as an
argument, and returns a list of strings (titles). Finally, push the symbol @code{foo}
into @code{org-roam-tag-sources}. You may need to rebuild the cache from scratch to
re-process all files to pick up the new tags.
@node File Refs
@section File Refs
@ -1020,25 +1135,9 @@ Org-roam file
@item
@code{[[roam:title*headline]]}links to the headline ``headline'' in the Org-roam
file with title or alias ``title''
@end itemize
@code{roam} links support auto-completion via @code{completion-at-point}: simply call
@code{completion-at-point} within a roam link. Users of @code{company-mode} may want to
prepend @code{company-capf} to the beginning of variable @code{company-backends}.
To easily insert @code{roam} links, one may wish to use a package like @uref{https://github.com/emacsorphanage/key-chord/, key-chord}. In
the following example, typing ``[['' will insert a stub @code{roam} link:
@lisp
(key-chord-define org-mode-map "[[" #'my/insert-roam-link)
(defun my/insert-roam-link ()
"Inserts an Org-roam link."
(interactive)
(insert "[[roam:]]")
(backward-char 2))
@end lisp
@defopt org-roam-link-title-format
To distinguish between org-roam links and regular links, one may choose to use
@ -1048,13 +1147,6 @@ If your version of Org is at least @code{9.2}, consider styling the link differe
by customizing the @code{org-roam-link}, and @code{org-roam-link-current} faces.
@end defopt
@defopt org-roam-completion-ignore-case
When non-nil, the @code{roam} link completions are ignore case. For example,
calling @code{completion-at-point} within @code{[[roam:fo]]} will present a completion
for a file with title ``Foo''. Defaults to @code{t}.
@end defopt
@defopt org-roam-link-auto-replace
When non-nil, @code{roam} links will be replaced with @code{file} or @code{id} links when
@ -1063,6 +1155,98 @@ desirable to maintain compatibility with vanilla Org, but resolved links are
harder to edit. Defaults to @code{t}.
@end defopt
@node Completions
@chapter Completions
Completions for Org-roam are provided via @code{completion-at-point}. Completion
suggestions are implemented as separate functions. Org-roam installs all
functions in @code{org-roam-completion-functions} to @code{completion-at-point-functions}.
@defvar org-roam-completion-functions
The list of functions to be used with @code{completion-at-point}.
@end defvar
@defopt org-roam-completion-ignore-case
When non-nil, the @code{roam} link completions are ignore case. For example,
calling @code{completion-at-point} within @code{[[roam:fo]]} will present a completion
for a file with title ``Foo''. Defaults to @code{t}.
@end defopt
To use the completions from Org-roam with @code{company-mode}, prepend @code{company-capf}
to variable @code{company-backends}.
@menu
* Link Completion::
* Tag Completion::
@end menu
@node Link Completion
@section Link Completion
@code{roam} links support auto-completion via @code{completion-at-point}: simply call
@code{M-x completion-at-point} within a roam link. That is, where the @code{|} character
represents the cursor:
@itemize
@item
@code{[[|]]}: completes for a file title
@item
@code{[[roam:]]}: completes for a file title
@item
@code{[[*|]]}: completes for a headline within this file
@item
@code{[[foo*|]]}: completes a headline within the file with title ``foo''
@item
@code{[[roam:foo*|]]} completes a headline within the file with title ``foo''
@end itemize
Completions account for the current input. For example, for @code{[[f|]]}, the
completions (by default) only show for files with titles that start with ``f''.
@defun org-roam-link-complete-at-point
Do appropriate completion for the link at point.
@end defun
@menu
* Link Completions Everywhere::
@end menu
@node Link Completions Everywhere
@subsection Link Completions Everywhere
Org-roam is able to provide completions from the current word at point, enabling
as-you-type link completions. However, this is disabled by default: the author
believes that linking should be a deliberate action and linking should be
performed with great care.
Setting @code{org-roam-completion-everywhere} to @code{t} will enable word-at-point
completions.
@defopt org-roam-completion-everywhere
If non-nil, provide completions from the current word at point. That is, in
the scenario @code{this is a sent|}, calling @code{completion-at-point} will show
completions for titles that begin with ``sent''.
@end defopt
@node Tag Completion
@section Tag Completion
Org-roam facilitates the insertion of existing tags via @code{completion-at-point}.
That is, suppose you have notes with tags ``foo'', and ``bar''. Now, in a note, if
you're on a line beginning with @code{#+roam_tags:}, completions for these will
appear as-you-type if they match.
This functionality is implemented in @code{org-roam-complete-tags-at-point}.
@node Navigating Around
@chapter Navigating Around
@ -1241,8 +1425,8 @@ are excluded.
This setting excludes all files whose path contain ``private'' or ``dailies''.
@node Org-roam Completion System
@chapter Org-roam Completion System
@node Minibuffer Completion
@chapter Minibuffer Completion
Org-roam allows customization of which minibuffer completion system to use for
its interactive commands. The default setting uses Emacs' standard
@ -1331,64 +1515,54 @@ make the new policy take effect.
See @uref{https://www.chromium.org/administrators/linux-quick-start, here} for more info on the @code{/etc/opt/chrome/policies/managed} directory and
@uref{https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExternalProtocolDialogShowAlwaysOpenCheckbox, here} for information on the @code{ExternalProtocolDialogShowAlwaysOpenCheckbox} policy.
For MacOS, one solution is to use @uref{https://github.com/sveinbjornt/Platypus, Platypus}. Here are the instructions for
setting up with Platypus and Chrome:
For MacOS, we need to create our own application.
@itemize
@item
Install and launch Platypus (with @uref{https://brew.sh/, Homebrew}):
Launch Script Editor
@item
Use the following script, paying attention to the path to @code{emacsclient}:
@end itemize
@lisp
on open location this_URL
set EC to "/usr/local/bin/emacsclient --no-wait "
set filePath to quoted form of this_URL
do shell script EC & filePath
tell application "Emacs" to activate
end open location
@end lisp
@itemize
@item
Save the script in @code{/Applications/OrgProtocolClient.app}, changing the script type to
``Application'', rather than ``Script''.
@item
Edit @code{/Applications/OrgProtocolClient.app/Contents/Info.plist}, adding the
following before the last @code{</dict>} tag:
@end itemize
@example
brew cask install platypus
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>org-protocol handler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>org-protocol</string>
</array>
</dict>
</array>
@end example
@itemize
@item
Create a script @code{launch_emacs.sh}:
Save the file, and run the @code{OrgProtocolClient.app} to register the protocol.
@end itemize
@example
#!/usr/bin/env bash
/usr/local/bin/emacsclient --no-wait $1
@end example
@itemize
@item
Create a Platypus app with the following settings:
@end itemize
@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaa}
@headitem Setting
@tab Value
@item App Name
@tab ``OrgProtocol''
@item Script Type
@tab ``env'' · ``/usr/bin/env''
@item Script Path
@tab ``path/to/launch-emacs.sh''
@item Interface
@tab None
@item Accept dropped items
@tab true
@item Remain running after execution
@tab false
@end multitable
Inside @code{Settings}:
@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaa}
@headitem Setting
@tab Value
@item Accept dropped files
@tab true
@item Register as URI scheme handler
@tab true
@item Protocol
@tab ``org-protocol''
@end multitable
To disable the ``confirm'' prompt in Chrome, you can also make Chrome
show a checkbox to tick, so that the @code{OrgProtocol} app will be used
without confirmation. To do this, run in a shell:
@ -1443,7 +1617,7 @@ in the generated graph.
@node The roam-ref protocol
@section The roam-ref protocol
This protocol finds or creates a new note with a given @code{roam_key} (see @ref{Anatomy of an Org-roam File}):
This protocol finds or creates a new note with a given @code{roam_key} (see @ref{Files}):
@image{images/roam-ref,,,,gif}
@ -1525,20 +1699,19 @@ specifying the outline-path to a heading:
#'org-roam-capture--get-point
"* %?"
:file-name "daily/%<%Y-%m-%d>"
:head "#+title: %<%Y-%m-%d>\n\n* Lab notes\n* Journal"
:olp ("Journal"))
:head "#+title: %<%Y-%m-%d>\n"
:olp ("Lab notes"))
("j" "journal" entry
#'org-roam-capture--get-point
"* %?"
:file-name "daily/%<%Y-%m-%d>"
:head "#+title: %<%Y-%m-%d>\n\n* Lab notes\n* Journal"
:olp ("Lab notes"))))
:head "#+title: %<%Y-%m-%d>\n"
:olp ("Journal"))))
@end lisp
The template @code{l} will put its notes under the heading Lab notes, and the
template @code{j} will put its notes under the heading Journal. When you use
@code{:olp}, make sure that the headings are present in @code{:head}.
template @code{j} will put its notes under the heading Journal.
@node Capturing and finding daily-notes
@section Capturing and finding daily-notes
@ -1613,7 +1786,7 @@ When in an daily-note, find the next one.
Org-roam provides a utility for diagnosing and repairing problematic files via
@code{org-roam-doctor}. By default, @code{org-roam-doctor} runs the check on the current
Org-roam file. To run the check only for the current file, run @code{C-u M-x
Org-roam file. To run the check only for all Org-roam files, run @code{C-u M-x
org-roam-doctor}, but note that this may take some time.
@defun org-roam-doctor &optional this-buffer
@ -1808,8 +1981,8 @@ using @uref{https://github.com/raxod502/el-patch, el-patch}:
@lisp
(use-package el-patch
:straight (:host github
:repo "raxod502/el-patch"
:branch "develop"))
:repo "raxod502/el-patch"
:branch "develop"))
(eval-when-compile
(require 'el-patch))
@ -1828,7 +2001,7 @@ used as title."
(if begin
(funcall deft-parse-title-function
(substring contents begin (match-end 0))))))
(org-roam--get-title-or-slug file))))
(org-roam-db--get-title file))))
@end lisp
The Deft interface can slow down quickly when the number of files get huge.
@ -1953,8 +2126,8 @@ variable using directory-local variables. This is what @code{.dir-locals.el} may
contain:
@lisp
((nil . ((org-roam-directory . ".")
(org-roam-db-location . "./org-roam.db"))))
((nil . ((org-roam-directory . (expand-file-name "."))
(org-roam-db-location . (expand-file-name "./org-roam.db")))))
@end lisp
All files within that directory will be treated as their own separate set of
@ -2007,5 +2180,5 @@ the candidate list.
@printindex vr
Emacs 28.0.50 (Org mode 9.4)
Emacs 28.0.50 (Org mode 9.5)
@bye

View File

@ -36,6 +36,8 @@
(require 's)
(require 'f)
(require 'ol)
(require 'org-element)
(require 'org-roam-macs)
(defvar org-roam-directory)
(defvar org-link-frame-setup)
@ -47,9 +49,10 @@
(defvar org-roam--org-link-bracket-typed-re)
(declare-function org-roam-db--ensure-built "org-roam-db")
(declare-function org-roam-db--get-title "org-roam-db")
(declare-function org-roam-db-has-file-p "org-roam-db")
(declare-function org-roam--extract-refs "org-roam")
(declare-function org-roam--extract-titles "org-roam")
(declare-function org-roam--get-title-or-slug "org-roam")
(declare-function org-roam--get-backlinks "org-roam")
(declare-function org-roam-backlinks-mode "org-roam")
(declare-function org-roam-mode "org-roam")
@ -63,11 +66,13 @@ Valid values are
* left,
* right,
* top,
* bottom."
* bottom,
* a function returning one of the above."
:type '(choice (const left)
(const right)
(const top)
(const bottom))
(const bottom)
function)
:group 'org-roam)
(defcustom org-roam-buffer-width 0.33
@ -95,6 +100,13 @@ Has an effect if and only if `org-roam-buffer-position' is `top' or `bottom'."
:type 'hook
:group 'org-roam)
(defcustom org-roam-buffer-preview-function #'org-roam-buffer--preview
"Function to obtain preview contents for a given link.
The function takes in two arguments, the FILE containing the
link, and the POINT of the link."
:type 'function
:group 'org-roam)
(defcustom org-roam-buffer-window-parameters nil
"Additional window parameters for the `org-roam-buffer' side window.
For example: (setq org-roam-buffer-window-parameters '((no-other-window . t)))"
@ -116,11 +128,22 @@ For example: (setq org-roam-buffer-window-parameters '((no-other-window . t)))"
(defun org-roam-buffer--insert-title ()
"Insert the org-roam-buffer title."
(insert (propertize (org-roam--get-title-or-slug
(insert (propertize (org-roam-db--get-title
(buffer-file-name org-roam-buffer--current))
'font-lock-face
'org-document-title)))
(defun org-roam-buffer--preview (file point)
"Get preview content for FILE at POINT."
(save-excursion
(org-roam--with-temp-buffer file
(goto-char point)
(let ((elem (org-element-at-point)))
(or (org-element-property :raw-value elem)
(when-let ((begin (org-element-property :begin elem))
(end (org-element-property :end elem)))
(string-trim (buffer-substring-no-properties begin end))))))))
(defun org-roam-buffer--pluralize (string number)
"Conditionally pluralize STRING if NUMBER is above 1."
(let ((l (pcase number
@ -164,11 +187,11 @@ ORIG-PATH is the path where the CONTENT originated."
(bls (cdr group)))
(insert (format "** %s\n"
(org-roam-format-link file-from
(org-roam--get-title-or-slug file-from)
(org-roam-db--get-title file-from)
"file")))
(dolist (backlink bls)
(pcase-let ((`(,file-from _ ,props) backlink))
(insert (if-let ((content (plist-get props :content)))
(insert (if-let ((content (funcall org-roam-buffer-preview-function file-from (plist-get props :point))))
(propertize (org-roam-buffer-expand-links content file-from)
'help-echo "mouse-1: visit backlinked note"
'file-from file-from
@ -195,7 +218,7 @@ ORIG-PATH is the path where the CONTENT originated."
(setq props (seq-sort-by (lambda (p) (plist-get p :point)) #'< props))
(insert (format "** %s\n"
(org-roam-format-link file-from
(org-roam--get-title-or-slug file-from)
(org-roam-db--get-title file-from)
"file")))
(dolist (prop props)
(insert "*** "
@ -205,7 +228,7 @@ ORIG-PATH is the path where the CONTENT originated."
(org-roam-buffer-expand-links file-from))
"Top")
"\n"
(if-let ((content (plist-get prop :content)))
(if-let ((content (funcall org-roam-buffer-preview-function file-from (plist-get prop :point))))
(propertize
(s-trim (s-replace "\n" " " (org-roam-buffer-expand-links content file-from)))
'help-echo "mouse-1: visit backlinked note"
@ -217,6 +240,7 @@ ORIG-PATH is the path where the CONTENT originated."
(defun org-roam-buffer-update ()
"Update the `org-roam-buffer'."
(interactive)
(org-roam-db--ensure-built)
(let* ((source-org-roam-directory org-roam-directory))
(with-current-buffer org-roam-buffer
@ -251,7 +275,8 @@ This needs to be quick or infrequent, because this is run at
(when (and (or redisplay
(not (eq org-roam-buffer--current buffer)))
(eq 'visible (org-roam-buffer--visibility))
(buffer-file-name buffer))
(buffer-file-name buffer)
(org-roam-db-has-file-p (buffer-file-name buffer)))
(setq org-roam-buffer--current buffer)
(org-roam-buffer-update))))
@ -290,14 +315,9 @@ Valid states are 'visible, 'exists and 'none."
(defun org-roam-buffer--get-create ()
"Set up the `org-roam' buffer at `org-roam-buffer-position'."
(let ((position
(if (member org-roam-buffer-position '(right left top bottom))
org-roam-buffer-position
(let ((text-quoting-style 'grave))
(lwarn '(org-roam) :error
"Invalid org-roam-buffer-position: %s. Defaulting to \\='right"
org-roam-buffer-position))
'right)))
(let ((position (if (functionp org-roam-buffer-position)
(funcall org-roam-buffer-position)
org-roam-buffer-position)))
(save-selected-window
(-> (get-buffer-create org-roam-buffer)
(display-buffer-in-side-window

View File

@ -32,6 +32,7 @@
;;;; Library Requires
(require 'org-capture)
(require 'org-roam-macs)
(require 'org-roam-db)
(require 'dash)
(require 's)
(require 'cl-lib)
@ -41,12 +42,13 @@
(defvar org-roam-directory)
(defvar org-roam-mode)
(defvar org-roam-title-to-slug-function)
(defvar org-roam-file-extensions)
(declare-function org-roam--get-title-path-completions "org-roam")
(declare-function org-roam--get-ref-path-completions "org-roam")
(declare-function org-roam--file-path-from-id "org-roam")
(declare-function org-roam--find-file "org-roam")
(declare-function org-roam-format-link "org-roam")
(declare-function org-roam--split-ref "org-roam")
(declare-function org-roam-mode "org-roam")
(declare-function org-roam-completion--completing-read "org-roam-completion")
@ -388,13 +390,25 @@ The file is saved if the original value of :no-save is not t and
(with-current-buffer (org-capture-get :buffer)
(save-buffer)))))
(defun org-roam-capture--new-file ()
"Return the path to the new file during an Org-roam capture.
(defun org-roam-capture--get-file-path (basename)
"Return path for Org-roam file with BASENAME."
(let* ((ext (or (car org-roam-file-extensions)
"org"))
(file (concat basename "." ext)))
(expand-file-name
(if org-roam-encrypt-files
(concat file ".gpg")
file)
org-roam-directory)))
(defun org-roam-capture--new-file (&optional allow-existing-file-p)
"Return the path to file during an Org-roam capture.
This function reads the file-name attribute of the currently
active Org-roam template.
If the file path already exists, it throw an error.
If the file path already exists, and not ALLOW-EXISTING-FILE-P,
raise a warning.
Else, to insert the header content in the file, `org-capture'
prepends the `:head' property of the Org-roam capture template.
@ -403,9 +417,7 @@ To prevent the creation of a new file if the capture process is
aborted, we do the following:
1. Save the original value of the capture template's :no-save.
2. Set the capture template's :no-save to t.
3. Add a function on `org-capture-before-finalize-hook' that saves
the file if the original value of :no-save is not t and
`org-note-abort' is not t."
@ -413,16 +425,18 @@ the file if the original value of :no-save is not t and
(user-error "Template needs to specify `:file-name'")))
(new-id (s-trim (org-roam-capture--fill-template
name-templ)))
(file-path (org-roam--file-path-from-id new-id))
(file-path (org-roam-capture--get-file-path new-id))
(roam-head (or (org-roam-capture--get :head)
""))
(org-template (org-capture-get :template))
(roam-template (concat roam-head org-template)))
(unless (or (file-exists-p file-path)
(cl-some (lambda (buffer)
(string= (buffer-file-name buffer)
file-path))
(buffer-list)))
(if (or (file-exists-p file-path)
(find-buffer-visiting file-path))
(unless allow-existing-file-p
(lwarn '(org-roam) :warning
"Attempted to recreate existing file: %s.
This can happen when your org-roam db is not in sync with your notes.
Using existing file..." file-path))
(make-directory (file-name-directory file-path) t)
(org-roam-capture--put :orig-no-save (org-capture-get :no-save)
:new-file t)
@ -438,10 +452,64 @@ the file if the original value of :no-save is not t and
(org-capture-put :template org-template))
(_
(org-capture-put :template roam-template
:type 'plain)))
:type 'plain)))
(org-capture-put :no-save t))
file-path))
(defun org-roam-capture-find-or-create-olp (olp)
"Return a marker pointing to the entry at OLP in the current buffer.
If OLP does not exist, create it. If anything goes wrong, throw
an error, and if you need to do something based on this error,
you can catch it with `condition-case'."
(let* ((level 1)
(lmin 1)
(lmax 1)
(start (point-min))
(end (point-max))
found flevel)
(unless (derived-mode-p 'org-mode)
(error "Buffer %s needs to be in Org mode" (current-buffer)))
(org-with-wide-buffer
(goto-char start)
(dolist (heading olp)
(let ((re (format org-complex-heading-regexp-format
(regexp-quote heading)))
(cnt 0))
(while (re-search-forward re end t)
(setq level (- (match-end 1) (match-beginning 1)))
(when (and (>= level lmin) (<= level lmax))
(setq found (match-beginning 0) flevel level cnt (1+ cnt))))
(when (> cnt 1)
(error "Heading not unique on level %d: %s" lmax heading))
(when (= cnt 0)
;; Create heading if it doesn't exist
(goto-char end)
(unless (bolp) (newline))
(org-insert-heading nil nil t)
(unless (= lmax 1) (org-do-demote))
(insert heading)
(setq end (point))
(goto-char start)
(while (re-search-forward re end t)
(setq level (- (match-end 1) (match-beginning 1)))
(when (and (>= level lmin) (<= level lmax))
(setq found (match-beginning 0) flevel level cnt (1+ cnt))))))
(goto-char found)
(setq lmin (1+ flevel) lmax (+ lmin (if org-odd-levels-only 1 0)))
(setq start found
end (save-excursion (org-end-of-subtree t t))))
(point-marker))))
(defun org-roam-capture--get-ref-path (type path)
"Get the file path to the ref with TYPE and PATH."
(caar (org-roam-db-query
[:select [file]
:from refs
:where (= type $s1)
:and (= ref $s2)
:limit 1]
type path)))
(defun org-roam-capture--get-point ()
"Return exact point to file for org-capture-template.
The file to use is dependent on the context:
@ -466,16 +534,18 @@ This function is used solely in Org-roam's capture templates: see
(org-roam-capture--new-file))
('dailies
(org-capture-put :default-time (cdr (assoc 'time org-roam-capture--info)))
(org-roam-capture--new-file))
(org-roam-capture--new-file 'allow-existing))
('ref
(let ((completions (org-roam--get-ref-path-completions))
(ref (cdr (assoc 'ref org-roam-capture--info))))
(if-let ((pl (cdr (assoc ref completions))))
(plist-get pl :path)
(org-roam-capture--new-file))))
(if-let ((ref (cdr (assoc 'ref org-roam-capture--info))))
(pcase (org-roam--split-ref ref)
(`(,type . ,path)
(or (org-roam-capture--get-ref-path type path)
(org-roam-capture--new-file)))
(_ (user-error "%s is not a valid ref" ref)))
(error "Ref not found in `org-roam-capture--info'")))
(_ (error "Invalid org-roam-capture-context")))))
(org-capture-put :template
(org-roam-capture--fill-template (org-capture-get :template)))
(org-roam-capture--fill-template (org-capture-get :template)))
(org-roam-capture--put :file-path file-path
:finalize (or (org-capture-get :finalize)
(org-roam-capture--get :finalize)))
@ -485,26 +555,16 @@ This function is used solely in Org-roam's capture templates: see
(org-roam-capture--put prop val)))
(set-buffer (org-capture-target-buffer file-path))
(widen)
(if-let* ((olp (when (eq org-roam-capture--context 'dailies)
(--> (org-roam-capture--get :olp)
(pcase it
((pred stringp)
(list it))
((pred listp)
it)
(wrong-type
(signal 'wrong-type-argument
`((stringp listp)
,wrong-type))))))))
(if-let* ((olp (org-roam-capture--get :olp)))
(condition-case err
(when-let ((marker (org-find-olp `(,file-path ,@olp))))
(when-let ((marker (org-roam-capture-find-or-create-olp olp)))
(goto-char marker)
(set-marker marker nil))
(error
(when (org-roam-capture--get :new-file)
(kill-buffer))
(signal (car err) (cdr err))))
(goto-char (point-min)))))
(goto-char (point-max)))))
(defun org-roam-capture--convert-template (template)
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax."
@ -545,8 +605,7 @@ GOTO and KEYS argument have the same functionality as
`org-capture'."
(let* ((org-capture-templates (mapcar #'org-roam-capture--convert-template org-roam-capture-templates))
(one-template-p (= (length org-capture-templates) 1))
org-capture-templates-contexts
(org-capture-link-is-already-stored t))
org-capture-templates-contexts)
(when one-template-p
(setq keys (caar org-capture-templates)))
(if (or one-template-p

View File

@ -75,8 +75,6 @@
"org-roam 1.1.0")
(define-obsolete-function-alias 'org-roam--capture 'org-roam-capture--capture
"org-roam 1.1.0")
(define-obsolete-function-alias 'org-roam-db--maybe-update 'org-roam-db--update-maybe
"org-roam 1.1.0")
(define-obsolete-function-alias 'org-roam-db--clear 'org-roam-db-clear
"org-roam 1.2.0")
(define-obsolete-function-alias 'org-roam-dailies-today 'org-roam-dailies-find-today

View File

@ -42,8 +42,8 @@
;;;; Declarations
(defvar org-roam-mode)
(defvar org-roam-directory)
(defvar org-roam-file-extensions)
(declare-function org-roam--org-file-p "org-roam")
(declare-function org-roam--file-path-from-id "org-roam")
(declare-function org-roam--find-file "org-roam")
(declare-function org-roam-mode "org-roam")
@ -59,18 +59,19 @@
:type 'hook)
(defcustom org-roam-dailies-capture-templates
'(("d" "default" entry (function org-roam-capture--get-point)
`(("d" "default" entry (function org-roam-capture--get-point)
"* %?"
:file-name "daily/%<%Y-%m-%d>"
:file-name ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
:head "#+title: %<%Y-%m-%d>\n"))
"Capture templates for daily-notes in Org-roam."
:group 'org-roam
;; Adapted from `org-capture-templates'
:type
'(repeat
`(repeat
(choice :value ("d" "default" plain (function org-roam-capture--get-point)
"%?"
:file-name "daily/%<%Y-%m-%d>"
:file-name ,(concat org-roam-dailies-directory
"%<%Y-%m-%d>")
:head "#+title: %<%Y-%m-%d>\n"
:unnarrowed t)
(list :tag "Multikey description"
@ -94,7 +95,8 @@ Template string :\n%v")
(const :format "" function)
(function :tag "Template function ")))
(const :format "File name format :" :file-name)
(string :format " %v" :value "daily/%<%Y-%m-%d>")
(string :format " %v" :value ,(concat org-roam-dailies-directory
"%<%Y-%m-%d>"))
(const :format "Header format :" :head)
(string :format " %v" :value "#+title: ${title}\n")
(plist :inline t
@ -122,10 +124,7 @@ Template string :\n%v")
;;;; Utilities
(defun org-roam-dailies-directory--get-absolute-path ()
"Get absolute path to `org-roam-dailies-directory'."
(-> (concat
(file-name-as-directory org-roam-directory)
org-roam-dailies-directory)
(file-truename)))
(expand-file-name org-roam-dailies-directory org-roam-directory))
(defun org-roam-dailies-find-directory ()
"Find and open `org-roam-dailies-directory'."
@ -140,7 +139,7 @@ If FILE is not specified, use the current buffer's file-path."
(-> (buffer-base-buffer)
(buffer-file-name))))
(directory (org-roam-dailies-directory--get-absolute-path)))
(setq path (file-truename path))
(setq path (expand-file-name path))
(save-match-data
(and
(org-roam--org-file-p path)
@ -155,7 +154,6 @@ When GOTO is non-nil, go the note without creating an entry."
(if goto (list (car it)) it)))
(org-roam-capture--info (list (cons 'time time)))
(org-roam-capture--context 'dailies))
(setq org-roam-capture-additional-template-props (list :finalize 'find-file))
(org-roam-capture--capture (when goto '(4)))))
;;;; Commands
@ -167,8 +165,7 @@ When GOTO is non-nil, go the note without creating an entry."
(interactive "P")
(org-roam-dailies--capture (current-time) goto)
(when goto
(run-hooks 'org-roam-dailies-find-file-hook)
(message "Showing daily-note for today")))
(run-hooks 'org-roam-dailies-find-file-hook)))
(defun org-roam-dailies-find-today ()
"Find the daily-note for today, creating it if necessary."
@ -270,8 +267,7 @@ creating an entry."
(time (org-read-date nil t time-str)))
(org-roam-dailies--capture time goto)
(when goto
(run-hooks 'org-roam-dailies-find-file-hook)
(message "Showing note for %s" time-str))))
(run-hooks 'org-roam-dailies-find-file-hook))))
(defun org-roam-dailies-find-date (&optional prefer-future)
"Find the daily-note for a date using the calendar, creating it if necessary.
@ -283,59 +279,44 @@ Prefer past dates, unless PREFER-FUTURE is non-nil."
;;; Navigation
(defun org-roam-dailies--list-files (&rest extra-files)
"List all files in `org-roam-dailies-directory'.
EXTRA-FILES can be used to append extra files to the list."
(let ((dir (org-roam-dailies-directory--get-absolute-path)))
(let ((dir (org-roam-dailies-directory--get-absolute-path))
(regexp (rx-to-string `(and "." (or ,@org-roam-file-extensions)))))
(append (--remove (let ((file (file-name-nondirectory it)))
(when (or (auto-save-file-name-p file)
(backup-file-name-p file)
(string-match "^\\." file))
it))
(directory-files-recursively dir ""))
(directory-files-recursively dir regexp))
extra-files)))
(defun org-roam-dailies--find-next-note-path (&optional n file)
"Find next daily-note from FILE.
With numeric argument N, find note N days in the future. If N is
negative, find note N days in the past.
If FILE is not provided, use the file visited by the current
buffer."
(unless (org-roam-dailies--daily-note-p file)
(user-error "Not in a daily-note"))
(let ((n (or n 1))
(file (or file
(-> (buffer-base-buffer)
(buffer-file-name)))))
;; Ensure that the buffer is saved before moving
(save-buffer file)
(let* ((list (org-roam-dailies--list-files))
(position
(cl-position-if (lambda (candidate)
(string= file candidate))
list)))
(pcase n
((pred (natnump))
(if (eq position (- (length list) 1))
(user-error "Already at newest note")
(message "Showing next daily-note")))
((pred (integerp))
(if (eq position 0)
(user-error "Already at oldest note")
(message "Showing previous daily-note"))))
(nth (+ position n) list))))
(defun org-roam-dailies-find-next-note (&optional n)
"Find next daily-note.
With numeric argument N, find note N days in the future. If N is
negative, find note N days in the past."
(interactive "p")
(let* ((n (or n 1))
(next (org-roam-dailies--find-next-note-path n)))
(find-file next)
(run-hooks 'org-roam-dailies-find-file-hook)))
(unless (org-roam-dailies--daily-note-p)
(user-error "Not in a daily-note"))
(setq n (or n 1))
(let* ((dailies (org-roam-dailies--list-files))
(position
(cl-position-if (lambda (candidate)
(string= (buffer-file-name (buffer-base-buffer)) candidate))
dailies))
note)
(unless position
(user-error "Can't find current note file - have you saved it yet?"))
(pcase n
((pred (natnump))
(when (eq position (- (length dailies) 1))
(user-error "Already at newest note")))
((pred (integerp))
(when (eq position 0)
(user-error "Already at oldest note"))))
(setq note (nth (+ position n) dailies))
(find-file note)
(run-hooks 'org-roam-dailies-find-file-hook)))
(defun org-roam-dailies-find-previous-note (&optional n)
"Find previous daily-note.

View File

@ -102,8 +102,8 @@ so that multi-directories are updated.")
`idle-timer'
Updates the database if dirty, if Emacs idles for `org-roam-db-update-idle-seconds'."
:type '(set (const :tag "idle-timer" idle-timer)
(const :tag "immediate" immediate))
:type '(choice (const :tag "idle-timer" idle-timer)
(const :tag "immediate" immediate))
:group 'org-roam)
(defcustom org-roam-db-update-idle-seconds 2
@ -134,7 +134,7 @@ Performs a database upgrade when required."
(when init-db
(org-roam-db--init conn))
(let* ((version (caar (emacsql conn "PRAGMA user_version")))
(version (org-roam-db--update-maybe conn version)))
(version (org-roam-db--upgrade-maybe conn version)))
(cond
((> version org-roam-db--version)
(emacsql-close conn)
@ -193,7 +193,7 @@ SQL can be either the emacsql vector representation, or a string."
(emacsql db [:create-table $i1 $S2] table schema))
(emacsql db (format "PRAGMA user_version = %s" org-roam-db--version))))
(defun org-roam-db--update-maybe (db version)
(defun org-roam-db--upgrade-maybe (db version)
"Upgrades the database schema for DB, if VERSION is old."
(emacsql-with-transaction db
'ignore
@ -255,15 +255,14 @@ This function is called on `org-roam-db-file-update-timer'."
(dolist (table (mapcar #'car org-roam-db--table-schemata))
(org-roam-db-query `[:delete :from ,table]))))
(defun org-roam-db--clear-file (&optional filepath)
"Remove any related links to the file at FILEPATH.
(defun org-roam-db--clear-file (&optional file)
"Remove any related links to the FILE.
This is equivalent to removing the node from the graph."
(let ((file (expand-file-name (or filepath
(buffer-file-name (buffer-base-buffer))))))
(dolist (table (mapcar #'car org-roam-db--table-schemata))
(org-roam-db-query `[:delete :from ,table
:where (= ,(if (eq table 'links) 'source 'file) $s1)]
file))))
(setq file (or file (buffer-file-name (buffer-base-buffer))))
(dolist (table (mapcar #'car org-roam-db--table-schemata))
(org-roam-db-query `[:delete :from ,table
:where (= ,(if (eq table 'links) 'source 'file) $s1)]
file)))
;;;;; Inserting
(defun org-roam-db--insert-meta (&optional update-p)
@ -395,6 +394,13 @@ Return the number of rows inserted."
0)))
;;;;; Fetching
(defun org-roam-db-has-file-p (file)
"Return t if FILE is in the database, nil otherwise."
(> (caar (org-roam-db-query [:select (funcall count) :from files
:where (= file $s1)]
file))
0))
(defun org-roam-db--get-current-files ()
"Return a hash-table of file to the hash of its file contents."
(let* ((current-files (org-roam-db-query [:select * :from files]))
@ -403,8 +409,8 @@ Return the number of rows inserted."
(puthash (car row) (cadr row) ht))
ht))
(defun org-roam-db--get-titles (file)
"Return the titles of FILE from the cache."
(defun org-roam-db--get-title (file)
"Return the main title of FILE from the cache."
(caar (org-roam-db-query [:select [title] :from titles
:where (= file $s1)
:limit 1]
@ -518,13 +524,8 @@ If FORCE, force a rebuild of the cache from scratch."
(org-agenda-files nil)
(org-roam-files (org-roam--list-all-files))
(current-files (org-roam-db--get-current-files))
(id-count 0)
(link-count 0)
(tag-count 0)
(title-count 0)
(ref-count 0)
(count-plist nil)
(deleted-count 0)
(modified-count 0)
(modified-files nil))
(dolist (file org-roam-files)
(let ((contents-hash (org-roam-db--file-hash file)))
@ -536,6 +537,46 @@ If FORCE, force a rebuild of the cache from scratch."
;; These files are no longer around, remove from cache...
(org-roam-db--clear-file file)
(setq deleted-count (1+ deleted-count)))
(setq count-plist (org-roam-db--update-files modified-files))
(org-roam-message "total: Δ%s, files-modified: Δ%s, ids: Δ%s, links: Δ%s, tags: Δ%s, titles: Δ%s, refs: Δ%s, deleted: Δ%s"
(- (length org-roam-files) (plist-get count-plist :error-count))
(plist-get count-plist :modified-count)
(plist-get count-plist :id-count)
(plist-get count-plist :link-count)
(plist-get count-plist :tag-count)
(plist-get count-plist :title-count)
(plist-get count-plist :ref-count)
deleted-count)))
(defun org-roam-db--get-file-hash-from-db (&optional file-path)
"Get hash from Org-roam database for FILE-PATH."
(setq file-path (or file-path
(buffer-file-name (buffer-base-buffer))))
(caar (org-roam-db-query [:select hash :from files
:where (= file $s1)] file-path)))
(defun org-roam-db-update-file (file-path)
"Update Org-roam cache for FILE-PATH.
If the file does not exist anymore, remove it from the cache.
If the file exists, update the cache with information."
(let ((content-hash (org-roam-db--file-hash file-path))
(db-hash (org-roam-db--get-file-hash-from-db file-path)))
(unless (string= content-hash db-hash)
(org-roam-db--update-files (list (cons file-path content-hash)))
(org-roam-message "Updated: %s" file-path))))
(defun org-roam-db--update-files (modified-files)
"Update Org-roam cache for a list of MODIFIED-FILES.
FILES is a list of (file . hash) pairs."
(let* ((gc-cons-threshold org-roam-db-gc-threshold)
(org-agenda-files nil)
(error-count 0)
(id-count 0)
(link-count 0)
(tag-count 0)
(title-count 0)
(ref-count 0)
(modified-count 0))
(pcase-dolist (`(,file . _) modified-files)
(org-roam-db--clear-file file))
;; Process all the files for IDs first
@ -555,39 +596,33 @@ If FORCE, force a rebuild of the cache from scratch."
(when org-roam-enable-headline-linking
(setq id-count (+ id-count (org-roam-db--insert-ids)))))
(file-error
(setq org-roam-files (remove file org-roam-files))
(setq error-count (1+ error-count))
(org-roam-db--clear-file file)
(lwarn '(org-roam) :warning
"Skipping unreadable file while building cache: %s" file)))))
;; Process titles, tags, links and ref links of file
(pcase-dolist (`(,file . _) modified-files)
(org-roam-message "Processed %s/%s modified files..." modified-count (length modified-files))
(condition-case nil
(org-roam--with-temp-buffer file
(setq modified-count (1+ modified-count))
(setq link-count (+ link-count (org-roam-db--insert-links)))
(setq tag-count (+ tag-count (org-roam-db--insert-tags)))
(setq title-count (+ title-count (org-roam-db--insert-titles)))
(setq ref-count (+ ref-count (org-roam-db--insert-refs))))
(file-error
(setq org-roam-files (remove file org-roam-files))
(org-roam-db--clear-file file)
(lwarn '(org-roam) :warning
"Skipping unreadable file while building cache: %s" file))))
(org-roam-message "total: Δ%s, files-modified: Δ%s, ids: Δ%s, links: Δ%s, tags: Δ%s, titles: Δ%s, refs: Δ%s, deleted: Δ%s"
(length org-roam-files)
modified-count
id-count
link-count
tag-count
title-count
ref-count
deleted-count)))
(org-roam--with-temp-buffer file
(setq modified-count (1+ modified-count))
(setq link-count (+ link-count (org-roam-db--insert-links)))
(setq tag-count (+ tag-count (org-roam-db--insert-tags)))
(setq title-count (+ title-count (org-roam-db--insert-titles)))
(setq ref-count (+ ref-count (org-roam-db--insert-refs))))
(file-error
(setq error-count (1+ error-count))
(org-roam-db--clear-file file)
(lwarn '(org-roam) :warning
"Skipping unreadable file while building cache: %s" file))))
(list :error-count error-count :modified-count modified-count :id-count id-count :title-count title-count :tag-count tag-count :link-count link-count :ref-count ref-count)))
(defun org-roam-db-update ()
"Update the database."
(pcase org-roam-db-update-method
('immediate
(org-roam-db-build-cache))
(org-roam-db-update-file (buffer-file-name (buffer-base-buffer))))
('idle-timer
(org-roam-db-mark-dirty))
(_

View File

@ -53,7 +53,6 @@
(declare-function org-roam--get-roam-buffers "org-roam")
(declare-function org-roam--list-all-files "org-roam")
(declare-function org-roam--org-roam-file-p "org-roam")
(declare-function org-roam--str-to-list "org-roam")
(declare-function org-roam-mode "org-roam")
(defvar org-roam-verbose)
@ -122,7 +121,7 @@ AST is the org-element parse tree."
(when (string-collate-equalp (org-element-property :key kw) "roam_tags" nil t)
(let ((tags (org-element-property :value kw)))
(condition-case nil
(org-roam--str-to-list tags)
(split-string-and-unquote tags)
(error
(push
`(,(org-element-property :begin kw)
@ -142,7 +141,7 @@ AST is the org-element parse tree."
(when (string-collate-equalp (org-element-property :key kw) "roam_alias" nil t)
(let ((aliases (org-element-property :value kw)))
(condition-case nil
(org-roam--str-to-list aliases)
(split-string-and-unquote aliases)
(error
(push
`(,(org-element-property :begin kw)

View File

@ -71,18 +71,21 @@ noabbrev Absolute path, no abbreviation of home directory."
(org-link-set-parameters "roam"
:follow #'org-roam-link-follow-link)
(defun org-roam-link-follow-link (path)
"Navigates to location specified by PATH."
(pcase-let ((`(,link-type ,loc ,desc ,mkr) (org-roam-link--get-location path)))
(defun org-roam-link-follow-link (_path)
"Navigates to location in Org-roam link.
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."
(pcase-let ((`(,link-type ,loc ,desc ,mkr) (org-roam-link--get-location)))
(when (and org-roam-link-auto-replace loc desc)
(org-roam-link--replace-link link-type loc desc))
(pcase link-type
("file"
(if loc
(org-roam--find-file loc)
(org-roam-find-file desc nil nil t)))
("id"
(org-goto-marker-or-bmk mkr)))))
("file"
(if loc
(org-roam--find-file loc)
(org-roam-find-file desc nil nil t)))
("id"
(org-goto-marker-or-bmk mkr)))))
;;; Retrieval Functions
(defun org-roam-link--get-titles ()
@ -94,15 +97,11 @@ noabbrev Absolute path, no abbreviation of home directory."
If FILE, return outline headings for passed FILE instead.
If WITH-MARKER, return a cons cell of (headline . marker).
If USE-STACK, include the parent paths as well."
(let* ((buf (or (and file
(or (find-buffer-visiting file)
(find-file-noselect file)))
(current-buffer)))
(outline-level-fn outline-level)
(path-separator "/")
(stack-level 0)
stack cands name level marker)
(with-current-buffer buf
(org-roam-with-file file (when with-marker 'keep)
(let* ((outline-level-fn outline-level)
(path-separator "/")
(stack-level 0)
stack cands name level marker)
(save-excursion
(goto-char (point-min))
(while (re-search-forward org-complex-heading-regexp nil t)
@ -126,8 +125,9 @@ If USE-STACK, include the parent paths as well."
path-separator)))
(push (if with-marker
(cons name marker)
name) cands)))))
(nreverse cands)))
name) cands))))
(nreverse cands))))
(defun org-roam-link--get-file-from-title (title &optional no-interactive)
"Return the file path corresponding to TITLE.
@ -143,14 +143,11 @@ When NO-INTERACTIVE, return nil if there are multiple options."
(completing-read "Select file: " files))))))
(defun org-roam-link--get-id-from-headline (headline &optional file)
"Return (marker . id) correspondng to HEADLINE.
If FILE, get headline from FILE instead.
"Return (marker . id) correspondng to HEADLINE in FILE.
If FILE is nil, get ID from current buffer.
If there is no corresponding headline, return nil."
(save-excursion
(with-current-buffer (or (and file
(or (find-buffer-visiting file)
(find-file-noselect file)))
(current-buffer))
(org-roam-with-file file 'keep
(let ((headlines (org-roam-link--get-headlines file 'with-markers)))
(when-let ((marker (cdr (assoc-string headline headlines))))
(goto-char marker)
@ -191,43 +188,59 @@ star-idx is the index of the asterisk, if any."
(t 'title+headline))))
(list type title headline star-index))))
(defun org-roam-link--get-location (link)
"Return the location of Org-roam fuzzy LINK.
(defun org-roam-link--get-location ()
"Return the location of the Org-roam fuzzy link at point.
The location is returned as a list containing (link-type loc desc marker).
nil is returned if there is no matching location.
link-type is either \"file\" or \"id\".
loc is the target location: e.g. a file path, or an id.
marker is a marker to the headline, if applicable."
(let (mkr link-type desc loc)
(pcase-let ((`(,type ,title ,headline _) (org-roam-link--split-path link)))
(pcase type
('title+headline
(let ((file (org-roam-link--get-file-from-title title)))
(if (not file)
(org-roam-message "Cannot find matching file")
(setq mkr (org-roam-link--get-id-from-headline headline file))
(pcase mkr
(`(,marker . ,target-id)
(setq mkr marker
loc target-id
link-type "id"
desc headline))
(_ (org-roam-message "cannot find matching id"))))))
('title
(setq loc (org-roam-link--get-file-from-title title)
desc title
link-type "file"))
('headline
(setq mkr (org-roam-link--get-id-from-headline headline))
(pcase mkr
(`(,marker . ,target-id)
(setq mkr marker
loc target-id
desc headline
link-type "id"))
(_ (org-roam-message "Cannot find matching headline")))))
(list link-type loc desc mkr))))
marker is a marker to the headline, if applicable.
desc is either the the description of the link under point, or
the target of LINK (title or heading content)."
(let ((context (org-element-context))
mkr link-type desc loc)
(pcase (org-element-lineage context '(link) t)
(`nil (error "Not at an Org link"))
(link
(if (not (string-equal "roam" (org-element-property :type link)))
(error "Not at Org-roam link")
(setq desc (and (org-element-property :contents-begin link)
(org-element-property :contents-end link)
(buffer-substring-no-properties
(org-element-property :contents-begin link)
(org-element-property :contents-end link))))
(pcase-let ((`(,type ,title ,headline _) (org-roam-link--split-path
(org-element-property :path link))))
(pcase type
('title+headline
(let ((file (org-roam-link--get-file-from-title title)))
(if (not file)
(org-roam-message "Cannot find matching file")
(setq mkr (org-roam-link--get-id-from-headline headline file))
(pcase mkr
(`(,marker . ,target-id)
(progn
(setq mkr marker
loc target-id
desc (or desc headline)
link-type "id")))
(_ (org-roam-message "Cannot find matching id"))))))
('title
(setq loc (org-roam-link--get-file-from-title title)
link-type "file"
desc (or desc title)))
('headline
(setq mkr (org-roam-link--get-id-from-headline headline))
(pcase mkr
(`(,marker . ,target-id)
(setq mkr marker
loc target-id
link-type "id"
desc (or desc headline)))
(_ (org-roam-message "Cannot find matching headline")))))))))
(list link-type loc desc mkr)))
;;; Conversion Functions
(defun org-roam-link--replace-link (link-type loc &optional desc)
@ -248,14 +261,11 @@ DESC is the link description."
(save-excursion
(goto-char (point-min))
(while (re-search-forward org-link-bracket-re nil t)
(let ((context (org-element-context)))
(pcase (org-element-lineage context '(link) t)
(`nil nil)
(link
(when (string-equal "roam" (org-element-property :type link))
(pcase-let ((`(,link-type ,loc ,desc _) (org-roam-link--get-location (org-element-property :path link))))
(when (and link-type loc)
(org-roam-link--replace-link link-type loc desc))))))))))
(condition-case nil
(pcase-let ((`(,link-type ,loc ,desc _) (org-roam-link--get-location)))
(when (and link-type loc)
(org-roam-link--replace-link link-type loc desc)))
(error nil)))))
(defun org-roam-link--replace-link-on-save ()
"Hook to replace all roam links on save."
@ -308,7 +318,7 @@ DESC is the link description."
(if headline-only-p 1 0)))
(insert (concat (unless (string= link-type "roam") "roam:")
(when headline-only-p "*")
str))))))))
(org-link-escape str)))))))))
(provide 'org-roam-link)
;;; org-roam-link.el ends here

View File

@ -52,6 +52,28 @@
(nconc new-lst (list separator it)))
new-lst)))
(defmacro org-roam-with-file (file keep-buf-p &rest body)
"Execute BODY within FILE.
If FILE is nil, execute BODY in the current buffer.
Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited."
(declare (indent 2) (debug t))
`(let* (new-buf
(buf (or (and (not ,file)
(current-buffer)) ;If FILE is nil, use current buffer
(find-buffer-visiting ,file) ; If FILE is already visited, find buffer
(progn
(setq new-buf t)
(find-file-noselect ,file)))) ; Else, visit FILE and return buffer
res)
(with-current-buffer buf
(setq res (progn ,@body))
(unless (and new-buf (not ,keep-buf-p))
(save-buffer)))
(if (and new-buf (not ,keep-buf-p))
(when (find-buffer-visiting ,file)
(kill-buffer (find-buffer-visiting ,file))))
res))
(defmacro org-roam--with-temp-buffer (file &rest body)
"Execute BODY within a temp buffer.
Like `with-temp-buffer', but propagates `org-roam-directory'.
@ -66,7 +88,8 @@ If FILE, set `org-roam-temp-file-name' to file and insert its contents."
(org-mode)
(when ,file
(insert-file-contents ,file)
(setq-local org-roam-file-name ,file))
(setq-local org-roam-file-name ,file)
(setq-local default-directory (file-name-directory ,file)))
,@body)))))
(defun org-roam-message (format-string &rest args)

View File

@ -65,7 +65,7 @@ It opens or creates a note with the given ref.
(push (cons 'slug (funcall org-roam-title-to-slug-function title)) decoded-alist))
(let-alist decoded-alist
(let* ((ref (org-protocol-sanitize-uri .ref))
(type (and (string-match "^\\([a-z]+\\):" ref)
(type (and (string-match org-link-plain-re ref)
(match-string 1 ref)))
(title (or .title ""))
(body (or .body ""))
@ -80,6 +80,7 @@ It opens or creates a note with the given ref.
(let* ((org-roam-capture-templates org-roam-capture-ref-templates)
(org-roam-capture--context 'ref)
(org-roam-capture--info decoded-alist)
(org-capture-link-is-already-stored t)
(template (cdr (assoc 'template decoded-alist))))
(raise-frame)
(org-roam-capture--capture nil template)

View File

@ -79,7 +79,7 @@
:group 'org
:prefix "org-roam-"
:link '(url-link :tag "Github" "https://github.com/org-roam/org-roam")
:link '(url-link :tag "Online Manual" "https://www.orgroam.com/manual/"))
:link '(url-link :tag "Online Manual" "https://www.orgroam.com/manual.html"))
(defcustom org-roam-directory (expand-file-name "~/org-roam/")
"Default path to Org-roam files.
@ -234,6 +234,35 @@ Function should return a filename string based on title."
:type 'function
:group 'org-roam)
(defcustom org-roam-slug-trim-chars
'(;; Combining Diacritical Marks https://www.unicode.org/charts/PDF/U0300.pdf
768 ; U+0300 COMBINING GRAVE ACCENT
769 ; U+0301 COMBINING ACUTE ACCENT
770 ; U+0302 COMBINING CIRCUMFLEX ACCENT
771 ; U+0303 COMBINING TILDE
772 ; U+0304 COMBINING MACRON
774 ; U+0306 COMBINING BREVE
775 ; U+0307 COMBINING DOT ABOVE
776 ; U+0308 COMBINING DIAERESIS
777 ; U+0309 COMBINING HOOK ABOVE
778 ; U+030A COMBINING RING ABOVE
780 ; U+030C COMBINING CARON
795 ; U+031B COMBINING HORN
803 ; U+0323 COMBINING DOT BELOW
804 ; U+0324 COMBINING DIAERESIS BELOW
805 ; U+0325 COMBINING RING BELOW
807 ; U+0327 COMBINING CEDILLA
813 ; U+032D COMBINING CIRCUMFLEX ACCENT BELOW
814 ; U+032E COMBINING BREVE BELOW
816 ; U+0330 COMBINING TILDE BELOW
817 ; U+0331 COMBINING MACRON BELOW
)
"Characters to trim from Unicode normalization for slug.
By default, the characters are specified to remove Diacritical Marks from the Latin alphabet."
:type '(repeat character)
:group 'org-roam)
(defcustom org-roam-title-sources '((title headline) alias)
"The list of sources from which to retrieve a note title.
Each element in the list is either:
@ -268,6 +297,13 @@ The currently supported symbols are:
(symbol)))
:group 'org-roam)
(defcustom org-roam-file-completion-tag-position 'prepend
"Prepend, append, or omit tags from the file titles during completion."
:type '(choice (const :tag "Prepend" prepend)
(const :tag "Append" append)
(const :tag "Omit" omit))
:group 'org-roam)
(defcustom org-roam-enable-headline-linking t
"Enable linking to headlines, which includes automatic :ID: creation and scanning of :ID:s for org-roam database."
:type 'boolean
@ -314,30 +350,6 @@ descriptive warnings when certain operations fail (e.g. parsing).")
(push (cons prop val) res)))
res))
(defun org-roam--str-to-list (str)
"Transform string STR into a list of strings.
If STR is nil, return nil.
This function can throw an error if STR is not a string, or if
str is malformed (e.g. missing a closing quote). Callers of this
function are expected to catch the error."
(when str
(unless (stringp str)
(signal 'wrong-type-argument `(stringp ,str)))
(let* ((str (org-trim str))
(format-str ":dummy '(%s)") ;The :dummy key is discarded in the `lst' var below.
(items (cdar (org-babel-parse-header-arguments (format format-str str)))))
(mapcar (lambda (item)
(cond
((stringp item)
item)
((symbolp item)
(symbol-name item))
((numberp item)
(number-to-string item))
(t
(signal 'wrong-type-argument `((stringp numberp symbolp) ,item))))) items))))
(defun org-roam--url-p (path)
"Check if PATH is a URL.
Assume the protocol is not present in PATH; e.g. URL `https://google.com' is
@ -504,31 +516,45 @@ Use external shell commands if defined in `org-roam-list-files-commands'."
(org-roam--list-files (expand-file-name org-roam-directory)))
;;;; Org extraction functions
(defun org-roam--extract-global-props-drawer (props)
"Extract PROPS from the file-level property drawer in Org."
(let (ret)
(org-with-point-at 1
(dolist (prop props ret)
(when-let ((v (org-entry-get (point) prop)))
(push (cons prop v) ret))))))
(defun org-roam--collect-keywords (keywords)
"Collect all Org KEYWORDS in the current buffer."
(if (functionp 'org-collect-keywords)
(org-collect-keywords keywords)
(let ((buf (org-element-parse-buffer))
res)
(dolist (k keywords)
(let ((p (org-element-map buf 'keyword
(lambda (kw)
(when (string-equal (org-element-property :key kw) k)
(org-element-property :value kw)))
:first-match nil)))
(push (cons k p) res)))
res)))
(defun org-roam--extract-global-props-keyword (keywords)
"Extract KEYWORDS from the current Org buffer."
(let (ret)
(pcase-dolist (`(,key . ,values) (org-roam--collect-keywords keywords))
(dolist (value values)
(push (cons key value) ret)))
ret))
(defun org-roam--extract-global-props (props)
"Extract PROPS from the current org buffer."
(let ((collected
;; Collect the raw props first
;; It'll be returned in the form of
;; (("PROP" "value" ...) ("PROP2" "value" ...))
(if (functionp 'org-collect-keywords)
(org-collect-keywords props)
(let ((buf (org-element-parse-buffer))
res)
(dolist (prop props)
(let ((p (org-element-map buf 'keyword
(lambda (kw)
(when (string-equal (org-element-property :key kw) prop)
(org-element-property :value kw)))
:first-match nil)))
(push (cons prop p) res)))
res))))
;; convert (("TITLE" "a" "b") ("Another" "c"))
;; to (("TITLE" . "a") ("TITLE" . "b") ("Another" . "c"))
(let (ret)
(pcase-dolist (`(,key . ,values) collected)
(dolist (value values)
(push (cons key value) ret)))
ret)))
"Extract PROPS from the current Org buffer.
Props are extracted from both the file-level property drawer (if
any), and Org keywords. Org keywords take precedence."
(append
(org-roam--extract-global-props-keyword props)
(org-roam--extract-global-props-drawer props)))
(defun org-roam--get-outline-path ()
"Return the outline path to the current entry.
@ -588,17 +614,8 @@ it as FILE-PATH."
(goto-char (org-element-property :begin link))
(let* ((type (org-roam--collate-types (org-element-property :type link)))
(path (org-element-property :path link))
(element (org-element-at-point))
(begin (or (org-element-property :contents-begin element)
(org-element-property :begin element)))
(end (or (org-element-property :contents-end element)
(org-element-property :end element)))
(content (or (org-element-property :raw-value element)
(when (and begin end)
(string-trim (buffer-substring-no-properties begin end)))))
(properties (list :outline (org-roam--get-outline-path)
:content content
:point begin))
:point (point)))
(names (pcase type
("id"
(when-let ((file-path (org-roam-id-get-file path)))
@ -647,9 +664,10 @@ If FILE-PATH is nil, use the current file."
"Return the aliases from the current buffer.
Reads from the \"roam_alias\" property."
(let* ((prop (org-roam--extract-global-props '("ROAM_ALIAS")))
(aliases (cdr (assoc "ROAM_ALIAS" prop))))
(aliases (or (cdr (assoc "ROAM_ALIAS" prop))
"")))
(condition-case nil
(org-roam--str-to-list aliases)
(split-string-and-unquote aliases)
(error
(progn
(lwarn '(org-roam) :error
@ -687,7 +705,7 @@ If NESTED, return the first successful result from SOURCES."
(setq coll (nconc coll res))
(setq coll res)
(cl-return))))
coll)))
(-uniq coll))))
(defun org-roam--extract-tags-all-directories (file)
"Extract tags from using the directory path FILE.
@ -713,9 +731,10 @@ tag."
(defun org-roam--extract-tags-prop (_file)
"Extract tags from the current buffer's \"#roam_tags\" global property."
(let* ((prop (cdr (assoc "ROAM_TAGS" (org-roam--extract-global-props '("ROAM_TAGS"))))))
(let* ((prop (or (cdr (assoc "ROAM_TAGS" (org-roam--extract-global-props '("ROAM_TAGS"))))
"")))
(condition-case nil
(org-roam--str-to-list prop)
(split-string-and-unquote prop)
(error
(progn
(lwarn '(org-roam) :error
@ -738,11 +757,12 @@ Tags are obtained via:
path is considered a tag.
2. The key #+roam_tags."
(let* ((file (or file (buffer-file-name (buffer-base-buffer))))
(tags (mapcan (lambda (source)
(funcall (intern (concat "org-roam--extract-tags-"
(symbol-name source)))
file))
org-roam-tag-sources)))
(tags (-uniq
(mapcan (lambda (source)
(funcall (intern (concat "org-roam--extract-tags-"
(symbol-name source)))
file))
org-roam-tag-sources))))
(pcase org-roam-tag-sort
('nil tags)
((pred booleanp) (cl-sort tags 'string-lessp :key 'downcase))
@ -764,6 +784,14 @@ backlinks."
"website")
(t type)))
(defun org-roam--split-ref (ref)
"Processes REF into its type and path.
Returns a cons cell of type and path if ref is a valid ref."
(save-match-data
(when (string-match org-link-plain-re ref)
(cons (org-roam--collate-types (match-string 1 ref))
(match-string 2 ref)))))
(defun org-roam--extract-refs ()
"Extract all refs (ROAM_KEY statements) from the current buffer.
@ -772,17 +800,13 @@ Each ref is returned as a cons of its type and its key."
(pcase-dolist
(`(,_ . ,roam-key)
(org-roam--extract-global-props '("ROAM_KEY")))
(let (type path)
(pcase roam-key
(pcase roam-key
('nil nil)
((pred string-empty-p)
(user-error "Org property #+roam_key cannot be empty"))
(ref
(when (string-match org-link-plain-re ref)
(setq type (org-roam--collate-types (match-string 1 ref))
path (match-string 2 ref)))))
(when (and type path)
(push (cons type path) refs))))
(when-let ((r (org-roam--split-ref ref)))
(push r refs)))))
refs))
(defun org-roam--extract-ref ()
@ -796,18 +820,14 @@ Each ref is returned as a cons of its type and its key."
(file-relative-name (expand-file-name org-roam-directory))
(file-name-sans-extension)))
(defun org-roam--get-title-or-slug (path)
"Convert `PATH' to the file title, if it exists. Else, return the path."
(or (org-roam-db--get-titles path)
(org-roam--path-to-slug path)))
(defun org-roam--title-to-slug (title)
"Convert TITLE to a filename-suitable slug."
(cl-flet* ((nonspacing-mark-p (char)
(eq 'Mn (get-char-code-property char 'general-category)))
(memq char org-roam-slug-trim-chars))
(strip-nonspacing-marks (s)
(apply #'string (seq-remove #'nonspacing-mark-p
(ucs-normalize-NFD-string s))))
(ucs-normalize-NFC-string
(apply #'string (seq-remove #'nonspacing-mark-p
(ucs-normalize-NFD-string s)))))
(cl-replace (title pair)
(replace-regexp-in-string (car pair) (cdr pair) title)))
(let* ((pairs `(("[^[:alnum:][:digit:]]" . "_") ;; convert anything not alphanumeric
@ -839,18 +859,27 @@ file."
(format org-roam-link-title-format description)))
(org-link-make-string (concat type ":" target) description))
(defun org-roam--prepend-tag-string (str tags)
"Prepend TAGS to STR."
(concat
(when tags
(propertize (format "(%s) " (s-join org-roam-tag-separator tags))
'face 'org-roam-tag))
str))
(defun org-roam--add-tag-string (str tags)
"Add TAGS to STR.
Depending on the value of `org-roam-file-completion-tag-position', this function
prepends TAGS to STR, appends TAGS to STR or omits TAGS from STR."
(pcase org-roam-file-completion-tag-position
('prepend (concat
(when tags (propertize (format "(%s) " (s-join org-roam-tag-separator tags))
'face 'org-roam-tag))
str))
('append (concat
str
(when tags (propertize (format " (%s)" (s-join org-roam-tag-separator tags))
'face 'org-roam-tag))))
('omit str)))
(defun org-roam--get-title-path-completions ()
"Return an alist for completion.
The car is the displayed title for completion, and the cdr is the
to the file."
The car is the displayed title for completion, and the cdr is a
plist containing the path and title for the file."
(let* ((rows (org-roam-db-query [:select [files:file titles:title tags:tags files:meta] :from titles
:left :join tags
:on (= titles:file tags:file)
@ -863,7 +892,7 @@ to the file."
rows))
(dolist (row rows completions)
(pcase-let ((`(,file-path ,title ,tags) row))
(let ((k (org-roam--prepend-tag-string title tags))
(let ((k (org-roam--add-tag-string title tags))
(v (list :path file-path :title title)))
(push (cons k v) completions))))))
@ -872,37 +901,33 @@ to the file."
The path to the index can be defined in `org-roam-index-file'.
Otherwise, it is assumed to be a note in `org-roam-directory'
whose title is 'Index'."
(let* ((index org-roam-index-file)
(path (pcase index
((pred functionp) (funcall index))
((pred stringp) index)
('nil (user-error "You need to set `org-roam-index-file' before you can jump to it"))
(wrong-type (signal 'wrong-type-argument
`((functionp stringp)
,wrong-type))))))
(if (f-relative-p index)
(concat (expand-file-name org-roam-directory) path)
index)))
(let ((path (pcase org-roam-index-file
((pred functionp) (funcall org-roam-index-file))
((pred stringp) org-roam-index-file)
('nil (user-error "You need to set `org-roam-index-file' before you can jump to it"))
(wrong-type (signal 'wrong-type-argument
`((functionp stringp)
,wrong-type))))))
(expand-file-name path org-roam-directory)))
;;;; dealing with file-wide properties
(defun org-roam--set-global-prop (name value)
"Set a file property called NAME to VALUE.
If the property is already set, it's value is replaced."
(save-excursion
(widen)
(goto-char (point-min))
(if (re-search-forward (concat "^#\\+" name ": \\(.*\\)") (point-max) t)
(replace-match (concat "#+" name ": " value) 'fixedcase)
(while (and (not (eobp))
(looking-at "^[#:]"))
(if (save-excursion (end-of-line) (eobp))
(progn
(end-of-line)
(insert "\n"))
(forward-line)
(beginning-of-line)))
(insert "#+" name ": " value "\n"))))
(org-with-point-at 1
(let ((case-fold-search t))
(if (re-search-forward (concat "^#\\+" name ":\\(.*\\)") (point-max) t)
(replace-match (concat " " value) 'fixedcase nil nil 1)
(while (and (not (eobp))
(looking-at "^[#:]"))
(if (save-excursion (end-of-line) (eobp))
(progn
(end-of-line)
(insert "\n"))
(forward-line)
(beginning-of-line)))
(insert "#+" name ": " value "\n")))))
;;;; org-roam-find-ref
(defun org-roam--get-ref-path-completions (&optional arg filter)
@ -950,8 +975,8 @@ FILTER can either be a string or a function:
(concat
(when org-roam-include-type-in-ref-path-completions
(format "{%s} " type))
(org-roam--prepend-tag-string (format "%s (%s)" title ref)
tags))
(org-roam--add-tag-string (format "%s (%s)" title ref)
tags))
ref))
(v (list :path file-path :type type :ref ref)))
(push (cons k v) completions)))))))
@ -976,17 +1001,6 @@ Return nil if the file does not exist."
(org-roam--org-roam-file-p (buffer-file-name it)))
(buffer-list)))
(defun org-roam--file-path-from-id (id)
"Return path for Org-roam file with ID."
(let* ((ext (or (car org-roam-file-extensions)
"org"))
(file (concat id "." ext)))
(expand-file-name
(if org-roam-encrypt-files
(concat file ".gpg")
file)
org-roam-directory)))
;;; org-roam-backlinks-mode
(define-minor-mode org-roam-backlinks-mode
"Minor mode for the `org-roam-buffer'.
@ -1083,12 +1097,8 @@ When STRICT is non-nil, only consider Org-roams database.
When KEEP-BUFFER-P is non-nil, keep the buffers navigated by Org-roam open."
(let ((file (org-roam-id-get-file id strict)))
(when file
(let ((existing-buf (find-buffer-visiting file))
(res (org-id-find-id-in-file id file markerp)))
(when (and (not keep-buffer-p)
(not existing-buf))
(kill-buffer (find-buffer-visiting file)))
res))))
(org-roam-with-file file keep-buffer-p
(org-id-find-id-in-file id file markerp)))))
(defun org-roam-id-open (id-or-marker &optional strict)
"Go to the entry with ID-OR-MARKER.
@ -1386,10 +1396,8 @@ To be added to `org-roam-title-change-hook'."
:where (= dest $s1)]
current-path)))
(dolist (file files-affected)
(with-current-buffer (or (find-buffer-visiting (car file))
(find-file-noselect (car file)))
(org-roam--replace-link current-path current-path old-title new-title)
(save-buffer)))))
(org-roam-with-file (car file) nil
(org-roam--replace-link current-path current-path old-title new-title)))))
(defun org-roam--update-file-name-on-title-change (old-title new-title)
"Update the file name on title change.
@ -1406,7 +1414,7 @@ To be added to `org-roam-title-change-hook'."
(when (string-match-p old-slug file-name)
(let* ((new-slug (funcall org-roam-title-to-slug-function new-title))
(new-file-name (replace-regexp-in-string old-slug new-slug file-name)))
(unless (string-match-p file-name new-file-name)
(unless (string-equal file-name new-file-name)
(rename-file file-name new-file-name)
(set-visited-file-name new-file-name t t)
(org-roam-db-update)
@ -1417,46 +1425,47 @@ To be added to `org-roam-title-change-hook'."
When NEW-FILE-OR-DIR is a directory, we use it to compute the new file path."
(let ((new-file (if (directory-name-p new-file-or-dir)
(expand-file-name (file-name-nondirectory old-file) new-file-or-dir)
new-file-or-dir)))
new-file-or-dir))
files-affected)
(setq new-file (expand-file-name new-file))
(setq old-file (expand-file-name old-file))
(when (and (not (auto-save-file-name-p old-file))
(not (auto-save-file-name-p new-file))
(not (backup-file-name-p old-file))
(not (backup-file-name-p new-file))
(org-roam--org-roam-file-p old-file))
(org-roam-db--ensure-built)
(let* ((new-buffer (or (find-buffer-visiting new-file)
(find-file-noselect new-file)))
(files-affected (org-roam-db-query [:select :distinct [source]
:from links
:where (= dest $s1)]
old-file)))
;; Remove database entries for old-file.org
(org-roam-db--clear-file old-file)
;; Replace links from old-file.org -> new-file.org in all Org-roam files with these links
(mapc (lambda (file)
(setq file (if (string-equal (car file) old-file)
new-file
(car file)))
(with-current-buffer (or (find-buffer-visiting file)
(find-file-noselect file))
(org-roam--replace-link old-file new-file)
(save-buffer)
(org-roam-db--update-file)))
files-affected)
;; If the new path is in a different directory, relative links
;; will break. Fix all file-relative links:
(unless (string= (file-name-directory old-file)
(file-name-directory new-file))
(with-current-buffer new-buffer
(org-roam--fix-relative-links old-file)
(save-buffer)))
(when (org-roam--org-roam-file-p new-file)
(org-roam-db--update-file new-file))))))
(setq files-affected (org-roam-db-query [:select :distinct [source]
:from links
:where (= dest $s1)]
old-file))
;; Remove database entries for old-file.org
(org-roam-db--clear-file old-file)
;; If the new path is in a different directory, relative links
;; will break. Fix all file-relative links:
(unless (string= (file-name-directory old-file)
(file-name-directory new-file))
(org-roam-with-file new-file nil
(org-roam--fix-relative-links old-file)))
(when (org-roam--org-roam-file-p new-file)
(org-roam-db--update-file new-file))
;; Replace links from old-file.org -> new-file.org in all Org-roam files with these links
(mapc (lambda (file)
(setq file (if (string-equal (car file) old-file)
new-file
(car file)))
(org-roam-with-file file nil
(org-roam--replace-link old-file new-file)
(save-buffer)
(org-roam-db--update-file)))
files-affected))))
(defun org-roam--id-new-advice (&rest _args)
"Update the database if a new Org ID is created."
(when (and org-roam-enable-headline-linking
(org-roam--org-roam-file-p))
(org-roam--org-roam-file-p)
(not (eq org-roam-db-update-method 'immediate))
(not (org-roam-capture-p)))
(org-roam-db-update)))
;;;###autoload
@ -1492,8 +1501,8 @@ M-x info for more information at Org-roam > Installation > Post-Installation Tas
(add-hook 'find-file-hook #'org-roam--find-file-hook-function)
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
(add-hook 'org-open-at-point-functions #'org-roam-open-id-at-point)
(if (and org-roam-db-file-update-timer
(eq org-roam-db-update-method 'idle-timer))
(when (and (not org-roam-db-file-update-timer)
(eq org-roam-db-update-method 'idle-timer))
(setq org-roam-db-file-update-timer (run-with-idle-timer org-roam-db-update-idle-seconds t #'org-roam-db-update-cache-on-timer)))
(advice-add 'rename-file :after #'org-roam--rename-file-advice)
(advice-add 'delete-file :before #'org-roam--delete-file-advice)
@ -1501,6 +1510,11 @@ M-x info for more information at Org-roam > Installation > Post-Installation Tas
(when (fboundp 'org-link-set-parameters)
(org-link-set-parameters "file" :face 'org-roam--file-link-face)
(org-link-set-parameters "id" :face 'org-roam--id-link-face))
(dolist (buf (org-roam--get-roam-buffers))
(with-current-buffer buf
(add-hook 'post-command-hook #'org-roam-buffer--update-maybe nil t)
(add-hook 'before-save-hook #'org-roam-link--replace-link-on-save nil t)
(add-hook 'after-save-hook #'org-roam-db-update nil t)))
(org-roam-db-build-cache))
(t
(setq org-execute-file-search-functions (delete 'org-roam--execute-file-row-col org-execute-file-search-functions))
@ -1628,7 +1642,7 @@ If DESCRIPTION is provided, use this as the link label. See
(_ (when (region-active-p)
(setq beg (set-marker (make-marker) (region-beginning)))
(setq end (set-marker (make-marker) (region-end)))
(setq region-text (buffer-substring-no-properties beg end))))
(setq region-text (org-link-display-format (buffer-substring-no-properties beg end)))))
(completions (--> (or completions
(org-roam--get-title-path-completions))
(if filter-fn
@ -1715,7 +1729,7 @@ Return added alias."
(when (string-empty-p alias)
(user-error "Alias can't be empty"))
(org-roam--set-global-prop
"ROAM_ALIAS"
"roam_alias"
(combine-and-quote-strings
(seq-uniq (cons alias
(org-roam--extract-titles-alias)))))
@ -1730,7 +1744,7 @@ Return added alias."
(if-let ((aliases (org-roam--extract-titles-alias)))
(let ((alias (completing-read "Alias: " aliases nil 'require-match)))
(org-roam--set-global-prop
"ROAM_ALIAS"
"roam_alias"
(combine-and-quote-strings (delete alias aliases)))
(org-roam-db--update-file (buffer-file-name (buffer-base-buffer))))
(user-error "No aliases to delete")))
@ -1748,7 +1762,7 @@ Return added tag."
(when (string-empty-p tag)
(user-error "Tag can't be empty"))
(org-roam--set-global-prop
"ROAM_TAGS"
"roam_tags"
(combine-and-quote-strings (seq-uniq (cons tag existing-tags))))
(org-roam-db--insert-tags 'update)
tag))
@ -1761,7 +1775,7 @@ Return added tag."
(tags (org-roam--extract-tags-prop file)))
(let ((tag (completing-read "Tag: " tags nil 'require-match)))
(org-roam--set-global-prop
"ROAM_TAGS"
"roam_tags"
(combine-and-quote-strings (delete tag tags)))
(org-roam-db--insert-tags 'update))
(user-error "No tag to delete")))
@ -1772,7 +1786,7 @@ Return added tag."
(interactive)
(let* ((roam-buffers (org-roam--get-roam-buffers))
(names-and-buffers (mapcar (lambda (buffer)
(cons (or (org-roam--get-title-or-slug
(cons (or (org-roam-db--get-title
(buffer-file-name buffer))
(buffer-name buffer))
buffer))
@ -1855,7 +1869,8 @@ the executable 'rg' in variable `exec-path'."
(let ((rowcol (concat row ":" col)))
(insert "- "
(org-link-make-string (concat "file:" file "::" rowcol)
(format "[%s] %s" rowcol (org-roam--get-title-or-slug file))))
(format "[%s] %s" rowcol (or (org-roam-db--get-title file)
file))))
(when (executable-find "sed") ; insert line contents when sed is available
(insert " :: "
(shell-command-to-string
@ -1871,7 +1886,6 @@ the executable 'rg' in variable `exec-path'."
(highlight-phrase (downcase title) 'bold-italic))
(goto-char (point-min))))))
;;;###autoload
(defun org-roam-version (&optional message)
"Return `org-roam' version.

View File

@ -52,25 +52,6 @@
(delete-file org-roam-db-location)
(org-roam-db--close))
(describe "org-roam--str-to-list"
(it "nil"
(expect (org-roam--str-to-list nil)
:to-be
nil))
(it "\"multi word\" prop 123"
(expect (org-roam--str-to-list "\"multi word\" prop 123")
:to-equal
'("multi word" "prop" "123")))
(it "prop \"multi word\" 123"
(expect (org-roam--str-to-list "\"multi word\" prop 123")
:to-equal
'("multi word" "prop" "123")))
(it "errors on bad input"
(expect (org-roam--str-to-list 1)
:to-throw)
(expect (org-roam--str-to-list "\"hello")
:to-throw)))
(describe "Ref extraction"
(before-all
(test-org-roam--init))