mirror of
https://github.com/org-roam/org-roam
synced 2025-08-03 12:27:23 -05:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
cb029c4ce8 | |||
571f65cebd | |||
316ad40b2c | |||
b78b545d31 | |||
8523fb43b4 | |||
dd4b1a97a1 | |||
3a8908f72a | |||
ddaf855b5d | |||
f458c6caf6 | |||
0346d3b16c | |||
63754d1ccd | |||
05ada41710 | |||
bdc13cd6bf | |||
2522b9d2fa | |||
edbe34a1d9 | |||
570467b34b | |||
e84ab1de9a | |||
996923f9d9 | |||
4a5531cde3 | |||
2d206134fd | |||
883eed0a5e | |||
659babf922 | |||
424de1f0cb | |||
618b7f6124 | |||
ce305af319 | |||
58590a0e9d | |||
9ae03532da | |||
159b64b538 | |||
8ec3b441d1 | |||
f048a6b866 | |||
9aba7ee094 | |||
ba91fc41a7 |
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something's not working.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: 'jethrokuan'
|
||||
---
|
||||
|
||||
### Description
|
||||
|
||||
#### Steps to Reproduce
|
||||
<!--
|
||||
Example:
|
||||
|
||||
1. Load Emacs
|
||||
2. Run `org-roam--build-cache-async`
|
||||
3. Run `org-roam-find-file`
|
||||
...
|
||||
-->
|
||||
|
||||
|
||||
#### Expected Results
|
||||
<!-- Example: File A is there -->
|
||||
|
||||
#### Actual Results
|
||||
<!-- Example: File A is missing -->
|
||||
|
||||
### Versions
|
||||
- Emacs (`C-h v emacs-version`): vX.X.X
|
||||
- Org-roam commit: https://github.com/jethrokuan/org-roam/commit/commithashhere
|
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Create a feature request to improve Org-roam
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: 'jethrokuan'
|
||||
---
|
||||
|
||||
### Brief Abstract
|
||||
|
||||
### Long Description
|
||||
|
||||
### Proposed Implementation (if any)
|
||||
|
||||
### Please check the following:
|
||||
- [ ] No similar feature requests
|
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
###### Motivation for this change
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/.sandbox/
|
||||
**/*.elc
|
||||
|
56
CHANGELOG.md
56
CHANGELOG.md
@ -1,5 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.2 (2020-02-21)
|
||||
|
||||
### Breaking Changes
|
||||
* [#143][gh-143] `org-roam-mode` is now a global mode. The installation instructions have changed accordingly.
|
||||
* [#103][gh-103] Change `org-roam-file-format` to a function: `org-roam-file-name-function` to allow more flexible file name customizaton. Also changes `org-roam-use-timestamp-as-filename` to `org-roam-filename-noconfirm` to better describe what it does.
|
||||
|
||||
### New Features
|
||||
* [#145][gh-145] `org-roam-show-graph`: Fallback to Emacs SVG viewer
|
||||
* [#141][gh-141] add variable `org-roam-new-file-directory` for new Org-roam files
|
||||
* [#138][gh-138] add `org-roam-switch-to-buffer`
|
||||
* [#124][gh-124], [#141][gh-141] Maintain cache consistency on file rename and delete
|
||||
* [#87][gh-87], [#90][gh-90] Support encrypted Org files
|
||||
* [#110][gh-110] Add prefix to `org-roam-insert`, for inserting titles down-cased
|
||||
* [#99][gh-99] Add keybinding so that `<return>` or `mouse-1` in the backlinks buffer visits the source file of the backlink at point
|
||||
|
||||
### Changes
|
||||
|
||||
* [#108][gh-108] Locally overwrite the link following behaviour in the org-roam-buffer to open files in the same window `org-roam` was called from
|
||||
|
||||
### Bugfixes
|
||||
* [#86][gh-86] Fix `org-roam--parse-content` incorrect `:to` computation for nested files
|
||||
* [#98][gh-98] Fix `org-roam--find-file` picking up temporary files
|
||||
* [#136][gh-136] Misc bugfixes
|
||||
|
||||
### Internal
|
||||
* [#122][gh-122], [#128][gh-128] Improve performance of post-command-hook
|
||||
* [#92][gh-92], [#105][gh-105] Add tests for core functionality
|
||||
|
||||
### New Contributors
|
||||
* [@frigge](https://github.com/frigge)
|
||||
* [@juergenhoetzel](https://github.com/juergenhoetzel)
|
||||
* [@chip2n](https://github.com/chip2n)
|
||||
* [@l3kn](https://github.com/l3kn)
|
||||
* [@jdormit](https://github.com/jdormit)
|
||||
* [@herbertjones](https://github.com/herbertjones)
|
||||
* [@CeleritasCelery](https://github.com/CeleritasCelery)
|
||||
* [@daniel-koudouna](https://github.com/daniel-koudouna)
|
||||
|
||||
## 0.1.1 (2020-02-15)
|
||||
|
||||
Mostly a documentation/cleanup release.
|
||||
@ -31,6 +69,24 @@ Mostly a documentation/cleanup release.
|
||||
[gh-75]: https://github.com/jethrokuan/org-roam/pull/75
|
||||
[gh-78]: https://github.com/jethrokuan/org-roam/pull/78
|
||||
[gh-82]: https://github.com/jethrokuan/org-roam/pull/82
|
||||
[gh-86]: https://github.com/jethrokuan/org-roam/pull/86
|
||||
[gh-87]: https://github.com/jethrokuan/org-roam/pull/87
|
||||
[gh-90]: https://github.com/jethrokuan/org-roam/pull/90
|
||||
[gh-92]: https://github.com/jethrokuan/org-roam/pull/92
|
||||
[gh-98]: https://github.com/jethrokuan/org-roam/pull/98
|
||||
[gh-99]: https://github.com/jethrokuan/org-roam/pull/99
|
||||
[gh-103]: https://github.com/jethrokuan/org-roam/pull/103
|
||||
[gh-105]: https://github.com/jethrokuan/org-roam/pull/105
|
||||
[gh-108]: https://github.com/jethrokuan/org-roam/pull/108
|
||||
[gh-110]: https://github.com/jethrokuan/org-roam/pull/110
|
||||
[gh-122]: https://github.com/jethrokuan/org-roam/pull/122
|
||||
[gh-124]: https://github.com/jethrokuan/org-roam/pull/124
|
||||
[gh-128]: https://github.com/jethrokuan/org-roam/pull/128
|
||||
[gh-136]: https://github.com/jethrokuan/org-roam/pull/136
|
||||
[gh-138]: https://github.com/jethrokuan/org-roam/pull/138
|
||||
[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
|
||||
|
||||
# Local Variables:
|
||||
# eval: (auto-fill-mode -1)
|
||||
|
22
README.md
22
README.md
@ -43,22 +43,20 @@ The recommended method is using use-package and straight, or a similar package m
|
||||
(use-package org-roam
|
||||
:after org
|
||||
:hook
|
||||
((org-mode . org-roam-mode)
|
||||
(after-init . org-roam--build-cache-async) ;; optional!
|
||||
)
|
||||
(after-init . org-roam-mode)
|
||||
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
|
||||
:custom
|
||||
(org-roam-directory "/path/to/org-files/")
|
||||
:bind
|
||||
("C-c n l" . org-roam)
|
||||
("C-c n t" . org-roam-today)
|
||||
("C-c n f" . org-roam-find-file)
|
||||
("C-c n i" . org-roam-insert)
|
||||
("C-c n g" . org-roam-show-graph))
|
||||
: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))))
|
||||
```
|
||||
|
||||
For more detailed installation instructions, please see [the
|
||||
installation
|
||||
For more detailed installation instructions (including instructions for
|
||||
Spacemacs users), please see [the installation
|
||||
documentation](https://org-roam.readthedocs.io/en/develop/installation/).
|
||||
|
||||
## Knowledge Bases using Org-Roam
|
||||
@ -74,7 +72,7 @@ A changelog is being maintained [here](CHANGELOG.md)
|
||||
|
||||
To report bugs and suggest new feature use the issue tracker. If you
|
||||
have some code which you would like to be merged, then open a pull
|
||||
request. Please also see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
request. Please also see [CONTRIBUTING.md](.github/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
|
@ -66,24 +66,20 @@ 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 specified by the string
|
||||
`org-roam-file-format`, which defaults to `"%Y%m%d%H%M%S"`. To see
|
||||
valid specifications, see the help (`C-h f`) for `format-time-string`.
|
||||
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.
|
||||
|
||||
There are several reasons for keeping filenames meaningful. For
|
||||
example, one may wish to publish the Org files, and some publishing
|
||||
methods such as Org-publish use the file names as slugs for the URLs.
|
||||
|
||||
If you wish to maintain manual control of filenames, set
|
||||
`org-roam-use-timestamp-as-filename` to `nil`:
|
||||
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-use-timestamp-as-filename nil)
|
||||
(setq org-roam-filename-noconfirm nil)
|
||||
```
|
||||
|
||||
When this setting is turned off, the user is instead manually prompted
|
||||
for a filename. It is then the user's responsibility to ensure that
|
||||
the file names are unique.
|
||||
It is then the user's responsibility to ensure that the file names are
|
||||
unique.
|
||||
|
||||
### Autopopulating Titles
|
||||
|
||||
@ -94,6 +90,15 @@ typically near the top of the file. The option
|
||||
attribute is automatically inserted into the files created via
|
||||
Org-roam commands. Setting it to `nil` will disable this behaviour.
|
||||
|
||||
### 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.
|
||||
|
||||
## Org-roam Graph Viewer
|
||||
|
||||
|
@ -5,7 +5,7 @@ A number of packages work well combined with Org-Roam:
|
||||
[Deft][deft] provides a nice interface for browsing and filtering
|
||||
org-roam notes.
|
||||
|
||||
```
|
||||
```emacs-lisp
|
||||
(use-package deft
|
||||
:after org
|
||||
:bind
|
||||
@ -14,8 +14,37 @@ org-roam notes.
|
||||
(deft-recursive t)
|
||||
(deft-use-filter-string-for-filename t)
|
||||
(deft-default-extension "org")
|
||||
(deft-directory "/path/to/org-roam-files/")
|
||||
(deft-use-filename-as-title t))
|
||||
(deft-directory "/path/to/org-roam-files/"))
|
||||
```
|
||||
|
||||
If the title of the Org file is not the first line, you might not get
|
||||
nice titles. You may choose to patch this to use `org-roam`'s
|
||||
functionality. Here I'm using [el-patch](https://github.com/raxod502/el-patch):
|
||||
|
||||
```emacs-lisp
|
||||
(use-package el-patch
|
||||
:straight (:host github
|
||||
:repo "raxod502/el-patch"
|
||||
:branch "develop"))
|
||||
|
||||
(eval-when-compile
|
||||
(require 'el-patch))
|
||||
|
||||
(use-package deft
|
||||
;; same as above...
|
||||
:config/el-patch
|
||||
(defun deft-parse-title (file contents)
|
||||
"Parse the given FILE and CONTENTS and determine the title.
|
||||
If `deft-use-filename-as-title' is nil, the title is taken to
|
||||
be the first non-empty line of the FILE. Else the base name of the FILE is
|
||||
used as title."
|
||||
(el-patch-swap (if deft-use-filename-as-title
|
||||
(deft-base-filename file)
|
||||
(let ((begin (string-match "^.+$" contents)))
|
||||
(if begin
|
||||
(funcall deft-parse-title-function
|
||||
(substring contents begin (match-end 0))))))
|
||||
(org-roam--get-title-or-slug file))))
|
||||
```
|
||||
|
||||
The Deft interface can slow down quickly when the number of files get
|
||||
@ -29,7 +58,7 @@ powerful alternative to the simple function `org-roam-today`. It
|
||||
provides better journaling capabilities, and a nice calendar interface
|
||||
to see all dated entries.
|
||||
|
||||
```
|
||||
```emacs-lisp
|
||||
(use-package org-journal
|
||||
:bind
|
||||
("C-c n j" . org-journal-new-entry)
|
||||
|
@ -5,7 +5,7 @@ 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`.
|
||||
|
||||
## Setting Up for Linux
|
||||
## Linux
|
||||
|
||||
Create a desktop application. I place mine in
|
||||
`~/.local/share/applications/roam.desktop`:
|
||||
@ -37,3 +37,42 @@ 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
|
||||
```
|
||||
|
@ -7,18 +7,16 @@ The recommended method is using [use-package][use-package] and
|
||||
(use-package org-roam
|
||||
:after org
|
||||
:hook
|
||||
((org-mode . org-roam-mode)
|
||||
(after-init . org-roam--build-cache-async) ;; optional!
|
||||
)
|
||||
(after-init . org-roam-mode)
|
||||
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
|
||||
:custom
|
||||
(org-roam-directory "/path/to/org-files/")
|
||||
:bind
|
||||
("C-c n l" . org-roam)
|
||||
("C-c n t" . org-roam-today)
|
||||
("C-c n f" . org-roam-find-file)
|
||||
("C-c n i" . org-roam-insert)
|
||||
("C-c n g" . org-roam-show-graph))
|
||||
: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))))
|
||||
```
|
||||
|
||||
If not using package.el, you can also clone it into your Emacs
|
||||
@ -33,17 +31,16 @@ git clone https://github.com/jethrokuan/org-roam/ ~/.emacs.d/elisp/org-roam
|
||||
:after org
|
||||
:load-path "elisp/"
|
||||
:hook
|
||||
((org-mode . org-roam-mode)
|
||||
(after-init . org-roam--build-cache-async) ;; optional!
|
||||
)
|
||||
(after-init . org-roam-mode)
|
||||
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
|
||||
:custom
|
||||
(org-roam-directory "/path/to/org-files/")
|
||||
:bind
|
||||
("C-c n l" . org-roam)
|
||||
("C-c n t" . org-roam-today)
|
||||
("C-c n f" . org-roam-find-file)
|
||||
("C-c n i" . org-roam-insert)
|
||||
("C-c n g" . org-roam-show-graph))
|
||||
: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:
|
||||
@ -59,3 +56,38 @@ affect the Roam workflow. Do look through them at the
|
||||
|
||||
[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.
|
||||
|
@ -5,7 +5,7 @@
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/jethrokuan/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 0.1.0
|
||||
;; Version: 0.1.2
|
||||
;; Package-Requires: ((emacs "26.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
@ -38,6 +38,24 @@
|
||||
(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)
|
||||
@ -53,7 +71,7 @@
|
||||
(when (not (string-match dir-ignore-regexp file))
|
||||
(setq result (append (org-roam--find-files file) result))))
|
||||
((and (file-readable-p file)
|
||||
(string= (file-name-extension file) "org"))
|
||||
(org-roam--org-file-p file))
|
||||
(setq result (cons (file-truename file) result)))))
|
||||
result)))
|
||||
|
||||
@ -65,25 +83,28 @@
|
||||
(path (org-element-property :path link))
|
||||
(start (org-element-property :begin link)))
|
||||
(when (and (string= type "file")
|
||||
(string= (file-name-extension path) "org"))
|
||||
(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
|
||||
(or (org-element-property :content-begin element)
|
||||
(org-element-property :begin element))
|
||||
begin
|
||||
(or (org-element-property :content-end element)
|
||||
(org-element-property :end element))))))
|
||||
(list :from (or file-path
|
||||
(file-truename (buffer-file-name (current-buffer))))
|
||||
:to (file-truename (expand-file-name path org-roam-directory))
|
||||
:content (string-trim content))))))))
|
||||
(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 :content preview-content)."
|
||||
(pcase-let ((`(:from ,p-from :to ,p-to :content ,content) item))
|
||||
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
|
||||
@ -96,14 +117,14 @@ ITEM is of the form: (:from from-path :to to-path :content preview-content)."
|
||||
(let ((contents-hash (gethash p-to backward)))
|
||||
(if contents-hash
|
||||
(if-let ((contents-list (gethash p-from contents-hash)))
|
||||
(let ((updated (cons content contents-list)))
|
||||
(let ((updated (cons props contents-list)))
|
||||
(puthash p-from updated contents-hash)
|
||||
(puthash p-to contents-hash backward))
|
||||
(progn
|
||||
(puthash p-from (list content) contents-hash)
|
||||
(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 content) contents-hash)
|
||||
(puthash p-from (list props) contents-hash)
|
||||
(puthash p-to contents-hash backward))))))
|
||||
|
||||
(defun org-roam--extract-title ()
|
||||
@ -116,6 +137,33 @@ ITEM is of the form: (:from from-path :to to-path :content preview-content)."
|
||||
(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
|
||||
|
486
org-roam.el
486
org-roam.el
@ -5,7 +5,7 @@
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/jethrokuan/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 0.1.0
|
||||
;; Version: 0.1.2
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (async "1.9.4"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
@ -55,7 +55,14 @@
|
||||
"Path to Org-roam files.
|
||||
|
||||
All Org files, at any level of nesting, is considered part of the Org-roam."
|
||||
:type 'directoy
|
||||
:type 'directory
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-new-file-directory nil
|
||||
"Path to where new Org-roam files are created.
|
||||
|
||||
If nil, default to the org-roam-directory (preferred)."
|
||||
:type 'directory
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-position 'right
|
||||
@ -68,18 +75,23 @@ Valid values are
|
||||
(const right))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-file-format "%Y%m%d%H%M%S"
|
||||
"The timestamp format to use filenames."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
(defcustom org-roam-file-name-function #'org-roam--file-name-timestamp-title
|
||||
"The function used to generate filenames.
|
||||
|
||||
The function takes as parameter `TITLE', a string the user inputs."
|
||||
:group 'org-roam
|
||||
:type '(choice (const :tag "Default" org-roam--file-name-timestamp-title)
|
||||
(function :tag "Personalized function")))
|
||||
|
||||
(defcustom org-roam-link-title-format "%s"
|
||||
"The format string used when inserting org-roam links that use their title."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-use-timestamp-as-filename t
|
||||
"Whether to use timestamp as a file name. If not true, prompt for a file name each time."
|
||||
(defcustom org-roam-filename-noconfirm t
|
||||
"Whether to prompt for confirmation of fil name for new files.
|
||||
|
||||
If nil, always ask for filename."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
@ -96,6 +108,11 @@ Valid values are
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-encrypt-files nil
|
||||
"Whether to encrypt new files. If true, create files with .org.gpg extension."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-viewer (executable-find "firefox")
|
||||
"Path to executable for viewing SVG."
|
||||
:type 'string
|
||||
@ -106,6 +123,16 @@ Valid values are
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-max-title-length 100
|
||||
"Maximum length of titles in graphviz graph nodes"
|
||||
:type 'number
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-node-shape "ellipse"
|
||||
"Maximum length of titles in graphviz graph nodes"
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
;;; Polyfills
|
||||
;; These are for functions I use that are only available in newer Emacs
|
||||
|
||||
@ -139,9 +166,12 @@ If called interactively, then PARENTS is non-nil."
|
||||
(defvar org-roam-titles-cache (make-hash-table :test #'equal)
|
||||
"Cache containing titles for org-roam files.")
|
||||
|
||||
(defvar org-roam-current-file nil
|
||||
(defvar org-roam--current-buffer nil
|
||||
"Currently displayed file in `org-roam' buffer.")
|
||||
|
||||
(defvar org-roam-last-window nil
|
||||
"Last window `org-roam' was called from.")
|
||||
|
||||
;;; Utilities
|
||||
(defun org-roam--ensure-cache-built ()
|
||||
"Ensures that org-roam cache is built."
|
||||
@ -149,11 +179,14 @@ If called interactively, then PARENTS is non-nil."
|
||||
(org-roam--build-cache-async)
|
||||
(user-error "Your Org-Roam cache isn't built yet! Please wait")))
|
||||
|
||||
(defun org-roam--org-roam-file-p ()
|
||||
"Return t if file is part of org-roam system, false otherwise."
|
||||
(and (buffer-file-name (current-buffer))
|
||||
(f-descendant-of-p (file-truename (buffer-file-name (current-buffer)))
|
||||
org-roam-directory)))
|
||||
(defun org-roam--org-roam-file-p (&optional file)
|
||||
"Return t if FILE is part of org-roam system, defaulting to the name of the current buffer. Else, return nil."
|
||||
(let ((path (or file
|
||||
(buffer-file-name (current-buffer)))))
|
||||
(and path
|
||||
(org-roam--org-file-p path)
|
||||
(f-descendant-of-p (file-truename path)
|
||||
(file-truename org-roam-directory)))))
|
||||
|
||||
(defun org-roam--get-title-from-cache (file)
|
||||
"Return title of `FILE' from the cache."
|
||||
@ -167,14 +200,17 @@ If called interactively, then PARENTS is non-nil."
|
||||
"Return all org-roam files."
|
||||
(org-roam--find-files (file-truename org-roam-directory)))
|
||||
|
||||
(defun org-roam--get-file-path (id &optional absolute)
|
||||
"Convert identifier `ID' to file path.
|
||||
(defun org-roam--make-new-file-path (id &optional absolute)
|
||||
"Make new file path from identifier `ID'.
|
||||
|
||||
If `ABSOLUTE', return the absolute file-path. Else, return the relative file-path."
|
||||
If `ABSOLUTE', return an absolute file-path. Else, return a relative file-path."
|
||||
(let ((absolute-file-path (file-truename
|
||||
(expand-file-name
|
||||
(concat id ".org")
|
||||
org-roam-directory))))
|
||||
(if org-roam-encrypt-files
|
||||
(concat id ".org.gpg")
|
||||
(concat id ".org"))
|
||||
(or org-roam-new-file-directory
|
||||
org-roam-directory)))))
|
||||
(if absolute
|
||||
absolute-file-path
|
||||
(file-relative-name absolute-file-path
|
||||
@ -195,6 +231,13 @@ If `ABSOLUTE', return the absolute file-path. Else, return the relative file-pat
|
||||
(s (s-join "_" s)))
|
||||
s))
|
||||
|
||||
(defun org-roam--file-name-timestamp-title (title)
|
||||
"Return a file name (without extension) for new files.
|
||||
|
||||
It uses TITLE and the current timestamp to form a unique title."
|
||||
(let ((timestamp (format-time-string "%Y%m%d%H%M%S" (current-time)))
|
||||
(slug (org-roam--title-to-slug title)))
|
||||
(format "%s_%s" timestamp slug)))
|
||||
|
||||
;;; Creating org-roam files
|
||||
(defun org-roam--populate-title (file &optional title)
|
||||
@ -218,51 +261,54 @@ If not provided, derive the title from the file name."
|
||||
"Create an org-roam file at FILE-PATH, optionally setting the TITLE attribute."
|
||||
(if (file-exists-p file-path)
|
||||
(error (format "Aborting, file already exists at %s" file-path))
|
||||
(make-empty-file file-path t)
|
||||
(if org-roam-autopopulate-title
|
||||
(org-roam--populate-title file-path title)
|
||||
(make-empty-file file-path))))
|
||||
(org-roam--populate-title file-path title))
|
||||
(save-excursion
|
||||
(with-current-buffer (find-file-noselect file-path)
|
||||
(org-roam--update-cache)))))
|
||||
|
||||
(defun org-roam--new-file-named (slug)
|
||||
"Create a new file named `SLUG'.
|
||||
`SLUG' is the short file name, without a path or a file extension."
|
||||
(interactive "sNew filename (without extension): ")
|
||||
(let ((file-path (org-roam--get-file-path slug t)))
|
||||
(let ((file-path (org-roam--make-new-file-path slug t)))
|
||||
(unless (file-exists-p file-path)
|
||||
(org-roam--make-file file-path))
|
||||
(find-file file-path)))
|
||||
|
||||
(defun org-roam--get-new-id (&optional title)
|
||||
"Return a new ID, generated from the current time.
|
||||
|
||||
Optionally pass it the title, for a smart file name."
|
||||
(if org-roam-use-timestamp-as-filename
|
||||
(format-time-string org-roam-file-format (current-time))
|
||||
(let* ((slug (read-string "Enter ID (without extension): "
|
||||
(if title
|
||||
(org-roam--title-to-slug title)
|
||||
"")))
|
||||
(file-path (org-roam--get-file-path slug t)))
|
||||
(if (file-exists-p file-path)
|
||||
(user-error "There's already a file at %s")
|
||||
slug))))
|
||||
(defun org-roam--get-new-id (title)
|
||||
"Return a new ID, given the note TITLE."
|
||||
(let* ((proposed-slug (funcall org-roam-file-name-function title))
|
||||
(new-slug (if org-roam-filename-noconfirm
|
||||
proposed-slug
|
||||
(read-string "Enter ID (without extension): "
|
||||
proposed-slug)))
|
||||
(file-path (org-roam--make-new-file-path new-slug t)))
|
||||
(if (file-exists-p file-path)
|
||||
(user-error "There's already a file at %s")
|
||||
new-slug)))
|
||||
|
||||
(defun org-roam-new-file ()
|
||||
"Quickly create a new file, using the current timestamp."
|
||||
(interactive)
|
||||
(org-roam--new-file-named (org-roam--get-new-id)))
|
||||
(org-roam--new-file-named (format-time-string "%Y%m%d%H%M%S" (current-time))))
|
||||
|
||||
;;; Inserting org-roam links
|
||||
(defun org-roam-insert ()
|
||||
"Find an org-roam file, and insert a relative org link to it at point."
|
||||
(interactive)
|
||||
(defun org-roam-insert (prefix)
|
||||
"Find an org-roam file, and insert a relative org link to it at point.
|
||||
|
||||
If PREFIX, downcase the title before insertion."
|
||||
(interactive "P")
|
||||
(let* ((completions (mapcar (lambda (file)
|
||||
(list (org-roam--get-title-or-slug file)
|
||||
file))
|
||||
(org-roam--find-all-files)))
|
||||
(title (completing-read "File: " completions))
|
||||
(absolute-file-path (or (cadr (assoc title completions))
|
||||
(org-roam--get-file-path (org-roam--get-new-id title) t)))
|
||||
(current-file-path (-> (current-buffer)
|
||||
(org-roam--make-new-file-path (org-roam--get-new-id title) t)))
|
||||
(current-file-path (-> (or (buffer-base-buffer)
|
||||
(current-buffer))
|
||||
(buffer-file-name)
|
||||
(file-truename)
|
||||
(file-name-directory))))
|
||||
@ -271,7 +317,9 @@ Optionally pass it the title, for a smart file name."
|
||||
(insert (format "[[%s][%s]]"
|
||||
(concat "file:" (file-relative-name absolute-file-path
|
||||
current-file-path))
|
||||
(format org-roam-link-title-format title)))))
|
||||
(format org-roam-link-title-format (if prefix
|
||||
(downcase title)
|
||||
title))))))
|
||||
|
||||
;;; Finding org-roam files
|
||||
(defun org-roam-find-file ()
|
||||
@ -282,15 +330,37 @@ Optionally pass it the title, for a smart file name."
|
||||
(org-roam--find-all-files)))
|
||||
(title-or-slug (completing-read "File: " completions))
|
||||
(absolute-file-path (or (cadr (assoc title-or-slug completions))
|
||||
(org-roam--get-file-path
|
||||
(org-roam--make-new-file-path
|
||||
(org-roam--get-new-id title-or-slug) t))))
|
||||
(unless (file-exists-p absolute-file-path)
|
||||
(org-roam--make-file absolute-file-path title-or-slug))
|
||||
(find-file absolute-file-path)))
|
||||
|
||||
;;; Building the org-roam cache (asynchronously)
|
||||
(defun org-roam--get-roam-buffers ()
|
||||
"Return a list of buffers that are org-roam files."
|
||||
(--filter (and (with-current-buffer it (derived-mode-p 'org-mode))
|
||||
(buffer-file-name it)
|
||||
(org-roam--org-roam-file-p (buffer-file-name it)))
|
||||
(buffer-list)))
|
||||
|
||||
(defun org-roam-switch-to-buffer ()
|
||||
"Switch to an existing org-roam buffer using completing-read."
|
||||
(interactive)
|
||||
(let* ((roam-buffers (org-roam--get-roam-buffers))
|
||||
(names-and-buffers (mapcar (lambda (buffer)
|
||||
(cons (or (org-roam--get-title-from-cache
|
||||
(buffer-file-name buffer))
|
||||
(buffer-name buffer))
|
||||
buffer))
|
||||
roam-buffers)))
|
||||
(unless roam-buffers
|
||||
(error "No roam buffers."))
|
||||
(when-let ((name (completing-read "Choose a buffer: " names-and-buffers)))
|
||||
(switch-to-buffer (cdr (assoc name names-and-buffers))))))
|
||||
|
||||
;;; Building the org-roam cache
|
||||
(defun org-roam--build-cache-async ()
|
||||
"Builds the cache asychronously, saving it into the org-roam caches."
|
||||
"Builds the caches asychronously."
|
||||
(interactive)
|
||||
(async-start
|
||||
`(lambda ()
|
||||
@ -298,30 +368,7 @@ Optionally pass it the title, for a smart file name."
|
||||
(package-initialize)
|
||||
(require 'org-roam-utils)
|
||||
,(async-inject-variables "org-roam-directory")
|
||||
(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 org-roam-directory))
|
||||
(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)))
|
||||
(mapcar (lambda (file)
|
||||
(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)))
|
||||
(org-roam--build-cache org-roam-directory))
|
||||
(lambda (cache)
|
||||
(setq org-roam-forward-links-cache (plist-get cache :forward))
|
||||
(setq org-roam-backward-links-cache (plist-get cache :backward))
|
||||
@ -330,10 +377,20 @@ Optionally pass it the title, for a smart file name."
|
||||
(message "Org-roam cache built!"))))
|
||||
|
||||
(defun org-roam--clear-cache ()
|
||||
"Clears all entries in the caches."
|
||||
(interactive)
|
||||
(setq org-roam-cache-initialized nil)
|
||||
(setq org-roam-forward-links-cache (make-hash-table :test #'equal))
|
||||
(setq org-roam-backward-links-cache (make-hash-table :test #'equal))
|
||||
(setq org-roam-titles-cache (make-hash-table :test #'equal)))
|
||||
|
||||
(defun org-roam--clear-file-from-cache (&optional filepath)
|
||||
"Remove any related links to the file.
|
||||
|
||||
This is equivalent to removing the node from the graph."
|
||||
(let ((file (file-truename (buffer-file-name (current-buffer)))))
|
||||
(let* ((path (or filepath
|
||||
(buffer-file-name (current-buffer))))
|
||||
(file (file-truename path)))
|
||||
;; Step 1: Remove all existing links for file
|
||||
(when-let ((forward-links (gethash file org-roam-forward-links-cache)))
|
||||
;; Delete backlinks to file
|
||||
@ -356,7 +413,7 @@ This is equivalent to removing the node from the graph."
|
||||
(defun org-roam--update-cache ()
|
||||
"Update org-roam caches for the current buffer file."
|
||||
(save-excursion
|
||||
(org-roam--clear-cache)
|
||||
(org-roam--clear-file-from-cache)
|
||||
;; Insert into title cache
|
||||
(org-roam--update-cache-title)
|
||||
;; Insert new items
|
||||
@ -386,16 +443,48 @@ This is equivalent to removing the node from the graph."
|
||||
(let ((time (org-read-date nil 'to-time nil "Date: ")))
|
||||
(org-roam--new-file-named (format-time-string "%Y-%m-%d" time))))
|
||||
|
||||
;;; Org-roam buffer updates
|
||||
;;; Org-roam buffer
|
||||
(define-derived-mode org-roam-backlinks-mode org-mode "Backlinks"
|
||||
"Major mode for the org-roam backlinks buffer
|
||||
|
||||
Bindings:
|
||||
\\{org-roam-backlinks-mode-map}")
|
||||
|
||||
(define-key org-roam-backlinks-mode-map [mouse-1] 'org-roam-jump-to-backlink)
|
||||
(define-key org-roam-backlinks-mode-map (kbd "RET") 'org-roam-jump-to-backlink)
|
||||
|
||||
(defun org-roam-jump-to-backlink ()
|
||||
"Jumps to original file and location of the backlink content snippet at point"
|
||||
(interactive)
|
||||
(let ((file-from (get-text-property (point) 'file-from))
|
||||
(p (get-text-property (point) 'file-from-point)))
|
||||
(when (and file-from p)
|
||||
(find-file file-from)
|
||||
(goto-char p)
|
||||
(org-show-context))))
|
||||
|
||||
(defun org-roam--find-file (file)
|
||||
"Open FILE in the window `org-roam' was called from."
|
||||
(if (and org-roam-last-window (window-valid-p org-roam-last-window))
|
||||
(progn (with-selected-window org-roam-last-window
|
||||
(find-file file))
|
||||
(select-window org-roam-last-window))
|
||||
(find-file file)))
|
||||
|
||||
(defun org-roam-update (file-path)
|
||||
"Show the backlinks for given org file for file at `FILE-PATH'."
|
||||
(org-roam--ensure-cache-built)
|
||||
(let ((buffer-title (org-roam--get-title-or-slug file-path)))
|
||||
(with-current-buffer org-roam-buffer
|
||||
;; Locally overwrite the file opening function to re-use the
|
||||
;; last window org-roam was called from
|
||||
(setq-local
|
||||
org-link-frame-setup
|
||||
(cons '(file . org-roam--find-file) org-link-frame-setup))
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(when (not (eq major-mode 'org-mode))
|
||||
(org-mode))
|
||||
(when (not (eq major-mode 'org-roam-backlinks-mode))
|
||||
(org-roam-backlinks-mode))
|
||||
(make-local-variable 'org-return-follows-link)
|
||||
(setq org-return-follows-link t)
|
||||
(insert
|
||||
@ -408,14 +497,165 @@ This is equivalent to removing the node from the graph."
|
||||
(insert (format "** [[file:%s][%s]]\n"
|
||||
file-from
|
||||
(org-roam--get-title-or-slug file-from)))
|
||||
(dolist (content contents)
|
||||
(insert (concat (propertize (s-trim (s-replace "\n" " " content))
|
||||
'font-lock-face 'org-block)
|
||||
"\n\n"))))
|
||||
(dolist (properties contents)
|
||||
(let ((content (propertize
|
||||
(s-trim (s-replace "\n" " "
|
||||
(plist-get properties :content)))
|
||||
'font-lock-face 'org-block
|
||||
'help-echo "mouse-1: visit backlinked note"
|
||||
'file-from file-from
|
||||
'file-from-point (plist-get properties :point))))
|
||||
(insert (format "%s \n\n" content)))))
|
||||
backlinks))
|
||||
(insert "\n\n* No backlinks!")))
|
||||
(read-only-mode 1)))
|
||||
(setq org-roam-current-file file-path))
|
||||
(read-only-mode 1))))
|
||||
|
||||
;;; Building the Graphviz graph
|
||||
(defun org-roam-build-graph ()
|
||||
"Build graphviz graph output."
|
||||
(org-roam--ensure-cache-built)
|
||||
(with-temp-buffer
|
||||
(insert "digraph {\n")
|
||||
(dolist (file (org-roam--find-all-files))
|
||||
(let ((title (org-roam--get-title-or-slug file)))
|
||||
(let ((shortened-title (s-truncate org-roam-graph-max-title-length title)))
|
||||
(insert
|
||||
(format " \"%s\" [label=\"%s\", shape=%s, URL=\"roam://%s\", tooltip=\"%s\"];\n"
|
||||
title
|
||||
shortened-title
|
||||
org-roam-graph-node-shape
|
||||
file
|
||||
title
|
||||
)))))
|
||||
(maphash
|
||||
(lambda (from-link to-links)
|
||||
(dolist (to-link to-links)
|
||||
(insert (format " \"%s\" -> \"%s\";\n"
|
||||
(org-roam--get-title-or-slug from-link)
|
||||
(org-roam--get-title-or-slug to-link)))))
|
||||
org-roam-forward-links-cache)
|
||||
(insert "}")
|
||||
(buffer-string)))
|
||||
|
||||
(defun org-roam-show-graph ()
|
||||
"Generate the org-roam graph in SVG format, and display it using `org-roam-graph-viewer'."
|
||||
(interactive)
|
||||
(unless org-roam-graphviz-executable
|
||||
(setq org-roam-graphviz-executable (executable-find "dot")))
|
||||
(unless org-roam-graphviz-executable
|
||||
(user-error "Can't find graphviz executable. Please check if it is in your path"))
|
||||
(declare (indent 0))
|
||||
(let ((temp-dot (expand-file-name "graph.dot" temporary-file-directory))
|
||||
(temp-graph (expand-file-name "graph.svg" temporary-file-directory))
|
||||
(graph (org-roam-build-graph)))
|
||||
(with-temp-file temp-dot
|
||||
(insert graph))
|
||||
(call-process org-roam-graphviz-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
|
||||
(if (and org-roam-graph-viewer (executable-find org-roam-graph-viewer))
|
||||
(call-process org-roam-graph-viewer nil 0 nil temp-graph)
|
||||
(view-file temp-graph))))
|
||||
|
||||
;;; Org-roam minor mode
|
||||
(cl-defun org-roam--maybe-update-buffer (&key redisplay)
|
||||
"Update `org-roam-buffer' with the necessary information.
|
||||
This needs to be quick/infrequent, because this is run at
|
||||
`post-command-hook'."
|
||||
(let ((buffer (window-buffer)))
|
||||
(when (and (or redisplay
|
||||
(not (eq org-roam--current-buffer buffer)))
|
||||
(eq 'visible (org-roam--current-visibility))
|
||||
(buffer-local-value 'buffer-file-truename buffer))
|
||||
(setq org-roam--current-buffer buffer)
|
||||
(org-roam-update (expand-file-name
|
||||
(buffer-local-value 'buffer-file-truename buffer))))))
|
||||
|
||||
(defun org-roam--find-file-hook-function ()
|
||||
"Called by `find-file-hook' when `org-roam-mode' is on."
|
||||
(when (org-roam--org-roam-file-p)
|
||||
(add-hook 'post-command-hook #'org-roam--maybe-update-buffer nil t)
|
||||
(add-hook 'after-save-hook #'org-roam--update-cache nil t)))
|
||||
|
||||
(defvar org-roam-mode-map
|
||||
(make-sparse-keymap)
|
||||
"Keymap for org-roam commands.")
|
||||
|
||||
(defun org-roam--delete-file-advice (file &optional _trash)
|
||||
"Advice for maintaining cache consistency during file deletes."
|
||||
(org-roam--clear-file-from-cache (file-truename file)))
|
||||
|
||||
(defun org-roam--rename-file-advice (file new-file &rest args)
|
||||
"Rename backlinks of FILE to refer to NEW-FILE."
|
||||
(when (and (not (auto-save-file-name-p file))
|
||||
(not (auto-save-file-name-p new-file))
|
||||
(org-roam--org-roam-file-p new-file))
|
||||
(org-roam--ensure-cache-built)
|
||||
(org-roam--clear-file-from-cache file)
|
||||
|
||||
(let* ((files (gethash file org-roam-backward-links-cache nil))
|
||||
(path (file-truename file))
|
||||
(new-path (file-truename new-file))
|
||||
(slug (org-roam--get-title-or-slug file))
|
||||
(old-title (format org-roam-link-title-format slug))
|
||||
(new-slug (or (org-roam--get-title-from-cache path)
|
||||
(org-roam--get-title-or-slug new-path)))
|
||||
(new-title (format org-roam-link-title-format new-slug)))
|
||||
(when files
|
||||
(maphash (lambda (file-from props)
|
||||
(let* ((file-dir (file-name-directory file-from))
|
||||
(relative-path (file-relative-name new-path file-dir))
|
||||
(old-relative-path (file-relative-name path file-dir))
|
||||
(slug-regex (regexp-quote (format "[[file:%s][%s]]" old-relative-path old-title)))
|
||||
(named-regex (concat
|
||||
(regexp-quote (format "[[file:%s][" old-relative-path))
|
||||
"\\(.*\\)"
|
||||
(regexp-quote "]]"))))
|
||||
(with-temp-file file-from
|
||||
(insert-file-contents file-from)
|
||||
(while (re-search-forward slug-regex nil t)
|
||||
(replace-match (format "[[file:%s][%s]]" relative-path new-title)))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward named-regex nil t)
|
||||
(replace-match (format "[[file:%s][\\1]]" relative-path))))
|
||||
(save-window-excursion
|
||||
(find-file file-from)
|
||||
(org-roam--update-cache))))
|
||||
files))
|
||||
(save-window-excursion
|
||||
(find-file new-path)
|
||||
(org-roam--update-cache)))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode org-roam-mode
|
||||
"Minor mode for Org-roam.
|
||||
|
||||
When called interactively, toggle `org-roam-mode'. with prefix ARG, enable `org-roam-mode'
|
||||
if ARG is posiwive, otherwise disable it.
|
||||
|
||||
When called from Lisp, enable `org-roam-mode' if ARG is omitted, nil, or positive.
|
||||
If ARG is `toggle', toggle `org-roam-mode'. Otherwise, behave as if called interactively."
|
||||
:lighter "Org-Roam "
|
||||
:keymap org-roam-mode-map
|
||||
:group 'org-roam
|
||||
:require 'org-roam
|
||||
:global t
|
||||
(cond
|
||||
(org-roam-mode
|
||||
(unless org-roam-cache-initialized
|
||||
(org-roam--build-cache-async))
|
||||
(add-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(advice-add 'rename-file :after #'org-roam--rename-file-advice)
|
||||
(advice-add 'delete-file :before #'org-roam--delete-file-advice))
|
||||
(t
|
||||
(remove-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(advice-remove 'rename-file #'org-roam--rename-file-advice)
|
||||
(advice-remove 'delete-file #'org-roam--delete-file-advice)
|
||||
;; Disable local hooks for all org-roam buffers
|
||||
(dolist (buf (org-roam--get-roam-buffers))
|
||||
(with-current-buffer buf
|
||||
(remove-hook 'post-command-hook #'org-roam--maybe-update-buffer t)
|
||||
(remove-hook 'after-save-hook #'org-roam--update-cache t))))))
|
||||
|
||||
(provide 'org-roam)
|
||||
|
||||
;;; Show/hide the org-roam buffer
|
||||
(define-inline org-roam--current-visibility ()
|
||||
@ -454,93 +694,11 @@ Valid states are 'visible, 'exists and 'none."
|
||||
(defun org-roam ()
|
||||
"Pops up the window `org-roam-buffer' accordingly."
|
||||
(interactive)
|
||||
(setq org-roam-last-window (get-buffer-window))
|
||||
(pcase (org-roam--current-visibility)
|
||||
('visible (delete-window (get-buffer-window org-roam-buffer)))
|
||||
('exists (org-roam--setup-buffer))
|
||||
('none (org-roam--setup-buffer))))
|
||||
|
||||
;;; The minor mode definition that updates the buffer
|
||||
(defun org-roam--maybe-enable ()
|
||||
"Enable org-roam updating for file, if file is an org-roam file."
|
||||
(when (org-roam--org-roam-file-p)
|
||||
(org-roam--enable)))
|
||||
|
||||
(defun org-roam--enable ()
|
||||
"Enable org-roam updating for file.
|
||||
|
||||
1. If the cache does not yet exist, build it asynchronously.
|
||||
2. Setup hooks for updating the cache, and the org-roam buffer."
|
||||
(unless org-roam-cache-initialized
|
||||
(org-roam--build-cache-async))
|
||||
(add-hook 'post-command-hook #'org-roam--maybe-update-buffer nil t)
|
||||
(add-hook 'after-save-hook #'org-roam--update-cache nil t))
|
||||
|
||||
(defun org-roam--disable ()
|
||||
"Disable org-roam updating for file.
|
||||
|
||||
1. Remove hooks for updating the cache, and the org-roam buffer."
|
||||
(remove-hook 'post-command-hook #'org-roam--maybe-update-buffer)
|
||||
(remove-hook 'after-save-hook #'org-roam--update-cache))
|
||||
|
||||
(cl-defun org-roam--maybe-update-buffer (&key redisplay)
|
||||
"Update `org-roam-buffer' with the necessary information.
|
||||
This needs to be quick/infrequent, because this is run at
|
||||
`post-command-hook'."
|
||||
(with-current-buffer (window-buffer)
|
||||
(when (and (get-buffer org-roam-buffer)
|
||||
(buffer-file-name (current-buffer))
|
||||
(file-exists-p (file-truename (buffer-file-name (current-buffer))))
|
||||
(or redisplay
|
||||
(not (string= org-roam-current-file
|
||||
(file-truename (buffer-file-name (current-buffer)))))))
|
||||
(org-roam-update (file-truename (buffer-file-name (window-buffer)))))))
|
||||
|
||||
(define-minor-mode org-roam-mode
|
||||
"Global minor mode to automatically update the org-roam buffer."
|
||||
:require 'org-roam
|
||||
(if org-roam-mode
|
||||
(org-roam--maybe-enable)
|
||||
(org-roam--disable)))
|
||||
|
||||
;;; Building the Graphviz graph
|
||||
(defun org-roam-build-graph ()
|
||||
"Build graphviz graph output."
|
||||
(org-roam--ensure-cache-built)
|
||||
(with-temp-buffer
|
||||
(insert "digraph {\n")
|
||||
(dolist (file (org-roam--find-all-files))
|
||||
(insert
|
||||
(format " \"%s\" [URL=\"roam://%s\"];\n"
|
||||
(org-roam--get-title-or-slug file)
|
||||
file)))
|
||||
(maphash
|
||||
(lambda (from-link to-links)
|
||||
(dolist (to-link to-links)
|
||||
(insert (format " \"%s\" -> \"%s\";\n"
|
||||
(org-roam--get-title-or-slug from-link)
|
||||
(org-roam--get-title-or-slug to-link)))))
|
||||
org-roam-forward-links-cache)
|
||||
(insert "}")
|
||||
(buffer-string)))
|
||||
|
||||
(defun org-roam-show-graph ()
|
||||
"Generate the org-roam graph in SVG format, and display it using `org-roam-graph-viewer'."
|
||||
(interactive)
|
||||
(unless org-roam-graphviz-executable
|
||||
(setq org-roam-graphviz-executable (executable-find "dot")))
|
||||
(unless org-roam-graphviz-executable
|
||||
(user-error "Can't find graphviz executable. Please check if it is in your path"))
|
||||
(declare (indent 0))
|
||||
(let ((temp-dot (expand-file-name "graph.dot" temporary-file-directory))
|
||||
(temp-graph (expand-file-name "graph.svg" temporary-file-directory))
|
||||
(graph (org-roam-build-graph)))
|
||||
(with-temp-file temp-dot
|
||||
(insert graph))
|
||||
(call-process org-roam-graphviz-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
|
||||
(call-process org-roam-graph-viewer nil 0 nil temp-graph)))
|
||||
|
||||
(provide 'org-roam)
|
||||
|
||||
;;; org-roam.el ends here
|
||||
|
||||
;; Local Variables:
|
||||
|
@ -1,9 +0,0 @@
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
name = "docs";
|
||||
buildInput = with pkgs; [
|
||||
mkdocs
|
||||
];
|
||||
}
|
7
tests/roam-files/f1.org
Normal file
7
tests/roam-files/f1.org
Normal file
@ -0,0 +1,7 @@
|
||||
#+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
|
3
tests/roam-files/f2.org
Normal file
3
tests/roam-files/f2.org
Normal file
@ -0,0 +1,3 @@
|
||||
#+TITLE: File 2
|
||||
|
||||
This file has no links.
|
5
tests/roam-files/f3.org
Normal file
5
tests/roam-files/f3.org
Normal file
@ -0,0 +1,5 @@
|
||||
#+TITLE: File 3
|
||||
|
||||
This file has a link to an file with no title.
|
||||
|
||||
[[file:no-title.org][no-title]]
|
4
tests/roam-files/nested/f1.org
Normal file
4
tests/roam-files/nested/f1.org
Normal file
@ -0,0 +1,4 @@
|
||||
#+TITLE: Nested File 1
|
||||
|
||||
Link to [[file:f2.org][Nested File 2]]
|
||||
Link to [[file:../f1.org][File 1]]
|
3
tests/roam-files/nested/f2.org
Normal file
3
tests/roam-files/nested/f2.org
Normal file
@ -0,0 +1,3 @@
|
||||
#+TITLE: Nested File 2
|
||||
|
||||
Link to [[file:f1.org][Nested File 1]]
|
1
tests/roam-files/no-title.org
Normal file
1
tests/roam-files/no-title.org
Normal file
@ -0,0 +1 @@
|
||||
no title in this file :O
|
@ -3,7 +3,7 @@
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
;; Package-Requires: ((buttercup) (with-simulated-input))
|
||||
|
||||
;; 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
|
||||
@ -27,9 +27,204 @@
|
||||
;;;; Requirements
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'with-simulated-input)
|
||||
(require 'org-roam)
|
||||
(require 'dash)
|
||||
|
||||
(defun abs-path (file-path)
|
||||
(file-truename (expand-file-name file-path org-roam-directory)))
|
||||
|
||||
(defun org-roam--test-find-new-file (path)
|
||||
(let ((path (abs-path path)))
|
||||
(make-directory (file-name-directory path) t)
|
||||
(find-file path)))
|
||||
|
||||
(defvar org-roam--tests-directory (file-truename (concat default-directory "tests/roam-files"))
|
||||
"Directory containing org-roam test org files.")
|
||||
|
||||
(defun org-roam--test-init ()
|
||||
(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)))
|
||||
|
||||
;;; Tests
|
||||
(describe "Org-roam cache"
|
||||
(it "Mock Test"
|
||||
(expect t :to-be t)))
|
||||
(describe "org-roam--build-cache-async"
|
||||
(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-async)
|
||||
(sleep-for 3) ;; Because it's async
|
||||
|
||||
;; 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)
|
||||
|
||||
;; 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"))))
|
||||
|
||||
(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))
|
||||
|
||||
;; 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))
|
||||
|
||||
;; 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)))
|
||||
|
||||
(describe "org-roam-insert"
|
||||
(before-each
|
||||
(org-roam--test-init)
|
||||
(org-roam--clear-cache)
|
||||
(org-roam--test-build-cache))
|
||||
|
||||
(it "temp1 -> f1"
|
||||
(let ((buf (org-roam--test-find-new-file "temp1.org")))
|
||||
(with-current-buffer buf
|
||||
(with-simulated-input
|
||||
"File SPC 1 RET"
|
||||
(org-roam-insert nil))))
|
||||
(expect (buffer-string) :to-match (regexp-quote "file:f1.org")))
|
||||
|
||||
(it "temp2 -> nested/f1"
|
||||
(let ((buf (org-roam--test-find-new-file "temp2.org")))
|
||||
(with-current-buffer buf
|
||||
(with-simulated-input
|
||||
"Nested SPC File SPC 1 RET"
|
||||
(org-roam-insert nil))))
|
||||
(expect (buffer-string) :to-match (regexp-quote "file:nested/f1.org")))
|
||||
|
||||
(it "nested/temp3 -> f1"
|
||||
(let ((buf (org-roam--test-find-new-file "nested/temp3.org")))
|
||||
(with-current-buffer buf
|
||||
(with-simulated-input
|
||||
"File SPC 1 RET"
|
||||
(org-roam-insert nil))))
|
||||
(expect (buffer-string) :to-match (regexp-quote "file:../f1.org")))
|
||||
|
||||
(it "a/b/temp4 -> nested/f1"
|
||||
(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"
|
||||
(org-roam-insert nil))))
|
||||
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/f1.org"))))
|
||||
|
||||
(describe "rename file updates cache"
|
||||
(before-each
|
||||
(org-roam--test-init)
|
||||
(org-roam--clear-cache)
|
||||
(org-roam--test-build-cache))
|
||||
|
||||
(it "f1 -> new_f1"
|
||||
(rename-file (abs-path "f1.org")
|
||||
(abs-path "new_f1.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 (->> org-roam-forward-links-cache
|
||||
(gethash (abs-path "new_f1.org"))) :not :to-be nil)
|
||||
|
||||
(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]]")))
|
||||
|
||||
|
||||
(it "f1 -> f1 with spaces"
|
||||
(rename-file (abs-path "f1.org")
|
||||
(abs-path "f1 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)
|
||||
;; 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]]")))
|
||||
|
||||
(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)
|
||||
|
||||
;; 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]]"))))
|
||||
|
||||
(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)))
|
||||
|
Reference in New Issue
Block a user