mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
9065f6a999 | |||
997ddcbf4b | |||
2d58651699 | |||
8ad57b1218 | |||
0b964ca428 | |||
643b98eeb3 | |||
b0fd12647b | |||
fde40dc1c4 | |||
96b0a52273 | |||
aa52b65a4a | |||
2a1c73c0a3 | |||
16c7a7bd93 | |||
1b3a0abd36 | |||
06e5814898 | |||
cc2572e48b | |||
05deb64d85 | |||
f10fbad386 | |||
05a9bc44f2 | |||
3fb4e21adf | |||
62bba9755c | |||
78a371cdc4 | |||
15d864a500 | |||
65c0f0dc8c | |||
48e195dd82 | |||
777f6d23ec | |||
8f1cf7b449 | |||
3ce6e299d4 | |||
ecf515f650 | |||
43831c5819 | |||
4d63f99fe8 | |||
57cfb3dbb7 | |||
9c23218553 | |||
7ad32e8395 | |||
d87dd011aa | |||
f2976fa3be | |||
8aa793b021 | |||
be95b42d32 | |||
cff1168ac1 | |||
06d0db736a | |||
fb0662efe7 | |||
9ca5461a2f | |||
33805c3ff5 | |||
3a4ff76508 | |||
bf41352c1c | |||
1b598a4618 | |||
ab34dd138d | |||
b17cc3b1e3 | |||
f9b1e53894 | |||
dbed2bcf5d | |||
060a29c91d | |||
8efec080e0 | |||
61e01430e0 | |||
d70198bba9 | |||
face683e00 | |||
baf0dd9d00 | |||
a9fd6c0fc7 | |||
6502874576 | |||
8401784cd2 | |||
6dc316c450 | |||
48ef3fee11 | |||
16c520068b | |||
b1608bf869 |
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@ -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
|
||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@ -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
113
README.md
@ -1,47 +1,35 @@
|
||||
[![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
[](https://img.shields.io/github/v/release/org-roam/org-roam)
|
||||
[](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:
|
||||
|
||||
[](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.
|
||||
|
||||

|
||||
- **[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
|
||||
|
@ -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);
|
||||
}
|
||||
|
349
doc/org-roam.org
349
doc/org-roam.org
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
119
org-roam-db.el
119
org-roam-db.el
@ -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))
|
||||
(_
|
||||
|
@ -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)
|
||||
|
144
org-roam-link.el
144
org-roam-link.el
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
380
org-roam.el
380
org-roam.el
@ -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-roam’s 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.
|
||||
|
@ -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))
|
||||
|
Reference in New Issue
Block a user