Compare commits

...

103 Commits

Author SHA1 Message Date
1433dbc316 (release): v1.0.0 (#341) 2020-03-23 15:34:21 +08:00
def3d27d25 (internal): small refactor (#340) 2020-03-22 12:12:40 +08:00
6fae1d8100 (chore): update internal Github settings (#339)
Removes sponsor button, and disables 26.3 in testing matrix
2020-03-22 11:37:23 +08:00
07672213b6 (chore): fix bytecompile errors and checkdoc errors (#338) 2020-03-22 03:16:38 +08:00
5406827451 (internal): use org-link-make-string (#337)
This takes care of properly escaping backslashes in file names.
2020-03-22 02:46:36 +08:00
214a8f771f (internal): reduce org-roam capture template key pollution (#336)
Following #332, nest all org-roam related customization in to a single
key.
2020-03-22 00:13:19 +08:00
57f5e73192 (internal): force a reconnect on org-roam-build-cache (#335)
This is to guard against scenarios where a db connection is still live, although it
may already be invalid (like in #331)
2020-03-21 22:30:30 +08:00
1352809451 ensure "%?" is present in the template string (#330)
* ensure "%?" is present in the template string

- addresses #298
- fixes `org-capture-place-plain-text` throwing 'invalid search bound'
  when both :unnarowed t and "%?" is missing from the template string

* ensure "%?" is present in the template string

- if "%?" is missing, add it at the end of the template
2020-03-21 20:56:20 +08:00
N V
8b37135aa2 (internal): refactor to remove hooks in a loop (#334)
Removing hooks in a loop should be easier to extend/maintain.
More "DRY" than repeating calls to `remove-hook'.

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-03-21 12:43:11 +08:00
N V
159e11c842 (internal): update cl.el functions to cl-lib versions (#333)
cl is a deprecated library. cl-lib replaces it.
2020-03-21 12:40:13 +08:00
63b2eaaf86 (bugfix): throw an error on nested org-roam capture processes (#329) 2020-03-20 20:17:56 +08:00
919d08732c (bugfix): fix nested org-roam-inserts not working (#328) 2020-03-20 00:57:32 +08:00
97dfc4b980 (bugfix): fix `org-roam--completing-read' for ido (#324)
For `ido-completing-read', `choices' must be a list of strings
2020-03-19 16:23:04 +08:00
4556f727ff (bugfix): fix org-roam-capture not saving file, and org-roam date functions (#321)
* (bugfix): fix org-roam-captures not saving file

* (bugfix): fix org-roam date functions
2020-03-18 01:11:08 +08:00
6775f15ad1 (bugfix): prevent file open if capture is cancelled (#316) 2020-03-17 17:20:55 +08:00
8f8ccf6797 (feat): clean-ups for org-roam-capture system (#315)
This introduces 2 user-facing changes:

1. If a capture process is aborted, the file is not created if it
doesn't yet exist

2. If a capture process is aborted, org-roam-insert will not insert a
link
2020-03-17 17:06:49 +08:00
81dd880357 (bugfix): fix ugly node labels in org-roam graph (#314) 2020-03-17 13:00:45 +08:00
c14ac7b613 (bugfix): fix Helm completing-read containing extra spaces on non-match (#309) 2020-03-16 17:42:57 +08:00
08667d9c7d (bugfix): Fix Helm integration for completing-read (#306)
Fixes #304, using PR #305 as the base, thanks @khinsen
2020-03-16 13:00:12 +08:00
f544bd9ca1 hexify file url for org-protocol link (#299)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-03-15 21:55:54 +08:00
66aff57cdb (bugfix): capture: prepend header template only on new file (#302) 2020-03-15 21:50:25 +08:00
f238e3fe02 (feat): order backlinks by filename (#300)
Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-03-15 15:26:24 +08:00
98fc273a0f (internal): less abusive use of org-capture (#294)
`org-roam--capture-new-file` does not write anything to a file, all the templating is delegated to vanilla `org-capture`
2020-03-15 13:41:36 +08:00
a4df95b518 Create FUNDING.yml 2020-03-14 17:52:51 +08:00
6f1154c90e (bugfix): remove nonexistent/redundant code (#297) 2020-03-14 14:55:53 +08:00
486ba9c5a8 (feat): upgrade org-roam-graph-exclude-matcher to accept a list (#296)
Closes #295
2020-03-14 01:17:32 +08:00
d28a83c992 (bugfix): fix unintuitive behaviour of capture insertion (#293)
Ref #285
2020-03-13 18:13:04 +08:00
62ed117eb6 (feat): Support multiple org-roam-directories for company backend (#292)
Fixes #276
2020-03-13 14:03:53 +08:00
a44b847596 (docs): add documentation for using neato (#291)
Some prefer using neato for generating a graph (ref #263). This
documents how to do so.
2020-03-13 13:37:55 +08:00
e64890c80e (feat): add org-roam-date-title-format and org-roam-date-filename-format (#290)
This PR supercedes #280. It allows users who use the date
functionality in Org-roam to customize the filename and title formats
of the created files.
2020-03-13 13:29:49 +08:00
4eaf69465e (feat): add 'ido and 'ivy support for completing-read (#289)
Also adds org-roam-fuzzy-match, which for ivy and helm controls
whether completion candidates are fuzzy matched.
2020-03-13 13:11:22 +08:00
0950ae3cc6 (feature): add support for Helm's completion system (#284)
adds org-roam-completion-system, supporting helm as the default completion option.
2020-03-12 19:34:27 +08:00
0cab668d9e (bugfix): propagate org-roam-directory to temp buffers (#275) 2020-03-11 15:41:18 +08:00
6095d01ef4 (feat): add a company-mode for link completion (#257)
Adds company-org-roam, a company backend which autocompletes text into org file links
2020-03-11 14:32:58 +08:00
bd425e4427 (docs):fix typo in platypus installation (#274) 2020-03-11 11:37:01 +08:00
65ead3c9ed (chore): fix more checkdoc errors (#270) 2020-03-11 00:55:42 +08:00
be1d1f1d7b (feat): add org-roam-graphviz-extra-options (#269) 2020-03-11 00:44:07 +08:00
f5d5b83b49 (bugfix): fix missed org-roam-link-title-format uses (#268)
In #261 we allowed org-roam-link-title-format to be a function, but missed some of its uses during the update. h.t. @alanz
2020-03-11 00:34:08 +08:00
8a3945945b (bugfix): emove duplicate line from `org-roam--db-clear' (#267) 2020-03-10 15:35:34 +08:00
7f09c76baf (fix): allow org-roam-insert in Org-roam file indirect buffers (#266) 2020-03-10 10:57:49 +08:00
f6e75f995a (chore): cleanup buffer-file-name calls (#265)
buffer-file-name uses the current-buffer if the argument is not passed
2020-03-10 10:41:43 +08:00
6ba9ffe9c8 (fix): Error if org-roam-insert is not called from Org-roam file (#264) 2020-03-10 10:36:36 +08:00
5e0b7440a3 (feat): Allow function for org-roam-link-title-format (#261)
Addresses #255
2020-03-09 22:18:04 +08:00
d16d001b9e (feat): add optional initial-prompt to org-roam-find-file (#259) 2020-03-09 16:43:48 +08:00
f67e0b025a (docs): fix typo in vanilla installation (#254) 2020-03-08 20:49:17 +08:00
b836f9fc35 (bugfix): Don't query about SQL proccess on Emacs exit (#253)
Graceful shutdown is already guaranteed via `kill-emacs-hook`.

Refs #200

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-03-08 19:57:48 +08:00
e138056115 (docs): fix documentation for manual installation (#252) 2020-03-08 17:43:07 +08:00
63e0558d96 (chore): Cleanups for MELPA release (#251)
* (chore): require minimum Org version 9.3

* fix bytecompile errors for org-roam.el

* fix checkdoc errors

* fix more things
2020-03-08 15:58:13 +08:00
3fccaef967 (chore): change main branch to master (#250) 2020-03-08 15:05:57 +08:00
6eb7238daf (feature): add org-roam-backlink face (#247) 2020-03-08 14:54:56 +08:00
fc79901682 (chore): drop org-roam-filename-noconfirm (#248)
With org-capture integration, it does not seem to be used anymore.
2020-03-08 11:15:41 +08:00
46afa483a9 (docs): add documentation for exporting backlinks in Org file (#244)
h.t. @jdiez17
2020-03-07 23:49:43 +08:00
f63f29ac05 (docs): Add link to slack invite (#242) 2020-03-07 15:05:16 +08:00
e357693297 (release): v1.1.0-rc1 (#238) 2020-03-06 20:39:28 +08:00
b7afb02015 (chore): Cleanup for MELPA release (#236) 2020-03-06 20:15:33 +08:00
4c6e4df474 (fix): fix org-roam-today date functionality overriding old files (#235) 2020-03-06 19:35:22 +08:00
b1e2209dfc (feature): org-roam-graph-exclude-matcher: exclude nodes from graph (#233) 2020-03-06 12:48:30 +08:00
8f83ffc2a3 org-roam-show-graph: PREFIX to generate a graph but not display it (#232)
This lets the viewer application watch the file, or have the user manually refresh the view rather than opening a new browser tab.
2020-03-06 11:22:39 +08:00
09a23c377b (fix): remove nonspacing marks from slugs (#230)
Linux and macOS use different Unicode normalization conventions in filenames
2020-03-06 01:47:22 +08:00
d1a47e090e (docs): update docs (#229) 2020-03-06 00:19:11 +08:00
7d1dd831db (fix): relative links should work from org-roam-buffer with multidir (#228) 2020-03-05 13:53:53 +08:00
c77c1b7316 (docs): add more org-protocol instructions for MacOS (#227) 2020-03-05 12:53:57 +08:00
55d7edd5ee (docs): fix org-roam-protocol docs formatting (#226) 2020-03-05 12:07:15 +08:00
1f02b0c5dd (docs): update org-protocol installation for MacOS (#225) 2020-03-05 11:51:28 +08:00
d96ae119ab (bugfix): remove interactive from org-roam-capture (#224) 2020-03-05 11:27:05 +08:00
b7a7741bb0 (feature): use org-capture templates (#216)
Instead of implementing our own templating system, we abuse org-capture's templating system. We add 2 additional properties:

- :head: a starting template that goes at the beginning of the file.
- :file-name: a string that expands to the file name

The templates are customizable at `org-roam-capture-templates` and `org-roam-ref-capture-templates`.
2020-03-05 00:21:24 +08:00
4de88b3c4f (bugfix): use "org-roam" as graphname (#221)
The graphname is displayed as tooltip when hovering over non-node
areas in the SVG viewer.

When grahname is undefined Chrome and Firefox just displays
"%3" (ETX/End of Text).
2020-03-03 21:28:08 +08:00
b74cc14377 (feature): add org-roam-yesterday (#215)
Closes #214
2020-03-02 02:53:53 +08:00
4c3d5b90a7 (docs): add Doom Emacs installation instructions (#212) 2020-03-01 23:56:05 +08:00
f98e7d22a5 (chore): cleanup and re-order code (#213) 2020-03-01 15:36:39 +08:00
a03ad54460 (bugfix): fix org-roam-delete-file-advice triggering on non-org-roam files (#211)
Adding a predicate for the delete-file advice ensures that expensive
SQL operations do not run when not necessary
2020-03-01 02:42:55 +08:00
a88076a704 (fix): build org-roam cache on org-roam-mode (#210)
`org-roam-build-cache` takes very little time, when the cache is
already built running it on `org-roam-mode` ensures that the cache is
consistent with the files.
2020-03-01 02:35:01 +08:00
9296470d17 (bugfix): populate database after org-roam--make-new-file (#208) 2020-03-01 01:58:54 +08:00
4da30a7134 (bugfix): escape strings for graph export (#207)
strings with quotes used to break the graph export. This change escapes the strings.
2020-03-01 01:08:54 +08:00
150ae65564 (feature): deprecate roam-protocol, extend org-protocol instead (#203)
Add 2 custom handlers:

1. roam-file?file=path: this simply opens the file at path in Emacs.
2. roam-ref?ref=ref&template=roam-template&title=title&...: attempts to open a roam note with a given ROAM_KEY. If the note doesn't exist, create one. Else, open it.
2020-02-29 22:09:04 +08:00
0c2aaad3df (feature): use sqlite as backing database (#200)
All org-roam related information will now be stored in the database. Henceforth, the cache needs to be built synchronously once (via `M-x org-roam-build-cache`), which is then incrementally updated.
2020-02-29 15:56:08 +08:00
d086d1675d (fix): change locals to hash tables (#196)
better support emacs 25
2020-02-27 10:34:54 +08:00
685aa2afcd Ensure cache consistency for refs cache (#194) 2020-02-26 23:04:02 +08:00
92d25b287e (feature): add a cache for ROAM_KEY (#192) 2020-02-26 22:26:02 +08:00
962ef23cce (fix): Require a minimum version of Org 9.2 for roam link styling (#190) 2020-02-26 16:29:20 +08:00
5e76c67cf6 (feature): emacs-lisp handling for roam:// links (#188)
We emulate org-protocol, and advise server-find-files, stripping the
roam protocol from the filename. This reduces the setup required to
open `roam://` links.
2020-02-26 15:35:20 +08:00
b382b1f21a (docs): add documentation for multiple org-roam directories (#187) 2020-02-26 15:23:29 +08:00
f1fb9f4680 (docs): remove :after from package install (#186)
Some of org-roam's functions (e.g. org-roam-today and
org-roam-find-file) should not need to have org loaded before use.
2020-02-26 11:00:24 +08:00
5b96cf806f (docs): Add documentation for #+ROAM_ALIAS attribute (#185) 2020-02-26 01:33:49 +08:00
a8d696e6e8 (performance): avoid path expansion by referencing cache obj (#184)
Prevent needless repeated calls to org-roam-directory-normalized by having a
reference to the cache object that matches the local buffer.

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
2020-02-26 00:15:38 +08:00
19f16e9c64 (feature): support file aliases (#182)
Closes #91
2020-02-26 00:11:38 +08:00
4b38b07c41 (feature): fix org-roam-open-at-point (#183) 2020-02-25 23:09:37 +08:00
b4d89c6a0c (feature): allow multiple org-roam directories (#178)
Each org-roam-directory gets its own cache. One can override the `org-roam-directory` variable locally via dir-locals:

((org-mode . ((eval . (setq-local org-roam-directory (expand-file-name "./"))))))
2020-02-25 23:06:40 +08:00
d780b6ffd1 (chore): require minimum org version of 9.0 (#177) 2020-02-24 18:58:03 +08:00
5f6ff4282c (docs): fix broken markdown src block (#176) 2020-02-24 17:08:29 +08:00
a0559a2709 (docs): add note about removing title timestamps in configuration.md (#175) 2020-02-24 14:55:07 +08:00
de1ac9d5cc Fix typo in tour.md (#172) 2020-02-24 10:28:16 +08:00
b2dc9b33f6 (bugfix): fix new file template default case (#169)
ref #165

Without this change I am getting the error:

    org-roam--make-new-file: Symbol’s function definition is void: file-name-fn
2020-02-23 22:54:59 +08:00
f9a903f52d add changelog entry for #165 (#168) 2020-02-23 17:16:57 +08:00
c54c206694 (feature) add templating functionality via org-roam-template (#165)
This allows users to customize the filename, and the content of the template.
2020-02-23 17:11:49 +08:00
d2843b816f (bugfix): keep unicode alphanumerics in filename (#164)
Ref #158

Example output:
    (mapcar #'org-roam--title-to-slug
            '("!!!!Org-Roam is great!!!!"
              "Sobre a formação dos planetas"
              "1\\2.3///**"
              "10-2+3=45"))
    ;; ("org_roam_is_great" "sobre_a_formação_dos_planetas" "1_2_3" "10_2_3_45")
2020-02-23 00:53:16 +08:00
c2c25f7d83 (bugfix): Use correct padding for modeline lighter (#163) 2020-02-22 21:40:29 +08:00
cba79b941a (feature): add custom font styling for org-roam links (#162)
* (feature): add custom font styling for org-roam links

This adds 'org-roam-link-face. When org-roam-mode is on, all roam
links take this face. By default, it is exactly the same as 'org-link,
and would require styling.

* update docs
2020-02-22 21:11:14 +08:00
a2a858a0fe (bugfix): Check future status to prevent multiple async tasks (#160)
Prevents that the `org-roam--build-cache-async` process is blocked forever if an async
error occurs.

Refs #149, #151
2020-02-22 16:56:45 +08:00
43ff60fec7 (feature): add org-roam-mute-cache-build to mute cache build message (#159) 2020-02-22 11:29:34 +08:00
fe3f0e3b6c (docs): documentation on how to enable Chrome external app checkbox on Linux (#157)
* Enable Chrome external app checkbox on Linux

* Fix command to write file
2020-02-22 11:12:33 +08:00
d02d48e559 (bugfix): prevent multiple async build processes (#149)
Prevent multiple async cache builds.  This can happen when restoring a session
or loading multiple org-roam files before a build has completed.
2020-02-21 15:45:54 +08:00
22f596d275 (feature): update org-roam-insert behaviour on region-selection (#148)
`org-roam-insert` now uses active region to pre-populate insert completion, and as the link description

fixes #127
2020-02-21 14:44:43 +08:00
35 changed files with 2467 additions and 1091 deletions

View File

@ -38,7 +38,6 @@ jobs:
fail-fast: false
matrix:
emacs_version:
- 26.3
- snapshot
steps:
- uses: purcell/setup-emacs@master

View File

@ -1,5 +1,49 @@
# Changelog
## 1.0.0 (23-03-2020)
Org-roam is now on MELPA! We have squashed most of the bugs, and Org-roam has
been stable for the most part.
## New Features
* [#269][gh-269] Add `org-roam-graphviz-extra-options`
* [#257][gh-257] Add a company-backend `company-org-roam`
* [#284][gh-284], [#289][gh-289] Configurable `org-roam-completion-system` with options `'default`, `'ido`, `'ivy` and `'helm`
* [#289][gh-289] Add customizable `org-roam-fuzzy-match` to allow fuzzy-matching of candidates
* [#290][gh-290] Add `org-roam-date-title-format` and `org-roam-date-filename-format` for customizing Org-roam's date files
* [#296][gh-296] Allow multiple exclusion matchers in `org-roam-graph-exclude-matcher`
## Bugfixes
* [#293][gh-293] Fix capture templates not working as expected for `org-roam-find-file`
* [#275][gh-275] Fix database rebuild when `org-roam-directory` is set locally
## 1.0.0-rc1 (06-03-2020)
This is a pre-release before the push to MELPA. It contains large
internal changes, with little user-facing changes. Most notably, the
backing storage has been changed to a SQLite database, and a
templating system using `org-capture` is introduced.
### Breaking Changes
* [#200][gh-200] Move Org-roam cache into a SQLite database.
* [#203][gh-203] Roam protocol is deprecated, in favour of extending org-roam-protocol.
### New Features
* [#182][gh-182] Support file name aliases via `#+ROAM_ALIAS`.
* [#216][gh-216] Adds templating functionality by extending org-capture.
* [#232][gh-232] Adds a prefix key to `org-roam-show-graph`, to generate graph without opening it.
* [#233][gh-233] Adds `org-roam-graph-exclude-matcher`, which allows exclusion of nodes from graph.
* [#247][gh-247] Add `org-roam-backlink` face, which allows customizing backlinks appearance
* [#259][gh-259] Add optional initial-prompt to `org-roam-find-file`
### Bugfixes
* [#207][gh-207], [#221][gh-221] small bugfixes to Org-roam graph generation
* [#230][gh-230] remove nonspacing marks from filenames, to prevent cross-platform errors
### New Contributors
* [@acowley][https://github.com/acowley]
* [@teesloane][https://github.com/teesloane]
## 0.1.2 (2020-02-21)
### Breaking Changes
@ -87,6 +131,23 @@ Mostly a documentation/cleanup release.
[gh-141]: https://github.com/jethrokuan/org-roam/pull/141
[gh-142]: https://github.com/jethrokuan/org-roam/pull/142
[gh-143]: https://github.com/jethrokuan/org-roam/pull/143
[gh-182]: https://github.com/jethrokuan/org-roam/pull/182
[gh-188]: https://github.com/jethrokuan/org-roam/pull/188
[gh-200]: https://github.com/jethrokuan/org-roam/pull/200
[gh-207]: https://github.com/jethrokuan/org-roam/pull/207
[gh-216]: https://github.com/jethrokuan/org-roam/pull/216
[gh-221]: https://github.com/jethrokuan/org-roam/pull/221
[gh-230]: https://github.com/jethrokuan/org-roam/pull/230
[gh-247]: https://github.com/jethrokuan/org-roam/pull/247
[gh-257]: https://github.com/jethrokuan/org-roam/pull/257
[gh-259]: https://github.com/jethrokuan/org-roam/pull/259
[gh-269]: https://github.com/jethrokuan/org-roam/pull/269
[gh-275]: https://github.com/jethrokuan/org-roam/pull/275
[gh-284]: https://github.com/jethrokuan/org-roam/pull/284
[gh-289]: https://github.com/jethrokuan/org-roam/pull/289
[gh-290]: https://github.com/jethrokuan/org-roam/pull/290
[gh-293]: https://github.com/jethrokuan/org-roam/pull/293
[gh-296]: https://github.com/jethrokuan/org-roam/pull/296
# Local Variables:
# eval: (auto-fill-mode -1)

View File

@ -1,50 +1,53 @@
[![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
[![Documentation Status](https://readthedocs.org/projects/org-roam/badge/?version=latest)](https://org-roam.readthedocs.io/en/latest/?badge=latest)
[![GitHub Release](https://img.shields.io/github/v/release/jethrokuan/org-roam)](https://img.shields.io/github/v/release/jethrokuan/org-roam)
[![MELPA](https://melpa.org/packages/org-roam-badge.svg)](https://melpa.org/#/org-roam)
## Synopsis
Org-roam is a rudimentary [Roam][roamresearch] replica built around
the all-powerful [Org-mode][org].
Org-roam is a [Roam][roamresearch] replica built on top of the
all-powerful [Org-mode][org].
Like Roam, Org-roam offers a powerful and effortless non-hierarchical
note-taking approach. With Org-roam, notes flow naturally, making
note-taking fun and easy. Org-roam *enables* a note-taking workflow that
is not fluid with vanilla Org-mode (more in [this blog
post](https://blog.jethro.dev/posts/how_to_take_smart_notes_org/)).
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.
The goal of the project is to implement core features of Roam around
Org-mode, and eventually introduce features enabled by the Emacs
ecosystem.
Visit [the documentation
page](https://org-roam.readthedocs.io/en/latest/) for a tutorial and
more links.
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.
As of February 2020, it is in a very early stage of development.
Important links:
- **[Documentation][docs]**
- **[Org-roam Slack][slack]**
## A Preview
Here's a screenshot of `org-roam`. The `org-roam` buffer shows
backlinks for the active org buffer in the left window, as well as the
surrounding content in the backlink file. The backlink database is
built asynchronously in the background, and is not noticeable to the
end user. The graph is generated from the link structure, and can be
used to navigate to the respective files.
surrounding content in the backlink file. The database is built once,
and updated incrementally. The graph is generated from the link
structure, and can be used to navigate to the respective files.
![img](doc/images/org-roam-graph.gif)
## Installation
The recommended method is using use-package and straight, or a similar package manager.
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`:
```emacs-lisp
(use-package org-roam
:after org
:hook
(after-init . org-roam-mode)
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
:custom
(org-roam-directory "/path/to/org-files/")
:bind (:map org-roam-mode-map
@ -55,11 +58,11 @@ The recommended method is using use-package and straight, or a similar package m
(("C-c n i" . org-roam-insert))))
```
For more detailed installation instructions (including instructions for
Spacemacs users), please see [the installation
documentation](https://org-roam.readthedocs.io/en/develop/installation/).
For more detailed installation and configuration instructions (including for
Doom and Spacemacs users), please see [the
documentation](https://org-roam.readthedocs.io/en/master/installation/).
## Knowledge Bases using Org-Roam
## Knowledge Bases using Org-roam
- [Jethro Kuan](https://braindump.jethro.dev/)
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
@ -82,3 +85,5 @@ 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
[docs]: https://org-roam.readthedocs.io/
[slack]: https://join.slack.com/t/orgroam/shared_invite/zt-clh0g0tx-j8xg1kVxnrWdKt16gmSGPQ

145
company-org-roam.el Normal file
View File

@ -0,0 +1,145 @@
;;; company-org-roam.el --- Company backend for Org-roam
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; URL: https://github.com/jethrokuan/org-roam
;; Keywords: org-mode, roam, convenience
;; Version: 1.0.0-rc1
;; Package-Requires: ((emacs "26.1") (company "0.9.0"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; `company-org-roam' is a `company' completion backend for Org-roam.
;; To use it, add `company-org-roam' to `company-backends':
;; (require 'company-org-roam)
;; (company-org-roam-init)
;;; Code:
(require 'cl-lib)
(require 'company)
(require 'org-roam)
(defgroup company-org-roam nil
"Company completion backend for Org-roam."
:prefix "company-org-roam-"
:group 'org-roam)
(defcustom company-org-roam-cache-expire 10
"Number of seconds before the caches expire.
A value of nil means the caches never expire."
:type '(integer :tag "Seconds")
:group 'company-org-roam)
(defvar company-org-roam-last-cache-time (make-hash-table :test #'equal)
"Time of last cache.")
(defvar company-org-roam-cache (make-hash-table :test #'equal)
"In-memory cache for completions.")
(defun company-org-roam-time-seconds ()
"Return the number of seconds since the unix epoch."
(cl-destructuring-bind (high low _usec _psec) (current-time)
(+ (lsh high 16) low)))
(defun company-org-roam--post-completion (candidate)
"The post-completion action for `company-org-roam'.
It deletes the inserted CANDIDATE, and replaces it with a
relative file link."
(let* ((cache (gethash (file-truename org-roam-directory) company-org-roam-cache))
(path (gethash candidate cache))
(current-file-path (-> (or (buffer-base-buffer)
(current-buffer))
(buffer-file-name)
(file-truename)
(file-name-directory))))
(delete-region (- (point) (length candidate)) (point))
(insert (format "[[file:%s][%s]]"
(file-relative-name path current-file-path)
candidate))))
(defun company-org-roam--filter-candidates (prefix candidates)
"Filter CANDIDATES that start with PREFIX.
The string match is case-insensitive."
(-filter (lambda (candidate)
(s-starts-with-p prefix candidate t)) candidates))
(defun company-org-roam--update-cache ()
"Update the cache with new entries.
Entries with no title do not appear in the completions."
(let ((dir (file-truename org-roam-directory))
(ht (make-hash-table :test #'equal)))
(dolist (row (org-roam-sql [:select [titles file] :from titles]))
(let ((titles (car row))
(file (cadr row)))
(dolist (title titles)
(puthash title file ht))))
(puthash dir ht company-org-roam-cache)))
(defun company-org-roam--cache-get-titles ()
"Return all the titles."
(let* ((dir (file-truename org-roam-directory))
(last-cache-time (gethash dir company-org-roam-last-cache-time))
(curr-time (company-org-roam-time-seconds)))
(when (or (null last-cache-time)
(< (+ last-cache-time company-org-roam-cache-expire)
curr-time))
(puthash dir curr-time company-org-roam-last-cache-time)
(company-org-roam--update-cache))
(hash-table-keys (gethash dir company-org-roam-cache))))
(defun company-org-roam--get-candidates (prefix)
"Get the candidates for PREFIX."
(->> (company-org-roam--cache-get-titles)
(-flatten)
(company-org-roam--filter-candidates prefix)))
;;;###autoload
(defun company-org-roam (command &optional arg &rest _)
"Define a company backend for Org-roam.
COMMAND and ARG are as per the documentation of `company-backends'."
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend #'company-org-roam))
(prefix
(and
(bound-and-true-p org-roam-mode)
(or (company-grab-symbol) 'stop)))
(candidates
(company-org-roam--get-candidates arg))
(post-completion (company-org-roam--post-completion arg))))
(defun company-org-roam--init-hook ()
"Conditional enabling of the `company-org-roam' backend."
(when (org-roam--org-roam-file-p (buffer-file-name (buffer-base-buffer)))
(setq-local company-backends
(cons'company-org-roam company-backends))))
;;;###autoload
(defun company-org-roam-init ()
"Injects `company-org-roam' as a completion backend."
(add-hook 'org-mode-hook #'company-org-roam--init-hook))
(provide 'company-org-roam)
;;; company-org-roam.el ends here

40
doc/anatomy.md Normal file
View File

@ -0,0 +1,40 @@
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. These functionality are not
crucial to effective use of Org-roam.
## File Aliases
Suppose you want a note to be referred to by different names (e.g.
"World War 2", "WWII"). You may specify such aliases using the
`#+ROAM_ALIAS` attribute:
```org
#+TITLE: World War 2
#+ROAM_ALIAS: "WWII" "World War II"
```
## File Refs
Refs are unique identifiers for files. Each note can only have 1 ref.
For example, a note for a website may contain a ref:
```org
#+TITLE: Google
#+ROAM_KEY: https://www.google.com/
```
These keys come in useful for when taking website notes, using the
`roam-ref` protocol (see [Roam Protocol](roam_protocol.md)).
Alternatively, add a ref for notes for a specific paper, using its
[org-ref](https://github.com/jkitchin/org-ref) citation key:
```org
#+TITLE: Neural Ordinary Differential Equations
#+ROAM_KEY: cite:chen18_neural_ordin_differ_equat
```
In future, the backlinks buffer will also show notes with that
citation key.

View File

@ -1,7 +1,7 @@
---
title: "Comparing Org-Roam With Other Packages"
metaTitle: "Comparing Org-Roam With Other Packages"
metaDescription: "Comparing Org-Roam With Other Packages"
title: "Comparing Org-roam With Other Packages"
metaTitle: "Comparing Org-roam With Other Packages"
metaDescription: "Comparing Org-roam With Other Packages"
---
# Org-brain

View File

@ -1,19 +1,13 @@
To ensure that Org-roam remains manageable, the number of
configuration options is deliberately kept small. However, we have
attempted to accommodate as many usage styles as possible.
In this section, we'll go over the main customization options
available to Org-Roam. This section is *crucial*. We need to exploit
the flexibility of Emacs, and mould our tools exactly to our liking.
The number of configuration options is deliberately kept small, to
keep the Org-roam codebase manageable. However, we attempt to
accommodate as many usage styles as possible.
All of Org-roam's customization options can be viewed via `M-x
customize-group org-roam`.
## Setting the Org-roam Directory
Perhaps the single most important variable to set is
`org-roam-directory`. Set `org-roam-directory` to the folder
containing all your Org files:
Set `org-roam-directory` to the folder containing all your Org files:
```emacs-lisp
(setq org-roam-directory "/path/to/org/")
@ -22,6 +16,24 @@ containing all your Org files:
Every Org file, at any level of nesting, within `/path/to/org/` is
considered part of the Org-roam ecosystem.
### Having More Than One Org-roam Directory
Emacs supports directory-local variables, allowing the value of
`org-roam-directory` to be different in different directories. It does
this by checking for a file named `.dir-locals.el`.
To add support for multiple directories, override the
`org-roam-directory` variable using directory-local variables. This is
what `.dir-locals.el` may contain:
```emacs-lisp
((nil . ((org-roam-directory . "/path/to/here/"))))
```
All files within that directory will be treated as their own separate
set of Org-roam files. Remember to run `org-roam-build-cache` from a
file within that directory, at least once.
## Org-roam Buffer
The Org-roam buffer defaults to popping up from the right. You may
@ -40,65 +52,41 @@ frame width. For example:
```
Will result in the Org-roam buffer taking up 40% of the screen width.
I have found this to be a good number.
You can change backlinks appearance in the buffer by customizing
`org-roam-backlink` face (`M-x customize-face org-roam-backlink`).
## Org-roam Links
By default, links are inserted with the title as the link description.
This can make them hard to distinguish from external links. If you
wish, you may choose add special indicators for Org-roam links by
tweaking `org-roam-link-title-format`, for example:
This can make them hard to distinguish from external links. You may
choose add special indicators for Org-roam links by tweaking
`org-roam-link-title-format`, for example:
```emacs-lisp
(setq org-roam-link-title-format "R:%s")
```
If your version of Org is at least `9.2`, you may also choose to
simply style the link differently, by customizing `org-roam-link` face
(`M-x customize-face org-roam-link`).
## Org-roam Files
These customization options revolve around the Org files created and
managed by Org-roam.
### Automatically Creating Files Using Timestamp
A common hassle is ensuring that files are uniquely named within the
Org-roam directory. Org-roam's default workflow utilizes the title of
Org files in all of its main commands (`org-roam-insert`,
`org-roam-find-file`). Hence, having any unique file name is a decent
option, and the default workflow uses the timestamp as the filename.
The format of the filename is controlled by the function
`org-roam-file-name-function`, which defaults to a format like
`YYYYMMDDHHMMSS_title_here.org`. You may choose to define your own
function to change this.
If you wish to be prompted to change the file name on creation, set
`org-roam-filename-noconfirm` to `nil`:
```emacs-lisp
(setq org-roam-filename-noconfirm nil)
```
It is then the user's responsibility to ensure that the file names are
unique.
### Autopopulating Titles
The default workflow uses the title of the Org file in several
commands. The title is specified via the `#+TITLE:` attribute,
typically near the top of the file. The option
`org-roam-autopopulate-title` defaults to `t`. When true, the title
attribute is automatically inserted into the files created via
Org-roam commands. Setting it to `nil` will disable this behaviour.
Org-roam files are created and prefilled using Org-roam's templating
system. The templating system is customizable, and the system is
described in detail in the [Org-roam Template](templating.md) page.
### Encryption
Encryption (via GPG) can be enabled for all new files by setting
`org-roam-encrypt-files` to `t`. When enabled, new files are created
with the .org.gpg extension and decryption are handled automatically
by EasyPG. Note that this causes Emacs to ask for password when the
cache is built (if you have an encrypted file in `org-roam-directory`)
as well as each time a new file is created. It might be a good idea to
cache the password in order to make this more managable.
with the `.org.gpg` extension and decryption are handled automatically
by EasyPG.
Note that Emacs will prompt for a password for encrypted files during
cache updates if it requires reading the encrypted file. To reduce the
number of password prompts, you may wish to cache the password.
## Org-roam Graph Viewer
@ -107,15 +95,48 @@ Org-roam generates an SVG image using
[Graph Setup](graph_setup.md) page.
Org-roam tries its best to locate the Graphviz executable from your
PATH, but if it fails to do so, you may set it manually:
`PATH`, but if it fails to do so, you may set it manually:
```
(setq org-roam-graphviz-executable "/path/to/dot")
```
Org-roam also attempts to use Firefox (located on PATH) to view the
You may also choose to use `neato` in place of `dot`, which generates a more compact graph layout.
```
(setq org-roam-graphviz-executable "/path/to/neato")
(setq org-roam-graphviz-extra-options '(("overlap" . "false")))
```
Org-roam also attempts to use Firefox (located on `PATH`) to view the
SVG, you may choose to set it to any compatible program:
```
(setq org-roam-graph-viewer "/path/to/image-viewer")
```
### Excluding Nodes and Edges
One may want to exclude certain files to declutter the graph. You can do so by setting `org-roam-graph-exclude-matcher`.
```
(setq org-roam-graph-exclude-matcher '("private" "dailies"))
```
This setting excludes all files whose path contain "private" or "dailies".
## Org-roam Completion System
Org-roam offers completion when choosing note titles etc.
The completion system is configurable. The default setting,
```
(setq org-roam-completion-system 'default)
```
uses Emacs' standard `completing-read`. If you prefer [Helm](https://emacs-helm.github.io/helm/), use
```
(setq org-roam-completion-system 'helm)
```
Other options included `'ido`, and `'ivy'`.

View File

@ -117,6 +117,12 @@ within Org-mode.
[Org-ref][org-ref] does citation and bibliography management in
Org-mode, and a great tool for scientific notes.
### Spaced Repetition
[Org-fc][org-fc] is a spaced repetition system that scales well with a
large number of files. Other alternatives include
[org-drill][org-drill], and [pamparam][pamparam].
[deft]: https://jblevins.org/projects/deft/
[notdeft]: https://github.com/hasu/notdeft
[org-download]: https://github.com/abo-abo/org-download
@ -124,3 +130,6 @@ Org-mode, and a great tool for scientific notes.
[org-noter]: https://github.com/weirdNox/org-noter
[interleave]: https://github.com/rudolfochrist/interleave
[org-ref]: https://github.com/jkitchin/org-ref
[org-fc]: https://github.com/l3kn/org-fc/
[org-drill]: https://orgmode.org/worg/org-contrib/org-drill.html
[pamparam]: https://github.com/abo-abo/pamparam

View File

@ -1,78 +0,0 @@
The setup is similar to that of org-protocol. Here `roam://` links are
defined, and need to be associated with an application.
The gist of the setup is setting up a Bash script to trim off the
`roam://` prefix from the link, causing the desktop application to
call `emacsclient path/to/org-roam-file.org`.
## Linux
Create a desktop application. I place mine in
`~/.local/share/applications/roam.desktop`:
```
[Desktop Entry]
Name=Org-Roam Client
Exec=/home/jethro/.local/bin/launch_emacs %u
Icon=emacs-icon
Type=Application
Terminal=false
MimeType=x-scheme-handler/roam
```
Note the `Exec` key is set to a bash script poorly named
`launch_emacs`. You can set it to whatever you want.
Create the corresponding bash script, and make it executable. Here's
how it looks like:
```bash
#!/usr/bin/env bash
emacsclient "${1#*:}"
```
Finally, associate `roam://` links with the desktop application by
running in your shell:
```bash
xdg-mime default roam.desktop x-scheme-handler/roam
```
## Mac OS
One solution to this, recommended in [Issue
#115](https://github.com/jethrokuan/org-roam/issues/115), is to use
[Platypus](https://github.com/sveinbjornt/Platypus). Here are the
instructions for setting up with Platypus and Chrome:
1. Create an executable `launch-emacs.sh` script:
```sh
#!/usr/bin/env bash
/usr/local/bin/emacsclient --no-wait "${1#*:}"
```
2. Install and launch Platypus (with [Homebrew](https://brew.sh/)):
```sh
brew cask install playtpus
```
3. Playtpus settings:
- App Name: `OrgRoam`
- Script Type: `env` and `/usr/bin/env`
- Script Path: `/path/to/your/launch-emacs.sh`
- Tick Accept dropped items and click Settings
- Tick Accept dropped files
- Tick Register as URI scheme handler
- Add `roam` as a protocol
- Create the app
To disable the "confirm" prompt in Chrome, you can also make Chrome
show a checkbox to tick, so that the `OrgRoam` app will be used
without confirmation. To do this, run in a shell:
```sh
defaults write com.google.Chrome ExternalProtocolDialogShowAlwaysOpenCheckbox -bool true
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
doc/images/roam-ref.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 MiB

View File

@ -1,40 +1,61 @@
![org-roam][org-roam-intro-image]
## What is Org-Roam?
## What is Org-roam?
Org-roam is a rudimentary [Roam][roamresearch] replica built around
the all-powerful [Org-mode][org].
Org-roam is a [Roam][roamresearch] replica built around the
all-powerful [Org-mode][org].
Like Roam, Org-roam offers a powerful and effortless non-hierarchical
note-taking approach. With Org-roam, notes flow naturally, making
note-taking fun and easy. To understand more about Roam, I recommend
the following links:
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.
- [Building a second brain in
Roam](https://reddit.com/r/RoamResearch/comments/eho7de/building_a_second_brain_in_roamand_why_you_might)
- [Roam: Why I Love It and How I Use
It](https://www.nateliason.com/blog/roam)
To understand more about Roam, a collection of links are available in
[the appendix](notetaking_workflow.md).
The goal of the project is to implement core features of Roam around
Org-mode, and eventually introduce features enabled by the Emacs
ecosystem.
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.
## Why build Org-Roam?
## Why use Org-roam?
With Org-roam, you:
##### Private and Secure
1. Never have to leave Emacs.
2. Can leverage all the powerful features of Org-mode: LaTeX, tables,
and the whole to-do ecosystem (org-agenda etc.)
3. Be in full control your second brain, and access it offline. Never
share your data with anyone
Edit your personal wiki completely offline, entirely in your control.
Encrypt your notes with GPG.
##### Longevity
Unlike web solutions like Roam research, the notes are first and
foremost plain Org-mode files -- Org-roam simply builds up an
auxilliary database to give the personal wiki superpowers. Having your
notes in plain-text is crucial for the longevity of your wiki. Never
have to worry about proprietary web solutions being taken down. Edit
your plain-text notes in notepad if all other editors cease to exist.
##### Free and Open-source
Org-roam is free and open-source, which means that if you feel unhappy
with any part of Org-roam, you may choose to extend Org-roam, or open
a PR.
##### Leverage the Org-mode Ecosystem
Over the years, Emacs and Org-mode has developed into a mature system
for plain-text organization. Building upon Org-mode already puts
Org-roam light-years ahead of many other solutions.
Emacs is also a fantastic interface for editing text, and we can
inherit many of the powerful text-navigation and editing packages
available to Emacs.
There are several packages that are similar to Org-roam, see the
[Comparison](comparison.md) page for a detailed comparison.
## Project Status
As of February 2020, it is in a very early stage of development.
As of March 2020, most of the core functionality and interfaces have
stabilized, and a stable release is near.
[org-roam-intro-image]: images/org-roam-intro.png
[roamresearch]: https://www.roamresearch.com/

View File

@ -1,93 +1,121 @@
## Installation
## Basic Install and Configuration
The recommended method is using [use-package][use-package] and
[straight][straight], or a similar package manager.
Org-roam is now available on MELPA, so you can install it via the following
command:
```
M-x package-install RET org-roam RET
```
Alternatively, you may use package managers such as [straight][straight] or
[quelpa][quelpa] to install the package.
The recommended method of configuration is to use [use-package][use-package].
```emacs-lisp
(use-package org-roam
:after org
:hook
(after-init . org-roam-mode)
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
:custom
(org-roam-directory "/path/to/org-files/")
:bind (:map org-roam-mode-map
(("C-c n l" . org-roam)
("C-c n f" . org-roam-find-file)
("C-c n b" . org-roam-switch-to-buffer)
("C-c n g" . org-roam-show-graph))
:map org-mode-map
(("C-c n i" . org-roam-insert))))
```
If not using package.el, you can also clone it into your Emacs
directory and add it to your load path:
Or without `use-package`:
```
git clone https://github.com/jethrokuan/org-roam/ ~/.emacs.d/elisp/org-roam
```
```
(use-package org-roam
:after org
:load-path "elisp/"
:hook
(after-init . org-roam-mode)
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
:custom
(org-roam-directory "/path/to/org-files/")
:bind (:map org-roam-mode-map
(("C-c n l" . org-roam)
("C-c n f" . org-roam-find-file)
("C-c n g" . org-roam-show-graph))
:map org-mode-map
(("C-c n i" . org-roam-insert))))
```
Or without use-package:
```
(add-to-list 'load-path "./elisp")
```emacs-lisp
(require 'org-roam)
(define-key org-roam-mode-map (kbd "C-c n l") #'org-roam)
(define-key org-roam-mode-map (kbd "C-c n f") #'org-roam-find-file)
(define-key org-roam-mode-map (kbd "C-c n b") #'org-roam-switch-to-buffer)
(define-key org-roam-mode-map (kbd "C-c n g") #'org-roam-show-graph)
(define-key org-mode-map (kbd "C-c n i") #'org-roam-insert)
(org-roam-mode +1)
```
There are a number of important configuration options, that greatly
affect the Roam workflow. Do look through them at the
[Configuration](configuration.md) page.
The [Configuration](configuration.md) page details some of the common
configuration options available.
## Spacemacs
If you are using Spacemacs, install org-roam by creating a simple layer that
wraps Org-roam. Paste the following into a new file
`~/.emacs.d/private/org-roam/packages.el`.
```emacs-lisp
(defconst org-roam-packages
'((org-roam :location
(recipe :fetcher github :repo "jethrokuan/org-roam"))))
(defun org-roam/init-org-roam ()
(use-package org-roam
:hook
(after-init . org-roam-mode)
:custom
(org-roam-directory "/path/to/org-files/")
:init
(progn
(spacemacs/declare-prefix "ar" "org-roam")
(spacemacs/set-leader-keys
"arl" 'org-roam
"art" 'org-roam-today
"arf" 'org-roam-find-file
"arg" 'org-roam-show-graph)
(spacemacs/declare-prefix-for-mode 'org-mode "mr" "org-roam")
(spacemacs/set-leader-keys-for-major-mode 'org-mode
"rl" 'org-roam
"rt" 'org-roam-today
"rb" 'org-roam-switch-to-buffer
"rf" 'org-roam-find-file
"ri" 'org-roam-insert
"rg" 'org-roam-show-graph))))
```
Next, append `org-roam` to the `dotspacemacs-configuration-layers`
list in your `.spacemacs` configuration file. Reload (`SPC f e R`) or
restart Emacs to load `org-roam`. It's functions are available under
the prefix `SPC a r` and `, r` when visiting an org-mode buffer.
## Doom Emacs
If you are using [Doom Emacs][doom], configure packages as explained in the
[getting started][doom-getting-started] guide.
Declare Org-roam as a package in your `~/.doom.d/packages.el`:
```elisp
;; ~/.doom.d/packages.el
(package! org-roam
:recipe (:host github :repo "jethrokuan/org-roam"))
```
Subsequently, in your `~/.doom.d/config.el` file, configure Org-roam:
```elisp
;; ~/.doom.d/config.el
(use-package! org-roam
:commands (org-roam-insert org-roam-find-file org-roam)
:init
(setq org-roam-directory "/path/to/org-files/")
(map! :leader
:prefix "n"
:desc "Org-Roam-Insert" "i" #'org-roam-insert
:desc "Org-Roam-Find" "/" #'org-roam-find-file
:desc "Org-Roam-Buffer" "r" #'org-roam)
:config
(org-roam-mode +1))
```
[use-package]: https://github.com/jwiegley/use-package
[straight]: https://github.com/raxod502/straight.el
## Spacemacs
If you are using Spacemacs, you can easily install org-roam by creating a simple layer that wraps org-roam. Paste the following into a new file `/.emacs.d/private/org-roam/packages.el`.
```
(defconst org-roam-packages
'((org-roam :location
(recipe :fetcher github :repo "jethrokuan/org-roam" :branch "develop"))))
(defun org-roam/init-org-roam ()
(use-package org-roam
:after org
:hook
(after-init . org-roam-mode)
:custom
(org-roam-directory "/path/to/org-files/")
:init
(progn
(spacemacs/declare-prefix "ar" "org-roam")
(spacemacs/set-leader-keys
"arl" 'org-roam
"art" 'org-roam-today
"arf" 'org-roam-find-file
"arg" 'org-roam-show-graph)
(spacemacs/declare-prefix-for-mode 'org-mode "mr" "org-roam")
(spacemacs/set-leader-keys-for-major-mode 'org-mode
"rl" 'org-roam
"rt" 'org-roam-today
"rb" 'org-roam-switch-to-buffer
"rf" 'org-roam-find-file
"ri" 'org-roam-insert
"rg" 'org-roam-show-graph))))
```
Next, append `org-roam` to the `dotspacemacs-configuration-layers` list in your `.spacemacs` configuration file. Reload (`SPC f e R`) or restart Emacs to load `org-roam`. It's functions are available under the prefix `SPC a r` and `, r` when visiting an org-mode buffer.
[quelpa]: https://github.com/quelpa/quelpa
[doom]: https://github.com/hlissner/doom-emacs
[doom-getting-started]: https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#configuring-packages

View File

@ -8,6 +8,9 @@
- [Roam: Why I Love It and How I Use It][5]
- [Adam Keesling's Twitter Thread][6]
## Threads
- [Ask HN: How to Take Good Notes][8]
## What to Do With Your Notes
- [How to Use Roam to Outline a New Article in Under 20 Minutes][2]
@ -18,3 +21,4 @@
[5]: https://www.nateliason.com/blog/roam
[6]: https://twitter.com/adam_keesling/status/1196864424725774336?s=20
[7]: https://blog.jethro.dev/posts/how_to_take_smart_notes_org/
[8]: https://news.ycombinator.com/item?id=22473209

23
doc/org_export.md Normal file
View File

@ -0,0 +1,23 @@
To include the backlinks in your Org file export -- whether using Org's
in-built publishing or ox-hugo -- use the following snippet to add a
"Backlinks" section at the end of the page:
```emacs-lisp
(defun my/org-roam--backlinks-list (file)
(if (org-roam--org-roam-file-p file)
(--reduce-from
(concat acc (format "- [[file:%s][%s]]\n"
(file-relative-name (car it) org-roam-directory)
(org-roam--get-title-or-slug (car it))))
"" (org-roam-sql [:select [file-from] :from file-links :where (= file-to $s1)] file))
""))
(defun my/org-export-preprocessor (backend)
(let ((links (my/org-roam--backlinks-list (buffer-file-name))))
(unless (string= links "")
(save-excursion
(goto-char (point-max))
(insert (concat "\n* Backlinks\n") links)))))
(add-hook 'org-export-before-processing-hook 'my/org-export-preprocessor)
```

161
doc/roam_protocol.md Normal file
View File

@ -0,0 +1,161 @@
## What is Roam protocol?
Org-roam extending `org-protocol` with 2 protocols: the `roam-file`
and `roam-ref` protocol.
## The `roam-file` protocol
This is a simple protocol that opens the path specified by the `file`
key (e.g. `org-protocol://roam-file?file=/tmp/file.org`). This is used
in the generated graph.
## The `roam-ref` Protocol
This protocol finds or creates a new note with a given `ROAM_KEY` (see
[Anatomy](anatomy.md)):
![roam-ref](images/roam-ref.gif)
To use this, create a Firefox bookmarklet as follows:
```javascript
javascript:location.href =
'org-protocol:/roam-ref?template=r&ref='
+ encodeURIComponent(location.href)
+ '&title='
+ encodeURIComponent(document.title)
```
where `template` is the template key for a template in
`org-roam-ref-capture-templates`. More documentation on the templating
system can be found [here](templating.md).
These templates should contain a `#+ROAM_KEY: ${ref}` in it.
## Setting up Org-roam protocol
To enable org-roam's protocol extensions, you have to add the
following to your init file:
```emacs-lisp
(require 'org-roam-protocol)
```
The instructions for setting up `org-protocol` can be found
[here][org-protocol-inst], but they are reproduced below.
We will also need to create a desktop application for `emacsclient`.
The instructions for various platforms are shown below:
## Linux
Create a desktop application. I place mine in
`~/.local/share/applications/org-protocol.desktop`:
```
[Desktop Entry]
Name=Org-Protocol
Exec=emacsclient %u
Icon=emacs-icon
Type=Application
Terminal=false
MimeType=x-scheme-handler/org-protocol
```
Associate `org-protocol://` links with the desktop application by
running in your shell:
```bash
xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol
```
To disable the "confirm" prompt in Chrome, you can also make Chrome
show a checkbox to tick, so that the `Org-Protocol Client` app will be used
without confirmation. To do this, run in a shell:
```sh
sudo mkdir -p /etc/opt/chrome/policies/managed/
sudo tee /etc/opt/chrome/policies/managed/external_protocol_dialog.json >/dev/null <<'EOF'
{
"ExternalProtocolDialogShowAlwaysOpenCheckbox": true
}
EOF
sudo chmod 644 /etc/opt/chrome/policies/managed/external_protocol_dialog.json
```
and then restart Chrome (for example, by navigating to <chrome://restart>) to
make the new policy take effect.
See [here](https://www.chromium.org/administrators/linux-quick-start)
for more info on the `/etc/opt/chrome/policies/managed` directory and
[here](https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExternalProtocolDialogShowAlwaysOpenCheckbox)
for information on the `ExternalProtocolDialogShowAlwaysOpenCheckbox`
policy.
## Mac OS
One solution is to use
[Platypus](https://github.com/sveinbjornt/Platypus). Here are the
instructions for setting up with Platypus and Chrome:
1. Install and launch Platypus (with [Homebrew](https://brew.sh/)):
```sh
brew cask install platypus
```
2. Create a script `launch_emacs.sh`:
```
#!/usr/bin/env bash
/usr/local/bin/emacsclient --no-wait $1
```
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" |
```
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
without confirmation. To do this, run in a shell:
```sh
defaults write com.google.Chrome ExternalProtocolDialogShowAlwaysOpenCheckbox -bool true
```
##### Note for Emacs Mac Port
If you're using [Emacs Mac Port](https://github.com/railwaycat/homebrew-emacsmacport), it
registered its `Emacs.app` as the default handler for the URL scheme
`org-protocol`. We have to make our `OrgProtocol.app` the default
handler instead (replace `org.yourusername.OrgProtocol` with your app
identifier):
```
$ defaults write com.apple.LaunchServices/com.apple.launchservices.secure LSHandlers -array-add \
'{"LSHandlerPreferredVersions" = { "LSHandlerRoleAll" = "-"; }; LSHandlerRoleAll = "org.yourusername.OrgProtocol"; LSHandlerURLScheme = "org-protocol";}'
```
Then restart your computer.
[org-protocol-inst]: https://orgmode.org/worg/org-contrib/org-protocol.html

83
doc/templating.md Normal file
View File

@ -0,0 +1,83 @@
Rather than creating blank files on `org-roam-insert` and
`org-roam-find-file`, it is may be desirable to prefill the file with
content. This may include:
- Time of creation
- File it was created from
- Clipboard content
- Any other data you may want to input manually
This requires a complex template insertion system, but fortunately,
Org ships with a powerful one: `org-capture`. However, org-capture was
not designed for such use. Org-roam abuses `org-capture` to some
extent, extending its syntax. To first understand how org-roam's
templating system works, it may be useful to look into org-capture.
## Org-roam Templates
The org-roam capture template extends org-capture's template with 2
additional properties:
1. `:file-name`: This is the file name template used when a new note
is created.
2. `:head`: This is the template that is inserted on initial note
creation.
### Org-roam Template Expansion
Org-roam's template definitions also extend org-capture's template
syntax, to allow prefilling of strings. In many scenarios,
`org-roam-capture` is passed a mapping between variables and strings.
For example, during `org-roam-insert`, a title is prompted for. If the
title doesn't already exist, we would like to create a new file,
without prompting for the title again.
Variables passed are expanded with the `${var}` syntax. For example,
eduring `org-roam-insert`, `${title}` is prefilled for expansion. Any
variables that do not contain strings, are prompted for values using
`completing-read`.
After doing this expansion, the org-capture's template expansion
system is used to fill up the rest of the template. You may read up
more on this on [org-capture's documentation
page](https://orgmode.org/manual/Template-expansion.html#Template-expansion).
For example, take the template: `"%<%Y%m%d%H%M%S>-${title}"`, with the title
`"Foo"`. The template is first expanded into `%<%Y%m%d%H%M%S>-Foo`. Then
org-capture expands `%<%Y%m%d%H%M%S>` with timestamp: e.g.
`20200213032037-Foo`.
This templating system is used throughout org-roam templates.
### Template examples
Here I walkthrough the default template, reproduced below.
```
("d" "default" plain (function org-roam--capture-get-point)
"%?"
:file-name "%<%Y%m%d%H%M%S>-${slug}"
:head "#+TITLE: ${title}\n"
:unnarrowed t)
```
1. The template has short key `"d"`. If you have only one template,
org-roam automatically chooses this template for you.
2. The template is given a description of `"default"`.
3. `plain` text is inserted. Other options include Org headings via
`entry`.
4. `(function org-roam--capture-get-point)` should not be changed.
5. `"%?"` is the template inserted on each call to `org-roam-capture`.
This template means don't insert any content, but place the cursor
here.
6. `:file-name` is the file-name template for a new note, if it
doesn't yet exist. This creates a file at path that looks like
`/path/to/org-roam-directory/20200213032037-foo.org`.
7. `:head` contains the initial template to be inserted (once only),
at the beginning of the file. Here, the title global attribute is
inserted.
8. `:unnarrowed t` tells org-capture to show the contents for the
whole file, rather than narrowing to just the entry.
Other options you may want to learn about include `:immediate-finish`.

View File

@ -1,69 +1,65 @@
Org-roam was built to support a workflow that was not possible with
vanilla Org-mode. This flow is modelled after the [Zettelkasten
method][zettelkasten], and many of [Roam Research][roam]'s workflows.
Understanding this flow is crucial! Org-roam doesn't auto-magically
make your note-taking better -- it's changing the note-taking workflow
that does.
It is crucial to understand that Org-roam does not auto-magically make
note-taking better -- it's changing the note-taking workflow that
does.
To understand more the methods and madness, the [Note-Taking
Workflow][appendix:ntw] page contains a page of useful references.
I've also written [a post][jethro-blog-post] about how I use Org-roam.
To understand more about the methods and madness, the [Note-Taking
Workflow][appendix:ntw] page contains a page of useful references. The
author has also written [a post][jethro-blog-post] about how he uses
Org-roam.
Without further ado, let's begin!
## Activating Org-roam
## Building the Cache
Org-roam's entry point is the global minor `org-roam-mode`. This sets
up Emacs with several hooks, for keeping the org-roam cache
consistently updated, as well as showing the backlinks buffer.
Assuming you've set `org-roam-directory` appropriately, running `M-x
org-roam--build-cache-async` should build up the caches that will
allow you to begin using Org-roam. I do this on startup:
```emacs-lisp
(add-hook 'after-init-hook 'org-roam--build-cache-async)
```
The cache is a sqlite database named `org-roam.db`, which resides at
the root of the `org-roam-directory`. Activating `org-roam-mode`
builds the cache, which may take a while the first time, but is
generally instantaneous in subsequent runs. To build the cache
manually again, run `M-x org-roam-build-cache`.
## Finding a Note
`org-roam-find-file` shows you the list of notes you currently have in
Org-roam. Selecting the title will bring you to the corresponding
note. Entering a title of a note that does not yet exist will create a
new note with that title.
`org-roam-find-file` shows the list of titles for notes that reside in
`org-roam-directory`. Selecting a note title will bring you to the
corresponding note. Entering a title of a note that does not yet exist
will create a new note with that title.
![org-roam-find-file](images/org-roam-find-file.gif)
## Inserting Links
Within your Org-roam notes, you are encouraged to liberally insert
links to existing (or new) Org-roam notes with `org-roam-insert`.
Entering a non-existent title will also create a new note with that
title.
`org-roam-insert` insert links to existing (or new) notes. Entering a
non-existent title will also create a new note with that title.
![org-roam-insert](images/org-roam-insert-filetag.gif)
It is crucial for good usage of Org-roam to insert links liberally,
where you want them the notes to resurface!
Good usage of Org-roam requires liberally linking files. This allows
the build-up of a dense knowledge graph.
## The Org-roam Buffer
All of Org-roam's operations are designed such that the built cache is
a consistent view of the inter-connectivity between your notes. The
Org-roam buffer shows backlinks: i.e. the files that link to the
currently viewed file, along with some surrounding context. The
Org-roam buffer will always show the backlinks for the current
Org-roam file in view.
The Org-roam buffer is often displayed in the side window. It shows
backlinks for the currently active Org-roam note, along with some
surrounding context.
![org-roam-buffer](images/org-roam-buffer.gif)
## Exporting the Graph
It's also possible to export the links as a graph, using graphviz. The
generated graph is navigable in Emacs, but requires some additional
setup, which I describe in the [Graph Appendix][appendix:graph-setup]
page.
Org-roam also uses Graphviz to generate a graph, 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
described in the [Roam Protocol][appendix:roam-protocol] page.
![org-roam-graph](images/org-roam-graph.gif)
[zettelkasten]: https://zettelkasten.de/
[appendix:ntw]: notetaking_workflow.md
[appendix:graph-setup]: graph_setup.md
[appendix:roam-protocol]: roam_protocol.md
[roam]: https://www.roamresearch.com/
[jethro-blog-post]: https://blog.jethro.dev/posts/how_to_take_smart_notes_org/

View File

@ -1,17 +1,24 @@
site_name: "Org-Roam: Roam + Org-Mode = ♥"
site_name: "Org-roam"
repo_url: https://github.com/jethrokuan/org-roam/
edit_uri: edit/master/doc/
copyright: "Copyright (C) 2020 Jethro Kuan and contributors"
docs_dir: doc
extra:
social:
- type: 'slack'
link: 'https://join.slack.com/t/orgroam/shared_invite/zt-clh0g0tx-j8xg1kVxnrWdKt16gmSGPQ'
nav:
- Home: index.md
- A Tour of Org-Roam: tour.md
- A Tour of Org-roam: tour.md
- Installation: installation.md
- Configuration: configuration.md
- Anatomy of an Org-roam file: anatomy.md
- The Templating System: templating.md
- Ecosystem: ecosystem.md
- Similar Packages: comparison.md
- "Appendix: Note-taking Workflow": notetaking_workflow.md
- "Appendix: Graph Setup": graph_setup.md
- "Appendix: Roam Protocol": roam_protocol.md
- "Appendix: Org Export": org_export.md
markdown_extensions:
- admonition
- pymdownx.betterem:

103
org-roam-protocol.el Normal file
View File

@ -0,0 +1,103 @@
;;; org-roam-protocol.el --- Protocol handler for roam:// links -*- coding: utf-8; lexical-binding: t -*-
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; URL: https://github.com/jethrokuan/org-roam
;; Keywords: org-mode, roam, convenience
;; Version: 1.0.0-rc1
;; Package-Requires: ((emacs "26.1") (org "9.3"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; We extend org-protocol, adding custom Org-roam handlers. The setup
;; instructions for `org-protocol' can be found in org-protocol.el.
;;
;; We define 2 protocols:
;;
;; 1. "roam-file": This protocol simply opens the file given by the FILE key
;; 2. "roam-ref": This protocol creates or opens a note with the given REF
;;
;;; Code:
(require 'org-protocol)
(require 'org-roam)
(declare-function org-roam-find-ref "org-roam" (&optional info))
(declare-function org-roam--capture-get-point "org-roam" ())
(defvar org-roam-ref-capture-templates
'(("r" "ref" plain (function org-roam--capture-get-point)
""
:file-name "${slug}"
:head "#+TITLE: ${title}
#+ROAM_KEY: ${ref}\n"
:unnarrowed t))
"The Org-roam templates used during a capture from the roam-ref protocol.
Details on how to specify for the template is given in `org-roam-capture-templates'.")
(defun org-roam-protocol-open-ref (info)
"Process an org-protocol://roam-ref?ref= style url with INFO.
It opens or creates a note with the given ref.
javascript:location.href = \\='org-protocol://roam-ref?template=r&ref=\\='+ \\
encodeURIComponent(location.href) + \\='&title=\\=' \\
encodeURIComponent(document.title) + \\='&body=\\=' + \\
encodeURIComponent(window.getSelection())"
(when-let* ((alist (org-roam--plist-to-alist info))
(decoded-alist (mapcar (lambda (k.v)
(let ((key (car k.v))
(val (cdr k.v)))
(cons key (org-link-decode val)))) alist)))
(unless (assoc 'ref decoded-alist)
(error "No ref key provided"))
(when-let ((title (cdr (assoc 'title decoded-alist))))
(push (cons 'slug (org-roam--title-to-slug title)) decoded-alist))
(let* ((org-roam-capture-templates org-roam-ref-capture-templates)
(org-roam--capture-context 'ref)
(org-roam--capture-info decoded-alist)
(template (cdr (assoc 'template decoded-alist))))
(raise-frame)
(org-roam-capture nil template)
(message "Item captured.")))
nil)
(defun org-roam-protocol-open-file (info)
"This handler simply opens the file with emacsclient.
INFO is an alist containing additional information passed by the protocol URL.
It should contain the FILE key, pointing to the path of the file to open.
Example protocol string:
org-protocol://roam-file?file=/path/to/file.org"
(when-let ((file (plist-get info :file)))
(raise-frame)
(find-file file))
nil)
(push '("org-roam-ref" :protocol "roam-ref" :function org-roam-protocol-open-ref)
org-protocol-protocol-alist)
(push '("org-roam-file" :protocol "roam-file" :function org-roam-protocol-open-file)
org-protocol-protocol-alist)
(provide 'org-roam-protocol)
;;; org-roam-protocol.el ends here

View File

@ -1,169 +0,0 @@
;;; org-roam-utils.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; URL: https://github.com/jethrokuan/org-roam
;; Keywords: org-mode, roam, convenience
;; Version: 0.1.2
;; Package-Requires: ((emacs "26.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; This library is an attempt at injecting Roam functionality into Org-mode.
;; This is achieved primarily through building caches for forward links,
;; backward links, and file titles.
;;
;;; Code:
(require 'org)
(require 'org-element)
(require 'subr-x)
(require 'cl-lib)
(defun org-roam--file-name-extension (filename)
"Return file name extension for FILENAME.
Like file-name-extension, but does not strip version number."
(save-match-data
(let ((file (file-name-nondirectory filename)))
(if (and (string-match "\\.[^.]*\\'" file)
(not (eq 0 (match-beginning 0))))
(substring file (+ (match-beginning 0) 1))))))
(defun org-roam--org-file-p (path)
"Check if PATH is pointing to an org file."
(let ((ext (org-roam--file-name-extension path)))
(or (string= ext "org")
(and
(string= ext "gpg")
(string= (org-roam--file-name-extension (file-name-sans-extension path)) "org")))))
(defun org-roam--find-files (dir)
"Return all `org-roam' files in `DIR'."
(if (file-exists-p dir)
(let ((files (directory-files dir t "." t))
(dir-ignore-regexp (concat "\\(?:"
"\\."
"\\|\\.\\."
"\\)$"))
result)
(dolist (file files)
(cond
((file-directory-p file)
(when (not (string-match dir-ignore-regexp file))
(setq result (append (org-roam--find-files file) result))))
((and (file-readable-p file)
(org-roam--org-file-p file))
(setq result (cons (file-truename file) result)))))
result)))
(defun org-roam--parse-content (&optional file-path)
"Parse the current buffer, and return a list of items for processing."
(org-element-map (org-element-parse-buffer) 'link
(lambda (link)
(let ((type (org-element-property :type link))
(path (org-element-property :path link))
(start (org-element-property :begin link)))
(when (and (string= type "file")
(org-roam--org-file-p path))
(goto-char start)
(let* ((element (org-element-at-point))
(begin (or (org-element-property :content-begin element)
(org-element-property :begin element)))
(content (or (org-element-property :raw-value element)
(buffer-substring
begin
(or (org-element-property :content-end element)
(org-element-property :end element)))))
(content (string-trim content))
(file-path (or file-path
(file-truename (buffer-file-name (current-buffer))))))
(list :from file-path
:to (file-truename (expand-file-name path (file-name-directory file-path)))
:properties (list :content content :point begin))))))))
(cl-defun org-roam--insert-item (item &key forward backward)
"Insert ITEM into FORWARD and BACKWARD cache.
ITEM is of the form: (:from from-path :to to-path :properties (:content preview-content :point point))."
(pcase-let ((`(:from ,p-from :to ,p-to :properties ,props) item))
;; Build forward-links
(let ((links (gethash p-from forward)))
(if links
(puthash p-from
(if (member p-to links)
links
(cons p-to links)) forward)
(puthash p-from (list p-to) forward)))
;; Build backward-links
(let ((contents-hash (gethash p-to backward)))
(if contents-hash
(if-let ((contents-list (gethash p-from contents-hash)))
(let ((updated (cons props contents-list)))
(puthash p-from updated contents-hash)
(puthash p-to contents-hash backward))
(progn
(puthash p-from (list props) contents-hash)
(puthash p-to contents-hash backward)))
(let ((contents-hash (make-hash-table :test #'equal)))
(puthash p-from (list props) contents-hash)
(puthash p-to contents-hash backward))))))
(defun org-roam--extract-title ()
"Extract the title from `BUFFER'."
(org-element-map
(org-element-parse-buffer)
'keyword
(lambda (kw)
(when (string= (org-element-property :key kw) "TITLE")
(org-element-property :value kw)))
:first-match t))
(defun org-roam--build-cache (dir)
"Build the org-roam caches in DIR."
(let ((backward-links (make-hash-table :test #'equal))
(forward-links (make-hash-table :test #'equal))
(file-titles (make-hash-table :test #'equal)))
(let* ((org-roam-files (org-roam--find-files dir))
(file-items (mapcar (lambda (file)
(with-temp-buffer
(insert-file-contents file)
(org-roam--parse-content file))) org-roam-files)))
(dolist (items file-items)
(dolist (item items)
(org-roam--insert-item
item
:forward forward-links
:backward backward-links)))
(dolist (file org-roam-files)
(with-temp-buffer
(insert-file-contents file)
(when-let ((title (org-roam--extract-title)))
(puthash file title file-titles)))
org-roam-files))
(list
:forward forward-links
:backward backward-links
:titles file-titles)))
(provide 'org-roam-utils)
;;; org-roam-utils.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
#+ROAM_ALIAS: "a1" "a 2"
#+TITLE: t1

3
tests/roam-files/bar.org Normal file
View File

@ -0,0 +1,3 @@
#+TITLE: Bar
This is file bar. Bar links to [[file:nested/bar.org][Nested Bar]].

View File

@ -1,7 +0,0 @@
#+TITLE: File 1
link to [[file:nested/f1.org][Nested File 1]]
link to [[file:f2.org][File 2]]
Arbitrary [[https://google.com][HTML]] link
Arbitrary text

View File

@ -1,5 +0,0 @@
#+TITLE: File 3
This file has a link to an file with no title.
[[file:no-title.org][no-title]]

8
tests/roam-files/foo.org Normal file
View File

@ -0,0 +1,8 @@
#+TITLE: Foo
This is the foo file. It contains a link to [[file:bar.org][Bar]].
To make the tests more robust, here are some arbitrary links:
- [[https:google.com][Google]]
- [[mailto:foo@john.com][mail to foo]]

View File

@ -0,0 +1,3 @@
#+TITLE: Nested Bar
This file is nested, 1 level deeper. It links to both [[file:../foo.org][Foo]] and [[file:foo.org][Nested Foo]].

View File

@ -1,4 +0,0 @@
#+TITLE: Nested File 1
Link to [[file:f2.org][Nested File 2]]
Link to [[file:../f1.org][File 1]]

View File

@ -1,3 +0,0 @@
#+TITLE: Nested File 2
Link to [[file:f1.org][Nested File 1]]

View File

@ -1,3 +1,3 @@
#+TITLE: File 2
#+TITLE: Nested Foo
This file has no links.

View File

@ -1 +1,3 @@
no title in this file :O
links to itself, with no title: [[file:no-title.org][no-title]]

View File

@ -0,0 +1,3 @@
#+TITLE: Unlinked
Nothing links here :(

View File

@ -0,0 +1 @@
#+ROAM_KEY: https://google.com/

View File

@ -43,188 +43,261 @@
"Directory containing org-roam test org files.")
(defun org-roam--test-init ()
(org-roam--db-close)
(let ((original-dir org-roam--tests-directory)
(new-dir (expand-file-name (make-temp-name "org-roam") temporary-file-directory)))
(copy-directory original-dir new-dir)
(setq org-roam-directory new-dir))
(org-roam-mode +1))
(defun org-roam--test-build-cache ()
"Builds the caches synchronously."
(let ((cache (org-roam--build-cache org-roam-directory)))
(setq org-roam-forward-links-cache (plist-get cache :forward))
(setq org-roam-backward-links-cache (plist-get cache :backward))
(setq org-roam-titles-cache (plist-get cache :titles))
(setq org-roam-cache-initialized t)))
(setq org-roam-directory new-dir)
(org-roam-mode +1)))
;;; Tests
(describe "org-roam--build-cache-async"
(describe "org-roam-build-cache"
(it "initializes correctly"
(org-roam--test-init)
(expect org-roam-cache-initialized :to-be nil)
(expect (hash-table-count org-roam-forward-links-cache) :to-be 0)
(expect (hash-table-count org-roam-backward-links-cache) :to-be 0)
(expect (hash-table-count org-roam-titles-cache) :to-be 0)
(org-roam-build-cache)
(org-roam--build-cache-async)
(sleep-for 3) ;; Because it's async
;; Cache
(expect (caar (org-roam-sql [:select (funcall count) :from files])) :to-be 8)
(expect (caar (org-roam-sql [:select (funcall count) :from file-links])) :to-be 5)
(expect (caar (org-roam-sql [:select (funcall count) :from titles])) :to-be 8)
(expect (caar (org-roam-sql [:select (funcall count) :from titles
:where titles :is-null])) :to-be 2)
(expect (caar (org-roam-sql [:select (funcall count) :from refs])) :to-be 1)
;; Caches should be populated
(expect org-roam-cache-initialized :to-be t)
(expect (hash-table-count org-roam-forward-links-cache) :to-be 4)
(expect (hash-table-count org-roam-backward-links-cache) :to-be 5)
(expect (hash-table-count org-roam-titles-cache) :to-be 5)
;; TODO Test files
;; Forward cache
(let ((f1 (gethash (abs-path "f1.org") org-roam-forward-links-cache))
(f2 (gethash (abs-path "f2.org") org-roam-forward-links-cache))
(nested-f1 (gethash (abs-path "nested/f1.org") org-roam-forward-links-cache))
(nested-f2 (gethash (abs-path "nested/f2.org") org-roam-forward-links-cache))
(expected-f1 (list (abs-path "nested/f1.org")
(abs-path "f2.org")))
(expected-nested-f1 (list (abs-path "nested/f2.org")
(abs-path "f1.org")))
(expected-nested-f2 (list (abs-path "nested/f1.org"))))
;; Links -- File-from
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
:where (= file-from $s1)]
(abs-path "foo.org"))) :to-be 1)
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
:where (= file-from $s1)]
(abs-path "nested/bar.org"))) :to-be 2)
(expect f1 :to-have-same-items-as expected-f1)
(expect f2 :to-be nil)
(expect nested-f1 :to-have-same-items-as expected-nested-f1)
(expect nested-f2 :to-have-same-items-as expected-nested-f2))
;; Links -- File-to
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
:where (= file-to $s1)]
(abs-path "nested/foo.org"))) :to-be 1)
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
:where (= file-to $s1)]
(abs-path "nested/bar.org"))) :to-be 1)
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
:where (= file-to $s1)]
(abs-path "unlinked.org"))) :to-be 0)
;; TODO Test titles
(expect (org-roam-sql [:select * :from titles])
:to-have-same-items-as
(list (list (abs-path "alias.org")
(list "t1" "a1" "a 2"))
(list (abs-path "bar.org")
(list "Bar"))
(list (abs-path "foo.org")
(list "Foo"))
(list (abs-path "nested/bar.org")
(list "Nested Bar"))
(list (abs-path "nested/foo.org")
(list "Nested Foo"))
(list (abs-path "no-title.org") nil)
(list (abs-path "web_ref.org") nil)
(list (abs-path "unlinked.org")
(list "Unlinked"))))
;; Backward cache
(let ((f1 (hash-table-keys (gethash (abs-path "f1.org") org-roam-backward-links-cache)))
(f2 (hash-table-keys (gethash (abs-path "f2.org") org-roam-backward-links-cache)))
(nested-f1 (hash-table-keys(gethash (abs-path "nested/f1.org") org-roam-backward-links-cache)))
(nested-f2 (hash-table-keys (gethash (abs-path "nested/f2.org") org-roam-backward-links-cache)))
(expected-f1 (list (abs-path "nested/f1.org")))
(expected-f2 (list (abs-path "f1.org")))
(expected-nested-f1 (list (abs-path "nested/f2.org")
(abs-path "f1.org")))
(expected-nested-f2 (list (abs-path "nested/f1.org"))))
(expect f1 :to-have-same-items-as expected-f1)
(expect f2 :to-have-same-items-as expected-f2)
(expect nested-f1 :to-have-same-items-as expected-nested-f1)
(expect nested-f2 :to-have-same-items-as expected-nested-f2))
(expect (org-roam-sql [:select * :from refs])
:to-have-same-items-as
(list (list "https://google.com/" (abs-path "web_ref.org"))))
;; Titles Cache
(expect (gethash (abs-path "f1.org") org-roam-titles-cache) :to-equal "File 1")
(expect (gethash (abs-path "f2.org") org-roam-titles-cache) :to-equal "File 2")
(expect (gethash (abs-path "nested/f1.org") org-roam-titles-cache) :to-equal "Nested File 1")
(expect (gethash (abs-path "nested/f2.org") org-roam-titles-cache) :to-equal "Nested File 2")
(expect (gethash (abs-path "no-title.org") org-roam-titles-cache) :to-be nil)))
;; Expect rebuilds to be really quick (nothing changed)
(expect (org-roam-build-cache)
:to-equal
(list :files 0 :links 0 :titles 0 :refs 0 :deleted 0))))
(describe "org-roam-insert"
(before-each
(org-roam--test-init)
(org-roam--clear-cache)
(org-roam--test-build-cache))
(org-roam--db-clear)
(org-roam-build-cache))
(it "temp1 -> f1"
(it "temp1 -> foo"
(let ((buf (org-roam--test-find-new-file "temp1.org")))
(with-current-buffer buf
(with-simulated-input
"File SPC 1 RET"
"Foo RET"
(org-roam-insert nil))))
(expect (buffer-string) :to-match (regexp-quote "file:f1.org")))
(expect (buffer-string) :to-match (regexp-quote "file:foo.org")))
(it "temp2 -> nested/f1"
(it "temp2 -> nested/foo"
(let ((buf (org-roam--test-find-new-file "temp2.org")))
(with-current-buffer buf
(with-simulated-input
"Nested SPC File SPC 1 RET"
"Nested SPC Foo RET"
(org-roam-insert nil))))
(expect (buffer-string) :to-match (regexp-quote "file:nested/f1.org")))
(expect (buffer-string) :to-match (regexp-quote "file:nested/foo.org")))
(it "nested/temp3 -> f1"
(it "nested/temp3 -> foo"
(let ((buf (org-roam--test-find-new-file "nested/temp3.org")))
(with-current-buffer buf
(with-simulated-input
"File SPC 1 RET"
"Foo RET"
(org-roam-insert nil))))
(expect (buffer-string) :to-match (regexp-quote "file:../f1.org")))
(expect (buffer-string) :to-match (regexp-quote "file:../foo.org")))
(it "a/b/temp4 -> nested/f1"
(it "a/b/temp4 -> nested/foo"
(let ((buf (org-roam--test-find-new-file "a/b/temp4.org")))
(with-current-buffer buf
(with-simulated-input
"Nested SPC File SPC 1 RET"
"Nested SPC Foo RET"
(org-roam-insert nil))))
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/f1.org"))))
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/foo.org"))))
(describe "rename file updates cache"
(before-each
(org-roam--test-init)
(org-roam--clear-cache)
(org-roam--test-build-cache))
(org-roam--db-clear)
(org-roam-build-cache))
(it "f1 -> new_f1"
(rename-file (abs-path "f1.org")
(abs-path "new_f1.org"))
(it "foo -> new_foo"
(rename-file (abs-path "foo.org")
(abs-path "new_foo.org"))
;; Cache should be cleared of old file
(expect (gethash (abs-path "f1.org") org-roam-forward-links-cache) :to-be nil)
(expect (->> org-roam-backward-links-cache
(gethash (abs-path "nested/f1.org"))
(hash-table-keys)
(member (abs-path "f1.org"))) :to-be nil)
(expect (caar (org-roam-sql [:select (funcall count)
:from titles
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from refs
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from file-links
:where (= file-from $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "new_f1.org"))) :not :to-be nil)
;; Cache should be updated
(expect (org-roam-sql [:select [file-to]
:from file-links
:where (= file-from $s1)]
(abs-path "new_foo.org"))
:to-have-same-items-as
(list (list (abs-path "bar.org"))))
(expect (org-roam-sql [:select [file-from]
:from file-links
:where (= file-to $s1)]
(abs-path "new_foo.org"))
:to-have-same-items-as
(list (list (abs-path "nested/bar.org"))))
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "new_f1.org"))
(member (abs-path "nested/f1.org"))) :not :to-be nil)
;; Links are updated
(expect (with-temp-buffer
(insert-file-contents (abs-path "nested/f1.org"))
(buffer-string)) :to-match (regexp-quote "[[file:../new_f1.org][File 1]]")))
(insert-file-contents (abs-path "nested/bar.org"))
(buffer-string))
:to-match
(regexp-quote "[[file:../new_foo.org][Foo]]")))
(it "f1 -> f1 with spaces"
(rename-file (abs-path "f1.org")
(abs-path "f1 with spaces.org"))
(it "foo -> foo with spaces"
(rename-file (abs-path "foo.org")
(abs-path "foo with spaces.org"))
;; Cache should be cleared of old file
(expect (gethash (abs-path "f1.org") org-roam-forward-links-cache) :to-be nil)
(expect (->> org-roam-backward-links-cache
(gethash (abs-path "nested/f1.org"))
(hash-table-keys)
(member (abs-path "f1.org"))) :to-be nil)
(expect (caar (org-roam-sql [:select (funcall count)
:from titles
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from refs
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from file-links
:where (= file-from $s1)]
(abs-path "foo.org"))) :to-be 0)
;; Cache should be updated
(expect (org-roam-sql [:select [file-to]
:from file-links
:where (= file-from $s1)]
(abs-path "foo with spaces.org"))
:to-have-same-items-as
(list (list (abs-path "bar.org"))))
(expect (org-roam-sql [:select [file-from]
:from file-links
:where (= file-to $s1)]
(abs-path "foo with spaces.org"))
:to-have-same-items-as
(list (list (abs-path "nested/bar.org"))))
;; Links are updated
(expect (with-temp-buffer
(insert-file-contents (abs-path "nested/f1.org"))
(buffer-string)) :to-match (regexp-quote "[[file:../f1 with spaces.org][File 1]]")))
(insert-file-contents (abs-path "nested/bar.org"))
(buffer-string))
:to-match
(regexp-quote "[[file:../foo with spaces.org][Foo]]")))
(it "no-title -> meaningful-title"
(rename-file (abs-path "no-title.org")
(abs-path "meaningful-title.org"))
;; File has no forward links
(expect (gethash (abs-path "no-title.org") org-roam-forward-links-cache) :to-be nil)
(expect (gethash (abs-path "meaningful-title.org") org-roam-forward-links-cache) :to-be nil)
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "f3.org"))
(member (abs-path "no-title.org"))) :to-be nil)
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "f3.org"))
(member (abs-path "meaningful-title.org"))) :not :to-be nil)
(expect (caar (org-roam-sql [:select (funcall count)
:from file-links
:where (= file-from $s1)]
(abs-path "no-title.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from file-links
:where (= file-from $s1)]
(abs-path "meaningful-title.org"))) :to-be 1)
;; Links are updated with the appropriate name
(expect (with-temp-buffer
(insert-file-contents (abs-path "f3.org"))
(buffer-string)) :to-match (regexp-quote "[[file:meaningful-title.org][meaningful-title]]"))))
(insert-file-contents (abs-path "meaningful-title.org"))
(buffer-string))
:to-match
(regexp-quote "[[file:meaningful-title.org][meaningful-title]]")))
(it "web_ref -> hello"
(expect (org-roam-sql
[:select [file] :from refs
:where (= ref $s1)]
"https://google.com/")
:to-equal
(list (list (abs-path "web_ref.org"))))
(rename-file (abs-path "web_ref.org")
(abs-path "hello.org"))
(expect (org-roam-sql
[:select [file] :from refs
:where (= ref $s1)]
"https://google.com/")
:to-equal (list (list (abs-path "hello.org"))))
(expect (caar (org-roam-sql
[:select [ref] :from refs
:where (= file $s1)]
(abs-path "web_ref.org")))
:to-equal nil)))
(describe "delete file updates cache"
(before-each
(org-roam--test-init)
(org-roam--clear-cache)
(org-roam--test-build-cache))
(it "delete f1"
(delete-file (abs-path "f1.org"))
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "f1.org"))) :to-be nil)
(expect (->> org-roam-backward-links-cache
(gethash (abs-path "nested/f1.org"))
(gethash (abs-path "f1.org"))) :to-be nil)
(expect (->> org-roam-backward-links-cache
(gethash (abs-path "nested/f1.org"))
(gethash (abs-path "nested/f2.org"))) :not :to-be nil)))
(org-roam--db-clear)
(org-roam-build-cache)
(sleep-for 1))
(it "delete foo"
(delete-file (abs-path "foo.org"))
(expect (caar (org-roam-sql [:select (funcall count)
:from titles
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from refs
:where (= file $s1)]
(abs-path "foo.org"))) :to-be 0)
(expect (caar (org-roam-sql [:select (funcall count)
:from file-links
:where (= file-from $s1)]
(abs-path "foo.org"))) :to-be 0))
(it "delete web_ref"
(expect (org-roam-sql [:select * :from refs])
:to-have-same-items-as
(list (list "https://google.com/" (abs-path "web_ref.org"))))
(delete-file (abs-path "web_ref.org"))
(expect (org-roam-sql [:select * :from refs])
:to-have-same-items-as
(list))))