mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
Compare commits
319 Commits
Author | SHA1 | Date | |
---|---|---|---|
2ff616fbd8 | |||
046822b512 | |||
cce9591c1c | |||
db4170a459 | |||
0037daaf3e | |||
888b5d1a67 | |||
ed272eaf56 | |||
551ff3b17e | |||
719594dfc7 | |||
b4b8d8c0ee | |||
0d51698839 | |||
7dc76b708b | |||
f3db974bcc | |||
bb08be4740 | |||
2490afe110 | |||
40b6c10d8a | |||
425d53d56d | |||
64e302c126 | |||
cad3518788 | |||
2a630476b3 | |||
9fd7c87b5b | |||
0b9fcbc97b | |||
e415610b05 | |||
3e186a8552 | |||
0b2218706d | |||
84334b7e16 | |||
3c52d581ae | |||
fdd834d9bf | |||
8e6938a39d | |||
a753ec097d | |||
43a5362ada | |||
a432539121 | |||
1ce760ccc7 | |||
76df9d1f3c | |||
74d714f789 | |||
6644cb27a9 | |||
94b826d759 | |||
edccf9be84 | |||
aa64cc9596 | |||
e93c77f6a5 | |||
2d1c5d78e8 | |||
f8b40a3109 | |||
7fdc7150cc | |||
8667e44187 | |||
2e94f55cc5 | |||
5c06471c3a | |||
b5436f3410 | |||
f7dc81e494 | |||
e73807efe1 | |||
1981dc3617 | |||
7d2f511251 | |||
74422df546 | |||
938c602faa | |||
45a6863e07 | |||
cdad2ee7f6 | |||
256fe73e7a | |||
f9228ce319 | |||
25c476791e | |||
78ee5c6814 | |||
3add6748ae | |||
e418037991 | |||
05f67901c6 | |||
c2e852e102 | |||
4e6f934690 | |||
d95d25615e | |||
7f453f3fff | |||
e435581215 | |||
917a325dd9 | |||
c386761914 | |||
171a8db32f | |||
bbac208fda | |||
fcefc1b1b4 | |||
0cd9b9e6d3 | |||
83a0b3d464 | |||
ed7d4f0a2e | |||
32557afdbf | |||
f144941dfb | |||
01843a6486 | |||
1f51ec91d5 | |||
2657f0b444 | |||
455f139d3e | |||
b2d9543fa2 | |||
c0871c42be | |||
007e76725c | |||
5483e65d5a | |||
b63ff2a7bb | |||
e8b4822a85 | |||
69116a4da4 | |||
3014c63d50 | |||
a073bcff5c | |||
b948cfbe37 | |||
3716817618 | |||
608feed855 | |||
6132155393 | |||
d8985aa245 | |||
61a544cebd | |||
ddaf7ec10e | |||
8318da895d | |||
9eaf91b801 | |||
3bb45afccb | |||
fee008cdfb | |||
36152590ad | |||
a69968fc12 | |||
d71675fb47 | |||
3782e88d50 | |||
b4f14eebae | |||
cce6a05630 | |||
cc3689f30f | |||
b179a5a1a6 | |||
9172001c11 | |||
c51cadfe25 | |||
f6950a9820 | |||
feb9179c9f | |||
f50d6e7376 | |||
65ea325071 | |||
62d311de22 | |||
c8a360afdd | |||
cebe77135a | |||
25d828c32e | |||
fd97c80a26 | |||
97a342fd3f | |||
d20480bb8d | |||
b163c900b8 | |||
0432b00485 | |||
ccfa97ec3a | |||
86e102d990 | |||
eed1df90f5 | |||
905564a7eb | |||
9f7a4a0b02 | |||
aafe4114c2 | |||
3e2716edf3 | |||
445e3594b2 | |||
6f5d65abd9 | |||
817d8036fb | |||
c17310f0de | |||
bf3ebe2121 | |||
47ad646d51 | |||
6263c3a956 | |||
69742c3d51 | |||
5b15159a2c | |||
c0c240b975 | |||
001de3a874 | |||
6170cc9928 | |||
cc95540135 | |||
24a683d58c | |||
86c9085363 | |||
b67bccd6c2 | |||
8b43093d1a | |||
679ef6ef00 | |||
ee9a8d423e | |||
d3c7d74329 | |||
3bf0a0a35d | |||
d0f17c6477 | |||
c90b2d68df | |||
91fd1083fe | |||
7ad5572741 | |||
b6d59e2238 | |||
2c5f429b24 | |||
7068d63e96 | |||
898295f4a0 | |||
abe63b4360 | |||
e992fc27e2 | |||
c3889b3b17 | |||
8c3c216191 | |||
023af3ec32 | |||
67f10864df | |||
c9efbe1dda | |||
ae533fa309 | |||
e4188179f6 | |||
91aae05810 | |||
a2e46db808 | |||
d93423d4e1 | |||
1af1639ee0 | |||
db573bbc4e | |||
50cc4d6990 | |||
b96efbb444 | |||
721f167563 | |||
25bc3acce3 | |||
d8c7f7d4e7 | |||
01c45f9dca | |||
239929045f | |||
d26047e6f7 | |||
36928d655a | |||
4a2b44252e | |||
3177b900e7 | |||
4d71fbdfe1 | |||
84f58cbc12 | |||
3e47f198c7 | |||
c789531e36 | |||
1b221a1d4a | |||
d0fd2c6959 | |||
2c75b194d8 | |||
c8f8c3e876 | |||
852042436e | |||
dafcf0dcf8 | |||
2b6f8ce615 | |||
5651f4598c | |||
e9299297f9 | |||
617e0021f5 | |||
f80515ab5f | |||
4af5ff662e | |||
73dfeb60cf | |||
5b933bd8ec | |||
3a54182bb1 | |||
34243a0a90 | |||
54d17cc50f | |||
54b63db350 | |||
8f2c51ad21 | |||
3b93c83b23 | |||
a101c548c1 | |||
1b8ece0219 | |||
0ecbd3a104 | |||
66e10df943 | |||
d2fc4be73b | |||
17d8e84ea5 | |||
777f969b50 | |||
bc833a9ff4 | |||
ec8d250f6c | |||
1795039ab9 | |||
a8a36a420b | |||
340215a16a | |||
941bd1f6b4 | |||
48a5d01726 | |||
0e6b93a253 | |||
cfabe0ec38 | |||
ab87a08e17 | |||
714ea7a3fc | |||
4c4b024b49 | |||
5dde894a0c | |||
74a6fd598a | |||
e9ae19c01c | |||
d25d477b4f | |||
04b7780ff9 | |||
858f531d96 | |||
f819720c51 | |||
887c49c89c | |||
d4cbb1d499 | |||
f29a770af3 | |||
4a7ecfbed8 | |||
df65654b2f | |||
b627298171 | |||
b8a66deae9 | |||
aafc8606bb | |||
95afbc676a | |||
59faa3fdaa | |||
c51ce08a40 | |||
8d4de78fac | |||
8a21131d9f | |||
3c59c7d74e | |||
5dce6261a2 | |||
39cd819cfa | |||
eb1d420c29 | |||
1f853ad8e6 | |||
3c396f9e91 | |||
f227c03672 | |||
39b2388768 | |||
b24d874f26 | |||
dbb4c592fa | |||
65b463d3c6 | |||
2d8dc8e31b | |||
56e66f92d2 | |||
1d03f87cd1 | |||
48d1c152f5 | |||
e8b720faad | |||
cf918c3d18 | |||
5fc57b2e06 | |||
829ee68860 | |||
127d6efa48 | |||
d3b7c9b921 | |||
a84da59b41 | |||
3f2d42142c | |||
b3b6277b96 | |||
20514b7c6d | |||
1db4c34c8a | |||
0c24540639 | |||
1848ca2495 | |||
e31ac73a4d | |||
33ed817826 | |||
de47f0a28d | |||
04a0fec5c1 | |||
7723f6ca88 | |||
6dc8dda5e5 | |||
4455762c38 | |||
6ce07cdbf7 | |||
9acd982332 | |||
028c95a011 | |||
d1e3a5d9be | |||
8e89bad945 | |||
9c10a3c04c | |||
1aba91eacd | |||
2fe233ffa0 | |||
d9015cb931 | |||
7d5b7e6185 | |||
3ea433c09d | |||
e5c735c86a | |||
23605546d8 | |||
f50e30dd51 | |||
a529b20a81 | |||
6cbd4ad3e8 | |||
e997c017de | |||
63450e9eaf | |||
da02453ab1 | |||
bbf1d97eb0 | |||
7d9fcf5288 | |||
d0be7f3b2a | |||
363dca1765 | |||
4c5a041556 | |||
5e42d854c1 | |||
681873759d | |||
2168490d5a | |||
3a78422a09 | |||
aee3467b3e | |||
756f6215b6 | |||
53c9a16e90 | |||
8ad1414030 | |||
f754160402 | |||
02e35e3b01 | |||
d2e933cc3e | |||
15c1a46e41 |
@ -1,6 +1,16 @@
|
||||
;;; Directory Local Variables
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((emacs-lisp-mode
|
||||
(eval . (require 'org-roam-dev))
|
||||
(eval . (org-roam-dev-mode))))
|
||||
(fill-column . 110)
|
||||
(indent-tabs-mode . nil)
|
||||
(elisp-lint-ignored-validators . ("byte-compile" "package-lint"))
|
||||
(elisp-lint-indent-specs . ((describe . 1)
|
||||
(it . 1)
|
||||
(thread-first . 0)
|
||||
(cl-flet . 1)
|
||||
(cl-flet* . 1)
|
||||
(org-element-map . defun)
|
||||
(org-roam-dolist-with-progress . 2)
|
||||
(org-roam-with-temp-buffer . 1)
|
||||
(org-with-point-at . 1)
|
||||
(magit-insert-section . defun)
|
||||
(magit-section-case . 0)
|
||||
(org-roam-with-file . 2)))))
|
||||
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1,6 +1,6 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [jethrokuan, zaeph]
|
||||
github: [jethrokuan]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -39,5 +39,3 @@ Example:
|
||||
### Environment
|
||||
|
||||
<!-- Please M-x org-roam-diagnostics and paste results here -->
|
||||
|
||||
- Org-roam commit: https://github.com/jethrokuan/org-roam/commit/commithashhere
|
||||
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -4,7 +4,7 @@ name: "Docs"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
43
.github/workflows/test.yml
vendored
43
.github/workflows/test.yml
vendored
@ -28,45 +28,38 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
emacs_version:
|
||||
# REVIEW: we do not yet have the bootstrapping in place to test
|
||||
# versions of emacs without built-in sqlite (pre 29.1)
|
||||
# - 27.2
|
||||
# - 28.2
|
||||
- 29.4
|
||||
- snapshot
|
||||
steps:
|
||||
- uses: purcell/setup-emacs@master
|
||||
with:
|
||||
version: ${{ matrix.emacs_version }}
|
||||
- uses: purcell/setup-emacs@master
|
||||
with:
|
||||
version: ${{ matrix.emacs_version }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Create Sandbox Directory
|
||||
run: |
|
||||
SANDBOX_DIR=$(mktemp -d) || exit 1
|
||||
echo "SANDBOX_DIR=$SANDBOX_DIR" >> $GITHUB_ENV
|
||||
- name: Install Eldev
|
||||
run: curl -fsSL https://raw.github.com/org-roam/org-roam/master/github-eldev | sh
|
||||
|
||||
- name: Initialize Sandbox
|
||||
run: |
|
||||
./makem.sh -vv --sandbox $SANDBOX_DIR --install-deps --install-linters
|
||||
- name: Install dependencies
|
||||
run: make prepare
|
||||
|
||||
# The "all" rule is not used, because it treats compilation warnings
|
||||
# as failures, so linting and testing are run as separate steps.
|
||||
|
||||
# org-roam-compat is excluded from linting because it contains
|
||||
# symbols/aliases from other packages
|
||||
- name: Lint
|
||||
continue-on-error: false
|
||||
run: ./makem.sh -vv --sandbox $SANDBOX_DIR --exclude org-roam-compat.el lint
|
||||
|
||||
- name: Test
|
||||
if: always() # Run test even if linting fails.
|
||||
run: ./makem.sh -vv --sandbox $SANDBOX_DIR test
|
||||
- name: Lint
|
||||
run: make lint
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
# Local Variables:
|
||||
# eval: (outline-minor-mode)
|
||||
# End:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@
|
||||
/doc/stats/
|
||||
/config.mk
|
||||
/doc/manual.html
|
||||
/.eldev/
|
||||
|
229
CHANGELOG.md
229
CHANGELOG.md
@ -1,5 +1,206 @@
|
||||
# Changelog
|
||||
## 1.2.4 (TBD)
|
||||
|
||||
## 2.3.0 (2025-05-24)
|
||||
|
||||
* (perf)node-read: filter before map to candidate by @russmatney in https://github.com/org-roam/org-roam/pull/2168
|
||||
* (chore): remove extraneous changelog line by @jethrokuan in https://github.com/org-roam/org-roam/pull/2172
|
||||
* (fix)org-roam-file-p: don't exclude org-roam-directory by @toregilhk in https://github.com/org-roam/org-roam/pull/2178
|
||||
* (chore) fix version numbers by @jethrokuan in https://github.com/org-roam/org-roam/pull/2182
|
||||
* (fix) org-roam-file-p handle opening a buffer with no path by @jethrokuan in https://github.com/org-roam/org-roam/pull/2185
|
||||
* (fix)db: update atime on file access by @jethrokuan in https://github.com/org-roam/org-roam/pull/2174
|
||||
* Fix org-roam-extract-subtree by @ralfdoering in https://github.com/org-roam/org-roam/pull/2191
|
||||
* (node): fix org-roam-node-at-point check by @jethrokuan in https://github.com/org-roam/org-roam/pull/2195
|
||||
* (chore): reapply #2178 by @jethrokuan in https://github.com/org-roam/org-roam/pull/2197
|
||||
* Solved issue #2192 (Respect blank lines in capture templates) by @alopezrivera in https://github.com/org-roam/org-roam/pull/2203
|
||||
* (chore)ci: /s/master/main by @jethrokuan in https://github.com/org-roam/org-roam/pull/2204
|
||||
* support custom minibuffer matching function by @cuttlefisch in https://github.com/org-roam/org-roam/pull/2177
|
||||
* (db)fix: org-roam-db-connector group by @jethrokuan in https://github.com/org-roam/org-roam/pull/2209
|
||||
* ensure unique candidates in `org-roam-ref-find` by @bdarcus in https://github.com/org-roam/org-roam/pull/2208
|
||||
* (db)fix: FOREIGN KEY error in narrowed buffer by @psii in https://github.com/org-roam/org-roam/pull/2215
|
||||
* (feat): org-roam-property-* code duplication removed (#2217) by @clanghans in https://github.com/org-roam/org-roam/pull/2218
|
||||
* (db)fix: file modification detection failing in some edge cases by @kisaragi-hiu in https://github.com/org-roam/org-roam/pull/2221
|
||||
* (fix): autoload org-roam-list-files by @jethrokuan in https://github.com/org-roam/org-roam/pull/2226
|
||||
* (fix): links not displayed properly in org-roam-buffer by @ntharim in https://github.com/org-roam/org-roam/pull/2236
|
||||
* (fix): remove use of deprecated org-font-lock-ensure by @jethrokuan in https://github.com/org-roam/org-roam/pull/2238
|
||||
* was missing the FULL parameter to `org-end-of-meta-data` - fixes #2242 by @jmay in https://github.com/org-roam/org-roam/pull/2246
|
||||
* (Docs): Fixed typos in the docs by @hnvy in https://github.com/org-roam/org-roam/pull/2256
|
||||
* Fix space chars in roam refs by @hwiorn in https://github.com/org-roam/org-roam/pull/2285
|
||||
* org-roam-tag-add: use tags separator as crm-separator by @Hugo-Heagren in https://github.com/org-roam/org-roam/pull/2282
|
||||
* Support multi-line org titles by @FelixBrendel in https://github.com/org-roam/org-roam/pull/2264
|
||||
* (Docs): orgmode.org/elpa has been shut down by @aviad in https://github.com/org-roam/org-roam/pull/2258
|
||||
* Minor typo in the commented text of the definition of `org-roam-refile` by @apc in https://github.com/org-roam/org-roam/pull/2263
|
||||
* fix outline of link in properties by @bhuztez in https://github.com/org-roam/org-roam/pull/2230
|
||||
* (fix): update `org-roam-unlinked-references-section` check by @Elilif in https://github.com/org-roam/org-roam/pull/2254
|
||||
* Add support for filtering backlinks. by @swflint in https://github.com/org-roam/org-roam/pull/2247
|
||||
* (docs): update documentation on database-connectors by @jethrokuan in https://github.com/org-roam/org-roam/pull/2298
|
||||
* (db): default to sqlite-builtin when possible by @jethrokuan in https://github.com/org-roam/org-roam/pull/2299
|
||||
* don't complete org-roam nodes in source blocks by @ParetoOptimalDev in https://github.com/org-roam/org-roam/pull/2292
|
||||
* (core): add org-roam-node-category by @jethrokuan in https://github.com/org-roam/org-roam/pull/2300
|
||||
* add discoverability support for age encrypted org files by @anticomputer in https://github.com/org-roam/org-roam/pull/2302
|
||||
* (minor): fix lints on main by @jethrokuan in https://github.com/org-roam/org-roam/pull/2320
|
||||
* utils: descendant-of-p: Defend against nils by @qzdl in https://github.com/org-roam/org-roam/pull/2319
|
||||
* Add customisable function for prompting when adding refs by @Hugo-Heagren in https://github.com/org-roam/org-roam/pull/2317
|
||||
* Fix org-fold-core-style in org-roam-buffer by @hwiorn in https://github.com/org-roam/org-roam/pull/2325
|
||||
* depend on snapshot of emacsql by @jethrokuan in https://github.com/org-roam/org-roam/pull/2327
|
||||
* (node): org-roam-node-at-point: don't error in non-org buffers by @Hugo-Heagren in https://github.com/org-roam/org-roam/pull/2329
|
||||
* (node) add optional NOCASE parameter to org-roam-node-from-title-or-alias by @nuthub in https://github.com/org-roam/org-roam/pull/2403
|
||||
* (docs): update org-protocol instructions by @benthamite in https://github.com/org-roam/org-roam/pull/2401
|
||||
* Fix some typos by @feltcat in https://github.com/org-roam/org-roam/pull/2430
|
||||
* (docs): fix name of my-org-roam-show-backlink-p by @Delapouite in https://github.com/org-roam/org-roam/pull/2447
|
||||
* Link Martin Edström's knowledge base by @meedstrom in https://github.com/org-roam/org-roam/pull/2394
|
||||
* Update org-roam.org by @marcosbodio in https://github.com/org-roam/org-roam/pull/2441
|
||||
* (chore): fix indent blocking ci lint execution by @Delapouite in https://github.com/org-roam/org-roam/pull/2448
|
||||
* Fix Issue #2410 - Unlinked References doesnt work if the org-roam-directory has a space in it. by @vikram-mandyam in https://github.com/org-roam/org-roam/pull/2411
|
||||
* (test): add "org-roam--list-files-search-globs" by @Delapouite in https://github.com/org-roam/org-roam/pull/2449
|
||||
* Backlink heading by @toregilhk in https://github.com/org-roam/org-roam/pull/2333
|
||||
* (test): add org-roam-id-find by @Delapouite in https://github.com/org-roam/org-roam/pull/2450
|
||||
* (test): add org-roam-id-at-point by @Delapouite in https://github.com/org-roam/org-roam/pull/2451
|
||||
* (test): add org-roam--buffer-promoteable-p by @Delapouite in https://github.com/org-roam/org-roam/pull/2452
|
||||
* (test): add org-roam--get-titles by @Delapouite in https://github.com/org-roam/org-roam/pull/2453
|
||||
* (test): add org-roam-node-from-{id|title-or-alias} by @Delapouite in https://github.com/org-roam/org-roam/pull/2454
|
||||
* (test): add org-roam-alias-{add|remove} by @Delapouite in https://github.com/org-roam/org-roam/pull/2455
|
||||
* (docs): add missing versions dates and obsolete notice by @Delapouite in https://github.com/org-roam/org-roam/pull/2456
|
||||
* (docs): explain org-roam-file-exclude-regexp by @adamoudad in https://github.com/org-roam/org-roam/pull/2458
|
||||
* (test): add org-roam-demote-entire-buffer by @Delapouite in https://github.com/org-roam/org-roam/pull/2459
|
||||
* (test): add org-roam-file-p by @Delapouite in https://github.com/org-roam/org-roam/pull/2460
|
||||
* (test): add org-roam-buffer-p by @Delapouite in https://github.com/org-roam/org-roam/pull/2461
|
||||
* (fix): remove dead-code about org-roam-shield feature by @Delapouite in https://github.com/org-roam/org-roam/pull/2462
|
||||
* (test): add org-roam-db--file-hash by @Delapouite in https://github.com/org-roam/org-roam/pull/2464
|
||||
* (test): add org-roam-db-get-{scheduled|deadline}-time by @Delapouite in https://github.com/org-roam/org-roam/pull/2465
|
||||
* Doc formatting: 2 fixes by @kevinrineer in https://github.com/org-roam/org-roam/pull/2475
|
||||
* Take org-roam-node as argument to #'org-roam-refile by @pestctrl in https://github.com/org-roam/org-roam/pull/2388
|
||||
* Compatibility with latest org-id version: advise org-id-find rather than overwriting id link by @ricklupton in https://github.com/org-roam/org-roam/pull/2432
|
||||
* Depend on emacsql 4.0.0 by @bcc32 in https://github.com/org-roam/org-roam/pull/2466
|
||||
* Use regexp match to replace hard-coded path equal test by @manphiz in https://github.com/org-roam/org-roam/pull/2497
|
||||
* Align sqlite integration with emacsql 4.0 by @dustinfarris in https://github.com/org-roam/org-roam/pull/2503
|
||||
* fix #2425 Prevent data loss when user has called org-roam-extract-subtree on folded org headline by @akashpal-21 in https://github.com/org-roam/org-roam/pull/2444
|
||||
* Rely on emacsql-sqlite-open to pick the best available back-end by @tarsius in https://github.com/org-roam/org-roam/pull/2486
|
||||
* Set org-roam-directory to a non-existent path to ensure robust test by @manphiz in https://github.com/org-roam/org-roam/pull/2499
|
||||
* (perf): Deprecate link :outline properties by @meedstrom in https://github.com/org-roam/org-roam/pull/2509
|
||||
* Bump DB version to avoid error by @meedstrom in https://github.com/org-roam/org-roam/pull/2514
|
||||
|
||||
|
||||
## 2.2.2 (2022-04-25)
|
||||
|
||||
### Breaking
|
||||
### Added
|
||||
- [#2138](https://github.com/org-roam/org-roam/pull/2138) export: add new module
|
||||
- [#2170](https://github.com/org-roam/org-roam/pull/2170) log: add new module for working with org logs
|
||||
- [#2158](https://github.com/org-roam/org-roam/pull/2158) db: support emacsql-sqlite-builtin and emacsql-sqlite-module
|
||||
- [#2160](https://github.com/org-roam/org-roam/pull/2160) core: support a list of `org-roam-file-exclude-regexp`
|
||||
|
||||
### Removed
|
||||
### Fixed
|
||||
- [#2091](https://github.com/org-roam/org-roam/pull/2091) node: fix org-roam-promote-entire-buffer structural errors
|
||||
- [#2130](https://github.com/org-roam/org-roam/pull/2130) buffer: unlinked-references section now also searches within symlinked directories
|
||||
- [#2152](https://github.com/org-roam/org-roam/pull/2152) org-roam-preview-default-function: doesn't copy copy content of next heading node when current node's content is empty
|
||||
- [#2159](https://github.com/org-roam/org-roam/pull/2159) db: fix db syncs on narrowed buffers
|
||||
- [#2156](https://github.com/org-roam/org-roam/pull/2157) capture: templates with functions are handled correctly to avoid signaling `char-or-string-p`
|
||||
|
||||
### Changed
|
||||
- [#2160](https://github.com/org-roam/org-roam/pull/2160) core: ignore files in `org-attach-id-dir` by default
|
||||
|
||||
## 2.2.1 (2022-03-15)
|
||||
|
||||
### Breaking
|
||||
- [#2054](https://github.com/org-roam/org-roam/pull/2054) node: simplify default `org-roam-node-display-template`.
|
||||
This was done so completions work fine by default on all completion systems. To restore the tabular vertical completion interface, set this in your configuration:
|
||||
|
||||
```emacs-lisp
|
||||
(setq org-roam-node-display-template
|
||||
(concat "${title:*} "
|
||||
(propertize "${tags:10}" 'face 'org-tag)))
|
||||
```
|
||||
|
||||
### Added
|
||||
- [#2042](https://github.com/org-roam/org-roam/pull/2042) db: add `org-roam-db-extra-links-elements` and `org-roam-db-extra-links-exclude-keys` for fine-grained control over additional link parsing
|
||||
- [#2049](https://github.com/org-roam/org-roam/pull/2049) capture: allow ID to be used as part of `org-roam-capture-templates`
|
||||
- [#2050](https://github.com/org-roam/org-roam/pull/2050) core: add `FILTER-FN` to `org-roam-node-random`
|
||||
- [#2065](https://github.com/org-roam/org-roam/pull/2065) dailies: add `keys` argument to the remaining dailies functions `org-roam-dailies-goto-yesterday`/`-today`/`-tomorrow`/`-date` and `org-roam-dailies-capture-yesterday`/`-tomorrow`/`-date` to give the abilty to get into a capture buffer bypassing the selection screen in all dailies commands. Extension of #2055
|
||||
- [#2079](https://github.com/org-roam/org-roam/pull/2079) capture: ensure that `:ref` info captured in all cases.
|
||||
- [#2121](https://github.com/org-roam/org-roam/pull/2121) buffer: add unique option to `org-roam-backlinks-section`
|
||||
|
||||
### Removed
|
||||
### Fixed
|
||||
- [#2086](https://github.com/org-roam/org-roam/pull/2086) capture: correctly update org-id-locations on capture
|
||||
- [#2082](https://github.com/org-roam/org-roam/pull/2082) buffer: don't destroy window if `org-roam-node-toggle` reuses window
|
||||
- [#2080](https://github.com/org-roam/org-roam/pull/2080) dailies: prevent multiple "dailies/" subdir expansions
|
||||
- [#2055](https://github.com/org-roam/org-roam/pull/2055) dailies: removed stray f require, which was causing require and compilation errors
|
||||
- [#2117](https://github.com/org-roam/org-roam/pull/2117) capture: preserve trailing whitespace content in capture templates
|
||||
|
||||
### Changed
|
||||
- [#2060](https://github.com/org-roam/org-roam/pull/2060) node: added double acute accent normalization for Unicode characters in titles
|
||||
- [#2040](https://github.com/org-roam/org-roam/pull/2040) completions: fix completions display-width for Helm users
|
||||
- [#2025](https://github.com/org-roam/org-roam/pull/2025) chore: removed the dependencies on f.el and s.el
|
||||
- [#2109](https://github.com/org-roam/org-roam/pull/2109) capture: `org-roam-node-insert` places cursor after inserted link where appropriate
|
||||
- [#2123](https://github.com/org-roam/org-roam/pull/2123), [#2124](https://github.com/org-roam/org-roam/pull/2124) buffer: `org-roam-mode-section-functions` renamed to `org-roam-mode-sections`, supports passing args into the section-rendering function
|
||||
|
||||
## 2.2.0 (2022-01-14)
|
||||
|
||||
### Added
|
||||
- [#1806](https://github.com/org-roam/org-roam/pull/1806), [#2017](https://github.com/org-roam/org-roam/pull/2017) db: support caching and usage of Org 9.5's in-built citations
|
||||
- [#1963](https://github.com/org-roam/org-roam/pull/1963) db: cache file title into files table
|
||||
- [#1977](https://github.com/org-roam/org-roam/pull/1977) db: support Org-ref v3 citations
|
||||
- [#1907](https://github.com/org-roam/org-roam/pull/1907), [#2009](https://github.com/org-roam/org-roam/pull/2009), [#2018](https://github.com/org-roam/org-roam/pull/2018) db: support sqlite3
|
||||
- [#2028](https://github.com/org-roam/org-roam/pull/2028) dailies: add `keys` argument to `org-roam-dailies-capture-today` and `org-roam-dailies--capture` functions to give the abilty to get into a capture buffer bypassing the selection screen
|
||||
|
||||
### Removed
|
||||
### Changed
|
||||
- [#1795](https://github.com/org-roam/org-roam/pull/1795) buffer: optimized reflinks fetch
|
||||
- [#1809](https://github.com/org-roam/org-roam/pull/1809) capture: the mandatory `:if-new` property of each capture template is now renamed to `:target`
|
||||
- [#1829](https://github.com/org-roam/org-roam/pull/1829) perf: file sql updates are now wrapped in a transaction
|
||||
- [#1877](https://github.com/org-roam/org-roam/pull/1877) dailies: stop asking for time, only date
|
||||
- [#1949](https://github.com/org-roam/org-roam/pull/1949) db: check for property drawers are now case-insensitive
|
||||
|
||||
### Fixed
|
||||
- [#1798](https://github.com/org-roam/org-roam/pull/1798) org-roam-node-at-point: do not skip invisible headings
|
||||
- [#1807](https://github.com/org-roam/org-roam/pull/1807) capture: always trigger `:if-new` template for existing nodes
|
||||
- [#1813](https://github.com/org-roam/org-roam/pull/1813) db: prevent empty ROAM_ALIASES from crashing db updates
|
||||
- [#1816](https://github.com/org-roam/org-roam/pull/1816) db: prevent invalid ROAM_REFS from crashing db updates
|
||||
- [#1893](https://github.com/org-roam/org-roam/pull/1893), [#1896](https://github.com/org-roam/org-roam/pull/1896), [#1901](https://github.com/org-roam/org-roam/pull/1901), [#1895](https://github.com/org-roam/org-roam/pull/1895), [#1904](https://github.com/org-roam/org-roam/pull/1904) completions: various mini-buffer related completion fixes
|
||||
- [#1931](https://github.com/org-roam/org-roam/pull/1931) utils: org-roam-set-keyword now skips over all drawers
|
||||
- [#1947](https://github.com/org-roam/org-roam/pull/1947) db: links in ROAM_REFS are no longer considered as links
|
||||
- [#1948](https://github.com/org-roam/org-roam/pull/1948) completions: fix same-line completions
|
||||
- [#1953](https://github.com/org-roam/org-roam/pull/1953) db: refresh CATEGORY before writing to db
|
||||
- [#1946](https://github.com/org-roam/org-roam/pull/1946), [#1946](https://github.com/org-roam/org-roam/pull/1946), [#1958](https://github.com/org-roam/org-roam/pull/1958) various performance improvements
|
||||
- [#1980](https://github.com/org-roam/org-roam/pull/1980) utils: fix org-roam-with-file changing the minor-mode
|
||||
- [#2016](https://github.com/org-roam/org-roam/pull/2016) db: fix node caching being affected by agenda variables
|
||||
- [#2033](https://github.com/org-roam/org-roam/pull/2023) db: respect local variables during db parsing
|
||||
|
||||
## 2.1.0 (2021-08-20)
|
||||
|
||||
### Added
|
||||
- [#1693](https://github.com/org-roam/org-roam/pull/1693) added `filter-fn` and `templates` parameter to `org-roam-capture`, `org-roam-node-find`, and `org-roam-node-insert`
|
||||
- [#1709](https://github.com/org-roam/org-roam/pull/1709) added ability to specify default value in org-roam capture templates
|
||||
- [#1710](https://github.com/org-roam/org-roam/pull/1710) added `org-roam-extract-subtree`
|
||||
- [#1720](https://github.com/org-roam/org-roam/pull/1720) added `org-roam-db-update-on-save`
|
||||
- [#1758](https://github.com/org-roam/org-roam/pull/1758) added `org-roam-db-autosync-mode`, replacing `org-roam-setup` and `org-roam-teardown`
|
||||
|
||||
### Removed
|
||||
- [#1716](https://github.com/org-roam/org-roam/pull/1716) helper function `org-roam-get-keyword` is now obsolete: prefer `org-collect-keywords`
|
||||
|
||||
### Changed
|
||||
- [#1595](https://github.com/org-roam/org-roam/pull/1595), [#1724](https://github.com/org-roam/org-roam/pull/1724) Major refactoring and restructuring of the codebase
|
||||
- [#1655](https://github.com/org-roam/org-roam/pull/1655) improved org-roam contents preview
|
||||
- [#1741](https://github.com/org-roam/org-roam/pull/1741) expose `org-roam-capture-` keys in interactive commands
|
||||
- [#1786](https://github.com/org-roam/org-roam/pull/1786) org-roam-version now outputs commit hash if found
|
||||
- [#1788](https://github.com/org-roam/org-roam/pull/1788) the point is not moved if the node is already visited
|
||||
|
||||
### Fixed
|
||||
- [#1608](https://github.com/org-roam/org-roam/pull/1608) migration: empty ROAM_REFS are now removed
|
||||
- [#1609](https://github.com/org-roam/org-roam/pull/1609) migration: fixed file-link replacement
|
||||
- [#1653](https://github.com/org-roam/org-roam/pull/1653) migration: fixed tags migration
|
||||
- [#1694](https://github.com/org-roam/org-roam/pull/1694) core: nodes with no title are now skipped
|
||||
- [#1637](https://github.com/org-roam/org-roam/pull/1637) core: fix org-ref multi-cite links not being split
|
||||
- [#1651](https://github.com/org-roam/org-roam/pull/1651) core: fix org-roam-file-p crashing when there is no corresponding file
|
||||
- [#1705](https://github.com/org-roam/org-roam/pull/1705) core: fix for add/remove of file-level tags
|
||||
- [#1769](https://github.com/org-roam/org-roam/pull/1769) core: gracefully handle absence of `org-id-locations-file`
|
||||
- [#1713](https://github.com/org-roam/org-roam/pull/1713) capture: check for file-existence before template insertion
|
||||
- [#1725](https://github.com/org-roam/org-roam/pull/1725) db: always compute hash of encrypted file to prevent re-processing of encrypted files
|
||||
|
||||
## 2.0.0 (2021-07-17)
|
||||
|
||||
A few symbols have been marked as obsolete. Have a look at the content of [org-roam-compat.el](https://github.com/org-roam/org-roam/blob/f819720c510185af713522c592833ec9f2934251/org-roam-compat.el#L159)
|
||||
|
||||
### Added
|
||||
- [#1396](https://github.com/org-roam/org-roam/pull/1396) add option to choose between prepending, appending, and omitting `roam_tags` in file completion
|
||||
@ -7,9 +208,13 @@
|
||||
- [#1353](https://github.com/org-roam/org-roam/pull/1353) support file-level property drawers
|
||||
|
||||
### Changed
|
||||
|
||||
- [#1352](https://github.com/org-roam/org-roam/pull/1352) prefer lower-case for roam_tag and roam_alias in interactive commands
|
||||
- [#1513](https://github.com/org-roam/org-roam/pull/1513) replaced hardcoded "svg" with defcustom org-roam-graph-filetype
|
||||
- [#1540](https://github.com/org-roam/org-roam/pull/1540) allow `roam_tag` and `roam_alias` to be specified on multiple lines
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#1281](https://github.com/org-roam/org-roam/pull/1281) fixed idle-timer not instantiated on `org-roam-mode`
|
||||
- [#1308](https://github.com/org-roam/org-roam/pull/1308) fixed file renames corrupting database
|
||||
- [#1325](https://github.com/org-roam/org-roam/pull/1325) make titles and tags extracted unique per note
|
||||
@ -18,17 +223,19 @@
|
||||
- [#1347](https://github.com/org-roam/org-roam/pull/1347) allow use of `%a` element in regular Org-roam captures
|
||||
- [#1352](https://github.com/org-roam/org-roam/pull/1352) fixed org-roam-{tag/alias}-{add/delete} altering the original case of the Org property
|
||||
- [#1374](https://github.com/org-roam/org-roam/pull/1374) fix headline completions erroring out
|
||||
- [#1375](https://github.com/org-roam/org-roam/pull/1375) fix org-roam-protocol to use existing ref file
|
||||
- [#1375](https://github.com/org-roam/org-roam/pull/1375) fix org-roam-protocol to use existing ref file
|
||||
- [#1403](https://github.com/org-roam/org-roam/issues/1403) fixed inconsistency between how we write and read props like alias and tags
|
||||
- [#1409](https://github.com/org-roam/org-roam/issues/1398) prevent inclusion of non-org-roam files in `org-roam-dailies--list-files`
|
||||
- [#1542](https://github.com/org-roam/org-roam/issues/1542) fix files not excluded when `org-roam-list-files-commands` is nil
|
||||
|
||||
## 1.2.3 (13-11-2020)
|
||||
## 1.2.3 (2020-11-13)
|
||||
|
||||
Primarily a stabilization and bug-fix release.
|
||||
|
||||
Org-roam-dailies has also been revamped to include new features, see [this video](https://www.youtube.com/watch?v=1q9x2aZCJJ4) for a quick overview.
|
||||
|
||||
### Added
|
||||
|
||||
- [#978](https://github.com/org-roam/org-roam/pull/978) Revamp org-roam-dailies
|
||||
- [#1183](https://github.com/org-roam/org-roam/pull/1183) Interactive functions for managing aliases and tags in Org-roam file, namely `org-roam-alias-add`, `org-roam-alias-delete`, `org-roam-tag-add`, and `org-roam-tag-delete`.
|
||||
- [#1215](https://github.com/org-roam/org-roam/pull/1215) Multiple `ROAM_KEY` keywords can now be specified in one file. This allows bibliographical entries to share the same note file.
|
||||
@ -37,9 +244,11 @@ Org-roam-dailies has also been revamped to include new features, see [this video
|
||||
- [#1264](https://github.com/org-roam/org-roam/pull/1264) add `org-roam-db-update-method` to control when the cache is rebuilt.
|
||||
|
||||
### Changed
|
||||
|
||||
- [#1264](https://github.com/org-roam/org-roam/pull/1264) renamed `org-roam-update-db-idle-seconds` to `org-roam-db-idle-idle-seconds`
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#1074](https://github.com/org-roam/org-roam/issues/1074) fix `org-roam--extract-links` to handle content boundaries.
|
||||
- [#1193](https://github.com/org-roam/org-roam/issues/1193) fix `org-roam-db-build-cache` by not killing temporary buffer in `org-roam--extract-links`.
|
||||
- [#1195](https://github.com/org-roam/org-roam/issues/1195) fix ID face showing as invalid if within Org ID files, but not Org-roam's.
|
||||
@ -50,7 +259,7 @@ Org-roam-dailies has also been revamped to include new features, see [this video
|
||||
- [#1233](https://github.com/org-roam/org-roam/issues/1233) fixes bug where descriptive file links become plain links during update for relative paths
|
||||
- [#1252](https://github.com/org-roam/org-roam/issues/1252) respect original link type during automatic replacement
|
||||
|
||||
## 1.2.2 (06-10-2020)
|
||||
## 1.2.2 (2020-10-06)
|
||||
|
||||
In this release we support fuzzy links of the form `[[roam:Title]]`, `[[roam:*Headline]]` and `[[roam:Title*Headline]]`. Completion for these fuzzy links is supported via `completion-at-point`.
|
||||
|
||||
@ -88,7 +297,7 @@ This change requires you to set `org-roam-directory` to the resolved path of a f
|
||||
- [#1057](https://github.com/org-roam/org-roam/pull/1057) Improve performance of database builds by preventing generation of LaTeX and image previews.
|
||||
- [#1103](https://github.com/org-roam/org-roam/pull/1103) Fix long build-times for Windows on files with URL links
|
||||
|
||||
## 1.2.1 (27-07-2020)
|
||||
## 1.2.1 (2020-07-27)
|
||||
|
||||
This release consisted of a big deal of refactoring and bug fixes. Notably, we fixed several catastrophic failures on db builds with bad setups (#854), and modularized tag and title extractions.
|
||||
|
||||
@ -121,7 +330,7 @@ We also added some new features that had been a long time coming:
|
||||
- [#894](https://github.com/org-roam/org-roam/pull/894) Perform link fixes on all Org-roam files
|
||||
- [#952](https://github.com/org-roam/org-roam/pull/952) Cache `${foo}` template variables so they do not need to be re-entered twice
|
||||
|
||||
## 1.2.0 (12-06-2020)
|
||||
## 1.2.0 (2020-06-12)
|
||||
|
||||
In this release, we improved the linking process by achieving feature parity between links to files and links to headlines. Before, we used the `file:foo::*bar` format to link to the headline `bar` in file `foo`, but this was prone to breakage upon renaming the file or modifying the headline. This is not the case anymore. Now, we use `org-id` to create IDs for those headlines, which are then stored in our database to compute the relationships and jump around. Note that this will work even if you’re not using `org-id` in your global configuration for Org-mode.
|
||||
|
||||
@ -149,7 +358,7 @@ We also add `org-roam-unlinked-references`, which naively finds text that could
|
||||
- [#759](https://github.com/org-roam/org-roam/pull/759), [#760](https://github.com/org-roam/org-roam/pull/760) Tags are only added to the tags table if there are actually tags present
|
||||
- [#700](https://github.com/org-roam/org-roam/pull/700), [#733](https://github.com/org-roam/org-roam/pull/733) Allow symlinks within the `org-roam` directory
|
||||
|
||||
## 1.1.1 (18-05-2020)
|
||||
## 1.1.1 (2020-05-18)
|
||||
|
||||
In this release, we added two new features:
|
||||
|
||||
@ -189,7 +398,7 @@ As usual, this release comes with a multitude of bug-fixes and refactorings.
|
||||
- [#606](https://github.com/org-roam/org-roam/pull/606) Added `org-roam-dev.el` for developer coding standards
|
||||
- [#622](https://github.com/org-roam/org-roam/pull/622) Online documentation now points to the Org-based documentation
|
||||
|
||||
## 1.1.0 (21-04-2020)
|
||||
## 1.1.0 (2020-04-21)
|
||||
|
||||
To the average user, this release is mainly a bugfix release with additional options to customize. However, the changes made to the source is significant. Most notably, in this release:
|
||||
|
||||
@ -227,7 +436,7 @@ In the coming months, you can expect work on bigger projects (e.g. revamping the
|
||||
- [#363](https://github.com/org-roam/org-roam/pull/363), [#473](https://github.com/org-roam/org-roam/pull/473) Modularize org-roam features.
|
||||
- [#497](https://github.com/org-roam/org-roam/pull/497) Simplify `org-roam--list-files` implementation
|
||||
|
||||
## 1.0.0 (23-03-2020)
|
||||
## 1.0.0 (2020-03-23)
|
||||
|
||||
Org-roam is now on MELPA! We have squashed most of the bugs, and Org-roam has
|
||||
been stable for the most part.
|
||||
@ -246,7 +455,7 @@ been stable for the most part.
|
||||
- [#293](https://github.com/org-roam/org-roam/pull/293) Fix capture templates not working as expected for `org-roam-find-file`
|
||||
- [#275](https://github.com/org-roam/org-roam/pull/275) Fix database rebuild when `org-roam-directory` is set locally
|
||||
|
||||
## 1.0.0-rc1 (06-03-2020)
|
||||
## 1.0.0-rc1 (2020-03-06)
|
||||
|
||||
This is a pre-release before the push to MELPA. It contains large
|
||||
internal changes, with little user-facing changes. Most notably, the
|
||||
|
29
Eldev
Normal file
29
Eldev
Normal file
@ -0,0 +1,29 @@
|
||||
; -*- mode: emacs-lisp; lexical-binding: t; no-byte-compile: t -*-
|
||||
|
||||
;; explicitly set main file
|
||||
(setf eldev-project-main-file "org-roam.el")
|
||||
|
||||
(eldev-use-package-archive 'gnu)
|
||||
(eldev-use-package-archive 'melpa-unstable)
|
||||
|
||||
;; allow to load test helpers
|
||||
(eldev-add-loading-roots 'test "test/utils")
|
||||
|
||||
;; Tell checkdoc not to demand two spaces after a period.
|
||||
(setq sentence-end-double-space nil)
|
||||
|
||||
(setf eldev-lint-default '(elisp))
|
||||
(setf eldev-standard-excludes `(:or ,eldev-standard-excludes "org-roam-macs.el"))
|
||||
|
||||
(with-eval-after-load 'elisp-lint
|
||||
;; We will byte-compile with Eldev.
|
||||
(setf elisp-lint-ignored-validators '("package-lint" "fill-column")
|
||||
enable-local-variables :all))
|
||||
|
||||
;; Teach linter how to properly indent emacsql vectors
|
||||
(eldev-add-extra-dependencies 'lint 'emacsql)
|
||||
(add-hook 'eldev-lint-hook
|
||||
(lambda ()
|
||||
(eldev-load-project-dependencies 'lint nil t)
|
||||
(require 'emacsql)
|
||||
(call-interactively #'emacsql-fix-vector-indentation)))
|
73
Makefile
73
Makefile
@ -1,70 +1,29 @@
|
||||
# * makem.sh/Makefile --- Script to aid building and testing Emacs Lisp packages
|
||||
.PHONY: clean
|
||||
clean:
|
||||
eldev clean all
|
||||
|
||||
# This Makefile is from the makem.sh repo: <https://github.com/alphapapa/makem.sh>.
|
||||
.PHONY: prepare
|
||||
prepare:
|
||||
eldev -C --unstable -p -dtT prepare
|
||||
|
||||
# * Arguments
|
||||
.PHONY: lint
|
||||
lint:
|
||||
eldev -C --unstable -T lint
|
||||
|
||||
# For consistency, we use only var=val options, not hyphen-prefixed options.
|
||||
|
||||
# NOTE: I don't like duplicating the arguments here and in makem.sh,
|
||||
# but I haven't been able to find a way to pass arguments which
|
||||
# conflict with Make's own arguments through Make to the script.
|
||||
# Using -- doesn't seem to do it.
|
||||
|
||||
ifdef install-deps
|
||||
INSTALL_DEPS = "--install-deps"
|
||||
endif
|
||||
ifdef install-linters
|
||||
INSTALL_LINTERS = "--install-linters"
|
||||
endif
|
||||
|
||||
ifdef sandbox
|
||||
ifeq ($(sandbox), t)
|
||||
SANDBOX = --sandbox
|
||||
else
|
||||
SANDBOX = --sandbox $(sandbox)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef debug
|
||||
DEBUG = "--debug"
|
||||
endif
|
||||
|
||||
# ** Verbosity
|
||||
|
||||
# Since the "-v" in "make -v" gets intercepted by Make itself, we have
|
||||
# to use a variable.
|
||||
|
||||
verbose = $(v)
|
||||
|
||||
ifneq (,$(findstring vv,$(verbose)))
|
||||
VERBOSE = "-vv"
|
||||
else ifneq (,$(findstring v,$(verbose)))
|
||||
VERBOSE = "-v"
|
||||
endif
|
||||
|
||||
# * Rules
|
||||
|
||||
# TODO: Handle cases in which "test" or "tests" are called and a
|
||||
# directory by that name exists, which can confuse Make.
|
||||
|
||||
%:
|
||||
@./makem.sh $(DEBUG) $(VERBOSE) $(SANDBOX) $(INSTALL_DEPS) $(INSTALL_LINTERS) $(@)
|
||||
|
||||
.DEFAULT: init
|
||||
init:
|
||||
@./makem.sh $(DEBUG) $(VERBOSE) $(SANDBOX) $(INSTALL_DEPS) $(INSTALL_LINTERS)
|
||||
.PHONY: test
|
||||
test:
|
||||
eldev -C --unstable -T test
|
||||
|
||||
docs:
|
||||
@$(MAKE) -C doc all
|
||||
make -C doc all
|
||||
|
||||
html:
|
||||
@$(MAKE) -C doc html-dir
|
||||
make -C doc html-dir
|
||||
|
||||
install: install-docs
|
||||
|
||||
install-docs: docs
|
||||
@$(MAKE) -C doc install-docs
|
||||
make -C doc install-docs
|
||||
|
||||
install-info: info
|
||||
@$(MAKE) -C doc install-info
|
||||
make -C doc install-info
|
||||
|
189
README.md
189
README.md
@ -1,5 +1,5 @@
|
||||
# Org-roam [![GitHub Release][release-badge]][release] [![MELPA][melpa-badge]][melpa] [![License GPL 3][gpl3-badge]][gpl3]
|
||||
|
||||
|
||||
<img src="https://www.orgroam.com/img/logo.svg" align="right" alt="Org-roam Logo" width="240">
|
||||
|
||||
Org-roam is a plain-text knowledge management system. It brings some of
|
||||
@ -33,33 +33,180 @@ solution for anyone already using Org-mode for their personal wiki.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install `org-roam` using `package.el`:
|
||||
Down below you will find basic installation instructions for how to quickly
|
||||
install `org-roam` using various environments for various purposes. For more
|
||||
detailed information, please read the [manual][docs].
|
||||
|
||||
### Using `package.el`
|
||||
<details>
|
||||
<summary>Toggle instructions</summary>
|
||||
|
||||
You can install `org-roam` from [MELPA](https://melpa.org/) or [MELPA
|
||||
Stable](https://stable.melpa.org/) using `package.el`:
|
||||
|
||||
```
|
||||
M-x package-install RET org-roam RET
|
||||
```
|
||||
</details>
|
||||
|
||||
Here's a sample configuration with `use-package`:
|
||||
### Using `straight.el`
|
||||
<details>
|
||||
<summary>Toggle instructions</summary>
|
||||
|
||||
Installation from MELPA or MELPA Stable using `straight.el`:
|
||||
|
||||
```emacs-lisp
|
||||
(straight-use-package 'org-roam)
|
||||
```
|
||||
|
||||
Or with `use-package`:
|
||||
|
||||
```emacs-lisp
|
||||
(use-package org-roam
|
||||
:ensure t
|
||||
:hook
|
||||
(after-init . org-roam-mode)
|
||||
: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-graph))
|
||||
:map org-mode-map
|
||||
(("C-c n i" . org-roam-insert))
|
||||
(("C-c n I" . org-roam-insert-immediate))))
|
||||
:straight t
|
||||
...)
|
||||
```
|
||||
|
||||
Org-roam requires sqlite to function. Org-roam optionally uses Graphviz for
|
||||
graph-related functionality. It is recommended to install PCRE-enabled ripgrep
|
||||
for better performance and extended functionality.
|
||||
If you need to install the package directly from the source repository, instead
|
||||
of from MELPA, the next sample shows how to do so:
|
||||
|
||||
```emacs-lisp
|
||||
(use-package org-roam
|
||||
:straight (:host github :repo "org-roam/org-roam"
|
||||
:files (:defaults "extensions/*"))
|
||||
...)
|
||||
```
|
||||
|
||||
If you plan to use your own local fork for the development and contribution, the
|
||||
next sample will get you there:
|
||||
|
||||
```emacs-lisp
|
||||
(use-package org-roam
|
||||
:straight (:local-repo "/path/to/org-roam-fork"
|
||||
:files (:defaults "extensions/*")
|
||||
:build (:not compile))
|
||||
...)
|
||||
```
|
||||
</details>
|
||||
|
||||
### Using Doom Emacs
|
||||
<details>
|
||||
<summary>Toggle instructions</summary>
|
||||
|
||||
Doom's `:lang org` module comes with support for `org-roam`, but it's not
|
||||
enabled by default. To activate it pass `+roam2` flag to `org` module in your
|
||||
`$DOOMDIR/init.el` (e.g. `(org +roam2)`), save the file and run `doom sync -u`
|
||||
in your shell.
|
||||
|
||||
To provide better stability, Doom pins the package to a specific commit. If you
|
||||
need to unpin it *(not recommended doing that, request Doom to bump the package
|
||||
instead)* use the next in your `packages.el`:
|
||||
|
||||
```emacs-lisp
|
||||
(unpin! org-roam)
|
||||
```
|
||||
|
||||
If for some reasons you want to use a different recipe for `org-roam`, you can
|
||||
use the next form in your `packages.el` to install the package from a recipe
|
||||
repository (e.g. MELPA):
|
||||
|
||||
```emacs-lisp
|
||||
(package! org-roam)
|
||||
```
|
||||
|
||||
You can pass `:pin "commit hash"` to pin the package to a specific commit.
|
||||
|
||||
With the next sample you can install the package directly from the source
|
||||
repository:
|
||||
|
||||
```emacs-lisp
|
||||
(package! org-roam
|
||||
:recipe (:host github :repo "org-roam/org-roam"
|
||||
:files (:defaults "extensions/*")))
|
||||
```
|
||||
|
||||
And if you plan to use your own local fork for the development or contribution,
|
||||
the next sample will get you there:
|
||||
|
||||
```emacs-lisp
|
||||
(package! org-roam
|
||||
:recipe (:local-repo "/path/to/org-roam-fork"
|
||||
:files (:defaults "extensions/*")
|
||||
:build (:not compile)))
|
||||
```
|
||||
</details>
|
||||
|
||||
### Without a package manager
|
||||
<details>
|
||||
<summary>Toggle instructions</summary>
|
||||
|
||||
To install the package without using a package manager you have the next two
|
||||
options:
|
||||
|
||||
1. Install the package by cloning it with `git` from the source repository.
|
||||
2. Or install the package by downloading the latest [release
|
||||
version](https://github.com/org-roam/org-roam/releases).
|
||||
|
||||
In both of the cases you will need to ensure that you have all the required
|
||||
dependencies. These include:
|
||||
|
||||
- dash
|
||||
- f
|
||||
- s
|
||||
- org (9.6 is the minimum required version!)
|
||||
- emacsql
|
||||
- magit-section
|
||||
- filenotify-recursive
|
||||
|
||||
After installing the package, you will need to properly setup `load-path` to the
|
||||
package:
|
||||
|
||||
``` emacs-lisp
|
||||
(add-to-list 'load-path "/path/to/org-roam/")
|
||||
(add-to-list 'load-path "/path/to-org-roam/extensions/")
|
||||
```
|
||||
|
||||
After which you should be able to resolve `(require 'org-roam)` call without any
|
||||
problems.
|
||||
|
||||
Org-roam also comes with `.texi` files to integrate with Emacs' built-in Info
|
||||
system. Read the manual to find more details for how to install them manually.
|
||||
</details>
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's a very basic sample for configuration of `org-roam` using `use-package`:
|
||||
|
||||
```emacs-lisp
|
||||
(use-package org-roam
|
||||
:ensure t
|
||||
:custom
|
||||
(org-roam-directory (file-truename "/path/to/org-files/"))
|
||||
:bind (("C-c n l" . org-roam-buffer-toggle)
|
||||
("C-c n f" . org-roam-node-find)
|
||||
("C-c n g" . org-roam-graph)
|
||||
("C-c n i" . org-roam-node-insert)
|
||||
("C-c n c" . org-roam-capture)
|
||||
;; Dailies
|
||||
("C-c n j" . org-roam-dailies-capture-today))
|
||||
:config
|
||||
;; If you're using a vertical completion framework, you might want a more informative completion interface
|
||||
(setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
|
||||
(org-roam-db-autosync-mode)
|
||||
;; If using org-roam-protocol
|
||||
(require 'org-roam-protocol))
|
||||
```
|
||||
|
||||
Note that the `file-truename` function is only necessary when you use symbolic
|
||||
link to `org-roam-directory`. Org-roam won't automatically resolve symbolic link
|
||||
to the directory.
|
||||
## Getting Started
|
||||
|
||||
[David Wilson](https://github.com/daviwil) of [System
|
||||
Crafters](https://www.youtube.com/c/SystemCrafters) has produced an introductory
|
||||
video that covers the basic commands:
|
||||
|
||||
[](https://www.youtube.com/watch?v=AyhPmypHDEw)
|
||||
|
||||
## Getting Help
|
||||
|
||||
@ -80,7 +227,9 @@ it has not already been addressed on [GitHub][issues] or on
|
||||
|
||||
- [Jethro Kuan](https://braindump.jethro.dev/)
|
||||
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
|
||||
- [Alexey Shmalko](https://braindump.rasen.dev/)
|
||||
- [Alexey Shmalko](https://www.alexeyshmalko.com/)
|
||||
- [Sidharth Arya](https://sidhartharya.github.io/braindump/index.html)
|
||||
- [Martin Edström](https://edstrom.dev/)
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -103,6 +252,6 @@ General Public License, Version 3.
|
||||
[release]: https://github.com/org-roam/org-roam/releases
|
||||
[docs]: https://www.orgroam.com/manual.html
|
||||
[discourse]: https://org-roam.discourse.group/
|
||||
[slack]: https://join.slack.com/t/orgroam/shared_invite/zt-deoqamys-043YQ~s5Tay3iJ5QRI~Lxg
|
||||
[slack]: https://join.slack.com/t/orgroam/shared_invite/zt-wuoize1z-x3UyQnQ0WHF0RhuEQ2NLnQ
|
||||
[issues]: https://github.com/org-roam/org-roam/issues
|
||||
[faq]: https://www.orgroam.com/manual.html#FAQ
|
||||
|
15
default.mk
15
default.mk
@ -44,16 +44,17 @@ HTMLDIRS = $(PACKAGES)
|
||||
PDFFILES = $(addsuffix .pdf,$(PACKAGES))
|
||||
EPUBFILES = $(addsuffix .epub,$(PACKAGES))
|
||||
|
||||
ELS = org-roam-buffer.el
|
||||
ELS = org-roam.el
|
||||
ELS += org-roam-capture.el
|
||||
ELS += org-roam-compat.el
|
||||
ELS += org-roam-completion.el
|
||||
ELS += org-roam-dailies.el
|
||||
ELS += org-roam-db.el
|
||||
ELS += org-roam.el
|
||||
ELS += org-roam-graph.el
|
||||
ELS += org-roam-macs.el
|
||||
ELS += org-roam-protocol.el
|
||||
ELS += org-roam-mode.el
|
||||
ELS += org-roam-node.el
|
||||
ELS += org-roam-utils.el
|
||||
ELS += extensions/org-roam-dailies.el
|
||||
ELS += extensions/org-roam-graph.el
|
||||
ELS += extensions/org-roam-overlay.el
|
||||
ELS += extensions/org-roam-protocol.el
|
||||
ELCS = $(ELS:.el=.elc)
|
||||
ELMS = org-roam.el $(filter-out $(addsuffix .el,$(PACKAGES)),$(ELS))
|
||||
ELGS = org-roam-autoloads.el org-roam-version.el
|
||||
|
@ -95,7 +95,7 @@
|
||||
<ul>
|
||||
<li>Read our documentation within Emacs, or on the <a href="https://www.orgroam.com/manual.html">Online Manual</a></li>
|
||||
<li>Participate in our forum discussions on <a href="https://org-roam.discourse.group">Discourse</a></li>
|
||||
<li>Chat with us on <a href="https://join.slack.com/t/orgroam/shared_invite/zt-deoqamys-043YQ~s5Tay3iJ5QRI~Lxg">Slack</a></li>
|
||||
<li>Chat with us on <a href="https://join.slack.com/t/orgroam/shared_invite/zt-wuoize1z-x3UyQnQ0WHF0RhuEQ2NLnQ">Slack</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
@ -116,8 +116,8 @@
|
||||
<div class="row">
|
||||
<a
|
||||
class="content footer-links"
|
||||
href="https://github.com/org-roam/org-roam-server"
|
||||
>org-roam-server</a
|
||||
href="https://github.com/org-roam/org-roam-ui"
|
||||
>org-roam-ui</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@ -151,7 +151,7 @@
|
||||
<div class="row">
|
||||
<a
|
||||
class="content footer-links"
|
||||
href="https://join.slack.com/t/orgroam/shared_invite/zt-deoqamys-043YQ~s5Tay3iJ5QRI~Lxg"
|
||||
href="https://join.slack.com/t/orgroam/shared_invite/zt-wuoize1z-x3UyQnQ0WHF0RhuEQ2NLnQ"
|
||||
>Slack</a
|
||||
>
|
||||
</div>
|
||||
|
2222
doc/org-roam.org
2222
doc/org-roam.org
File diff suppressed because it is too large
Load Diff
2799
doc/org-roam.texi
2799
doc/org-roam.texi
File diff suppressed because it is too large
Load Diff
366
extensions/org-roam-dailies.el
Normal file
366
extensions/org-roam-dailies.el
Normal file
@ -0,0 +1,366 @@
|
||||
;;; org-roam-dailies.el --- Daily-notes for Org-roam -*- coding: utf-8; lexical-binding: t; -*-
|
||||
;;;
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Copyright © 2020 Leo Vivier <leo.vivier+dev@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Leo Vivier <leo.vivier+dev@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org-roam "2.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 extension provides functionality for creating daily-notes, or shortly
|
||||
;; "dailies". Dailies implemented here as a unique node per unique file, where
|
||||
;; each file named after certain date and stored in `org-roam-dailies-directory'.
|
||||
;;
|
||||
;; One can use dailies for various purposes, e.g. journaling, fleeting notes,
|
||||
;; scratch notes or whatever else you can think of.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'dash)
|
||||
(require 'org-roam)
|
||||
|
||||
;;; Faces
|
||||
(defface org-roam-dailies-calendar-note
|
||||
'((t :inherit (org-link) :underline nil))
|
||||
"Face for dates with a daily-note in the calendar."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
;;; Options
|
||||
(defcustom org-roam-dailies-directory "daily/"
|
||||
"Path to daily-notes.
|
||||
This path is relative to `org-roam-directory'."
|
||||
:group 'org-roam
|
||||
:type 'string)
|
||||
|
||||
(defcustom org-roam-dailies-find-file-hook nil
|
||||
"Hook that is run right after navigating to a daily-note."
|
||||
:group 'org-roam
|
||||
:type 'hook)
|
||||
|
||||
(defcustom org-roam-dailies-capture-templates
|
||||
`(("d" "default" entry
|
||||
"* %?"
|
||||
:target (file+head "%<%Y-%m-%d>.org"
|
||||
"#+title: %<%Y-%m-%d>\n")))
|
||||
"Capture templates for daily-notes in Org-roam.
|
||||
Note that for daily files to show up in the calendar, they have to be of format
|
||||
\"org-time-string.org\".
|
||||
See `org-roam-capture-templates' for the template documentation."
|
||||
:group 'org-roam
|
||||
:type '(repeat
|
||||
(choice (list :tag "Multikey description"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description"))
|
||||
(list :tag "Template entry"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description ")
|
||||
(choice :tag "Capture Type " :value entry
|
||||
(const :tag "Org entry" entry)
|
||||
(const :tag "Plain list item" item)
|
||||
(const :tag "Checkbox item" checkitem)
|
||||
(const :tag "Plain text" plain)
|
||||
(const :tag "Table line" table-line))
|
||||
(choice :tag "Template "
|
||||
(string)
|
||||
(list :tag "File"
|
||||
(const :format "" file)
|
||||
(file :tag "Template file"))
|
||||
(list :tag "Function"
|
||||
(const :format "" function)
|
||||
(function :tag "Template function")))
|
||||
(plist :inline t
|
||||
;; Give the most common options as checkboxes
|
||||
:options (((const :format "%v " :target)
|
||||
(choice :tag "Node location"
|
||||
(list :tag "File"
|
||||
(const :format "" file)
|
||||
(string :tag " File"))
|
||||
(list :tag "File & Head Content"
|
||||
(const :format "" file+head)
|
||||
(string :tag " File")
|
||||
(string :tag " Head Content"))
|
||||
(list :tag "File & Outline path"
|
||||
(const :format "" file+olp)
|
||||
(string :tag " File")
|
||||
(list :tag "Outline path"
|
||||
(repeat (string :tag "Headline"))))
|
||||
(list :tag "File & Head Content & Outline path"
|
||||
(const :format "" file+head+olp)
|
||||
(string :tag " File")
|
||||
(string :tag " Head Content")
|
||||
(list :tag "Outline path"
|
||||
(repeat (string :tag "Headline"))))))
|
||||
((const :format "%v " :prepend) (const t))
|
||||
((const :format "%v " :immediate-finish) (const t))
|
||||
((const :format "%v " :jump-to-captured) (const t))
|
||||
((const :format "%v " :empty-lines) (const 1))
|
||||
((const :format "%v " :empty-lines-before) (const 1))
|
||||
((const :format "%v " :empty-lines-after) (const 1))
|
||||
((const :format "%v " :clock-in) (const t))
|
||||
((const :format "%v " :clock-keep) (const t))
|
||||
((const :format "%v " :clock-resume) (const t))
|
||||
((const :format "%v " :time-prompt) (const t))
|
||||
((const :format "%v " :tree-type) (const week))
|
||||
((const :format "%v " :unnarrowed) (const t))
|
||||
((const :format "%v " :table-line-pos) (string))
|
||||
((const :format "%v " :kill-buffer) (const t))))))))
|
||||
|
||||
;;; Commands
|
||||
;;;; Today
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-capture-today (&optional goto keys)
|
||||
"Create an entry in the daily-note for today.
|
||||
When GOTO is non-nil, go the note without creating an entry.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "P")
|
||||
(org-roam-dailies--capture (current-time) goto keys))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-goto-today (&optional keys)
|
||||
"Find the daily-note for today, creating it if necessary.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive)
|
||||
(org-roam-dailies-capture-today t keys))
|
||||
|
||||
;;;; Tomorrow
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-capture-tomorrow (n &optional goto keys)
|
||||
"Create an entry in the daily-note for tomorrow.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the future.
|
||||
|
||||
With a `C-u' prefix or when GOTO is non-nil, go the note without
|
||||
creating an entry.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "p")
|
||||
(org-roam-dailies--capture (time-add (* n 86400) (current-time)) goto keys))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-goto-tomorrow (n &optional keys)
|
||||
"Find the daily-note for tomorrow, creating it if necessary.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the
|
||||
future.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow n t keys))
|
||||
|
||||
;;;; Yesterday
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-capture-yesterday (n &optional goto keys)
|
||||
"Create an entry in the daily-note for yesteday.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the past.
|
||||
|
||||
When GOTO is non-nil, go the note without creating an entry.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow (- n) goto keys))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-goto-yesterday (n &optional keys)
|
||||
"Find the daily-note for yesterday, creating it if necessary.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the
|
||||
future.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow (- n) t keys))
|
||||
|
||||
;;;; Date
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-capture-date (&optional goto prefer-future keys)
|
||||
"Create an entry in the daily-note for a date using the calendar.
|
||||
Prefer past dates, unless PREFER-FUTURE is non-nil.
|
||||
With a `C-u' prefix or when GOTO is non-nil, go the note without
|
||||
creating an entry.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive "P")
|
||||
(let ((time (let ((org-read-date-prefer-future prefer-future))
|
||||
(org-read-date nil t nil (if goto
|
||||
"Find daily-note: "
|
||||
"Capture to daily-note: ")))))
|
||||
(org-roam-dailies--capture time goto keys)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-goto-date (&optional prefer-future keys)
|
||||
"Find the daily-note for a date using the calendar, creating it if necessary.
|
||||
Prefer past dates, unless PREFER-FUTURE is non-nil.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(interactive)
|
||||
(org-roam-dailies-capture-date t prefer-future keys))
|
||||
|
||||
;;;; Navigation
|
||||
(defun org-roam-dailies-goto-next-note (&optional n)
|
||||
"Find next daily-note.
|
||||
|
||||
With numeric argument N, find note N days in the future. If N is
|
||||
negative, find note N days in the past."
|
||||
(interactive "p")
|
||||
(unless (org-roam-dailies--daily-note-p)
|
||||
(user-error "Not in a daily-note"))
|
||||
(setq n (or n 1))
|
||||
(let* ((dailies (org-roam-dailies--list-files))
|
||||
(position
|
||||
(cl-position-if (lambda (candidate)
|
||||
(string= (buffer-file-name (buffer-base-buffer)) candidate))
|
||||
dailies))
|
||||
note)
|
||||
(unless position
|
||||
(user-error "Can't find current note file - have you saved it yet?"))
|
||||
(pcase n
|
||||
((pred (natnump))
|
||||
(when (eq position (- (length dailies) 1))
|
||||
(user-error "Already at newest note")))
|
||||
((pred (integerp))
|
||||
(when (eq position 0)
|
||||
(user-error "Already at oldest note"))))
|
||||
(setq note (nth (+ position n) dailies))
|
||||
(find-file note)
|
||||
(run-hooks 'org-roam-dailies-find-file-hook)))
|
||||
|
||||
(defun org-roam-dailies-goto-previous-note (&optional n)
|
||||
"Find previous daily-note.
|
||||
|
||||
With numeric argument N, find note N days in the past. If N is
|
||||
negative, find note N days in the future."
|
||||
(interactive "p")
|
||||
(let ((n (if n (- n) -1)))
|
||||
(org-roam-dailies-goto-next-note n)))
|
||||
|
||||
(defun org-roam-dailies--list-files (&rest extra-files)
|
||||
"List all files in `org-roam-dailies-directory'.
|
||||
EXTRA-FILES can be used to append extra files to the list."
|
||||
(let ((dir (expand-file-name org-roam-dailies-directory org-roam-directory))
|
||||
(regexp (rx-to-string `(and "." (or ,@org-roam-file-extensions)))))
|
||||
(append (--remove (let ((file (file-name-nondirectory it)))
|
||||
(when (or (auto-save-file-name-p file)
|
||||
(backup-file-name-p file)
|
||||
(string-match "^\\." file))
|
||||
it))
|
||||
(directory-files-recursively dir regexp))
|
||||
extra-files)))
|
||||
|
||||
(defun org-roam-dailies--daily-note-p (&optional file)
|
||||
"Return t if FILE is an Org-roam daily-note, nil otherwise.
|
||||
If FILE is not specified, use the current buffer's file-path."
|
||||
(when-let ((path (expand-file-name
|
||||
(or file
|
||||
(buffer-file-name (buffer-base-buffer)))))
|
||||
(directory (expand-file-name org-roam-dailies-directory org-roam-directory)))
|
||||
(setq path (expand-file-name path))
|
||||
(save-match-data
|
||||
(and
|
||||
(org-roam-file-p path)
|
||||
(org-roam-descendant-of-p path directory)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-dailies-find-directory ()
|
||||
"Find and open `org-roam-dailies-directory'."
|
||||
(interactive)
|
||||
(find-file (expand-file-name org-roam-dailies-directory org-roam-directory)))
|
||||
|
||||
;;; Calendar integration
|
||||
(defun org-roam-dailies-calendar--file-to-date (file)
|
||||
"Convert FILE to date.
|
||||
Return (MONTH DAY YEAR) or nil if not an Org time-string."
|
||||
(ignore-errors
|
||||
(cl-destructuring-bind (_ _ _ d m y _ _ _)
|
||||
(org-parse-time-string
|
||||
(file-name-sans-extension
|
||||
(file-name-nondirectory file)))
|
||||
(list m d y))))
|
||||
|
||||
(defun org-roam-dailies-calendar-mark-entries ()
|
||||
"Mark days in the calendar for which a daily-note is present."
|
||||
(when (file-exists-p (expand-file-name org-roam-dailies-directory org-roam-directory))
|
||||
(dolist (date (remove nil
|
||||
(mapcar #'org-roam-dailies-calendar--file-to-date
|
||||
(org-roam-dailies--list-files))))
|
||||
(when (calendar-date-is-visible-p date)
|
||||
(calendar-mark-visible-date date 'org-roam-dailies-calendar-note)))))
|
||||
|
||||
(add-hook 'calendar-today-visible-hook #'org-roam-dailies-calendar-mark-entries)
|
||||
(add-hook 'calendar-today-invisible-hook #'org-roam-dailies-calendar-mark-entries)
|
||||
|
||||
;;; Capture implementation
|
||||
(add-to-list 'org-roam-capture--template-keywords :override-default-time)
|
||||
|
||||
(defun org-roam-dailies--capture (time &optional goto keys)
|
||||
"Capture an entry in a daily-note for TIME, creating it if necessary.
|
||||
When GOTO is non-nil, go the note without creating an entry.
|
||||
|
||||
ELisp programs can set KEYS to a string associated with a template.
|
||||
In this case, interactive selection will be bypassed."
|
||||
(let ((org-roam-directory (expand-file-name org-roam-dailies-directory org-roam-directory))
|
||||
(org-roam-dailies-directory "./"))
|
||||
(org-roam-capture- :goto (when goto '(4))
|
||||
:keys keys
|
||||
:node (org-roam-node-create)
|
||||
:templates org-roam-dailies-capture-templates
|
||||
:props (list :override-default-time time)))
|
||||
(when goto (run-hooks 'org-roam-dailies-find-file-hook)))
|
||||
|
||||
(add-hook 'org-roam-capture-preface-hook #'org-roam-dailies--override-capture-time-h)
|
||||
(defun org-roam-dailies--override-capture-time-h ()
|
||||
"Override the `:default-time' with the time from `:override-default-time'."
|
||||
(prog1 nil
|
||||
(when (org-roam-capture--get :override-default-time)
|
||||
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))))
|
||||
|
||||
;;; Bindings
|
||||
(defvar org-roam-dailies-map (make-sparse-keymap)
|
||||
"Keymap for `org-roam-dailies'.")
|
||||
|
||||
(define-prefix-command 'org-roam-dailies-map)
|
||||
|
||||
(define-key org-roam-dailies-map (kbd "d") #'org-roam-dailies-goto-today)
|
||||
(define-key org-roam-dailies-map (kbd "y") #'org-roam-dailies-goto-yesterday)
|
||||
(define-key org-roam-dailies-map (kbd "t") #'org-roam-dailies-goto-tomorrow)
|
||||
(define-key org-roam-dailies-map (kbd "n") #'org-roam-dailies-capture-today)
|
||||
(define-key org-roam-dailies-map (kbd "f") #'org-roam-dailies-goto-next-note)
|
||||
(define-key org-roam-dailies-map (kbd "b") #'org-roam-dailies-goto-previous-note)
|
||||
(define-key org-roam-dailies-map (kbd "c") #'org-roam-dailies-goto-date)
|
||||
(define-key org-roam-dailies-map (kbd "v") #'org-roam-dailies-capture-date)
|
||||
(define-key org-roam-dailies-map (kbd ".") #'org-roam-dailies-find-directory)
|
||||
|
||||
(provide 'org-roam-dailies)
|
||||
|
||||
;;; org-roam-dailies.el ends here
|
75
extensions/org-roam-export.el
Normal file
75
extensions/org-roam-export.el
Normal file
@ -0,0 +1,75 @@
|
||||
;;; org-roam-export.el --- Org-roam org-export tweaks -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.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 package provides the necessary changes required to make org-export work out-of-the-box.
|
||||
;;
|
||||
;; To enable it, run:
|
||||
;;
|
||||
;; (require 'org-roam-export)
|
||||
;;
|
||||
;; The key issue Org's export-to-html functionality has is that it does not respect the ID property, which
|
||||
;; Org-roam relies heavily on. This patches the necessary function in ox-html to export ID links correctly,
|
||||
;; pointing to the correct place.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'ox-html)
|
||||
|
||||
(defun org-roam-export--org-html--reference (datum info &optional named-only)
|
||||
"Org-roam's patch for `org-html--reference' to support ID link export.
|
||||
See `org-html--reference' for DATUM, INFO and NAMED-ONLY."
|
||||
(let* ((type (org-element-type datum))
|
||||
(user-label
|
||||
(org-element-property
|
||||
(pcase type
|
||||
((or `headline `inlinetask) :CUSTOM_ID)
|
||||
((or `radio-target `target) :value)
|
||||
(_ :name))
|
||||
datum))
|
||||
(user-label
|
||||
(or user-label
|
||||
(when-let ((path (org-element-property :ID datum)))
|
||||
;; see `org-html-link' for why we use "ID-"
|
||||
;; (search for "ID-" in ox-html.el)
|
||||
(concat "ID-" path)))))
|
||||
(cond
|
||||
((and user-label
|
||||
(or (plist-get info :html-prefer-user-labels)
|
||||
(memq type '(headline inlinetask))))
|
||||
user-label)
|
||||
((and named-only
|
||||
(not (memq type '(headline inlinetask radio-target target)))
|
||||
(not user-label))
|
||||
nil)
|
||||
(t
|
||||
(org-export-get-reference datum info)))))
|
||||
|
||||
(advice-add 'org-html--reference :override #'org-roam-export--org-html--reference)
|
||||
|
||||
(provide 'org-roam-export)
|
||||
;;; org-roam-export.el ends here
|
302
extensions/org-roam-graph.el
Normal file
302
extensions/org-roam-graph.el
Normal file
@ -0,0 +1,302 @@
|
||||
;;; org-roam-graph.el --- Basic graphing functionality for Org-roam -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.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 extension implements capability to build and generate graphs in Org-roam
|
||||
;; with the help of Graphviz.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'xml) ;xml-escape-string
|
||||
(require 'org-roam)
|
||||
|
||||
;;; Options
|
||||
(defcustom org-roam-graph-viewer (executable-find "firefox")
|
||||
"Method to view the org-roam graph.
|
||||
It may be one of the following:
|
||||
- a string representing the path to the executable for viewing the graph.
|
||||
- a function accepting a single argument: the graph file path.
|
||||
- nil uses `view-file' to view the graph."
|
||||
:type '(choice
|
||||
(string :tag "Path to executable")
|
||||
(function :tag "Function to display graph" eww-open-file)
|
||||
(const :tag "view-file"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-executable "dot"
|
||||
"Path to graphing executable, or its name."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-filetype "svg"
|
||||
"File type to generate when producing graphs."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
|
||||
(defcustom org-roam-graph-extra-config nil
|
||||
"Extra options passed to graphviz.
|
||||
Example:
|
||||
'((\"rankdir\" . \"LR\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-edge-extra-config nil
|
||||
"Extra edge options passed to graphviz.
|
||||
Example:
|
||||
'((\"dir\" . \"back\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-node-extra-config
|
||||
'(("id" . (("style" . "bold,rounded,filled")
|
||||
("fillcolor" . "#EEEEEE")
|
||||
("color" . "#C9C9C9")
|
||||
("fontcolor" . "#111111")))
|
||||
("http" . (("style" . "rounded,filled")
|
||||
("fillcolor" . "#EEEEEE")
|
||||
("color" . "#C9C9C9")
|
||||
("fontcolor" . "#0A97A6")))
|
||||
("https" . (("style" . "rounded,filled")
|
||||
("fillcolor" . "#EEEEEE")
|
||||
("color" . "#C9C9C9")
|
||||
("fontcolor" . "#0A97A6"))))
|
||||
"Extra options for graphviz nodes."
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-link-hidden-types
|
||||
'("file")
|
||||
"What sort of links to hide from the Org-roam graph."
|
||||
:type '(repeat string)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-max-title-length 100
|
||||
"Maximum length of titles in graph nodes."
|
||||
:type 'number
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-shorten-titles 'truncate
|
||||
"Determines how long titles appear in graph nodes.
|
||||
Recognized values are the symbols `truncate' and `wrap', in which
|
||||
cases the title will be truncated or wrapped, respectively, if it
|
||||
is longer than `org-roam-graph-max-title-length'.
|
||||
|
||||
All other values including nil will have no effect."
|
||||
:type '(choice
|
||||
(const :tag "truncate" truncate)
|
||||
(const :tag "wrap" wrap)
|
||||
(const :tag "no" nil))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-link-builder 'org-roam-org-protocol-link-builder
|
||||
"Function used to build the Org-roam graph links.
|
||||
Given a node name, return a string to be used for the link fed to
|
||||
the graph generation utility."
|
||||
:type 'function
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-generation-hook nil
|
||||
"Functions to run after the graph has been generated.
|
||||
Each function is called with two arguments: the filename
|
||||
containing the graph generation tool, and the generated graph."
|
||||
:type 'hook
|
||||
:group 'org-roam)
|
||||
|
||||
(defun org-roam-org-protocol-link-builder (node)
|
||||
"Default org-roam link builder. Generate an org-protocol link using NODE."
|
||||
(concat "org-protocol://roam-node?node="
|
||||
(url-hexify-string (org-roam-node-id node))))
|
||||
|
||||
;;; Interactive command
|
||||
;;;###autoload
|
||||
(defun org-roam-graph (&optional arg node)
|
||||
"Build and possibly display a graph for NODE.
|
||||
ARG may be any of the following values:
|
||||
- nil show the graph.
|
||||
- `\\[universal-argument]' show the graph for NODE.
|
||||
- `\\[universal-argument]' N show the graph for NODE limiting nodes to N steps."
|
||||
(interactive
|
||||
(list current-prefix-arg
|
||||
(and current-prefix-arg
|
||||
(org-roam-node-at-point 'assert))))
|
||||
(let ((graph (cl-typecase arg
|
||||
(null (org-roam-graph--dot nil 'all-nodes))
|
||||
(cons (org-roam-graph--dot (org-roam-graph--connected-component
|
||||
(org-roam-node-id node) 0)))
|
||||
(integer (org-roam-graph--dot (org-roam-graph--connected-component
|
||||
(org-roam-node-id node) (abs arg)))))))
|
||||
(org-roam-graph--build graph #'org-roam-graph--open)))
|
||||
|
||||
;;; Generation and Build process
|
||||
(defun org-roam-graph--build (graph &optional callback)
|
||||
"Generate the GRAPH, and execute CALLBACK when process exits successfully.
|
||||
CALLBACK is passed the graph file as its sole argument."
|
||||
(unless (stringp org-roam-graph-executable)
|
||||
(user-error "`org-roam-graph-executable' is not a string"))
|
||||
(unless (executable-find org-roam-graph-executable)
|
||||
(user-error (concat "Cannot find executable \"%s\" to generate the graph. "
|
||||
"Please adjust `org-roam-graph-executable'")
|
||||
org-roam-graph-executable))
|
||||
(let* ((temp-dot (make-temp-file "graph." nil ".dot" graph))
|
||||
(temp-graph (make-temp-file "graph." nil (concat "." org-roam-graph-filetype))))
|
||||
(org-roam-message "building graph")
|
||||
(make-process
|
||||
:name "*org-roam-graph*"
|
||||
:buffer " *org-roam-graph*"
|
||||
:command `(,org-roam-graph-executable ,temp-dot "-T" ,org-roam-graph-filetype "-o" ,temp-graph)
|
||||
:sentinel (when callback
|
||||
(lambda (process _event)
|
||||
(when (= 0 (process-exit-status process))
|
||||
(progn (funcall callback temp-graph)
|
||||
(run-hook-with-args 'org-roam-graph-generation-hook temp-dot temp-graph))))))))
|
||||
|
||||
(defun org-roam-graph--dot (&optional edges all-nodes)
|
||||
"Build the graphviz given the EDGES of the graph.
|
||||
If ALL-NODES, include also nodes without edges."
|
||||
(let ((org-roam-directory-temp org-roam-directory)
|
||||
(nodes-table (make-hash-table :test #'equal))
|
||||
(seen-nodes (list))
|
||||
(edges (or edges (org-roam-db-query [:select :distinct [source dest type] :from links]))))
|
||||
(pcase-dolist (`(,id ,file ,title)
|
||||
(org-roam-db-query [:select [id file title] :from nodes]))
|
||||
(puthash id (org-roam-node-create :file file :id id :title title) nodes-table))
|
||||
(with-temp-buffer
|
||||
(setq-local org-roam-directory org-roam-directory-temp)
|
||||
(insert "digraph \"org-roam\" {\n")
|
||||
(dolist (option org-roam-graph-extra-config)
|
||||
(insert (org-roam-graph--dot-option option) ";\n"))
|
||||
(insert (format " edge [%s];\n"
|
||||
(mapconcat (lambda (var)
|
||||
(org-roam-graph--dot-option var nil "\""))
|
||||
org-roam-graph-edge-extra-config
|
||||
",")))
|
||||
(pcase-dolist (`(,source ,dest ,type) edges)
|
||||
(unless (member type org-roam-graph-link-hidden-types)
|
||||
(pcase-dolist (`(,node ,node-type) `((,source "id")
|
||||
(,dest ,type)))
|
||||
(unless (member node seen-nodes)
|
||||
(insert (org-roam-graph--format-node
|
||||
(or (gethash node nodes-table) node) node-type))
|
||||
(push node seen-nodes)))
|
||||
(insert (format " \"%s\" -> \"%s\";\n"
|
||||
(xml-escape-string source)
|
||||
(xml-escape-string dest)))))
|
||||
(when all-nodes
|
||||
(maphash (lambda (id node)
|
||||
(unless (member id seen-nodes)
|
||||
(insert (org-roam-graph--format-node node "id"))))
|
||||
nodes-table))
|
||||
(insert "}")
|
||||
(buffer-string))))
|
||||
|
||||
(defun org-roam-graph--connected-component (id distance)
|
||||
"Return the edges for all nodes reachable from/connected to ID.
|
||||
DISTANCE is the maximum distance away from the root node."
|
||||
(let* ((query
|
||||
(if (= distance 0)
|
||||
"
|
||||
WITH RECURSIVE
|
||||
links_of(source, dest) AS
|
||||
(SELECT source, dest FROM links UNION
|
||||
SELECT dest, source FROM links),
|
||||
connected_component(source) AS
|
||||
(SELECT dest FROM links_of WHERE source = $s1 UNION
|
||||
SELECT dest FROM links_of JOIN connected_component USING(source))
|
||||
SELECT DISTINCT source, dest, type FROM links
|
||||
WHERE source IN connected_component OR dest IN connected_component;"
|
||||
"
|
||||
WITH RECURSIVE
|
||||
links_of(source, dest) AS
|
||||
(SELECT source, dest FROM links UNION
|
||||
SELECT dest, source FROM links),
|
||||
connected_component(source, trace) AS
|
||||
(VALUES ($s1 , json_array($s1)) UNION
|
||||
SELECT lo.dest, json_insert(cc.trace, '$[' || json_array_length(cc.trace) || ']', lo.dest) FROM
|
||||
connected_component AS cc JOIN links_of AS lo USING(source)
|
||||
WHERE (
|
||||
-- Avoid cycles by only visiting each node once.
|
||||
(SELECT count(*) FROM json_each(cc.trace) WHERE json_each.value == lo.dest) == 0
|
||||
-- Note: BFS is cut off early here.
|
||||
AND json_array_length(cc.trace) < $s2)),
|
||||
nodes(source) as (SELECT DISTINCT source
|
||||
FROM connected_component GROUP BY source ORDER BY min(json_array_length(trace)))
|
||||
SELECT DISTINCT source, dest, type FROM links WHERE source IN nodes OR dest IN nodes;")))
|
||||
(org-roam-db-query query id distance)))
|
||||
|
||||
(defun org-roam-graph--dot-option (option &optional wrap-key wrap-val)
|
||||
"Return dot string of form KEY=VAL for OPTION cons.
|
||||
If WRAP-KEY is non-nil it wraps the KEY.
|
||||
If WRAP-VAL is non-nil it wraps the VAL."
|
||||
(concat wrap-key (car option) wrap-key
|
||||
"="
|
||||
wrap-val (cdr option) wrap-val))
|
||||
|
||||
(defun org-roam-graph--format-node (node type)
|
||||
"Return a graphviz NODE with TYPE.
|
||||
Handles both Org-roam nodes, and string nodes (e.g. urls)."
|
||||
(let (node-id node-properties)
|
||||
(if (org-roam-node-p node)
|
||||
(let* ((title (org-roam-quote-string (org-roam-node-title node)))
|
||||
(shortened-title
|
||||
(org-roam-quote-string
|
||||
(pcase org-roam-graph-shorten-titles
|
||||
(`truncate (truncate-string-to-width title org-roam-graph-max-title-length nil nil "..."))
|
||||
(`wrap (org-roam-word-wrap org-roam-graph-max-title-length title))
|
||||
(_ title)))))
|
||||
(setq node-id (org-roam-node-id node)
|
||||
node-properties `(("label" . ,shortened-title)
|
||||
("URL" . ,(funcall org-roam-graph-link-builder node))
|
||||
("tooltip" . ,(xml-escape-string title)))))
|
||||
(setq node-id node
|
||||
node-properties (append `(("label" . ,(concat type ":" node)))
|
||||
(when (member type (list "http" "https"))
|
||||
`(("URL" . ,(xml-escape-string (concat type ":" node))))))))
|
||||
(format "\"%s\" [%s];\n"
|
||||
node-id
|
||||
(mapconcat (lambda (n)
|
||||
(org-roam-graph--dot-option n nil "\""))
|
||||
(append (cdr (assoc type org-roam-graph-node-extra-config))
|
||||
node-properties) ","))))
|
||||
|
||||
(defun org-roam-graph--open (file)
|
||||
"Open FILE using `org-roam-graph-viewer' with `view-file' as a fallback."
|
||||
(pcase org-roam-graph-viewer
|
||||
((pred stringp)
|
||||
(if (executable-find org-roam-graph-viewer)
|
||||
(condition-case err
|
||||
(call-process org-roam-graph-viewer nil 0 nil file)
|
||||
(error (user-error "Failed to open org-roam graph: %s" err)))
|
||||
(user-error "Executable not found: \"%s\"" org-roam-graph-viewer)))
|
||||
((pred functionp) (funcall org-roam-graph-viewer file))
|
||||
('nil (view-file file))
|
||||
(_ (signal 'wrong-type-argument `((functionp stringp null) ,org-roam-graph-viewer)))))
|
||||
|
||||
|
||||
(provide 'org-roam-graph)
|
||||
|
||||
;;; org-roam-graph.el ends here
|
96
extensions/org-roam-overlay.el
Normal file
96
extensions/org-roam-overlay.el
Normal file
@ -0,0 +1,96 @@
|
||||
;;; org-roam-overlay.el --- Link overlay for [id:] links to Org-roam nodes -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.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 extension allows to render [[id:]] links that don't have an associated
|
||||
;; descriptor with an overlay that displays the node's current title.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-roam)
|
||||
|
||||
(defface org-roam-overlay
|
||||
'((((class color) (background light))
|
||||
:background "grey90" :box (:line-width -1 :color "black"))
|
||||
(((class color) (background dark))
|
||||
:background "grey10" :box (:line-width -1 :color "white")))
|
||||
"Face for the Org-roam overlay."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defun org-roam-overlay--make (l r &rest props)
|
||||
"Make an overlay from L to R with PROPS."
|
||||
(let ((o (make-overlay l (or r l))))
|
||||
(overlay-put o 'category 'org-roam)
|
||||
(while props (overlay-put o (pop props) (pop props)))
|
||||
o))
|
||||
|
||||
(defun org-roam-overlay-make-link-overlay (link)
|
||||
"Create overlay for LINK."
|
||||
(save-excursion
|
||||
(save-match-data
|
||||
(let* ((type (org-element-property :type link))
|
||||
(id (org-element-property :path link))
|
||||
(pos (org-element-property :end link))
|
||||
(desc-p (org-element-property :contents-begin link))
|
||||
node)
|
||||
(when (and (string-equal type "id")
|
||||
(setq node (org-roam-node-from-id id))
|
||||
(not desc-p))
|
||||
(org-roam-overlay--make
|
||||
pos pos
|
||||
'after-string (format "%s "
|
||||
(propertize (org-roam-node-title node)
|
||||
'face 'org-roam-overlay))))))))
|
||||
|
||||
(defun org-roam-overlay-enable ()
|
||||
"Enable Org-roam overlays."
|
||||
(org-roam-db-map-links
|
||||
(list #'org-roam-overlay-make-link-overlay)))
|
||||
|
||||
(defun org-roam-overlay-disable ()
|
||||
"Disable Org-roam overlays."
|
||||
(remove-overlays nil nil 'category 'org-roam))
|
||||
|
||||
(defun org-roam-overlay-redisplay ()
|
||||
"Redisplay Org-roam overlays."
|
||||
(org-roam-overlay-disable)
|
||||
(org-roam-overlay-enable))
|
||||
|
||||
(define-minor-mode org-roam-overlay-mode
|
||||
"Overlays for Org-roam ID links.
|
||||
Org-roam overlay mode is a minor mode. When enabled,
|
||||
overlay displaying the node's title is displayed."
|
||||
:lighter " org-roam-overlay"
|
||||
(if org-roam-overlay-mode
|
||||
(progn
|
||||
(org-roam-overlay-enable)
|
||||
(add-hook 'after-save-hook #'org-roam-overlay-redisplay nil t))
|
||||
(org-roam-overlay-disable)
|
||||
(remove-hook 'after-save-hook #'org-roam-overlay-redisplay t)))
|
||||
|
||||
(provide 'org-roam-overlay)
|
||||
;;; org-roam-overlay.el ends here
|
174
extensions/org-roam-protocol.el
Normal file
174
extensions/org-roam-protocol.el
Normal file
@ -0,0 +1,174 @@
|
||||
;;; org-roam-protocol.el --- Protocol handler for roam:// links -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.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 extension extends `org-protocol', adding custom Org-roam handlers to it
|
||||
;; to provide the next new protocols:
|
||||
;;
|
||||
;; 1. "roam-node": This protocol simply opens the node given by the node ID
|
||||
;; 2. "roam-ref": This protocol creates or opens the node with the given REF
|
||||
;;
|
||||
;; You can find detailed instructions on how to setup the protocol in the
|
||||
;; documentation for Org-roam.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-protocol)
|
||||
(require 'ol) ;; for org-link-decode
|
||||
(require 'org-roam)
|
||||
|
||||
;;; Options
|
||||
(defcustom org-roam-protocol-store-links nil
|
||||
"Whether to store links when capturing websites with `org-roam-protocol'."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-capture-ref-templates
|
||||
'(("r" "ref" plain "%?"
|
||||
:target (file+head "${slug}.org"
|
||||
"#+title: ${title}")
|
||||
:unnarrowed t))
|
||||
"The Org-roam templates used during a capture from the roam-ref protocol.
|
||||
See `org-roam-capture-templates' for the template documentation."
|
||||
:group 'org-roam
|
||||
:type '(repeat
|
||||
(choice (list :tag "Multikey description"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description"))
|
||||
(list :tag "Template entry"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description ")
|
||||
(choice :tag "Capture Type " :value entry
|
||||
(const :tag "Org entry" entry)
|
||||
(const :tag "Plain list item" item)
|
||||
(const :tag "Checkbox item" checkitem)
|
||||
(const :tag "Plain text" plain)
|
||||
(const :tag "Table line" table-line))
|
||||
(choice :tag "Template "
|
||||
(string)
|
||||
(list :tag "File"
|
||||
(const :format "" file)
|
||||
(file :tag "Template file"))
|
||||
(list :tag "Function"
|
||||
(const :format "" function)
|
||||
(function :tag "Template function")))
|
||||
(plist :inline t
|
||||
;; Give the most common options as checkboxes
|
||||
:options (((const :format "%v " :target)
|
||||
(choice :tag "Node location"
|
||||
(list :tag "File"
|
||||
(const :format "" file)
|
||||
(string :tag " File"))
|
||||
(list :tag "File & Head Content"
|
||||
(const :format "" file+head)
|
||||
(string :tag " File")
|
||||
(string :tag " Head Content"))
|
||||
(list :tag "File & Outline path"
|
||||
(const :format "" file+olp)
|
||||
(string :tag " File")
|
||||
(list :tag "Outline path"
|
||||
(repeat (string :tag "Headline"))))
|
||||
(list :tag "File & Head Content & Outline path"
|
||||
(const :format "" file+head+olp)
|
||||
(string :tag " File")
|
||||
(string :tag " Head Content")
|
||||
(list :tag "Outline path"
|
||||
(repeat (string :tag "Headline"))))))
|
||||
((const :format "%v " :prepend) (const t))
|
||||
((const :format "%v " :immediate-finish) (const t))
|
||||
((const :format "%v " :jump-to-captured) (const t))
|
||||
((const :format "%v " :empty-lines) (const 1))
|
||||
((const :format "%v " :empty-lines-before) (const 1))
|
||||
((const :format "%v " :empty-lines-after) (const 1))
|
||||
((const :format "%v " :clock-in) (const t))
|
||||
((const :format "%v " :clock-keep) (const t))
|
||||
((const :format "%v " :clock-resume) (const t))
|
||||
((const :format "%v " :time-prompt) (const t))
|
||||
((const :format "%v " :tree-type) (const week))
|
||||
((const :format "%v " :unnarrowed) (const t))
|
||||
((const :format "%v " :table-line-pos) (string))
|
||||
((const :format "%v " :kill-buffer) (const t))))))))
|
||||
|
||||
;;; Handlers
|
||||
(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())"
|
||||
(unless (plist-get info :ref)
|
||||
(user-error "No ref key provided"))
|
||||
(org-roam-plist-map! (lambda (k v)
|
||||
(org-link-decode
|
||||
(if (equal k :ref)
|
||||
(org-protocol-sanitize-uri v)
|
||||
v))) info)
|
||||
(when org-roam-protocol-store-links
|
||||
(push (list (plist-get info :ref)
|
||||
(plist-get info :title)) org-stored-links))
|
||||
(org-link-store-props :type (and (string-match org-link-plain-re
|
||||
(plist-get info :ref))
|
||||
(match-string 1 (plist-get info :ref)))
|
||||
:link (plist-get info :ref)
|
||||
:annotation (org-link-make-string (plist-get info :ref)
|
||||
(or (plist-get info :title)
|
||||
(plist-get info :ref)))
|
||||
:initial (or (plist-get info :body) ""))
|
||||
(raise-frame)
|
||||
(let ((org-capture-link-is-already-stored t))
|
||||
(org-roam-capture-
|
||||
:keys (plist-get info :template)
|
||||
:node (org-roam-node-create :title (plist-get info :title))
|
||||
:info (list :ref (plist-get info :ref)
|
||||
:body (plist-get info :body))
|
||||
:templates org-roam-capture-ref-templates))
|
||||
nil)
|
||||
|
||||
(defun org-roam-protocol-open-node (info)
|
||||
"This handler simply opens the file with emacsclient.
|
||||
|
||||
INFO is a plist 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-node?node=uuid"
|
||||
(when-let ((node (plist-get info :node)))
|
||||
(raise-frame)
|
||||
(org-roam-node-visit (org-roam-populate (org-roam-node-create :id node)) nil 'force))
|
||||
nil)
|
||||
|
||||
(push '("org-roam-ref" :protocol "roam-ref" :function org-roam-protocol-open-ref)
|
||||
org-protocol-protocol-alist)
|
||||
(push '("org-roam-node" :protocol "roam-node" :function org-roam-protocol-open-node)
|
||||
org-protocol-protocol-alist)
|
||||
|
||||
(provide 'org-roam-protocol)
|
||||
|
||||
;;; org-roam-protocol.el ends here
|
11
github-eldev
Executable file
11
github-eldev
Executable file
@ -0,0 +1,11 @@
|
||||
#! /bin/sh
|
||||
set -e
|
||||
|
||||
ELDEV_BIN_DIR=~/.local/bin
|
||||
|
||||
# `$GITHUB_PATH' is a magic file which contents is translated to environment variable `$PATH'.
|
||||
echo "$ELDEV_BIN_DIR" >> $GITHUB_PATH
|
||||
|
||||
mkdir -p $ELDEV_BIN_DIR
|
||||
curl -fsSL https://raw.githubusercontent.com/doublep/eldev/f111d19cda305e5e8fcb70a5675b87173041cb68/bin/eldev > $ELDEV_BIN_DIR/eldev
|
||||
chmod a+x $ELDEV_BIN_DIR/eldev
|
@ -1,357 +0,0 @@
|
||||
;;; org-roam-buffer.el --- Metadata buffer -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides the org-roam-buffer functionality for org-roam
|
||||
;;; Code:
|
||||
;;;; Library Requires
|
||||
(eval-when-compile (require 'subr-x))
|
||||
(require 'cl-lib)
|
||||
(require 'dash)
|
||||
(require 's)
|
||||
(require 'f)
|
||||
(require 'ol)
|
||||
(require 'org-element)
|
||||
(require 'org-roam-macs)
|
||||
|
||||
(defvar org-roam-directory)
|
||||
(defvar org-link-frame-setup)
|
||||
(defvar org-return-follows-link)
|
||||
(defvar org-roam-backlinks-mode)
|
||||
(defvar org-roam-last-window)
|
||||
(defvar org-ref-cite-types) ;; in org-ref-core.el
|
||||
(defvar org-roam-mode)
|
||||
(defvar org-roam--org-link-bracket-typed-re)
|
||||
|
||||
(declare-function org-roam-db--ensure-built "org-roam-db")
|
||||
(declare-function org-roam-db--get-title "org-roam-db")
|
||||
(declare-function org-roam-db-has-file-p "org-roam-db")
|
||||
(declare-function org-roam--extract-refs "org-roam")
|
||||
(declare-function org-roam--extract-titles "org-roam")
|
||||
(declare-function org-roam--get-backlinks "org-roam")
|
||||
(declare-function org-roam-backlinks-mode "org-roam")
|
||||
(declare-function org-roam-mode "org-roam")
|
||||
(declare-function org-roam--find-file "org-roam")
|
||||
(declare-function org-roam-format-link "org-roam")
|
||||
(declare-function org-roam-link-get-path "org-roam-link")
|
||||
|
||||
(defcustom org-roam-buffer-position 'right
|
||||
"Position of `org-roam' buffer.
|
||||
Valid values are
|
||||
* left,
|
||||
* right,
|
||||
* top,
|
||||
* bottom,
|
||||
* a function returning one of the above."
|
||||
:type '(choice (const left)
|
||||
(const right)
|
||||
(const top)
|
||||
(const bottom)
|
||||
function)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-width 0.33
|
||||
"Width of `org-roam' buffer.
|
||||
Has an effect if and only if `org-roam-buffer-position' is `left' or `right'."
|
||||
:type 'number
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-height 0.27
|
||||
"Height of `org-roam' buffer.
|
||||
Has an effect if and only if `org-roam-buffer-position' is `top' or `bottom'."
|
||||
:type 'number
|
||||
:group 'org-roam)
|
||||
|
||||
|
||||
(defcustom org-roam-buffer "*org-roam*"
|
||||
"Org-roam buffer name."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-prepare-hook '(org-roam-buffer--insert-title
|
||||
org-roam-buffer--insert-backlinks
|
||||
org-roam-buffer--insert-ref-links)
|
||||
"Hook run in the `org-roam-buffer' before it is displayed."
|
||||
:type 'hook
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-preview-function #'org-roam-buffer--preview
|
||||
"Function to obtain preview contents for a given link.
|
||||
The function takes in two arguments, the FILE containing the
|
||||
link, and the POINT of the link."
|
||||
:type 'function
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-buffer-window-parameters nil
|
||||
"Additional window parameters for the `org-roam-buffer' side window.
|
||||
For example: (setq org-roam-buffer-window-parameters '((no-other-window . t)))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defvar org-roam-buffer--current nil
|
||||
"Currently displayed file in `org-roam' buffer.")
|
||||
|
||||
(defun org-roam-buffer--find-file (file)
|
||||
"Open FILE in the window `org-roam' was called from."
|
||||
(setq file (expand-file-name file))
|
||||
(let ((last-window org-roam-last-window))
|
||||
(if (window-valid-p last-window)
|
||||
(progn (with-selected-window last-window
|
||||
(org-roam--find-file file))
|
||||
(select-window last-window))
|
||||
(org-roam--find-file file))))
|
||||
|
||||
(defun org-roam-buffer--insert-title ()
|
||||
"Insert the org-roam-buffer title."
|
||||
(insert (propertize (org-roam-db--get-title
|
||||
(buffer-file-name org-roam-buffer--current))
|
||||
'font-lock-face
|
||||
'org-document-title)))
|
||||
|
||||
(defun org-roam-buffer--preview (file point)
|
||||
"Get preview content for FILE at POINT."
|
||||
(save-excursion
|
||||
(org-roam--with-temp-buffer file
|
||||
(goto-char point)
|
||||
(let ((elem (org-element-at-point)))
|
||||
(or (org-element-property :raw-value elem)
|
||||
(when-let ((begin (org-element-property :begin elem))
|
||||
(end (org-element-property :end elem)))
|
||||
(string-trim (buffer-substring-no-properties begin end))))))))
|
||||
|
||||
(defun org-roam-buffer--pluralize (string number)
|
||||
"Conditionally pluralize STRING if NUMBER is above 1."
|
||||
(let ((l (pcase number
|
||||
((pred (listp)) (length number))
|
||||
((pred (integerp)) number)
|
||||
(wrong-type (signal 'wrong-type-argument
|
||||
`((listp integerp)
|
||||
,wrong-type))))))
|
||||
(concat string (when (> l 1) "s"))))
|
||||
|
||||
(defun org-roam-buffer-expand-links (content orig-path)
|
||||
"Crawl CONTENT for relative links and corrects them to be correctly displayed.
|
||||
ORIG-PATH is the path where the CONTENT originated."
|
||||
(with-temp-buffer
|
||||
(insert content)
|
||||
(goto-char (point-min))
|
||||
(let (link link-type)
|
||||
(while (re-search-forward org-roam--org-link-bracket-typed-re (point-max) t)
|
||||
(setq link-type (match-string 1)
|
||||
link (match-string 2))
|
||||
(when (and (string-equal link-type "file")
|
||||
(f-relative-p link))
|
||||
(replace-match (org-roam-link-get-path (expand-file-name link (file-name-directory orig-path)))
|
||||
nil t nil 2))))
|
||||
(buffer-string)))
|
||||
|
||||
(defun org-roam-buffer--insert-ref-links ()
|
||||
"Insert ref backlinks for the current buffer."
|
||||
(when-let* ((refs (with-temp-buffer
|
||||
(insert-buffer-substring org-roam-buffer--current)
|
||||
(org-roam--extract-refs)))
|
||||
(paths (mapcar #'cdr refs)))
|
||||
(if-let* ((key-backlinks (mapcan #'org-roam--get-backlinks paths))
|
||||
(grouped-backlinks (--group-by (nth 0 it) key-backlinks)))
|
||||
(progn
|
||||
(insert (let ((l (length key-backlinks)))
|
||||
(format "\n\n* %d %s\n"
|
||||
l (org-roam-buffer--pluralize "Ref Backlink" l))))
|
||||
(dolist (group grouped-backlinks)
|
||||
(let ((file-from (car group))
|
||||
(bls (cdr group)))
|
||||
(insert (format "** %s\n"
|
||||
(org-roam-format-link file-from
|
||||
(org-roam-db--get-title file-from)
|
||||
"file")))
|
||||
(dolist (backlink bls)
|
||||
(pcase-let ((`(,file-from _ ,props) backlink))
|
||||
(insert (if-let ((content (funcall org-roam-buffer-preview-function file-from (plist-get props :point))))
|
||||
(propertize (org-roam-buffer-expand-links content file-from)
|
||||
'help-echo "mouse-1: visit backlinked note"
|
||||
'file-from file-from
|
||||
'file-from-point (plist-get props :point))
|
||||
"")
|
||||
"\n\n"))))))
|
||||
(insert "\n\n* No ref backlinks!"))))
|
||||
|
||||
(defun org-roam-buffer--insert-backlinks ()
|
||||
"Insert the org-roam-buffer backlinks string for the current buffer."
|
||||
(let (props file-from)
|
||||
(if-let* ((file-path (buffer-file-name org-roam-buffer--current))
|
||||
(titles (with-current-buffer org-roam-buffer--current
|
||||
(org-roam--extract-titles)))
|
||||
(backlinks (org-roam--get-backlinks (push file-path titles)))
|
||||
(grouped-backlinks (--group-by (nth 0 it) backlinks)))
|
||||
(progn
|
||||
(insert (let ((l (length backlinks)))
|
||||
(format "\n\n* %d %s\n"
|
||||
l (org-roam-buffer--pluralize "Backlink" l))))
|
||||
(dolist (group grouped-backlinks)
|
||||
(setq file-from (car group))
|
||||
(setq props (mapcar (lambda (row) (nth 2 row)) (cdr group)))
|
||||
(setq props (seq-sort-by (lambda (p) (plist-get p :point)) #'< props))
|
||||
(insert (format "** %s\n"
|
||||
(org-roam-format-link file-from
|
||||
(org-roam-db--get-title file-from)
|
||||
"file")))
|
||||
(dolist (prop props)
|
||||
(insert "*** "
|
||||
(if-let ((outline (plist-get prop :outline)))
|
||||
(-> outline
|
||||
(string-join " > ")
|
||||
(org-roam-buffer-expand-links file-from))
|
||||
"Top")
|
||||
"\n"
|
||||
(if-let ((content (funcall org-roam-buffer-preview-function file-from (plist-get prop :point))))
|
||||
(propertize
|
||||
(s-trim (s-replace "\n" " " (org-roam-buffer-expand-links content file-from)))
|
||||
'help-echo "mouse-1: visit backlinked note"
|
||||
'file-from file-from
|
||||
'file-from-point (plist-get prop :point))
|
||||
"")
|
||||
"\n\n"))))
|
||||
(insert "\n\n* No backlinks!"))))
|
||||
|
||||
(defun org-roam-buffer-update ()
|
||||
"Update the `org-roam-buffer'."
|
||||
(interactive)
|
||||
(org-roam-db--ensure-built)
|
||||
(let* ((source-org-roam-directory org-roam-directory))
|
||||
(with-current-buffer org-roam-buffer
|
||||
;; When dir-locals.el is used to override org-roam-directory,
|
||||
;; org-roam-buffer should have a different local org-roam-directory and
|
||||
;; default-directory, as relative links are relative from the overridden
|
||||
;; org-roam-directory.
|
||||
(setq-local org-roam-directory source-org-roam-directory)
|
||||
(setq-local default-directory source-org-roam-directory)
|
||||
;; 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)
|
||||
(unless (eq major-mode 'org-mode)
|
||||
(org-mode))
|
||||
(unless org-roam-backlinks-mode
|
||||
(org-roam-backlinks-mode))
|
||||
(make-local-variable 'org-return-follows-link)
|
||||
(setq org-return-follows-link t)
|
||||
(run-hooks 'org-roam-buffer-prepare-hook)
|
||||
(read-only-mode 1)))))
|
||||
|
||||
|
||||
(cl-defun org-roam-buffer--update-maybe (&key redisplay)
|
||||
"Reconstructs `org-roam-buffer'.
|
||||
This needs to be quick or infrequent, because this is run at
|
||||
`post-command-hook'. If REDISPLAY, force an update of
|
||||
`org-roam-buffer'."
|
||||
(let ((buffer (window-buffer)))
|
||||
(when (and (or redisplay
|
||||
(not (eq org-roam-buffer--current buffer)))
|
||||
(eq 'visible (org-roam-buffer--visibility))
|
||||
(buffer-file-name buffer)
|
||||
(org-roam-db-has-file-p (buffer-file-name buffer)))
|
||||
(setq org-roam-buffer--current buffer)
|
||||
(org-roam-buffer-update))))
|
||||
|
||||
;;;; Toggling the org-roam buffer
|
||||
(define-inline org-roam-buffer--visibility ()
|
||||
"Return whether the current visibility state of the org-roam buffer.
|
||||
Valid states are 'visible, 'exists and 'none."
|
||||
(declare (side-effect-free t))
|
||||
(inline-quote
|
||||
(cond
|
||||
((get-buffer-window org-roam-buffer) 'visible)
|
||||
((get-buffer org-roam-buffer) 'exists)
|
||||
(t 'none))))
|
||||
|
||||
(defun org-roam-buffer--set-width (width)
|
||||
"Set the width of `org-roam-buffer' to `WIDTH'."
|
||||
(unless (one-window-p)
|
||||
(let ((window-size-fixed)
|
||||
(w (max width window-min-width)))
|
||||
(cond
|
||||
((> (window-width) w)
|
||||
(shrink-window-horizontally (- (window-width) w)))
|
||||
((< (window-width) w)
|
||||
(enlarge-window-horizontally (- w (window-width))))))))
|
||||
|
||||
(defun org-roam-buffer--set-height (height)
|
||||
"Set the height of `org-roam-buffer' to `HEIGHT'."
|
||||
(unless (one-window-p)
|
||||
(let ((window-size-fixed)
|
||||
(h (max height window-min-height)))
|
||||
(cond
|
||||
((> (window-height) h)
|
||||
(shrink-window (- (window-height) h)))
|
||||
((< (window-height) h)
|
||||
(enlarge-window (- h (window-height))))))))
|
||||
|
||||
(defun org-roam-buffer--get-create ()
|
||||
"Set up the `org-roam' buffer at `org-roam-buffer-position'."
|
||||
(let ((position (if (functionp org-roam-buffer-position)
|
||||
(funcall org-roam-buffer-position)
|
||||
org-roam-buffer-position)))
|
||||
(save-selected-window
|
||||
(-> (get-buffer-create org-roam-buffer)
|
||||
(display-buffer-in-side-window
|
||||
`((side . ,position)
|
||||
(window-parameters . ,org-roam-buffer-window-parameters)))
|
||||
(select-window))
|
||||
(pcase position
|
||||
((or 'right 'left)
|
||||
(org-roam-buffer--set-width
|
||||
(round (* (frame-width) org-roam-buffer-width))))
|
||||
((or 'top 'bottom)
|
||||
(org-roam-buffer--set-height
|
||||
(round (* (frame-height) org-roam-buffer-height))))))))
|
||||
|
||||
(defun org-roam-buffer-activate ()
|
||||
"Activate display of the `org-roam-buffer'."
|
||||
(interactive)
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(setq org-roam-last-window (get-buffer-window))
|
||||
(org-roam-buffer--get-create))
|
||||
|
||||
(defun org-roam-buffer-deactivate ()
|
||||
"Deactivate display of the `org-roam-buffer'."
|
||||
(interactive)
|
||||
(setq org-roam-last-window (get-buffer-window))
|
||||
(delete-window (get-buffer-window org-roam-buffer)))
|
||||
|
||||
(defun org-roam-buffer-toggle-display ()
|
||||
"Toggle display of the `org-roam-buffer'."
|
||||
(interactive)
|
||||
(pcase (org-roam-buffer--visibility)
|
||||
('visible (org-roam-buffer-deactivate))
|
||||
((or 'exists 'none) (org-roam-buffer-activate))))
|
||||
|
||||
(provide 'org-roam-buffer)
|
||||
|
||||
;;; org-roam-buffer.el ends here
|
1232
org-roam-capture.el
1232
org-roam-capture.el
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,12 @@
|
||||
;;; org-roam-compat.el --- Compatibility Code -*- coding: utf-8; lexical-binding: t; -*-
|
||||
;;; org-roam-compat.el --- Backward compatibility code -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
@ -27,85 +27,214 @@
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This file contains code needed for backward compatibility with older Emacsen
|
||||
;; and previous versions of org-roam.
|
||||
;; This file is dedicated to maintain backward compatibility with older older
|
||||
;; Emacsen and Org-roam versions.
|
||||
;;
|
||||
;;; Code:
|
||||
;;;; Library Requires
|
||||
(require 'org-roam)
|
||||
|
||||
;;; Backports
|
||||
;; REVIEW Remove when 26.x support is dropped. This is exact the same as
|
||||
;; `directory-files-recursively' from Emacs 26, but with FOLLOW-SYMLINKS
|
||||
;; parameter from Emacs 27.
|
||||
(defun org-roam--directory-files-recursively (dir regexp
|
||||
&optional include-directories predicate
|
||||
follow-symlinks)
|
||||
"Return list of all files under directory DIR whose names match REGEXP.
|
||||
This function works recursively. Files are returned in \"depth
|
||||
first\" order, and files from each directory are sorted in
|
||||
alphabetical order. Each file name appears in the returned list
|
||||
in its absolute form.
|
||||
|
||||
By default, the returned list excludes directories, but if
|
||||
optional argument INCLUDE-DIRECTORIES is non-nil, they are
|
||||
included.
|
||||
|
||||
PREDICATE can be either nil (which means that all subdirectories
|
||||
of DIR are descended into), t (which means that subdirectories that
|
||||
can't be read are ignored), or a function (which is called with
|
||||
the name of each subdirectory, and should return non-nil if the
|
||||
subdirectory is to be descended into).
|
||||
|
||||
If FOLLOW-SYMLINKS is non-nil, symbolic links that point to
|
||||
directories are followed. Note that this can lead to infinite
|
||||
recursion."
|
||||
(let* ((result nil)
|
||||
(files nil)
|
||||
(dir (directory-file-name dir))
|
||||
;; When DIR is "/", remote file names like "/method:" could
|
||||
;; also be offered. We shall suppress them.
|
||||
(tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
|
||||
(dolist (file (sort (file-name-all-completions "" dir)
|
||||
'string<))
|
||||
(unless (member file '("./" "../"))
|
||||
(if (directory-name-p file)
|
||||
(let* ((leaf (substring file 0 (1- (length file))))
|
||||
(full-file (concat dir "/" leaf)))
|
||||
;; Don't follow symlinks to other directories.
|
||||
(when (and (or (not (file-symlink-p full-file))
|
||||
(and (file-symlink-p full-file)
|
||||
follow-symlinks))
|
||||
;; Allow filtering subdirectories.
|
||||
(or (eq predicate nil)
|
||||
(eq predicate t)
|
||||
(funcall predicate full-file)))
|
||||
(let ((sub-files
|
||||
(if (eq predicate t)
|
||||
(condition-case nil
|
||||
(org-roam--directory-files-recursively
|
||||
full-file regexp include-directories
|
||||
predicate follow-symlinks)
|
||||
(file-error nil))
|
||||
(org-roam--directory-files-recursively
|
||||
full-file regexp include-directories
|
||||
predicate follow-symlinks))))
|
||||
(setq result (nconc result sub-files))))
|
||||
(when (and include-directories
|
||||
(string-match regexp leaf))
|
||||
(setq result (nconc result (list full-file)))))
|
||||
(when (string-match regexp file)
|
||||
(push (concat dir "/" file) files)))))
|
||||
(nconc result (nreverse files))))
|
||||
|
||||
;;; Compatibility hacks and patches
|
||||
(advice-add #'org-id-add-location :around #'org-roam--handle-absent-org-id-locations-file-a)
|
||||
(defun org-roam--handle-absent-org-id-locations-file-a (fn &rest args)
|
||||
"Gracefully handle errors related to absence of `org-id-locations-file'.
|
||||
FN is `org-id-add-location' that comes from advice and ARGS are
|
||||
passed to it."
|
||||
(condition-case err
|
||||
(apply fn args)
|
||||
;; `org-id' makes the assumption that `org-id-locations-file' will be stored in `user-emacs-directory'
|
||||
;; which always exist if you have Emacs, so it uses `with-temp-file' to write to the file. However, the
|
||||
;; users *do* change the path to this file and `with-temp-file' unable to create the file, if the path to
|
||||
;; it consists of directories that don't exist. We'll have to handle this ourselves.
|
||||
(error
|
||||
(advice-remove 'org-id-add-location #'org-roam--handle-absent-org-id-locations-file-a)
|
||||
(if (file-exists-p (file-truename org-id-locations-file))
|
||||
(signal (car err) (cdr err))
|
||||
;; Pre-allocate the hash table to avoid weird access related errors during the regeneration.
|
||||
(or org-id-locations (setq org-id-locations (make-hash-table :test 'equal)))
|
||||
;; If permissions allow that, try to create the user specified directory path to
|
||||
;; `org-id-locations-file' ourselves.
|
||||
(condition-case _err
|
||||
(progn (org-roam-message (concat "`org-id-locations-file' (%s) doesn't exist. "
|
||||
"Trying to regenerate it (this may take a while)...")
|
||||
org-id-locations-file)
|
||||
(make-directory (file-name-directory (file-truename org-id-locations-file)))
|
||||
(org-roam-update-org-id-locations)
|
||||
(apply fn args))
|
||||
;; In case of failure (lack of permissions), we'll patch it to at least handle the current session
|
||||
;; without errors.
|
||||
(file-error (org-roam-message "Failed to regenerate `org-id-locations-file'")
|
||||
(lwarn 'org-roam :error "
|
||||
--------
|
||||
WARNING: `org-id-locations-file' (%s) doesn't exist!
|
||||
Org-roam is unable to create it for you.
|
||||
--------
|
||||
|
||||
This happens when Emacs doesn't have permissions to create the
|
||||
path to your `org-id-locations-file'. Org-roam will now fallback
|
||||
storing the file in your current `org-roam-directory', but the
|
||||
warning will keep popup with each new session.
|
||||
|
||||
To stop this warning from popping up, set `org-id-locations-file'
|
||||
to the location you want and ensure that the path exists on your
|
||||
filesystem, then run M-x `org-roam-update-org-id-locations'.
|
||||
|
||||
Note: While Org-roam doesn't depend on `org-id-locations-file' to
|
||||
lookup IDs for the nodes that are stored in the database, it
|
||||
still tries to keep it updated so IDs work across other files in
|
||||
Org-mode, so the IDs used in your `org-roam-directory' would be
|
||||
able to cross-reference outside of `org-roam-directory'. It also
|
||||
allows to keep linking with \"id:\" links within the current
|
||||
`org-roam-directory' to headings and files that are excluded from
|
||||
identification (e.g. with \"ROAM_EXCLUDE\" property) as Org-roam
|
||||
nodes." org-id-locations-file)
|
||||
(setq org-id-locations-file
|
||||
(expand-file-name ".orgids" (file-truename org-roam-directory)))
|
||||
(apply fn args)))))))
|
||||
|
||||
;;;; Deprecated :if-new capture template keyword
|
||||
(with-eval-after-load 'org-roam-capture
|
||||
(add-to-list 'org-roam-capture--template-keywords :if-new)
|
||||
|
||||
(let ((inhibit-warning-p t)) ; REVIEW Set this to nil close to next major release
|
||||
(advice-add 'org-roam-capture--get-target :around #'org-roam-capture--get-if-new-target-a)
|
||||
(defun org-roam-capture--get-if-new-target-a (fn &rest args)
|
||||
"Get the current capture target using deprecated :if-new property."
|
||||
(if-let ((target (org-roam-capture--get :if-new)))
|
||||
(prog1 target
|
||||
(unless inhibit-warning-p
|
||||
(lwarn 'org-roam-capture :warning
|
||||
(mapconcat
|
||||
#'identity
|
||||
["`:if-new' property is deprecated in favor of `:target'."
|
||||
"This warning will popup once per each session. In order to get"
|
||||
"rid of it, rename all the references to the `:if-new' property"
|
||||
"in your capture templates to `:target'."]
|
||||
"\n"))
|
||||
;; Don't irritate the user too much. Displaying the warning once per session should be enough.
|
||||
(setq inhibit-warning-p t)))
|
||||
(apply fn args)))))
|
||||
|
||||
;;; Obsolete aliases (remove after next major release)
|
||||
;;;; Functions
|
||||
(define-obsolete-function-alias 'org-roam--capture-get-point 'org-roam-capture--get-point
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-build-cache 'org-roam-db-build-cache
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-sql 'org-roam-db-query
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--db-clear 'org-roam-db--clear
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-show-graph 'org-roam-graph-show
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--maybe-update-buffer
|
||||
'org-roam-buffer--update-maybe "org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--current-visibility
|
||||
'org-roam-buffer--visibility "org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-update 'org-roam-buffer-update
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--set-width 'org-roam-buffer--set-width
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--set-height 'org-roam-buffer--set-height
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam--set-up-buffer 'org-roam-buffer--get-create
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-today 'org-roam-dailies-today
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-tomorrow 'org-roam-dailies-tomorrow
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-yesterday 'org-roam-dailies-yesterday
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-date 'org-roam-dailies-date
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-graph-show 'org-roam-graph
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-graph-build 'org-roam-graph
|
||||
"org-roam 1.0.0")
|
||||
(define-obsolete-function-alias 'org-roam-find-index 'org-roam-jump-to-index
|
||||
"org-roam 1.1.0")
|
||||
(define-obsolete-function-alias 'org-roam--pluralize 'org-roam-buffer--pluralize
|
||||
"org-roam 1.1.0")
|
||||
(define-obsolete-function-alias 'org-roam--capture 'org-roam-capture--capture
|
||||
"org-roam 1.1.0")
|
||||
(define-obsolete-function-alias 'org-roam-db--clear 'org-roam-db-clear
|
||||
"org-roam 1.2.0")
|
||||
(define-obsolete-function-alias 'org-roam-dailies-today 'org-roam-dailies-find-today
|
||||
"org-roam 1.2.2")
|
||||
(define-obsolete-function-alias 'org-roam-dailies-yesterday 'org-roam-dailies-find-yesterday
|
||||
"org-roam 1.2.2")
|
||||
(define-obsolete-function-alias 'org-roam-dailies-tomorrow 'org-roam-dailies-find-tomorrow
|
||||
"org-roam 1.2.2")
|
||||
(define-obsolete-function-alias 'org-roam-dailies-date 'org-roam-dailies-find-date
|
||||
"org-roam 1.2.2")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-setup
|
||||
'org-roam-db-autosync-enable "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-teardown
|
||||
'org-roam-db-autosync-disable "org-roam 2.0")
|
||||
|
||||
;;;; Variables
|
||||
(define-obsolete-variable-alias 'org-roam-graphviz-extra-options
|
||||
'org-roam-graph-extra-config "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam-grapher-extra-options
|
||||
'org-roam-graph-extra-config "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam-graph-node-shape
|
||||
'org-roam-graph-node-extra-config "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam--db-connection
|
||||
'org-roam-db--connection "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam--current-buffer
|
||||
'org-roam-buffer--current "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam-date-title-format
|
||||
'org-roam-dailies-capture-templates "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam-date-filename-format
|
||||
'org-roam-dailies-capture-templates "org-roam 1.0.0")
|
||||
(define-obsolete-variable-alias 'org-roam-update-db-idle-seconds
|
||||
'org-roam-db-update-idle-seconds "org-roam 1.2.2")
|
||||
(define-obsolete-variable-alias
|
||||
'org-roam-current-node
|
||||
'org-roam-buffer-current-node "org-roam 2.0")
|
||||
(define-obsolete-variable-alias
|
||||
'org-roam-current-directory
|
||||
'org-roam-buffer-current-directory "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-buffer-render
|
||||
'org-roam-buffer-render-contents "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-buffer
|
||||
'org-roam-buffer-display-dedicated "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-visit-thing
|
||||
'org-roam-buffer-visit-thing "org-roam 2.0")
|
||||
|
||||
(make-obsolete-variable 'org-roam-buffer-no-delete-other-windows
|
||||
'org-roam-buffer-window-parameters "org-roam 1.1.1")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-today
|
||||
'org-roam-dailies-goto-today "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-yesterday
|
||||
'org-roam-dailies-goto-yesterday "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-tomorrow
|
||||
'org-roam-dailies-goto-tomorrow "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-next-note
|
||||
'org-roam-dailies-goto-next-note "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-previous-note
|
||||
'org-roam-dailies-goto-previous-note "org-roam 2.0")
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-dailies-find-date
|
||||
'org-roam-dailies-goto-date "org-roam 2.0")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-add-property
|
||||
'org-roam-property-add "org-roam 2.1")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'org-roam-remove-property
|
||||
'org-roam-property-remove "org-roam 2.1")
|
||||
|
||||
(define-obsolete-variable-alias
|
||||
'org-roam-mode-section-functions
|
||||
'org-roam-mode-sections "org-roam 2.2.0")
|
||||
|
||||
;;; Obsolete functions
|
||||
(make-obsolete 'org-roam-get-keyword 'org-collect-keywords "org-roam 2.0")
|
||||
|
||||
(provide 'org-roam-compat)
|
||||
|
||||
|
@ -1,117 +0,0 @@
|
||||
;;; org-roam-completion.el --- Completion features -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides completion for org-roam.
|
||||
;;; Code:
|
||||
;;;; Library Requires
|
||||
(require 'cl-lib)
|
||||
(require 's)
|
||||
|
||||
(defvar helm-pattern)
|
||||
(declare-function helm "ext:helm")
|
||||
(declare-function helm-make-source "ext:helm-source" (name class &rest args) t)
|
||||
|
||||
(defcustom org-roam-completion-system 'default
|
||||
"The completion system to be used by `org-roam'."
|
||||
:type '(radio
|
||||
(const :tag "Default" default)
|
||||
(const :tag "Ido" ido)
|
||||
(const :tag "Ivy" ivy)
|
||||
(const :tag "Helm" helm)
|
||||
(function :tag "Custom function"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-completion-ignore-case t
|
||||
"Whether to ignore case in Org-roam `completion-at-point' completions."
|
||||
:group 'org-roam
|
||||
:type 'boolean)
|
||||
|
||||
(defun org-roam-completion--helm-candidate-transformer (candidates _source)
|
||||
"Transforms CANDIDATES for Helm-based completing read.
|
||||
SOURCE is not used."
|
||||
(let ((prefix (propertize "[?] "
|
||||
'face 'helm-ff-prefix)))
|
||||
(cons (propertize helm-pattern
|
||||
'display (concat prefix helm-pattern))
|
||||
candidates)))
|
||||
|
||||
(cl-defun org-roam-completion--completing-read (prompt choices &key
|
||||
require-match initial-input
|
||||
action)
|
||||
"Present a PROMPT with CHOICES and optional INITIAL-INPUT.
|
||||
If REQUIRE-MATCH is t, the user must select one of the CHOICES.
|
||||
Return user choice."
|
||||
(let (res)
|
||||
(setq res
|
||||
(cond
|
||||
((eq org-roam-completion-system 'ido)
|
||||
(let ((candidates (mapcar #'car choices)))
|
||||
(ido-completing-read prompt candidates nil require-match initial-input)))
|
||||
((eq org-roam-completion-system 'default)
|
||||
(completing-read prompt choices nil require-match initial-input))
|
||||
((eq org-roam-completion-system 'ivy)
|
||||
(if (fboundp 'ivy-read)
|
||||
(ivy-read prompt choices
|
||||
:initial-input initial-input
|
||||
:preselect initial-input
|
||||
:require-match require-match
|
||||
:action (prog1 action
|
||||
(setq action nil))
|
||||
:caller 'org-roam--completing-read)
|
||||
(user-error "Please install ivy from \
|
||||
https://github.com/abo-abo/swiper")))
|
||||
((eq org-roam-completion-system 'helm)
|
||||
(unless (and (fboundp 'helm)
|
||||
(fboundp 'helm-make-source))
|
||||
(user-error "Please install helm from \
|
||||
https://github.com/emacs-helm/helm"))
|
||||
(let ((source (helm-make-source prompt 'helm-source-sync
|
||||
:candidates (mapcar #'car choices)
|
||||
:filtered-candidate-transformer
|
||||
(and (not require-match)
|
||||
#'org-roam-completion--helm-candidate-transformer)))
|
||||
(buf (concat "*org-roam "
|
||||
(s-downcase (s-chop-suffix ":" (s-trim prompt)))
|
||||
"*")))
|
||||
(or (helm :sources source
|
||||
:action (if action
|
||||
(prog1 action
|
||||
(setq action nil))
|
||||
#'identity)
|
||||
:prompt prompt
|
||||
:input initial-input
|
||||
:buffer buf)
|
||||
(keyboard-quit))))))
|
||||
(if action
|
||||
(funcall action res)
|
||||
res)))
|
||||
|
||||
(provide 'org-roam-completion)
|
||||
|
||||
;;; org-roam-completion.el ends here
|
@ -1,348 +0,0 @@
|
||||
;;; org-roam-dailies.el --- Daily-notes for Org-roam -*- coding: utf-8; lexical-binding: t; -*-
|
||||
;;;
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Copyright © 2020 Leo Vivier <leo.vivier+dev@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Leo Vivier <leo.vivier+dev@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides functionality for creating daily-notes. This is a
|
||||
;; concept borrowed from Roam Research.
|
||||
;;
|
||||
;;; Code:
|
||||
;;; Library Requires
|
||||
(require 'org-capture)
|
||||
(require 'org-roam-capture)
|
||||
(require 'org-roam-macs)
|
||||
(require 'f)
|
||||
|
||||
;;;; Declarations
|
||||
(defvar org-roam-mode)
|
||||
(defvar org-roam-directory)
|
||||
(defvar org-roam-file-extensions)
|
||||
(declare-function org-roam--org-file-p "org-roam")
|
||||
(declare-function org-roam--find-file "org-roam")
|
||||
(declare-function org-roam-mode "org-roam")
|
||||
|
||||
;;;; Customizable variables
|
||||
(defcustom org-roam-dailies-directory "daily/"
|
||||
"Path to daily-notes."
|
||||
:group 'org-roam
|
||||
:type 'string)
|
||||
|
||||
(defcustom org-roam-dailies-find-file-hook nil
|
||||
"Hook that is run right after navigating to a daily-note."
|
||||
:group 'org-roam
|
||||
:type 'hook)
|
||||
|
||||
(defcustom org-roam-dailies-capture-templates
|
||||
`(("d" "default" entry (function org-roam-capture--get-point)
|
||||
"* %?"
|
||||
:file-name ,(concat org-roam-dailies-directory "%<%Y-%m-%d>")
|
||||
:head "#+title: %<%Y-%m-%d>\n"))
|
||||
"Capture templates for daily-notes in Org-roam."
|
||||
:group 'org-roam
|
||||
;; Adapted from `org-capture-templates'
|
||||
:type
|
||||
`(repeat
|
||||
(choice :value ("d" "default" plain (function org-roam-capture--get-point)
|
||||
"%?"
|
||||
:file-name ,(concat org-roam-dailies-directory
|
||||
"%<%Y-%m-%d>")
|
||||
:head "#+title: %<%Y-%m-%d>\n"
|
||||
:unnarrowed t)
|
||||
(list :tag "Multikey description"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description"))
|
||||
(list :tag "Template entry"
|
||||
(string :tag "Keys ")
|
||||
(string :tag "Description ")
|
||||
(choice :tag "Type "
|
||||
(const :tag "Plain" plain)
|
||||
(const :tag "Entry (for creating headlines)" entry))
|
||||
(const :format "" #'org-roam-capture--get-point)
|
||||
(choice :tag "Template "
|
||||
(string :tag "String"
|
||||
:format "String:\n \
|
||||
Template string :\n%v")
|
||||
(list :tag "File"
|
||||
(const :format "" file)
|
||||
(file :tag "Template file "))
|
||||
(list :tag "Function"
|
||||
(const :format "" function)
|
||||
(function :tag "Template function ")))
|
||||
(const :format "File name format :" :file-name)
|
||||
(string :format " %v" :value ,(concat org-roam-dailies-directory
|
||||
"%<%Y-%m-%d>"))
|
||||
(const :format "Header format :" :head)
|
||||
(string :format " %v" :value "#+title: ${title}\n")
|
||||
(plist :inline t
|
||||
:tag "Options"
|
||||
;; Give the most common options as checkboxes
|
||||
:options
|
||||
(((const :tag "Outline path" :olp)
|
||||
(repeat :tag "Headings"
|
||||
(string :tag "Heading")))
|
||||
((const :format "%v " :unnarrowed) (const t))
|
||||
((const :format "%v " :prepend) (const t))
|
||||
((const :format "%v " :immediate-finish) (const t))
|
||||
((const :format "%v " :jump-to-captured) (const t))
|
||||
((const :format "%v " :empty-lines) (const 1))
|
||||
((const :format "%v " :empty-lines-before) (const 1))
|
||||
((const :format "%v " :empty-lines-after) (const 1))
|
||||
((const :format "%v " :clock-in) (const t))
|
||||
((const :format "%v " :clock-keep) (const t))
|
||||
((const :format "%v " :clock-resume) (const t))
|
||||
((const :format "%v " :time-prompt) (const t))
|
||||
((const :format "%v " :tree-type) (const week))
|
||||
((const :format "%v " :table-line-pos) (string))
|
||||
((const :format "%v " :kill-buffer) (const t))))))))
|
||||
|
||||
;;;; Utilities
|
||||
(defun org-roam-dailies-directory--get-absolute-path ()
|
||||
"Get absolute path to `org-roam-dailies-directory'."
|
||||
(expand-file-name org-roam-dailies-directory org-roam-directory))
|
||||
|
||||
(defun org-roam-dailies-find-directory ()
|
||||
"Find and open `org-roam-dailies-directory'."
|
||||
(interactive)
|
||||
(org-roam--find-file (org-roam-dailies-directory--get-absolute-path)))
|
||||
|
||||
(defun org-roam-dailies--daily-note-p (&optional file)
|
||||
"Return t if FILE is an Org-roam daily-note, nil otherwise.
|
||||
|
||||
If FILE is not specified, use the current buffer's file-path."
|
||||
(when-let ((path (or file
|
||||
(-> (buffer-base-buffer)
|
||||
(buffer-file-name))))
|
||||
(directory (org-roam-dailies-directory--get-absolute-path)))
|
||||
(setq path (expand-file-name path))
|
||||
(save-match-data
|
||||
(and
|
||||
(org-roam--org-file-p path)
|
||||
(f-descendant-of-p path directory)))))
|
||||
|
||||
(defun org-roam-dailies--capture (time &optional goto)
|
||||
"Capture an entry in a daily-note for TIME, creating it if necessary.
|
||||
|
||||
When GOTO is non-nil, go the note without creating an entry."
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(let ((org-roam-capture-templates (--> org-roam-dailies-capture-templates
|
||||
(if goto (list (car it)) it)))
|
||||
(org-roam-capture--info (list (cons 'time time)))
|
||||
(org-roam-capture--context 'dailies))
|
||||
(org-roam-capture--capture (when goto '(4)))))
|
||||
|
||||
;;;; Commands
|
||||
;;; Today
|
||||
(defun org-roam-dailies-capture-today (&optional goto)
|
||||
"Create an entry in the daily-note for today.
|
||||
|
||||
When GOTO is non-nil, go the note without creating an entry."
|
||||
(interactive "P")
|
||||
(org-roam-dailies--capture (current-time) goto)
|
||||
(when goto
|
||||
(run-hooks 'org-roam-dailies-find-file-hook)))
|
||||
|
||||
(defun org-roam-dailies-find-today ()
|
||||
"Find the daily-note for today, creating it if necessary."
|
||||
(interactive)
|
||||
(org-roam-dailies-capture-today t))
|
||||
|
||||
;;; Tomorrow
|
||||
(defun org-roam-dailies-capture-tomorrow (n &optional goto)
|
||||
"Create an entry in the daily-note for tomorrow.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the future.
|
||||
|
||||
With a `C-u' prefix or when GOTO is non-nil, go the note without
|
||||
creating an entry."
|
||||
(interactive "p")
|
||||
(org-roam-dailies--capture (time-add (* n 86400) (current-time)) goto))
|
||||
|
||||
(defun org-roam-dailies-find-tomorrow (n)
|
||||
"Find the daily-note for tomorrow, creating it if necessary.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the
|
||||
future."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow n t))
|
||||
|
||||
;;; Yesterday
|
||||
(defun org-roam-dailies-capture-yesterday (n &optional goto)
|
||||
"Create an entry in the daily-note for yesteday.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the past.
|
||||
|
||||
When GOTO is non-nil, go the note without creating an entry."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow (- n) goto))
|
||||
|
||||
(defun org-roam-dailies-find-yesterday (n)
|
||||
"Find the daily-note for yesterday, creating it if necessary.
|
||||
|
||||
With numeric argument N, use the daily-note N days in the
|
||||
future."
|
||||
(interactive "p")
|
||||
(org-roam-dailies-capture-tomorrow (- n) t))
|
||||
|
||||
;;; Calendar
|
||||
(defvar org-roam-dailies-calendar-hook (list 'org-roam-dailies-calendar-mark-entries)
|
||||
"Hooks to run when showing the `org-roam-dailies-calendar'.")
|
||||
|
||||
(defun org-roam-dailies-calendar--install-hook ()
|
||||
"Install Org-roam-dailies hooks to calendar."
|
||||
(add-hook 'calendar-today-visible-hook #'org-roam-dailies-calendar--run-hook)
|
||||
(add-hook 'calendar-today-invisible-hook #'org-roam-dailies-calendar--run-hook))
|
||||
|
||||
(defun org-roam-dailies-calendar--run-hook ()
|
||||
"Run Org-roam-dailies hooks to calendar."
|
||||
(run-hooks 'org-roam-dailies-calendar-hook)
|
||||
(remove-hook 'calendar-today-visible-hook #'org-roam-dailies-calendar--run-hook)
|
||||
(remove-hook 'calendar-today-invisible-hook #'org-roam-dailies-calendar--run-hook))
|
||||
|
||||
(defun org-roam-dailies-calendar--file-to-date (&optional file)
|
||||
"Convert FILE to date.
|
||||
|
||||
Return (MONTH DAY YEAR)."
|
||||
(let ((file (or file
|
||||
(-> (buffer-base-buffer)
|
||||
(buffer-file-name)))))
|
||||
(cl-destructuring-bind (_ _ _ d m y _ _ _)
|
||||
(-> file
|
||||
(file-name-nondirectory)
|
||||
(file-name-sans-extension)
|
||||
(org-parse-time-string))
|
||||
(list m d y))))
|
||||
|
||||
(defun org-roam-dailies-calendar--date-to-time (date)
|
||||
"Convert DATE as returned from the calendar (MONTH DAY YEAR) to a time."
|
||||
(encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))
|
||||
|
||||
(defun org-roam-dailies-calendar-mark-entries ()
|
||||
"Mark days in the calendar for which a daily-note is present."
|
||||
(when (file-exists-p (org-roam-dailies-directory--get-absolute-path))
|
||||
(dolist (date (mapcar #'org-roam-dailies-calendar--file-to-date
|
||||
(org-roam-dailies--list-files)))
|
||||
(when (calendar-date-is-visible-p date)
|
||||
(calendar-mark-visible-date date 'org-roam-dailies-calendar-note)))))
|
||||
|
||||
;;; Date
|
||||
(defun org-roam-dailies-capture-date (&optional goto prefer-future)
|
||||
"Create an entry in the daily-note for a date using the calendar.
|
||||
|
||||
Prefer past dates, unless PREFER-FUTURE is non-nil.
|
||||
|
||||
With a `C-u' prefix or when GOTO is non-nil, go the note without
|
||||
creating an entry."
|
||||
(interactive "P")
|
||||
(org-roam-dailies-calendar--install-hook)
|
||||
(let* ((time-str (let ((org-read-date-prefer-future prefer-future))
|
||||
(org-read-date nil nil nil (if goto
|
||||
"Find daily-note: "
|
||||
"Capture to daily-note: "))))
|
||||
(time (org-read-date nil t time-str)))
|
||||
(org-roam-dailies--capture time goto)
|
||||
(when goto
|
||||
(run-hooks 'org-roam-dailies-find-file-hook))))
|
||||
|
||||
(defun org-roam-dailies-find-date (&optional prefer-future)
|
||||
"Find the daily-note for a date using the calendar, creating it if necessary.
|
||||
|
||||
Prefer past dates, unless PREFER-FUTURE is non-nil."
|
||||
(interactive)
|
||||
(org-roam-dailies-capture-date t prefer-future))
|
||||
|
||||
;;; Navigation
|
||||
(defun org-roam-dailies--list-files (&rest extra-files)
|
||||
"List all files in `org-roam-dailies-directory'.
|
||||
EXTRA-FILES can be used to append extra files to the list."
|
||||
(let ((dir (org-roam-dailies-directory--get-absolute-path))
|
||||
(regexp (rx-to-string `(and "." (or ,@org-roam-file-extensions)))))
|
||||
(append (--remove (let ((file (file-name-nondirectory it)))
|
||||
(when (or (auto-save-file-name-p file)
|
||||
(backup-file-name-p file)
|
||||
(string-match "^\\." file))
|
||||
it))
|
||||
(directory-files-recursively dir regexp))
|
||||
extra-files)))
|
||||
|
||||
(defun org-roam-dailies-find-next-note (&optional n)
|
||||
"Find next daily-note.
|
||||
|
||||
With numeric argument N, find note N days in the future. If N is
|
||||
negative, find note N days in the past."
|
||||
(interactive "p")
|
||||
(unless (org-roam-dailies--daily-note-p)
|
||||
(user-error "Not in a daily-note"))
|
||||
(setq n (or n 1))
|
||||
(let* ((dailies (org-roam-dailies--list-files))
|
||||
(position
|
||||
(cl-position-if (lambda (candidate)
|
||||
(string= (buffer-file-name (buffer-base-buffer)) candidate))
|
||||
dailies))
|
||||
note)
|
||||
(unless position
|
||||
(user-error "Can't find current note file - have you saved it yet?"))
|
||||
(pcase n
|
||||
((pred (natnump))
|
||||
(when (eq position (- (length dailies) 1))
|
||||
(user-error "Already at newest note")))
|
||||
((pred (integerp))
|
||||
(when (eq position 0)
|
||||
(user-error "Already at oldest note"))))
|
||||
(setq note (nth (+ position n) dailies))
|
||||
(find-file note)
|
||||
(run-hooks 'org-roam-dailies-find-file-hook)))
|
||||
|
||||
(defun org-roam-dailies-find-previous-note (&optional n)
|
||||
"Find previous daily-note.
|
||||
|
||||
With numeric argument N, find note N days in the past. If N is
|
||||
negative, find note N days in the future."
|
||||
(interactive "p")
|
||||
(let ((n (if n (- n) -1)))
|
||||
(org-roam-dailies-find-next-note n)))
|
||||
|
||||
;;;; Bindings
|
||||
(defvar org-roam-dailies-map (make-sparse-keymap)
|
||||
"Keymap for `org-roam-dailies'.")
|
||||
|
||||
(define-prefix-command 'org-roam-dailies-map)
|
||||
|
||||
(define-key org-roam-dailies-map (kbd "d") #'org-roam-dailies-find-today)
|
||||
(define-key org-roam-dailies-map (kbd "y") #'org-roam-dailies-find-yesterday)
|
||||
(define-key org-roam-dailies-map (kbd "t") #'org-roam-dailies-find-tomorrow)
|
||||
(define-key org-roam-dailies-map (kbd "n") #'org-roam-dailies-capture-today)
|
||||
(define-key org-roam-dailies-map (kbd "f") #'org-roam-dailies-find-next-note)
|
||||
(define-key org-roam-dailies-map (kbd "b") #'org-roam-dailies-find-previous-note)
|
||||
(define-key org-roam-dailies-map (kbd "c") #'org-roam-dailies-find-date)
|
||||
(define-key org-roam-dailies-map (kbd "v") #'org-roam-dailies-capture-date)
|
||||
(define-key org-roam-dailies-map (kbd ".") #'org-roam-dailies-find-directory)
|
||||
|
||||
(provide 'org-roam-dailies)
|
||||
|
||||
;;; org-roam-dailies.el ends here
|
1028
org-roam-db.el
1028
org-roam-db.el
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
||||
;;; org-roam-dev.el --- Org-roam development code -mode -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides code for org-roam developers.
|
||||
;; It is intended to be loaded before editing org-roam source files.
|
||||
;; It ensures consistent application of various developer settings.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'emacsql)
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode org-roam-dev-mode
|
||||
"Minor mode for setting the dev environment of Org-roam."
|
||||
:lighter " ORD"
|
||||
(when org-roam-dev-mode
|
||||
(emacsql-fix-vector-indentation)
|
||||
(setq-local sentence-end-double-space nil)))
|
||||
|
||||
(provide 'org-roam-dev)
|
||||
;;; org-roam-dev.el ends here
|
@ -1,317 +0,0 @@
|
||||
;;; org-roam-doctor.el --- Linter for Org-roam files -*- 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.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides `org-roam-doctor', a utility for diagnosing and fixing
|
||||
;; Org-roam files. Running `org-roam-doctor' launches a list of checks defined
|
||||
;; by `org-roam-doctor--checkers'. Every checker is an instance of
|
||||
;; `org-roam-doctor-checker'.
|
||||
;;
|
||||
;; Each checker is given the Org parse tree (AST), and is expected to return a
|
||||
;; list of errors. The checker can also provide "actions" for auto-fixing errors
|
||||
;; (see `org-roam-doctor--remove-link' for an example).
|
||||
;;
|
||||
;; The UX experience is inspired by both org-lint and checkdoc, and their code
|
||||
;; is heavily referenced.
|
||||
;;
|
||||
;;; Code:
|
||||
;; Library Requires
|
||||
(require 'cl-lib)
|
||||
(require 'org)
|
||||
(require 'org-element)
|
||||
(require 's)
|
||||
(require 'dash)
|
||||
(require 'org-roam-macs)
|
||||
|
||||
(declare-function org-roam-insert "org-roam")
|
||||
(declare-function org-roam--get-roam-buffers "org-roam")
|
||||
(declare-function org-roam--list-all-files "org-roam")
|
||||
(declare-function org-roam--org-roam-file-p "org-roam")
|
||||
(declare-function org-roam-mode "org-roam")
|
||||
|
||||
(defvar org-roam-verbose)
|
||||
(defvar org-roam-mode)
|
||||
|
||||
(defcustom org-roam-doctor-inhibit-startup t
|
||||
"Inhibit `org-mode' startup when processing files with `org-doctor'.
|
||||
When non-nil, images and LaTeX preview will not be generated,
|
||||
tables will not be aligned, and headlines will not respect
|
||||
startup visability. This significantly improves performance when
|
||||
processing multiple files"
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(cl-defstruct (org-roam-doctor-checker (:copier nil))
|
||||
(name 'missing-checker-name)
|
||||
(description "")
|
||||
(actions nil))
|
||||
|
||||
(defconst org-roam-doctor--checkers
|
||||
(list
|
||||
(make-org-roam-doctor-checker
|
||||
:name 'org-roam-doctor-broken-links
|
||||
:description "Fix broken links."
|
||||
:actions '(("d" . ("Unlink" . org-roam-doctor--remove-link))
|
||||
("r" . ("Replace link" . org-roam-doctor--replace-link))
|
||||
("R" . ("Replace link (keep label)" . org-roam-doctor--replace-link-keep-label))))
|
||||
(make-org-roam-doctor-checker
|
||||
:name 'org-roam-doctor-check-roam-props
|
||||
:description "Check #+roam_* properties.")
|
||||
(make-org-roam-doctor-checker
|
||||
:name 'org-roam-doctor-check-tags
|
||||
:description "Check #+roam_tags.")
|
||||
(make-org-roam-doctor-checker
|
||||
:name 'org-roam-doctor-check-alias
|
||||
:description "Check #+roam_alias.")))
|
||||
|
||||
(defconst org-roam-doctor--supported-roam-properties
|
||||
'("roam_tags" "roam_alias" "roam_key")
|
||||
"List of supported Org-roam properties.")
|
||||
|
||||
(defun org-roam-doctor-check-roam-props (ast)
|
||||
"Checker for detecting invalid #+roam_* properties.
|
||||
AST is the org-element parse tree."
|
||||
(let (reports)
|
||||
(org-element-map ast 'keyword
|
||||
(lambda (kw)
|
||||
(let ((key (org-element-property :key kw)))
|
||||
(when (and (string-prefix-p "ROAM_" key t)
|
||||
(not (member (downcase key) org-roam-doctor--supported-roam-properties)))
|
||||
(push
|
||||
`(,(org-element-property :begin kw)
|
||||
,(concat "Possible mispelled key: "
|
||||
(prin1-to-string key)
|
||||
"\nOrg-roam supports the following keys: "
|
||||
(s-join ", " org-roam-doctor--supported-roam-properties)))
|
||||
reports)))))
|
||||
reports))
|
||||
|
||||
(defun org-roam-doctor-check-tags (ast)
|
||||
"Checker for detecting invalid #+roam_tags.
|
||||
AST is the org-element parse tree."
|
||||
(let (reports)
|
||||
(org-element-map ast 'keyword
|
||||
(lambda (kw)
|
||||
(when (string-collate-equalp (org-element-property :key kw) "roam_tags" nil t)
|
||||
(let ((tags (org-element-property :value kw)))
|
||||
(condition-case nil
|
||||
(split-string-and-unquote tags)
|
||||
(error
|
||||
(push
|
||||
`(,(org-element-property :begin kw)
|
||||
,(concat "Unable to parse tags: "
|
||||
tags
|
||||
(when (s-contains? "," tags)
|
||||
"\nCheck that your tags are not comma-separated.")))
|
||||
reports)))))))
|
||||
reports))
|
||||
|
||||
(defun org-roam-doctor-check-alias (ast)
|
||||
"Checker for detecting invalid #+roam_alias.
|
||||
AST is the org-element parse tree."
|
||||
(let (reports)
|
||||
(org-element-map ast 'keyword
|
||||
(lambda (kw)
|
||||
(when (string-collate-equalp (org-element-property :key kw) "roam_alias" nil t)
|
||||
(let ((aliases (org-element-property :value kw)))
|
||||
(condition-case nil
|
||||
(split-string-and-unquote aliases)
|
||||
(error
|
||||
(push
|
||||
`(,(org-element-property :begin kw)
|
||||
,(concat "Unable to parse aliases: "
|
||||
aliases
|
||||
(when (s-contains? "," aliases)
|
||||
"\nCheck that your aliases are not comma-separated.")))
|
||||
reports)))))))
|
||||
reports))
|
||||
|
||||
(defun org-roam-doctor-broken-links (ast)
|
||||
"Checker for detecting broken links.
|
||||
AST is the org-element parse tree."
|
||||
(let (reports)
|
||||
(org-element-map ast 'link
|
||||
(lambda (l)
|
||||
(when (equal "file" (org-element-property :type l))
|
||||
(let ((file (org-element-property :path l)))
|
||||
(or (file-exists-p file)
|
||||
(file-remote-p file)
|
||||
(push
|
||||
`(,(org-element-property :begin l)
|
||||
,(format (if (org-element-lineage l '(link))
|
||||
"Link to non-existent image file \"%s\"\
|
||||
in link description"
|
||||
"Link to non-existent local file \"%s\"")
|
||||
file))
|
||||
reports))))))
|
||||
reports))
|
||||
|
||||
(defun org-roam-doctor--check (buffer checkers)
|
||||
"Check BUFFER for errors.
|
||||
CHECKERS is the list of checkers used."
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let* ((ast (org-element-parse-buffer))
|
||||
(errors (sort (cl-mapcan
|
||||
(lambda (c)
|
||||
(mapcar
|
||||
(lambda (report)
|
||||
(list (set-marker (make-marker) (car report))
|
||||
(nth 1 report) c))
|
||||
(save-excursion
|
||||
(funcall
|
||||
(org-roam-doctor-checker-name c)
|
||||
ast))))
|
||||
checkers)
|
||||
#'car-less-than-car)))
|
||||
(dolist (e errors)
|
||||
(pcase-let ((`(,m ,msg ,checker) e))
|
||||
(switch-to-buffer buffer)
|
||||
(goto-char m)
|
||||
(org-reveal)
|
||||
(undo-boundary)
|
||||
(org-roam-doctor--resolve msg checker)
|
||||
(set-marker m nil)))
|
||||
errors))))
|
||||
|
||||
;;; Actions
|
||||
(defun org-roam-doctor--recursive-edit ()
|
||||
"Launch into a recursive edit."
|
||||
(message "When you're done editing press C-M-c to continue.")
|
||||
(recursive-edit))
|
||||
|
||||
(defun org-roam-doctor--skip ()
|
||||
"Skip the current error."
|
||||
(org-roam-message "Skipping..."))
|
||||
|
||||
(defun org-roam-doctor--replace-link ()
|
||||
"Replace the current link with a new link."
|
||||
(save-match-data
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(let ((orig (buffer-string))
|
||||
(p (point)))
|
||||
(condition-case nil
|
||||
(save-excursion
|
||||
(replace-match "")
|
||||
(org-roam-insert))
|
||||
(quit (progn
|
||||
(replace-buffer-contents orig)
|
||||
(goto-char p)))))))
|
||||
|
||||
(defun org-roam-doctor--replace-link-keep-label ()
|
||||
"Replace the current link with a new link, keeping the current link's label."
|
||||
(save-match-data
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(let ((orig (buffer-string))
|
||||
(p (point)))
|
||||
(condition-case nil
|
||||
(save-excursion
|
||||
(let ((label (if (match-end 2)
|
||||
(match-string-no-properties 2)
|
||||
(org-link-unescape (match-string-no-properties 1)))))
|
||||
(replace-match "")
|
||||
(org-roam-insert nil nil label)))
|
||||
(quit (progn
|
||||
(replace-buffer-contents orig)
|
||||
(goto-char p)))))))
|
||||
|
||||
(defun org-roam-doctor--remove-link ()
|
||||
"Unlink the text at point."
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(save-excursion
|
||||
(let ((label (if (match-end 2)
|
||||
(match-string-no-properties 2)
|
||||
(org-link-unescape (match-string-no-properties 1)))))
|
||||
(delete-region (match-beginning 0) (match-end 0))
|
||||
(insert label))))
|
||||
|
||||
(defun org-roam-doctor--resolve (msg checker)
|
||||
"Resolve an error.
|
||||
MSG is the error that was found, which is displayed in a help buffer.
|
||||
CHECKER is a org-roam-doctor checker instance."
|
||||
(let ((actions (org-roam-doctor-checker-actions checker))
|
||||
c)
|
||||
(push '("e" . ("Edit" . org-roam-doctor--recursive-edit)) actions)
|
||||
(push '("s" . ("Skip" . org-roam-doctor--skip)) actions)
|
||||
(with-output-to-temp-buffer "*Org-roam-doctor Help*"
|
||||
(mapc #'princ
|
||||
(list "Error message:\n " msg "\n\n"))
|
||||
(dolist (action actions)
|
||||
(princ (format "[%s]: %s\n"
|
||||
(car action)
|
||||
(cadr action))))
|
||||
(princ "\n\n"))
|
||||
(shrink-window-if-larger-than-buffer
|
||||
(get-buffer-window "*Org-roam-doctor Help*"))
|
||||
(message "Press key for command:")
|
||||
(unwind-protect
|
||||
(progn
|
||||
(cl-loop
|
||||
do (setq c (char-to-string (read-char-exclusive)))
|
||||
until (assoc c actions)
|
||||
do (message "Please enter a valid key for command:"))
|
||||
(funcall (cddr (assoc c actions)))
|
||||
(redisplay))
|
||||
(when (get-buffer-window "*Org-roam-doctor Help*")
|
||||
(delete-window (get-buffer-window "*Org-roam-doctor Help*"))
|
||||
(kill-buffer "*Org-roam-doctor Help*")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-doctor (&optional checkall)
|
||||
"Perform a check on the current buffer to ensure cleanliness.
|
||||
If CHECKALL, run the check for all Org-roam files."
|
||||
(interactive "P")
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(let ((files (if checkall
|
||||
(org-roam--list-all-files)
|
||||
(unless (org-roam--org-roam-file-p)
|
||||
(user-error "Not in an org-roam file"))
|
||||
`(,(buffer-file-name)))))
|
||||
(org-roam-doctor-start files org-roam-doctor--checkers)))
|
||||
|
||||
(defun org-roam-doctor-start (files checkers)
|
||||
"Lint FILES using CHECKERS."
|
||||
(save-window-excursion
|
||||
(let ((existing-buffers (org-roam--get-roam-buffers))
|
||||
(org-inhibit-startup org-roam-doctor-inhibit-startup))
|
||||
(dolist (f files)
|
||||
(let ((buf (find-file-noselect f)))
|
||||
(org-roam-doctor--check buf checkers)
|
||||
(unless (memq buf existing-buffers)
|
||||
(save-buffer buf)
|
||||
(kill-buffer buf))))))
|
||||
(org-roam-message "Linting completed."))
|
||||
|
||||
(provide 'org-roam-doctor)
|
||||
|
||||
;;; org-roam-doctor.el ends here
|
@ -1,77 +0,0 @@
|
||||
;;; org-roam-faces.el --- Face definitions -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; 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 file contains the face definitions for Org-roam.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defgroup org-roam-faces nil
|
||||
"Faces used by Org-roam."
|
||||
:group 'org-roam
|
||||
:group 'faces)
|
||||
|
||||
;;; Definitions
|
||||
(defface org-roam-link
|
||||
'((t :inherit org-link))
|
||||
"Face for Org-roam links."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-tag
|
||||
'((t :weight bold))
|
||||
"Face for Org-roam tags in minibuffer commands."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-link-current
|
||||
'((t :inherit org-link))
|
||||
"Face for Org-roam links pointing to the current buffer."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-link-invalid
|
||||
'((t :inherit (error org-link)))
|
||||
"Face for Org-roam links that are not valid.
|
||||
This face is used for links without a destination."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-link-shielded
|
||||
'((t :inherit (warning org-link)))
|
||||
"Face for Org-roam links that are shielded.
|
||||
This face is used on the region target by `org-roam-insertion'
|
||||
during an `org-roam-capture'."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-dailies-calendar-note
|
||||
'((t :inherit (org-roam-link) :underline nil))
|
||||
"Face for dates with a daily-note in the calendar"
|
||||
:group 'org-roam-faces)
|
||||
|
||||
;;; _
|
||||
|
||||
(provide 'org-roam-faces)
|
||||
|
||||
;;; org-roam-faces.el ends here
|
@ -1,309 +0,0 @@
|
||||
;;; org-roam-graph.el --- Graphing API -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 provides graphing functionality for org-roam.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'xml) ;xml-escape-string
|
||||
(require 's) ;s-truncate, s-replace
|
||||
(eval-and-compile
|
||||
(require 'org-roam-macs))
|
||||
(require 'org-roam-db)
|
||||
|
||||
;;;; Declarations
|
||||
(defvar org-roam-directory)
|
||||
(defvar org-roam-mode)
|
||||
(declare-function org-roam--org-roam-file-p "org-roam")
|
||||
(declare-function org-roam--path-to-slug "org-roam")
|
||||
(declare-function org-roam-mode "org-roam")
|
||||
|
||||
;;;; Options
|
||||
(defcustom org-roam-graph-viewer (executable-find "firefox")
|
||||
"Method to view the org-roam graph.
|
||||
It may be one of the following:
|
||||
- a string representing the path to the executable for viewing the graph.
|
||||
- a function accepting a single argument: the graph file path.
|
||||
- nil uses `view-file' to view the graph."
|
||||
:type '(choice
|
||||
(string :tag "Path to executable")
|
||||
(function :tag "Function to display graph" eww-open-file)
|
||||
(const :tag "view-file"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-executable "dot"
|
||||
"Path to graphing executable, or its name."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-extra-config nil
|
||||
"Extra options passed to graphviz.
|
||||
Example:
|
||||
'((\"rankdir\" . \"LR\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-node-extra-config
|
||||
'(("shape" . "underline")
|
||||
("style" . "rounded,filled")
|
||||
("fillcolor" . "#EEEEEE")
|
||||
("color" . "#C9C9C9")
|
||||
("fontcolor" . "#111111"))
|
||||
"Extra options for graphviz nodes.
|
||||
Example:
|
||||
'((\"color\" . \"skyblue\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-edge-extra-config
|
||||
'(("color" . "#333333"))
|
||||
"Extra options for graphviz edges.
|
||||
Example:
|
||||
'((\"dir\" . \"back\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-edge-cites-extra-config '(("color" . "red"))
|
||||
"Extra options for graphviz edges for citation links.
|
||||
Example:
|
||||
'((\"dir\" . \"back\"))"
|
||||
:type '(alist)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-max-title-length 100
|
||||
"Maximum length of titles in graph nodes."
|
||||
:type 'number
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-shorten-titles 'truncate
|
||||
"Determines how long titles appear in graph nodes.
|
||||
Recognized values are the symbols `truncate' and `wrap', in which
|
||||
cases the title will be truncated or wrapped, respectively, if it
|
||||
is longer than `org-roam-graph-max-title-length'.
|
||||
|
||||
All other values including nil will have no effect."
|
||||
:type '(choice
|
||||
(const :tag "truncate" truncate)
|
||||
(const :tag "wrap" wrap)
|
||||
(const :tag "no" nil))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-graph-exclude-matcher nil
|
||||
"Matcher for excluding nodes from the generated graph.
|
||||
Any nodes and links for file paths matching this string is
|
||||
excluded from the graph.
|
||||
|
||||
If value is a string, the string is the only matcher.
|
||||
|
||||
If value is a list, all file paths matching any of the strings
|
||||
are excluded."
|
||||
:type '(choice
|
||||
(string :tag "Matcher")
|
||||
(list :tag "Matchers"))
|
||||
:group 'org-roam)
|
||||
|
||||
;;;; Functions
|
||||
(defun org-roam-graph--expand-matcher (col &optional negate where)
|
||||
"Return the exclusion regexp from `org-roam-graph-exclude-matcher'.
|
||||
COL is the symbol to be matched against. if NEGATE, add :not to sql query.
|
||||
set WHERE to true if WHERE query already exists."
|
||||
(let ((matchers (pcase org-roam-graph-exclude-matcher
|
||||
('nil nil)
|
||||
((pred stringp) `(,(concat "%" org-roam-graph-exclude-matcher "%")))
|
||||
((pred listp) (mapcar (lambda (m)
|
||||
(concat "%" m "%"))
|
||||
org-roam-graph-exclude-matcher))
|
||||
(_ (error "Invalid org-roam-graph-exclude-matcher"))))
|
||||
res)
|
||||
(dolist (match matchers)
|
||||
(if where
|
||||
(push :and res)
|
||||
(push :where res)
|
||||
(setq where t))
|
||||
(push col res)
|
||||
(when negate
|
||||
(push :not res))
|
||||
(push :like res)
|
||||
(push match res))
|
||||
(nreverse res)))
|
||||
|
||||
(defun org-roam-graph--dot-option (option &optional wrap-key wrap-val)
|
||||
"Return dot string of form KEY=VAL for OPTION cons.
|
||||
If WRAP-KEY is non-nil it wraps the KEY.
|
||||
If WRAP-VAL is non-nil it wraps the VAL."
|
||||
(concat wrap-key (car option) wrap-key
|
||||
"="
|
||||
wrap-val (cdr option) wrap-val))
|
||||
|
||||
(defun org-roam-graph--dot (node-query)
|
||||
"Build the graphviz dot string for NODE-QUERY.
|
||||
The Org-roam database titles table is read, to obtain the list of titles.
|
||||
The links table is then read to obtain all directed links, and formatted
|
||||
into a digraph."
|
||||
(org-roam-db--ensure-built)
|
||||
(org-roam--with-temp-buffer nil
|
||||
(let* ((nodes (org-roam-db-query node-query))
|
||||
(edges-query
|
||||
`[:with selected :as [:select [file] :from ,node-query]
|
||||
:select :distinct [dest source] :from links
|
||||
:where (and (in dest selected) (in source selected))])
|
||||
(edges-cites-query
|
||||
`[:with selected :as [:select [file] :from ,node-query]
|
||||
:select :distinct [file source]
|
||||
:from links :inner :join refs :on (and (= links:dest refs:ref)
|
||||
(= links:type "cite")
|
||||
(= refs:type "cite"))
|
||||
:where (and (in file selected) (in source selected))])
|
||||
(edges (org-roam-db-query edges-query))
|
||||
(edges-cites (org-roam-db-query edges-cites-query)))
|
||||
(insert "digraph \"org-roam\" {\n")
|
||||
(dolist (option org-roam-graph-extra-config)
|
||||
(insert (org-roam-graph--dot-option option) ";\n"))
|
||||
(dolist (attribute '("node" "edge"))
|
||||
(insert (format " %s [%s];\n" attribute
|
||||
(mapconcat (lambda (var)
|
||||
(org-roam-graph--dot-option var nil "\""))
|
||||
(symbol-value
|
||||
(intern (concat "org-roam-graph-" attribute "-extra-config")))
|
||||
","))))
|
||||
(dolist (node nodes)
|
||||
(let* ((file (xml-escape-string (car node)))
|
||||
(title (or (cadr node)
|
||||
(org-roam--path-to-slug file)))
|
||||
(shortened-title (pcase org-roam-graph-shorten-titles
|
||||
(`truncate (s-truncate org-roam-graph-max-title-length title))
|
||||
(`wrap (s-word-wrap org-roam-graph-max-title-length title))
|
||||
(_ title)))
|
||||
(shortened-title (org-roam-string-quote shortened-title))
|
||||
(title (org-roam-string-quote title))
|
||||
(node-properties
|
||||
`(("label" . ,shortened-title)
|
||||
("URL" . ,(concat "org-protocol://roam-file?file=" (url-hexify-string file)))
|
||||
("tooltip" . ,(xml-escape-string title)))))
|
||||
(insert
|
||||
(format " \"%s\" [%s];\n" file
|
||||
(mapconcat (lambda (n)
|
||||
(org-roam-graph--dot-option n nil "\""))
|
||||
node-properties ",")))))
|
||||
(dolist (edge edges)
|
||||
(insert (apply #'format `(" \"%s\" -> \"%s\";\n"
|
||||
,@(mapcar #'xml-escape-string edge)))))
|
||||
(insert (format " edge [%s];\n"
|
||||
(mapconcat #'org-roam-graph--dot-option
|
||||
org-roam-graph-edge-cites-extra-config ",")))
|
||||
(dolist (edge edges-cites)
|
||||
(insert (apply #'format `(" \"%s\" -> \"%s\";\n"
|
||||
,@(mapcar #'xml-escape-string edge)))))
|
||||
(insert "}")
|
||||
(buffer-string))))
|
||||
|
||||
(defun org-roam-graph--build (&optional node-query callback)
|
||||
"Generate a graph showing the relations between nodes in NODE-QUERY.
|
||||
Execute CALLBACK when process exits successfully.
|
||||
CALLBACK is passed the graph file as its sole argument."
|
||||
(unless (stringp org-roam-graph-executable)
|
||||
(user-error "`org-roam-graph-executable' is not a string"))
|
||||
(unless (executable-find org-roam-graph-executable)
|
||||
(user-error (concat "Cannot find executable \"%s\" to generate the graph. "
|
||||
"Please adjust `org-roam-graph-executable'")
|
||||
org-roam-graph-executable))
|
||||
(let* ((node-query (or node-query
|
||||
`[:select [file title] :from titles
|
||||
,@(org-roam-graph--expand-matcher 'file t)
|
||||
:group :by file]))
|
||||
(graph (org-roam-graph--dot node-query))
|
||||
(temp-dot (make-temp-file "graph." nil ".dot" graph))
|
||||
(temp-graph (make-temp-file "graph." nil ".svg")))
|
||||
(org-roam-message "building graph")
|
||||
(make-process
|
||||
:name "*org-roam-graph--build-process*"
|
||||
:buffer "*org-roam-graph--build-process*"
|
||||
:command `(,org-roam-graph-executable ,temp-dot "-Tsvg" "-o" ,temp-graph)
|
||||
:sentinel (when callback
|
||||
(lambda (process _event)
|
||||
(when (= 0 (process-exit-status process))
|
||||
(funcall callback temp-graph)))))))
|
||||
|
||||
(defun org-roam-graph--open (file)
|
||||
"Open FILE using `org-roam-graph-viewer' with `view-file' as a fallback."
|
||||
(pcase org-roam-graph-viewer
|
||||
((pred stringp)
|
||||
(if (executable-find org-roam-graph-viewer)
|
||||
(condition-case err
|
||||
(call-process org-roam-graph-viewer nil 0 nil file)
|
||||
(error (user-error "Failed to open org-roam graph: %s" err)))
|
||||
(user-error "Executable not found: \"%s\"" org-roam-graph-viewer)))
|
||||
((pred functionp) (funcall org-roam-graph-viewer file))
|
||||
('nil (view-file file))
|
||||
(_ (signal 'wrong-type-argument `((functionp stringp null) ,org-roam-graph-viewer)))))
|
||||
|
||||
(defun org-roam-graph--build-connected-component (file &optional max-distance callback)
|
||||
"Build a graph of nodes connected to FILE.
|
||||
If MAX-DISTANCE is non-nil, limit nodes to MAX-DISTANCE steps.
|
||||
CALLBACK is passed to `org-roam-graph--build'."
|
||||
(let* ((file (expand-file-name file))
|
||||
(files (or (if (and max-distance (>= max-distance 0))
|
||||
(org-roam-db--links-with-max-distance file max-distance)
|
||||
(org-roam-db--connected-component file))
|
||||
(list file)))
|
||||
(query `[:select [file title]
|
||||
:from titles
|
||||
:where (in file [,@files])]))
|
||||
(org-roam-graph--build query callback)))
|
||||
|
||||
;;;; Commands
|
||||
;;;###autoload
|
||||
(defun org-roam-graph (&optional arg file node-query)
|
||||
"Build and possibly display a graph for FILE from NODE-QUERY.
|
||||
If FILE is nil, default to current buffer's file name.
|
||||
ARG may be any of the following values:
|
||||
- nil show the graph.
|
||||
- `\\[universal-argument]' show the graph for FILE.
|
||||
- `\\[universal-argument]' N show the graph for FILE limiting nodes to N steps.
|
||||
- `\\[universal-argument] \\[universal-argument]' build the graph.
|
||||
- `\\[universal-argument]' - build the graph for FILE.
|
||||
- `\\[universal-argument]' -N build the graph for FILE limiting nodes to N steps."
|
||||
(interactive "P")
|
||||
(unless org-roam-mode (org-roam-mode))
|
||||
(let ((file (or file (buffer-file-name (buffer-base-buffer)))))
|
||||
(unless (or (not arg) (equal arg '(16)))
|
||||
(unless file
|
||||
(user-error "Cannot build graph for nil file. Is current buffer visiting a file?"))
|
||||
(unless (org-roam--org-roam-file-p file)
|
||||
(user-error "\"%s\" is not an org-roam file" file)))
|
||||
(pcase arg
|
||||
('nil (org-roam-graph--build node-query #'org-roam-graph--open))
|
||||
('(4) (org-roam-graph--build-connected-component file nil #'org-roam-graph--open))
|
||||
((pred integerp) (org-roam-graph--build-connected-component file (abs arg) (when (>= arg 0) #'org-roam-graph--open)))
|
||||
('(16) (org-roam-graph--build node-query))
|
||||
('- (org-roam-graph--build-connected-component file))
|
||||
(_ (user-error "Unrecognized ARG: %s" arg)))))
|
||||
|
||||
(provide 'org-roam-graph)
|
||||
|
||||
;;; org-roam-graph.el ends here
|
94
org-roam-id.el
Normal file
94
org-roam-id.el
Normal file
@ -0,0 +1,94 @@
|
||||
;;; org-roam-id.el --- ID-related utilities for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (magit-section "3.0.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:
|
||||
;;
|
||||
;; This module provides ID-related facilities using the Org-roam database.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-id)
|
||||
|
||||
(defun org-roam-id-at-point ()
|
||||
"Return the ID at point, if any.
|
||||
Recursively traverses up the headline tree to find the
|
||||
first encapsulating ID."
|
||||
(org-with-wide-buffer
|
||||
(org-back-to-heading-or-point-min t)
|
||||
(while (and (not (org-roam-db-node-p))
|
||||
(not (bobp)))
|
||||
(org-roam-up-heading-or-point-min))
|
||||
(when (org-roam-db-node-p)
|
||||
(org-id-get))))
|
||||
|
||||
(defun org-roam-id-find (id &optional markerp)
|
||||
"Return the location of the entry with the id ID using the Org-roam db.
|
||||
The return value is a cons cell (file-name . position), or nil
|
||||
if there is no entry with that ID.
|
||||
With optional argument MARKERP, return the position as a new marker."
|
||||
(cond
|
||||
((symbolp id) (setq id (symbol-name id)))
|
||||
((numberp id) (setq id (number-to-string id))))
|
||||
(let ((node (org-roam-populate (org-roam-node-create :id id))))
|
||||
(when-let ((file (org-roam-node-file node)))
|
||||
(if markerp
|
||||
(let ((buffer (or (find-buffer-visiting file)
|
||||
(find-file-noselect file))))
|
||||
(with-current-buffer buffer
|
||||
(move-marker (make-marker) (org-roam-node-point node) buffer)))
|
||||
(cons (org-roam-node-file node)
|
||||
(org-roam-node-point node))))))
|
||||
|
||||
(defalias 'org-roam-id-open 'org-id-open
|
||||
"Obsolete alias - use `org-id-open' directly.")
|
||||
|
||||
(advice-add 'org-id-find :before-until #'org-roam-id-find)
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-update-org-id-locations (&rest directories)
|
||||
"Scan Org-roam files to update `org-id' related state.
|
||||
This is like `org-id-update-id-locations', but will automatically
|
||||
use the currently bound `org-directory' and `org-roam-directory'
|
||||
along with DIRECTORIES (if any), where the lookup for files in
|
||||
these directories will be always recursive.
|
||||
|
||||
Note: Org-roam doesn't have hard dependency on
|
||||
`org-id-locations-file' to lookup IDs for nodes that are stored
|
||||
in the database, but it still tries to properly integrates with
|
||||
`org-id'. This allows the user to cross-reference IDs outside of
|
||||
the current `org-roam-directory', and also link with \"id:\"
|
||||
links to headings/files within the current `org-roam-directory'
|
||||
that are excluded from identification in Org-roam as
|
||||
`org-roam-node's, e.g. with \"ROAM_EXCLUDE\" property."
|
||||
(interactive)
|
||||
(cl-loop for dir in (cons org-roam-directory directories)
|
||||
for org-roam-directory = dir
|
||||
nconc (org-roam-list-files) into files
|
||||
finally (org-id-update-id-locations files org-roam-verbose)))
|
||||
|
||||
(provide 'org-roam-id)
|
||||
|
||||
;;; org-roam-id.el ends here
|
324
org-roam-link.el
324
org-roam-link.el
@ -1,324 +0,0 @@
|
||||
;;; org-roam-link.el --- Custom links for Org-roam -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Alan Carroll
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 adds the custom `roam:' link to Org-roam. `roam:' links allow linking to
|
||||
;; Org-roam files via their titles and headlines.
|
||||
;;
|
||||
;;; Code:
|
||||
;;;; Dependencies
|
||||
|
||||
(require 'ol)
|
||||
(require 'org-roam-compat)
|
||||
(require 'org-roam-macs)
|
||||
(require 'org-roam-db)
|
||||
|
||||
(require 'org-element)
|
||||
|
||||
(defvar org-roam-completion-ignore-case)
|
||||
(defvar org-roam-directory)
|
||||
(declare-function org-roam--find-file "org-roam")
|
||||
(declare-function org-roam-find-file "org-roam")
|
||||
(declare-function org-roam-format-link "org-roam")
|
||||
|
||||
(defcustom org-roam-link-auto-replace t
|
||||
"When non-nil, replace Org-roam's roam links with file or id links whenever possible."
|
||||
:group 'org-roam
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom org-roam-link-file-path-type 'relative
|
||||
"How the path name in file links should be stored.
|
||||
Valid values are:
|
||||
|
||||
relative Relative to the current directory, i.e. the directory of the file
|
||||
into which the link is being inserted.
|
||||
absolute Absolute path, if possible with ~ for home directory.
|
||||
noabbrev Absolute path, no abbreviation of home directory."
|
||||
:group 'org-roam
|
||||
:type '(choice
|
||||
(const relative)
|
||||
(const absolute)
|
||||
(const noabbrev))
|
||||
:safe #'symbolp)
|
||||
|
||||
;;; the roam: link
|
||||
(org-link-set-parameters "roam"
|
||||
:follow #'org-roam-link-follow-link)
|
||||
|
||||
(defun org-roam-link-follow-link (_path)
|
||||
"Navigates to location in Org-roam link.
|
||||
This function is called by Org when following links of the type
|
||||
`roam'. While the path is passed, assume that the cursor is on
|
||||
the link."
|
||||
(pcase-let ((`(,link-type ,loc ,desc ,mkr) (org-roam-link--get-location)))
|
||||
(when (and org-roam-link-auto-replace loc desc)
|
||||
(org-roam-link--replace-link link-type loc desc))
|
||||
(pcase link-type
|
||||
("file"
|
||||
(if loc
|
||||
(org-roam--find-file loc)
|
||||
(org-roam-find-file desc nil nil t)))
|
||||
("id"
|
||||
(org-goto-marker-or-bmk mkr)))))
|
||||
|
||||
;;; Retrieval Functions
|
||||
(defun org-roam-link--get-titles ()
|
||||
"Return all titles within Org-roam."
|
||||
(mapcar #'car (org-roam-db-query [:select [titles:title] :from titles])))
|
||||
|
||||
(defun org-roam-link--get-headlines (&optional file with-marker use-stack)
|
||||
"Return all outline headings for the current buffer.
|
||||
If FILE, return outline headings for passed FILE instead.
|
||||
If WITH-MARKER, return a cons cell of (headline . marker).
|
||||
If USE-STACK, include the parent paths as well."
|
||||
(org-roam-with-file file (when with-marker 'keep)
|
||||
(let* ((outline-level-fn outline-level)
|
||||
(path-separator "/")
|
||||
(stack-level 0)
|
||||
stack cands name level marker)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward org-complex-heading-regexp nil t)
|
||||
(save-excursion
|
||||
(setq name (substring-no-properties (or (match-string 4) "")))
|
||||
(setq marker (point-marker))
|
||||
(when use-stack
|
||||
(goto-char (match-beginning 0))
|
||||
(setq level (funcall outline-level-fn))
|
||||
;; Update stack. The empty entry guards against incorrect
|
||||
;; headline hierarchies, e.g. a level 3 headline
|
||||
;; immediately following a level 1 entry.
|
||||
(while (<= level stack-level)
|
||||
(pop stack)
|
||||
(cl-decf stack-level))
|
||||
(while (> level stack-level)
|
||||
(push name stack)
|
||||
(cl-incf stack-level))
|
||||
(setq name (mapconcat #'identity
|
||||
(reverse stack)
|
||||
path-separator)))
|
||||
(push (if with-marker
|
||||
(cons name marker)
|
||||
name) cands))))
|
||||
(nreverse cands))))
|
||||
|
||||
|
||||
(defun org-roam-link--get-file-from-title (title &optional no-interactive)
|
||||
"Return the file path corresponding to TITLE.
|
||||
When NO-INTERACTIVE, return nil if there are multiple options."
|
||||
(let ((files (mapcar #'car (org-roam-db-query [:select [titles:file] :from titles
|
||||
:where (= titles:title $v1)]
|
||||
(vector title)))))
|
||||
(pcase files
|
||||
('nil nil)
|
||||
(`(,file) file)
|
||||
(_
|
||||
(unless no-interactive
|
||||
(completing-read "Select file: " files))))))
|
||||
|
||||
(defun org-roam-link--get-id-from-headline (headline &optional file)
|
||||
"Return (marker . id) correspondng to HEADLINE in FILE.
|
||||
If FILE is nil, get ID from current buffer.
|
||||
If there is no corresponding headline, return nil."
|
||||
(save-excursion
|
||||
(org-roam-with-file file 'keep
|
||||
(let ((headlines (org-roam-link--get-headlines file 'with-markers)))
|
||||
(when-let ((marker (cdr (assoc-string headline headlines))))
|
||||
(goto-char marker)
|
||||
(cons marker
|
||||
(when org-roam-link-auto-replace
|
||||
(org-id-get-create))))))))
|
||||
|
||||
;;; Path-related functions
|
||||
(defun org-roam-link-get-path (path &optional type)
|
||||
"Return the PATH of the link to use.
|
||||
If TYPE is non-nil, create a link of TYPE. Otherwise, respect
|
||||
`org-link-file-path-type'."
|
||||
(pcase (or type org-roam-link-file-path-type)
|
||||
('absolute
|
||||
(abbreviate-file-name (expand-file-name path)))
|
||||
('noabbrev
|
||||
(expand-file-name path))
|
||||
('relative
|
||||
(file-relative-name path))))
|
||||
|
||||
(defun org-roam-link--split-path (path)
|
||||
"Splits PATH into title and headline.
|
||||
Return a list of the form (type title has-headline-p headline star-idx).
|
||||
type is one of `title', `headline', `title+headline'.
|
||||
title is the title component of the path.
|
||||
headline is the headline component of the path.
|
||||
star-idx is the index of the asterisk, if any."
|
||||
(save-match-data
|
||||
(let* ((star-index (string-match-p "\\*" path))
|
||||
(title (substring-no-properties path 0 star-index))
|
||||
(headline (if star-index
|
||||
(substring-no-properties path (+ 1 star-index))
|
||||
""))
|
||||
(type (cond ((not star-index)
|
||||
'title)
|
||||
((= 0 star-index)
|
||||
'headline)
|
||||
(t 'title+headline))))
|
||||
(list type title headline star-index))))
|
||||
|
||||
(defun org-roam-link--get-location ()
|
||||
"Return the location of the Org-roam fuzzy link at point.
|
||||
The location is returned as a list containing (link-type loc desc marker).
|
||||
nil is returned if there is no matching location.
|
||||
|
||||
link-type is either \"file\" or \"id\".
|
||||
loc is the target location: e.g. a file path, or an id.
|
||||
marker is a marker to the headline, if applicable.
|
||||
|
||||
desc is either the the description of the link under point, or
|
||||
the target of LINK (title or heading content)."
|
||||
(let ((context (org-element-context))
|
||||
mkr link-type desc loc)
|
||||
(pcase (org-element-lineage context '(link) t)
|
||||
(`nil (error "Not at an Org link"))
|
||||
(link
|
||||
(if (not (string-equal "roam" (org-element-property :type link)))
|
||||
(error "Not at Org-roam link")
|
||||
(setq desc (and (org-element-property :contents-begin link)
|
||||
(org-element-property :contents-end link)
|
||||
(buffer-substring-no-properties
|
||||
(org-element-property :contents-begin link)
|
||||
(org-element-property :contents-end link))))
|
||||
(pcase-let ((`(,type ,title ,headline _) (org-roam-link--split-path
|
||||
(org-element-property :path link))))
|
||||
(pcase type
|
||||
('title+headline
|
||||
(let ((file (org-roam-link--get-file-from-title title)))
|
||||
(if (not file)
|
||||
(org-roam-message "Cannot find matching file")
|
||||
(setq mkr (org-roam-link--get-id-from-headline headline file))
|
||||
(pcase mkr
|
||||
(`(,marker . ,target-id)
|
||||
(progn
|
||||
(setq mkr marker
|
||||
loc target-id
|
||||
desc (or desc headline)
|
||||
link-type "id")))
|
||||
(_ (org-roam-message "Cannot find matching id"))))))
|
||||
('title
|
||||
(setq loc (org-roam-link--get-file-from-title title)
|
||||
link-type "file"
|
||||
desc (or desc title)))
|
||||
('headline
|
||||
(setq mkr (org-roam-link--get-id-from-headline headline))
|
||||
(pcase mkr
|
||||
(`(,marker . ,target-id)
|
||||
(setq mkr marker
|
||||
loc target-id
|
||||
link-type "id"
|
||||
desc (or desc headline)))
|
||||
(_ (org-roam-message "Cannot find matching headline")))))))))
|
||||
(list link-type loc desc mkr)))
|
||||
|
||||
;;; Conversion Functions
|
||||
(defun org-roam-link--replace-link (link-type loc &optional desc)
|
||||
"Replace link at point with a vanilla Org link.
|
||||
LINK-TYPE is the Org link type, typically \"file\" or \"id\".
|
||||
LOC is path for the Org link.
|
||||
DESC is the link description."
|
||||
(save-excursion
|
||||
(save-match-data
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(replace-match "")
|
||||
(insert (org-roam-format-link loc desc link-type)))))
|
||||
|
||||
(defun org-roam-link-replace-all ()
|
||||
"Replace all roam links in the current buffer."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward org-link-bracket-re nil t)
|
||||
(condition-case nil
|
||||
(pcase-let ((`(,link-type ,loc ,desc _) (org-roam-link--get-location)))
|
||||
(when (and link-type loc)
|
||||
(org-roam-link--replace-link link-type loc desc)))
|
||||
(error nil)))))
|
||||
|
||||
(defun org-roam-link--replace-link-on-save ()
|
||||
"Hook to replace all roam links on save."
|
||||
(when org-roam-link-auto-replace
|
||||
(org-roam-link-replace-all)))
|
||||
|
||||
;;; Completion
|
||||
(defun org-roam-link-complete-at-point ()
|
||||
"Do appropriate completion for the link at point."
|
||||
(let ((end (point))
|
||||
(start (point))
|
||||
collection link-type headline-only-p)
|
||||
(when (org-in-regexp org-link-bracket-re 1)
|
||||
(setq start (match-beginning 1)
|
||||
end (match-end 1))
|
||||
(let ((context (org-element-context)))
|
||||
(pcase (org-element-lineage context '(link) t)
|
||||
(`nil nil)
|
||||
(link
|
||||
(setq link-type (org-element-property :type link))
|
||||
(when (member link-type '("roam" "fuzzy"))
|
||||
(when (string= link-type "roam") (setq start (+ start (length "roam:"))))
|
||||
(pcase-let ((`(,type ,title _ ,star-idx)
|
||||
(org-roam-link--split-path (org-element-property :path link))))
|
||||
(pcase type
|
||||
('title+headline
|
||||
(when-let ((file (org-roam-link--get-file-from-title title t)))
|
||||
(setq collection (apply-partially #'org-roam-link--get-headlines file))
|
||||
(setq start (+ start star-idx 1))))
|
||||
('title
|
||||
(setq collection #'org-roam-link--get-titles))
|
||||
('headline
|
||||
(setq collection #'org-roam-link--get-headlines)
|
||||
(setq start (+ start star-idx 1))
|
||||
(setq headline-only-p t)))))))))
|
||||
(when collection
|
||||
(let ((prefix (buffer-substring-no-properties start end)))
|
||||
(list start end
|
||||
(if (functionp collection)
|
||||
(completion-table-case-fold
|
||||
(completion-table-dynamic
|
||||
(lambda (_)
|
||||
(cl-remove-if (apply-partially #'string= prefix)
|
||||
(funcall collection))))
|
||||
(not org-roam-completion-ignore-case))
|
||||
collection)
|
||||
:exit-function
|
||||
(lambda (str &rest _)
|
||||
(delete-char (- 0 (length str)
|
||||
(if headline-only-p 1 0)))
|
||||
(insert (concat (unless (string= link-type "roam") "roam:")
|
||||
(when headline-only-p "*")
|
||||
(org-link-escape str)))))))))
|
||||
|
||||
(provide 'org-roam-link)
|
||||
;;; org-roam-link.el ends here
|
53
org-roam-log.el
Normal file
53
org-roam-log.el
Normal file
@ -0,0 +1,53 @@
|
||||
;;; org-roam-log.el --- Integrations with Org-log -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2022-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.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:
|
||||
;;
|
||||
;; This module provides integrations with Org-log.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-roam)
|
||||
|
||||
(defcustom org-roam-log-setup-hook nil
|
||||
"Hook run when a log for an Org-roam file is setup."
|
||||
:group 'org-roam
|
||||
:type 'hook)
|
||||
|
||||
(defun org-roam-log-p ()
|
||||
"Return t if the log buffer is for an Org-roam file, nil otherwise."
|
||||
(and org-log-note-marker
|
||||
(org-roam-file-p (buffer-file-name (marker-buffer org-log-note-marker)))))
|
||||
|
||||
(defun org-roam-log--setup ()
|
||||
"Run hooks in `org-roam-log-setup-hook'."
|
||||
(run-hooks 'org-roam-log-setup-hook))
|
||||
|
||||
(add-hook 'org-roam-log-setup-hook #'org-roam--register-completion-functions-h)
|
||||
(add-hook 'org-log-buffer-setup-hook #'org-roam-log--setup)
|
||||
|
||||
(provide 'org-roam-log)
|
||||
;;; org-roam-log.el ends here
|
130
org-roam-macs.el
130
org-roam-macs.el
@ -1,130 +0,0 @@
|
||||
;;; org-roam-macs.el --- Macros/utility functions -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite3 "1.0.2"))
|
||||
|
||||
;; 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 implements macros and utility functions used throughout
|
||||
;; org-roam.
|
||||
;;
|
||||
;;
|
||||
;;; Code:
|
||||
;;;; Library Requires
|
||||
(require 'dash)
|
||||
(require 's)
|
||||
|
||||
(defvar org-roam-verbose)
|
||||
|
||||
;; This is necessary to ensure all dependents on this module see
|
||||
;; `org-mode-hook' and `org-inhibit-startup' as dynamic variables,
|
||||
;; regardless of whether Org is loaded before their compilation.
|
||||
(require 'org)
|
||||
|
||||
;;;; Utility Functions
|
||||
(defun org-roam--list-interleave (lst separator)
|
||||
"Interleaves elements in LST with SEPARATOR."
|
||||
(when lst
|
||||
(let ((new-lst (list (pop lst))))
|
||||
(dolist (it lst)
|
||||
(nconc new-lst (list separator it)))
|
||||
new-lst)))
|
||||
|
||||
(defmacro org-roam-with-file (file keep-buf-p &rest body)
|
||||
"Execute BODY within FILE.
|
||||
If FILE is nil, execute BODY in the current buffer.
|
||||
Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited."
|
||||
(declare (indent 2) (debug t))
|
||||
`(let* (new-buf
|
||||
(buf (or (and (not ,file)
|
||||
(current-buffer)) ;If FILE is nil, use current buffer
|
||||
(find-buffer-visiting ,file) ; If FILE is already visited, find buffer
|
||||
(progn
|
||||
(setq new-buf t)
|
||||
(find-file-noselect ,file)))) ; Else, visit FILE and return buffer
|
||||
res)
|
||||
(with-current-buffer buf
|
||||
(setq res (progn ,@body))
|
||||
(unless (and new-buf (not ,keep-buf-p))
|
||||
(save-buffer)))
|
||||
(if (and new-buf (not ,keep-buf-p))
|
||||
(when (find-buffer-visiting ,file)
|
||||
(kill-buffer (find-buffer-visiting ,file))))
|
||||
res))
|
||||
|
||||
(defmacro org-roam--with-temp-buffer (file &rest body)
|
||||
"Execute BODY within a temp buffer.
|
||||
Like `with-temp-buffer', but propagates `org-roam-directory'.
|
||||
If FILE, set `org-roam-temp-file-name' to file and insert its contents."
|
||||
(declare (indent 1) (debug t))
|
||||
(let ((current-org-roam-directory (make-symbol "current-org-roam-directory")))
|
||||
`(let ((,current-org-roam-directory org-roam-directory))
|
||||
(with-temp-buffer
|
||||
(let ((org-roam-directory ,current-org-roam-directory)
|
||||
(org-mode-hook nil)
|
||||
(org-inhibit-startup t))
|
||||
(org-mode)
|
||||
(when ,file
|
||||
(insert-file-contents ,file)
|
||||
(setq-local org-roam-file-name ,file)
|
||||
(setq-local default-directory (file-name-directory ,file)))
|
||||
,@body)))))
|
||||
|
||||
(defun org-roam-message (format-string &rest args)
|
||||
"Pass FORMAT-STRING and ARGS to `message' when `org-roam-verbose' is t."
|
||||
(when org-roam-verbose
|
||||
(apply #'message `(,(concat "(org-roam) " format-string) ,@args))))
|
||||
|
||||
(defun org-roam-string-quote (str)
|
||||
"Quote STR."
|
||||
(->> str
|
||||
(s-replace "\\" "\\\\")
|
||||
(s-replace "\"" "\\\"")))
|
||||
|
||||
;;; Shielding regions
|
||||
(defun org-roam-shield-region (beg end)
|
||||
"Shield REGION against modifications.
|
||||
REGION must be a cons-cell containing the marker to the region
|
||||
beginning and maximum values."
|
||||
(when (and beg end)
|
||||
(add-text-properties beg end
|
||||
'(font-lock-face org-roam-link-shielded
|
||||
read-only t)
|
||||
(marker-buffer beg))
|
||||
(cons beg end)))
|
||||
|
||||
(defun org-roam-unshield-region (beg end)
|
||||
"Unshield the shielded REGION."
|
||||
(when (and beg end)
|
||||
(let ((inhibit-read-only t))
|
||||
(remove-text-properties beg end
|
||||
'(font-lock-face org-roam-link-shielded
|
||||
read-only t)
|
||||
(marker-buffer beg)))
|
||||
(cons beg end)))
|
||||
|
||||
(provide 'org-roam-macs)
|
||||
|
||||
;;; org-roam-macs.el ends here
|
168
org-roam-migrate.el
Normal file
168
org-roam-migrate.el
Normal file
@ -0,0 +1,168 @@
|
||||
;;; org-roam-migrate.el --- Migration utilities from v1 to v2 -*- coding: utf-8; lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.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:
|
||||
;;
|
||||
;; This is a special library provided for the v1 users of this package. It's
|
||||
;; purpose is to ease the transition from v1 to v2, by providing migration
|
||||
;; utilities to convert from v1 notes to v2 nodes.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-roam)
|
||||
|
||||
;;; Migration wizard (v1 -> v2)
|
||||
;;;###autoload
|
||||
(defun org-roam-migrate-wizard ()
|
||||
"Migrate all notes from to be compatible with Org-roam v2.
|
||||
1. Convert all notes from v1 format to v2.
|
||||
2. Rebuild the cache.
|
||||
3. Replace all file links with ID links."
|
||||
(interactive)
|
||||
(when (yes-or-no-p "Org-roam will now convert all your notes from v1 to v2.
|
||||
This will take a while. Are you sure you want to do this?")
|
||||
;; Back up notes
|
||||
(let ((backup-dir (expand-file-name "org-roam.bak"
|
||||
(file-name-directory (directory-file-name org-roam-directory)))))
|
||||
(message "Backing up files to %s" backup-dir)
|
||||
(copy-directory org-roam-directory backup-dir))
|
||||
|
||||
;; Upgrade database to v2
|
||||
(org-roam-db-sync 'force)
|
||||
|
||||
;; Convert v1 to v2
|
||||
(dolist (f (org-roam-list-files))
|
||||
(org-roam-with-file f nil
|
||||
(org-roam-migrate-v1-to-v2)))
|
||||
|
||||
;; Rebuild cache
|
||||
(org-roam-db-sync 'force)
|
||||
|
||||
;;Replace all file links with ID links
|
||||
(dolist (f (org-roam-list-files))
|
||||
(org-roam-with-file f nil
|
||||
(org-roam-migrate-replace-file-links-with-id)
|
||||
(save-buffer)))))
|
||||
|
||||
(defun org-roam-migrate-v1-to-v2 ()
|
||||
"Convert the current buffer to v2 format."
|
||||
;; Create file level ID
|
||||
(org-with-point-at 1
|
||||
(org-id-get-create))
|
||||
;; Replace roam_key into properties drawer roam_ref
|
||||
(when-let* ((refs (mapcan #'split-string-and-unquote
|
||||
(cdar (org-collect-keywords '("roam_key"))))))
|
||||
(let ((case-fold-search t))
|
||||
(org-with-point-at 1
|
||||
(dolist (ref refs)
|
||||
(org-roam-ref-add ref))
|
||||
(while (re-search-forward "^#\\+roam_key:" (point-max) t)
|
||||
(beginning-of-line)
|
||||
(kill-line 1)))))
|
||||
|
||||
;; Replace roam_alias into properties drawer roam_aliases
|
||||
(when-let* ((aliases (mapcan #'split-string-and-unquote
|
||||
(cdar (org-collect-keywords '("roam_alias"))))))
|
||||
(dolist (alias aliases)
|
||||
(org-roam-alias-add alias)))
|
||||
(let ((case-fold-search t))
|
||||
(org-with-point-at 1
|
||||
(while (re-search-forward "^#\\+roam_alias:" (point-max) t)
|
||||
(beginning-of-line)
|
||||
(kill-line 1))))
|
||||
|
||||
;; Replace #+roam_tags into #+filetags
|
||||
(org-with-point-at 1
|
||||
(let* ((roam-tags (org-roam-migrate-get-prop-list "ROAM_TAGS"))
|
||||
(file-tags (cl-mapcan (lambda (value)
|
||||
(cl-mapcan
|
||||
(lambda (k) (org-split-string k ":"))
|
||||
(split-string value)))
|
||||
(org-roam-migrate-get-prop-list "FILETAGS")))
|
||||
(tags (append roam-tags file-tags))
|
||||
(tags (seq-map (lambda (tag)
|
||||
(replace-regexp-in-string
|
||||
"[^[:alnum:]_@#%]"
|
||||
"_"
|
||||
tag)) tags))
|
||||
(tags (seq-uniq tags)))
|
||||
(when tags
|
||||
(org-roam-migrate-prop-set "filetags" (org-make-tag-string tags))))
|
||||
(let ((case-fold-search t))
|
||||
(org-with-point-at 1
|
||||
(while (re-search-forward "^#\\+roam_tags:" (point-max) t)
|
||||
(beginning-of-line)
|
||||
(kill-line 1)))))
|
||||
(save-buffer))
|
||||
|
||||
(defun org-roam-migrate-get-prop-list (keyword)
|
||||
"Return prop list for KEYWORD."
|
||||
(let ((re (format "^#\\+%s:[ \t]*\\([^\n]+\\)" (upcase keyword)))
|
||||
lst)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward re 2048 t)
|
||||
(setq lst (append lst (split-string-and-unquote
|
||||
(buffer-substring-no-properties
|
||||
(match-beginning 1) (match-end 1))))))
|
||||
lst))
|
||||
|
||||
(defun org-roam-migrate-prop-set (name value)
|
||||
"Set a file property called NAME to VALUE in buffer file.
|
||||
If the property is already set, replace its value."
|
||||
(setq name (downcase name))
|
||||
(org-with-point-at 1
|
||||
(let ((case-fold-search t))
|
||||
(if (re-search-forward (concat "^#\\+" name ":\\(.*\\)")
|
||||
(point-max) t)
|
||||
(replace-match (concat "#+" name ": " value) 'fixedcase)
|
||||
(while (and (not (eobp))
|
||||
(looking-at "^[#:]"))
|
||||
(if (save-excursion (end-of-line) (eobp))
|
||||
(progn
|
||||
(end-of-line)
|
||||
(insert "\n"))
|
||||
(forward-line)
|
||||
(beginning-of-line)))
|
||||
(insert "#+" name ": " value "\n")))))
|
||||
|
||||
(defun org-roam-migrate-replace-file-links-with-id ()
|
||||
"Replace all file: links with ID links in current buffer."
|
||||
(org-with-point-at 1
|
||||
(while (re-search-forward org-link-bracket-re nil t)
|
||||
(let* ((mdata (match-data))
|
||||
(path (match-string 1))
|
||||
(desc (match-string 2)))
|
||||
(when (string-prefix-p "file:" path)
|
||||
(setq path (expand-file-name (substring path 5)))
|
||||
(when-let ((node-id (caar (org-roam-db-query [:select [id] :from nodes
|
||||
:where (= file $s1)
|
||||
:and (= level 0)] path))))
|
||||
(set-match-data mdata)
|
||||
(replace-match (org-link-make-string (concat "id:" node-id)
|
||||
desc) nil t)))))))
|
||||
|
||||
(provide 'org-roam-migrate)
|
||||
;;; org-roam-migrate.el ends here
|
711
org-roam-mode.el
Normal file
711
org-roam-mode.el
Normal file
@ -0,0 +1,711 @@
|
||||
;;; org-roam-mode.el --- Major mode for special Org-roam buffers -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.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:
|
||||
;;
|
||||
;; This module implements `org-roam-mode', which is a major mode that used by
|
||||
;; special Org-roam buffers to display various content in a section-like manner
|
||||
;; about the nodes and relevant to them information (e.g. backlinks) with which
|
||||
;; the user can interact with.
|
||||
;;
|
||||
;;; Code:
|
||||
(require 'org-roam)
|
||||
|
||||
;;;; Declarations
|
||||
(defvar org-ref-buffer-hacked)
|
||||
|
||||
;;; Options
|
||||
(defcustom org-roam-mode-sections (list #'org-roam-backlinks-section
|
||||
#'org-roam-reflinks-section)
|
||||
"A list of sections for the `org-roam-mode' based buffers.
|
||||
Each section is a function that is passed the `org-roam-node'
|
||||
for which the section will be constructed as the first
|
||||
argument. Normally this node is `org-roam-buffer-current-node'.
|
||||
The function may also accept other optional arguments. Each item
|
||||
in the list is either:
|
||||
|
||||
1. A function, which is called only with the `org-roam-node' as the argument
|
||||
2. A list, containing the function and the optional arguments.
|
||||
|
||||
For example, one can add
|
||||
|
||||
(org-roam-backlinks-section :unique t)
|
||||
|
||||
to the list to pass :unique t to the section-rendering function."
|
||||
:group 'org-roam
|
||||
:type `(repeat (choice (symbol :tag "Function")
|
||||
(list :tag "Function with arguments"
|
||||
(symbol :tag "Function")
|
||||
(repeat :tag "Arguments" :inline t (sexp :tag "Arg"))))))
|
||||
|
||||
(defcustom org-roam-buffer-postrender-functions (list)
|
||||
"Functions to run after the Org-roam buffer is rendered.
|
||||
Each function accepts no arguments, and is run with the Org-roam
|
||||
buffer as the current buffer."
|
||||
:group 'org-roam
|
||||
:type 'hook)
|
||||
|
||||
(defcustom org-roam-preview-function #'org-roam-preview-default-function
|
||||
"The preview function to use to populate the Org-roam buffer.
|
||||
|
||||
The function takes no arguments, but the point is temporarily set
|
||||
to the exact location of the backlink."
|
||||
:group 'org-roam
|
||||
:type 'function)
|
||||
|
||||
(defcustom org-roam-preview-postprocess-functions (list #'org-roam-strip-comments)
|
||||
"A list of functions to postprocess the preview content.
|
||||
|
||||
Each function takes a single argument, the string for the preview
|
||||
content, and returns the post-processed string. The functions are
|
||||
applied in order of appearance in the list."
|
||||
:group 'org-roam
|
||||
:type 'hook)
|
||||
|
||||
;;; Faces
|
||||
(defface org-roam-header-line
|
||||
`((((class color) (background light))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:foreground "DarkGoldenrod4"
|
||||
:weight bold)
|
||||
(((class color) (background dark))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:foreground "LightGoldenrod2"
|
||||
:weight bold))
|
||||
"Face for the `header-line' in some Org-roam modes."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-title
|
||||
'((t :weight bold))
|
||||
"Face for Org-roam titles."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-olp
|
||||
'((((class color) (background light)) :foreground "grey60")
|
||||
(((class color) (background dark)) :foreground "grey40"))
|
||||
"Face for the OLP of the node."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-preview-heading
|
||||
`((((class color) (background light))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:background "grey80"
|
||||
:foreground "grey30")
|
||||
(((class color) (background dark))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:background "grey25"
|
||||
:foreground "grey70"))
|
||||
"Face for preview headings."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-preview-heading-highlight
|
||||
`((((class color) (background light))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:background "grey75"
|
||||
:foreground "grey30")
|
||||
(((class color) (background dark))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:background "grey35"
|
||||
:foreground "grey70"))
|
||||
"Face for current preview headings."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-preview-heading-selection
|
||||
`((((class color) (background light))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:inherit org-roam-preview-heading-highlight
|
||||
:foreground "salmon4")
|
||||
(((class color) (background dark))
|
||||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||||
:inherit org-roam-preview-heading-highlight
|
||||
:foreground "LightSalmon3"))
|
||||
"Face for selected preview headings."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-preview-region
|
||||
`((t :inherit bold
|
||||
,@(and (>= emacs-major-version 27)
|
||||
(list :extend (ignore-errors (face-attribute 'region :extend))))))
|
||||
"Face used by `org-roam-highlight-preview-region-using-face'.
|
||||
|
||||
This face is overlaid over text that uses other hunk faces,
|
||||
and those normally set the foreground and background colors.
|
||||
The `:foreground' and especially the `:background' properties
|
||||
should be avoided here. Setting the latter would cause the
|
||||
loss of information. Good properties to set here are `:weight'
|
||||
and `:slant'."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
(defface org-roam-dim
|
||||
'((((class color) (background light)) :foreground "grey60")
|
||||
(((class color) (background dark)) :foreground "grey40"))
|
||||
"Face for the dimmer part of the widgets."
|
||||
:group 'org-roam-faces)
|
||||
|
||||
;;; Major mode
|
||||
(defvar org-roam-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map magit-section-mode-map)
|
||||
(define-key map [C-return] 'org-roam-buffer-visit-thing)
|
||||
(define-key map (kbd "C-m") 'org-roam-buffer-visit-thing)
|
||||
(define-key map [remap revert-buffer] 'org-roam-buffer-refresh)
|
||||
map)
|
||||
"Parent keymap for all keymaps of modes derived from `org-roam-mode'.")
|
||||
|
||||
(define-derived-mode org-roam-mode magit-section-mode "Org-roam"
|
||||
"Major mode for displaying relevant information about Org-roam nodes.
|
||||
This mode is used by special Org-roam buffers, such as persistent
|
||||
`org-roam-buffer' and dedicated Org-roam buffers
|
||||
\(`org-roam-buffer-display-dedicated'), which render the
|
||||
information in a section-like manner (see
|
||||
`org-roam-mode-sections'), with which the user can
|
||||
interact with."
|
||||
:group 'org-roam
|
||||
(face-remap-add-relative 'header-line 'org-roam-header-line))
|
||||
|
||||
;;; Buffers
|
||||
(defvar org-roam-buffer-current-node nil
|
||||
"The node for which an `org-roam-mode' based buffer displays its contents.
|
||||
This set both, locally and globally. Normally the local value is
|
||||
only set in the `org-roam-mode' based buffers, while the global
|
||||
value shows the current node in the persistent `org-roam-buffer'.")
|
||||
|
||||
(put 'org-roam-buffer-current-node 'permanent-local t)
|
||||
|
||||
(defvar org-roam-buffer-current-directory nil
|
||||
"The `org-roam-directory' value of `org-roam-buffer-current-node'.
|
||||
Set both, locally and globally in the same way as
|
||||
`org-roam-buffer-current-node'.")
|
||||
|
||||
(put 'org-roam-buffer-current-directory 'permanent-local t)
|
||||
|
||||
;;;; Library
|
||||
(defun org-roam-buffer-visit-thing ()
|
||||
"This is a placeholder command.
|
||||
Where applicable, section-specific keymaps bind another command
|
||||
which visits the thing at point."
|
||||
(interactive)
|
||||
(user-error "There is no thing at point that could be visited"))
|
||||
|
||||
(defun org-roam-buffer-file-at-point (&optional assert)
|
||||
"Return the file at point in the current `org-roam-mode' based buffer.
|
||||
If ASSERT, throw an error."
|
||||
(if-let ((file (magit-section-case
|
||||
(org-roam-node-section (org-roam-node-file (oref it node)))
|
||||
(org-roam-grep-section (oref it file))
|
||||
(org-roam-preview-section (oref it file))
|
||||
(t (cl-assert (derived-mode-p 'org-roam-mode))))))
|
||||
file
|
||||
(when assert
|
||||
(user-error "No file at point"))))
|
||||
|
||||
(defun org-roam-buffer-refresh ()
|
||||
"Refresh the contents of the currently selected Org-roam buffer."
|
||||
(interactive)
|
||||
(cl-assert (derived-mode-p 'org-roam-mode))
|
||||
(save-excursion (org-roam-buffer-render-contents)))
|
||||
|
||||
(defun org-roam-buffer-render-contents ()
|
||||
"Recompute and render the contents of an Org-roam buffer.
|
||||
Assumes that the current buffer is an `org-roam-mode' based
|
||||
buffer."
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(org-roam-mode)
|
||||
(setq-local default-directory org-roam-buffer-current-directory)
|
||||
(setq-local org-roam-directory org-roam-buffer-current-directory)
|
||||
(org-roam-buffer-set-header-line-format
|
||||
(org-roam-node-title org-roam-buffer-current-node))
|
||||
(magit-insert-section (org-roam)
|
||||
(magit-insert-heading)
|
||||
(dolist (section org-roam-mode-sections)
|
||||
(pcase section
|
||||
((pred functionp)
|
||||
(funcall section org-roam-buffer-current-node))
|
||||
(`(,fn . ,args)
|
||||
(apply fn (cons org-roam-buffer-current-node args)))
|
||||
(_
|
||||
(user-error "Invalid `org-roam-mode-sections' specification")))))
|
||||
(run-hooks 'org-roam-buffer-postrender-functions)
|
||||
(goto-char 0)))
|
||||
|
||||
(defun org-roam-buffer-set-header-line-format (string)
|
||||
"Set the header-line using STRING.
|
||||
If the `face' property of any part of STRING is already set, then
|
||||
that takes precedence. Also pad the left side of STRING so that
|
||||
it aligns with the text area."
|
||||
(setq-local header-line-format
|
||||
(concat (propertize " " 'display '(space :align-to 0))
|
||||
string)))
|
||||
|
||||
;;;; Dedicated buffer
|
||||
;;;###autoload
|
||||
(defun org-roam-buffer-display-dedicated (node)
|
||||
"Launch NODE dedicated Org-roam buffer.
|
||||
Unlike the persistent `org-roam-buffer', the contents of this
|
||||
buffer won't be automatically changed and will be held in place.
|
||||
|
||||
In interactive calls prompt to select NODE, unless called with
|
||||
`universal-argument', in which case NODE will be set to
|
||||
`org-roam-node-at-point'."
|
||||
(interactive
|
||||
(list (if current-prefix-arg
|
||||
(org-roam-node-at-point 'assert)
|
||||
(org-roam-node-read nil nil nil 'require-match))))
|
||||
(let ((buffer (get-buffer-create (org-roam-buffer--dedicated-name node))))
|
||||
(with-current-buffer buffer
|
||||
(setq-local org-roam-buffer-current-node node)
|
||||
(setq-local org-roam-buffer-current-directory org-roam-directory)
|
||||
(org-roam-buffer-render-contents))
|
||||
(display-buffer buffer)))
|
||||
|
||||
(defun org-roam-buffer--dedicated-name (node)
|
||||
"Construct buffer name for NODE dedicated Org-roam buffer."
|
||||
(let ((title (org-roam-node-title node))
|
||||
(filename (file-relative-name (org-roam-node-file node) org-roam-directory)))
|
||||
(format "*org-roam: %s<%s>*" title filename)))
|
||||
|
||||
(defun org-roam-buffer-dedicated-p (&optional buffer)
|
||||
"Return t if an Org-roam BUFFER is a node dedicated one.
|
||||
See `org-roam-buffer-display-dedicated' for more details.
|
||||
If BUFFER is nil, default it to `current-buffer'."
|
||||
(or buffer (setq buffer (current-buffer)))
|
||||
(string-match-p (concat "^" (regexp-quote "*org-roam: "))
|
||||
(buffer-name buffer)))
|
||||
|
||||
;;;; Persistent buffer
|
||||
(defvar org-roam-buffer "*org-roam*"
|
||||
"The persistent Org-roam buffer name. Must be surround with \"*\".
|
||||
The content inside of this buffer will be automatically updated
|
||||
to the nearest node at point that comes from the current buffer.
|
||||
To toggle its display use `org-roam-buffer-toggle' command.")
|
||||
|
||||
(defun org-roam-buffer-toggle ()
|
||||
"Toggle display of the persistent `org-roam-buffer'."
|
||||
(interactive)
|
||||
(pcase (org-roam-buffer--visibility)
|
||||
('visible
|
||||
(progn
|
||||
(quit-window nil (get-buffer-window org-roam-buffer))
|
||||
(remove-hook 'post-command-hook #'org-roam-buffer--redisplay-h)))
|
||||
((or 'exists 'none)
|
||||
(progn
|
||||
(display-buffer (get-buffer-create org-roam-buffer))
|
||||
(org-roam-buffer-persistent-redisplay)))))
|
||||
|
||||
(define-inline org-roam-buffer--visibility ()
|
||||
"Return the current visibility state of the persistent `org-roam-buffer'.
|
||||
Valid states are `visible', `exists' and `none'."
|
||||
(declare (side-effect-free t))
|
||||
(inline-quote
|
||||
(cond
|
||||
((get-buffer-window org-roam-buffer) 'visible)
|
||||
((get-buffer org-roam-buffer) 'exists)
|
||||
(t 'none))))
|
||||
|
||||
(defun org-roam-buffer-persistent-redisplay ()
|
||||
"Recompute contents of the persistent `org-roam-buffer'.
|
||||
Has no effect when there's no `org-roam-node-at-point'."
|
||||
(when-let ((node (org-roam-node-at-point)))
|
||||
(unless (equal node org-roam-buffer-current-node)
|
||||
(setq org-roam-buffer-current-node node
|
||||
org-roam-buffer-current-directory org-roam-directory)
|
||||
(with-current-buffer (get-buffer-create org-roam-buffer)
|
||||
(org-roam-buffer-render-contents)
|
||||
(add-hook 'kill-buffer-hook #'org-roam-buffer--persistent-cleanup-h nil t)))))
|
||||
|
||||
(defun org-roam-buffer--persistent-cleanup-h ()
|
||||
"Clean-up global state that's dedicated for the persistent `org-roam-buffer'."
|
||||
(setq-default org-roam-buffer-current-node nil
|
||||
org-roam-buffer-current-directory nil))
|
||||
|
||||
(add-hook 'org-roam-find-file-hook #'org-roam-buffer--setup-redisplay-h)
|
||||
(defun org-roam-buffer--setup-redisplay-h ()
|
||||
"Setup automatic redisplay of the persistent `org-roam-buffer'."
|
||||
(add-hook 'post-command-hook #'org-roam-buffer--redisplay-h nil t))
|
||||
|
||||
(defun org-roam-buffer--redisplay-h ()
|
||||
"Reconstruct the persistent `org-roam-buffer'.
|
||||
This needs to be quick or infrequent, because this designed to
|
||||
run at `post-command-hook'."
|
||||
(and (get-buffer-window org-roam-buffer)
|
||||
(org-roam-buffer-persistent-redisplay)))
|
||||
|
||||
;;; Sections
|
||||
;;;; Node
|
||||
(defvar org-roam-node-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map org-roam-mode-map)
|
||||
(define-key map [remap org-roam-buffer-visit-thing] 'org-roam-node-visit)
|
||||
map)
|
||||
"Keymap for `org-roam-node-section's.")
|
||||
|
||||
(defclass org-roam-node-section (magit-section)
|
||||
((keymap :initform 'org-roam-node-map)
|
||||
(node :initform nil))
|
||||
"A `magit-section' used by `org-roam-mode' to outline NODE in its own heading.")
|
||||
|
||||
(cl-defun org-roam-node-insert-section (&key source-node point properties)
|
||||
"Insert section for a link from SOURCE-NODE to some other node.
|
||||
The other node is normally `org-roam-buffer-current-node'.
|
||||
|
||||
SOURCE-NODE is an `org-roam-node' that links or references with
|
||||
the other node.
|
||||
|
||||
POINT is a character position where the link is located in
|
||||
SOURCE-NODE's file.
|
||||
|
||||
PROPERTIES (a plist) contains additional information about the
|
||||
link.
|
||||
|
||||
Despite the name, this function actually inserts 2 sections at
|
||||
the same time:
|
||||
|
||||
1. `org-roam-node-section' for a heading that describes
|
||||
SOURCE-NODE. Acts as a parent section of the following one.
|
||||
|
||||
2. `org-roam-preview-section' for a preview content that comes
|
||||
from SOURCE-NODE's file for the link (that references the
|
||||
other node) at POINT. Acts a child section of the previous
|
||||
one."
|
||||
(magit-insert-section section (org-roam-node-section)
|
||||
(let ((outline (if-let ((outline (plist-get properties :outline)))
|
||||
(mapconcat #'org-link-display-format outline " > ")
|
||||
"Top")))
|
||||
(insert (concat (propertize (org-roam-node-title source-node)
|
||||
'font-lock-face 'org-roam-title)
|
||||
(format " (%s)"
|
||||
(propertize outline 'font-lock-face 'org-roam-olp)))))
|
||||
(magit-insert-heading)
|
||||
(oset section node source-node)
|
||||
(magit-insert-section section (org-roam-preview-section)
|
||||
(insert (org-roam-fontify-like-in-org-mode
|
||||
(org-roam-preview-get-contents (org-roam-node-file source-node) point))
|
||||
"\n")
|
||||
(oset section file (org-roam-node-file source-node))
|
||||
(oset section point point)
|
||||
(insert ?\n))))
|
||||
|
||||
;;;; Preview
|
||||
(defvar org-roam-preview-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map org-roam-mode-map)
|
||||
(define-key map [remap org-roam-buffer-visit-thing] 'org-roam-preview-visit)
|
||||
map)
|
||||
"Keymap for `org-roam-preview-section's.")
|
||||
|
||||
(defclass org-roam-preview-section (magit-section)
|
||||
((keymap :initform 'org-roam-preview-map)
|
||||
(file :initform nil)
|
||||
(point :initform nil))
|
||||
"A `magit-section' used by `org-roam-mode' to contain preview content.
|
||||
The preview content comes from FILE, and the link as at POINT.")
|
||||
|
||||
(defun org-roam-preview-visit (file point &optional other-window)
|
||||
"Visit FILE at POINT and return the visited buffer.
|
||||
With OTHER-WINDOW non-nil do so in another window.
|
||||
In interactive calls OTHER-WINDOW is set with
|
||||
`universal-argument'."
|
||||
(interactive (list (org-roam-buffer-file-at-point 'assert)
|
||||
(oref (magit-current-section) point)
|
||||
current-prefix-arg))
|
||||
(let ((buf (find-file-noselect file))
|
||||
(display-buffer-fn (if other-window
|
||||
#'switch-to-buffer-other-window
|
||||
#'pop-to-buffer-same-window)))
|
||||
(funcall display-buffer-fn buf)
|
||||
(with-current-buffer buf
|
||||
(widen)
|
||||
(goto-char point))
|
||||
(when (org-invisible-p) (org-fold-show-context))
|
||||
buf))
|
||||
|
||||
(defun org-roam-preview-default-function ()
|
||||
"Return the preview content at point.
|
||||
|
||||
This function returns the all contents under the current
|
||||
headline, up to the next headline."
|
||||
(let ((beg (save-excursion
|
||||
(org-roam-end-of-meta-data t)
|
||||
(point)))
|
||||
(end (save-excursion
|
||||
(org-next-visible-heading 1)
|
||||
(point))))
|
||||
(string-trim (buffer-substring-no-properties beg end))))
|
||||
|
||||
(defun org-roam-preview-get-contents (file pt)
|
||||
"Get preview content for FILE at PT."
|
||||
(save-excursion
|
||||
(org-roam-with-temp-buffer file
|
||||
(org-with-wide-buffer
|
||||
(goto-char pt)
|
||||
(let ((s (funcall org-roam-preview-function)))
|
||||
(dolist (fn org-roam-preview-postprocess-functions)
|
||||
(setq s (funcall fn s)))
|
||||
s)))))
|
||||
|
||||
;;;; Backlinks
|
||||
(cl-defstruct (org-roam-backlink (:constructor org-roam-backlink-create)
|
||||
(:copier nil))
|
||||
source-node target-node
|
||||
point properties)
|
||||
|
||||
(cl-defmethod org-roam-populate ((backlink org-roam-backlink))
|
||||
"Populate BACKLINK from database."
|
||||
(setf (org-roam-backlink-source-node backlink)
|
||||
(org-roam-populate (org-roam-backlink-source-node backlink))
|
||||
(org-roam-backlink-target-node backlink)
|
||||
(org-roam-populate (org-roam-backlink-target-node backlink)))
|
||||
backlink)
|
||||
|
||||
(cl-defun org-roam-backlinks-get (node &key unique)
|
||||
"Return the backlinks for NODE.
|
||||
|
||||
When UNIQUE is nil, show all positions where references are found.
|
||||
When UNIQUE is t, limit to unique sources."
|
||||
(let* ((sql (if unique
|
||||
[:select :distinct [source dest pos properties]
|
||||
:from links
|
||||
:where (= dest $s1)
|
||||
:and (= type "id")
|
||||
:group :by source
|
||||
:having (funcall min pos)]
|
||||
[:select [source dest pos properties]
|
||||
:from links
|
||||
:where (= dest $s1)
|
||||
:and (= type "id")]))
|
||||
(backlinks (org-roam-db-query sql (org-roam-node-id node))))
|
||||
(cl-loop for backlink in backlinks
|
||||
collect (pcase-let ((`(,source-id ,dest-id ,pos ,properties) backlink))
|
||||
(org-roam-populate
|
||||
(org-roam-backlink-create
|
||||
:source-node (org-roam-node-create :id source-id)
|
||||
:target-node (org-roam-node-create :id dest-id)
|
||||
:point pos
|
||||
:properties properties))))))
|
||||
|
||||
(defun org-roam-backlinks-sort (a b)
|
||||
"Default sorting function for backlinks A and B.
|
||||
Sorts by title."
|
||||
(string< (org-roam-node-title (org-roam-backlink-source-node a))
|
||||
(org-roam-node-title (org-roam-backlink-source-node b))))
|
||||
|
||||
(cl-defun org-roam-backlinks-section (node &key (unique nil) (show-backlink-p nil)
|
||||
(section-heading "Backlinks:"))
|
||||
"The backlinks section for NODE.
|
||||
|
||||
When UNIQUE is nil, show all positions where references are found.
|
||||
When UNIQUE is t, limit to unique sources.
|
||||
|
||||
When SHOW-BACKLINK-P is not null, only show backlinks for which
|
||||
this predicate is not nil.
|
||||
|
||||
SECTION-HEADING is the string used as a heading for the backlink section."
|
||||
(when-let ((backlinks (seq-sort #'org-roam-backlinks-sort (org-roam-backlinks-get node :unique unique))))
|
||||
(magit-insert-section (org-roam-backlinks)
|
||||
(magit-insert-heading section-heading)
|
||||
(dolist (backlink backlinks)
|
||||
(when (or (null show-backlink-p)
|
||||
(and (not (null show-backlink-p))
|
||||
(funcall show-backlink-p backlink)))
|
||||
(org-roam-node-insert-section
|
||||
:source-node (org-roam-backlink-source-node backlink)
|
||||
:point (org-roam-backlink-point backlink)
|
||||
:properties (org-roam-backlink-properties backlink))))
|
||||
(insert ?\n))))
|
||||
|
||||
;;;; Reflinks
|
||||
(cl-defstruct (org-roam-reflink (:constructor org-roam-reflink-create)
|
||||
(:copier nil))
|
||||
source-node ref
|
||||
point properties)
|
||||
|
||||
(cl-defmethod org-roam-populate ((reflink org-roam-reflink))
|
||||
"Populate REFLINK from database."
|
||||
(setf (org-roam-reflink-source-node reflink)
|
||||
(org-roam-populate (org-roam-reflink-source-node reflink)))
|
||||
reflink)
|
||||
|
||||
(defun org-roam-reflinks-get (node)
|
||||
"Return the reflinks for NODE."
|
||||
(let ((refs (org-roam-db-query [:select :distinct [refs:ref links:source links:pos links:properties]
|
||||
:from refs
|
||||
:left-join links
|
||||
:where (= refs:node-id $s1)
|
||||
:and (= links:dest refs:ref)
|
||||
:union
|
||||
:select :distinct [refs:ref citations:node-id
|
||||
citations:pos citations:properties]
|
||||
:from refs
|
||||
:left-join citations
|
||||
:where (= refs:node-id $s1)
|
||||
:and (= citations:cite-key refs:ref)]
|
||||
(org-roam-node-id node)))
|
||||
links)
|
||||
(pcase-dolist (`(,ref ,source-id ,pos ,properties) refs)
|
||||
(push (org-roam-populate
|
||||
(org-roam-reflink-create
|
||||
:source-node (org-roam-node-create :id source-id)
|
||||
:ref ref
|
||||
:point pos
|
||||
:properties properties)) links))
|
||||
links))
|
||||
|
||||
(defun org-roam-reflinks-sort (a b)
|
||||
"Default sorting function for reflinks A and B.
|
||||
Sorts by title."
|
||||
(string< (org-roam-node-title (org-roam-reflink-source-node a))
|
||||
(org-roam-node-title (org-roam-reflink-source-node b))))
|
||||
|
||||
(defun org-roam-reflinks-section (node)
|
||||
"The reflinks section for NODE."
|
||||
(when-let ((refs (org-roam-node-refs node))
|
||||
(reflinks (seq-sort #'org-roam-reflinks-sort (org-roam-reflinks-get node))))
|
||||
(magit-insert-section (org-roam-reflinks)
|
||||
(magit-insert-heading "Reflinks:")
|
||||
(dolist (reflink reflinks)
|
||||
(org-roam-node-insert-section
|
||||
:source-node (org-roam-reflink-source-node reflink)
|
||||
:point (org-roam-reflink-point reflink)
|
||||
:properties (org-roam-reflink-properties reflink)))
|
||||
(insert ?\n))))
|
||||
|
||||
;;;; Grep
|
||||
(defvar org-roam-grep-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map org-roam-mode-map)
|
||||
(define-key map [remap org-roam-buffer-visit-thing] 'org-roam-grep-visit)
|
||||
map)
|
||||
"Keymap for Org-roam grep result sections.")
|
||||
|
||||
(defclass org-roam-grep-section (magit-section)
|
||||
((keymap :initform 'org-roam-grep-map)
|
||||
(file :initform nil)
|
||||
(row :initform nil)
|
||||
(col :initform nil))
|
||||
"A `magit-section' used by `org-roam-mode' to contain grep output.")
|
||||
|
||||
(defun org-roam-grep-visit (file &optional other-window row col)
|
||||
"Visit FILE at row ROW (if any) and column COL (if any). Return the buffer.
|
||||
With OTHER-WINDOW non-nil (in interactive calls set with
|
||||
`universal-argument') display the buffer in another window
|
||||
instead."
|
||||
(interactive (list (org-roam-buffer-file-at-point t)
|
||||
current-prefix-arg
|
||||
(oref (magit-current-section) row)
|
||||
(oref (magit-current-section) col)))
|
||||
(let ((buf (find-file-noselect file))
|
||||
(display-buffer-fn (if other-window
|
||||
#'switch-to-buffer-other-window
|
||||
#'pop-to-buffer-same-window)))
|
||||
(funcall display-buffer-fn buf)
|
||||
(with-current-buffer buf
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(when row
|
||||
(forward-line (1- row)))
|
||||
(when col
|
||||
(forward-char (1- col))))
|
||||
(when (org-invisible-p) (org-fold-show-context))
|
||||
buf))
|
||||
|
||||
;;;; Unlinked references
|
||||
(defvar org-roam-unlinked-references-result-re
|
||||
(rx (group (one-or-more anything))
|
||||
":"
|
||||
(group (one-or-more digit))
|
||||
":"
|
||||
(group (one-or-more digit))
|
||||
":"
|
||||
(group (zero-or-more anything)))
|
||||
"Regex for the return result of a ripgrep query.")
|
||||
|
||||
(defun org-roam-unlinked-references-preview-line (file row)
|
||||
"Return the preview line from FILE.
|
||||
This is the ROW within FILE."
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(forward-line (1- row))
|
||||
(buffer-substring-no-properties
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(point))
|
||||
(save-excursion
|
||||
(end-of-line)
|
||||
(point)))))
|
||||
|
||||
(defun org-roam-unlinked-references--rg-command (titles)
|
||||
"Return the ripgrep command searching for TITLES."
|
||||
(concat "rg --follow --only-matching --vimgrep --pcre2 --ignore-case "
|
||||
(mapconcat (lambda (glob) (concat "--glob " glob))
|
||||
(org-roam--list-files-search-globs org-roam-file-extensions)
|
||||
" ")
|
||||
(format " '\\[([^[]]++|(?R))*\\]%s' "
|
||||
(mapconcat (lambda (title)
|
||||
(format "|(\\b%s\\b)" (shell-quote-argument title)))
|
||||
titles ""))
|
||||
(shell-quote-argument org-roam-directory)))
|
||||
|
||||
(defun org-roam-unlinked-references-section (node)
|
||||
"The unlinked references section for NODE.
|
||||
References from FILE are excluded."
|
||||
(when (and (executable-find "rg")
|
||||
(org-roam-node-title node)
|
||||
(not (string-match "PCRE2 is not available"
|
||||
(shell-command-to-string "rg --pcre2-version"))))
|
||||
(let* ((titles (cons (org-roam-node-title node)
|
||||
(org-roam-node-aliases node)))
|
||||
(rg-command (org-roam-unlinked-references--rg-command titles))
|
||||
(results (split-string (shell-command-to-string rg-command) "\n"))
|
||||
f row col match)
|
||||
(magit-insert-section (unlinked-references)
|
||||
(magit-insert-heading "Unlinked References:")
|
||||
(dolist (line results)
|
||||
(save-match-data
|
||||
(when (string-match org-roam-unlinked-references-result-re line)
|
||||
(setq f (match-string 1 line)
|
||||
row (string-to-number (match-string 2 line))
|
||||
col (string-to-number (match-string 3 line))
|
||||
match (match-string 4 line))
|
||||
(when (and match
|
||||
(not (file-equal-p (org-roam-node-file node) f))
|
||||
(member (downcase match) (mapcar #'downcase titles)))
|
||||
(magit-insert-section section (org-roam-grep-section)
|
||||
(oset section file f)
|
||||
(oset section row row)
|
||||
(oset section col col)
|
||||
(insert (propertize (format "%s:%s:%s"
|
||||
(truncate-string-to-width (file-name-base f) 15 nil nil t)
|
||||
row col) 'font-lock-face 'org-roam-dim)
|
||||
" "
|
||||
(org-roam-fontify-like-in-org-mode
|
||||
(org-roam-unlinked-references-preview-line f row))
|
||||
"\n"))))))
|
||||
(insert ?\n)))))
|
||||
|
||||
(provide 'org-roam-mode)
|
||||
;;; org-roam-mode.el ends here
|
1150
org-roam-node.el
Normal file
1150
org-roam-node.el
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,111 +0,0 @@
|
||||
;;; 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/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 1.2.3
|
||||
;; 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)
|
||||
(require 'ol) ;; for org-link-decode
|
||||
|
||||
(defcustom org-roam-protocol-store-links nil
|
||||
"Whether to store links when capturing websites with `org-roam-protocol'."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
;;;; Functions
|
||||
(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 (funcall org-roam-title-to-slug-function title)) decoded-alist))
|
||||
(let-alist decoded-alist
|
||||
(let* ((ref (org-protocol-sanitize-uri .ref))
|
||||
(type (and (string-match org-link-plain-re ref)
|
||||
(match-string 1 ref)))
|
||||
(title (or .title ""))
|
||||
(body (or .body ""))
|
||||
(orglink
|
||||
(org-link-make-string ref (or (org-string-nw-p title) ref))))
|
||||
(when org-roam-protocol-store-links
|
||||
(push (list ref title) org-stored-links))
|
||||
(org-link-store-props :type type
|
||||
:link ref
|
||||
:annotation orglink
|
||||
:initial body)))
|
||||
(let* ((org-roam-capture-templates org-roam-capture-ref-templates)
|
||||
(org-roam-capture--context 'ref)
|
||||
(org-roam-capture--info decoded-alist)
|
||||
(org-capture-link-is-already-stored t)
|
||||
(template (cdr (assoc 'template decoded-alist))))
|
||||
(raise-frame)
|
||||
(org-roam-capture--capture nil template)
|
||||
(org-roam-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)
|
||||
(org-roam--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
|
458
org-roam-utils.el
Normal file
458
org-roam-utils.el
Normal file
@ -0,0 +1,458 @@
|
||||
;;; org-roam-utils.el --- Utilities for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; URL: https://github.com/org-roam/org-roam
|
||||
;; Keywords: org-mode, roam, convenience
|
||||
;; Version: 2.3.0
|
||||
;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.6"))
|
||||
|
||||
;; 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 provides definitions for utilities that used throughout the
|
||||
;; whole package.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'org-roam)
|
||||
|
||||
(defun org-roam-require (libs)
|
||||
"Require LIBS."
|
||||
(dolist (lib libs)
|
||||
(require lib nil 'noerror)))
|
||||
|
||||
;;; String utilities
|
||||
;; TODO Refactor this.
|
||||
(defun org-roam-replace-string (old new s)
|
||||
"Replace OLD with NEW in S."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(replace-regexp-in-string (regexp-quote old) new s t t))
|
||||
|
||||
(defun org-roam-quote-string (s)
|
||||
"Quotes string S."
|
||||
(->> s
|
||||
(org-roam-replace-string "\\" "\\\\")
|
||||
(org-roam-replace-string "\"" "\\\"")))
|
||||
|
||||
(defun org-roam-word-wrap (len s)
|
||||
"If S is longer than LEN, wrap the words with newlines."
|
||||
(declare (side-effect-free t))
|
||||
(save-match-data
|
||||
(with-temp-buffer
|
||||
(insert s)
|
||||
(let ((fill-column len))
|
||||
(fill-region (point-min) (point-max)))
|
||||
(buffer-substring (point-min) (point-max)))))
|
||||
|
||||
(defun org-roam-string-equal (s1 s2)
|
||||
"Return t if S1 and S2 are equal.
|
||||
Like `string-equal', but case-insensitive."
|
||||
(and (= (length s1) (length s2))
|
||||
(or (string-equal s1 s2)
|
||||
(string-equal (downcase s1) (downcase s2)))))
|
||||
|
||||
(defun org-roam-whitespace-content (s)
|
||||
"Return the whitespace content at the end of S."
|
||||
(with-temp-buffer
|
||||
(insert s)
|
||||
(skip-chars-backward " \t\n")
|
||||
(buffer-substring-no-properties
|
||||
(point) (point-max))))
|
||||
|
||||
(defun org-roam-strip-comments (s)
|
||||
"Strip Org comments from string S."
|
||||
(with-temp-buffer
|
||||
(insert s)
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(if (org-at-comment-p)
|
||||
(delete-region (line-beginning-position)
|
||||
(progn (forward-line) (point)))
|
||||
(forward-line)))
|
||||
(buffer-string)))
|
||||
|
||||
;;; List utilities
|
||||
(defun org-roam-plist-map! (fn plist)
|
||||
"Map FN over PLIST, modifying it in-place and returning it.
|
||||
FN must take two arguments: the key and the value."
|
||||
(let ((plist-index plist))
|
||||
(while plist-index
|
||||
(let ((key (pop plist-index)))
|
||||
(setf (car plist-index) (funcall fn key (car plist-index))
|
||||
plist-index (cdr plist-index)))))
|
||||
plist)
|
||||
|
||||
(defmacro org-roam-dolist-with-progress (spec msg &rest body)
|
||||
"Loop over a list and report progress in the echo area.
|
||||
Like `dolist-with-progress-reporter', but falls back to `dolist'
|
||||
if the function does not yet exist.
|
||||
|
||||
Evaluate BODY with VAR bound to each car from LIST, in turn.
|
||||
Then evaluate RESULT to get return value, default nil.
|
||||
|
||||
MSG is a progress reporter object or a string. In the latter
|
||||
case, use this string to create a progress reporter.
|
||||
|
||||
SPEC is a list, as per `dolist'."
|
||||
(declare (indent 2))
|
||||
(if (fboundp 'dolist-with-progress-reporter)
|
||||
`(dolist-with-progress-reporter ,spec ,msg ,@body)
|
||||
`(dolist ,spec ,@body)))
|
||||
|
||||
;;; File utilities
|
||||
(defun org-roam-descendant-of-p (a b)
|
||||
"Return t if A is descendant of B."
|
||||
(unless (and a b (equal (file-truename a) (file-truename b)))
|
||||
(string-prefix-p (replace-regexp-in-string "^\\([A-Za-z]\\):" 'downcase (expand-file-name b) t t)
|
||||
(replace-regexp-in-string "^\\([A-Za-z]\\):" 'downcase (expand-file-name a) t t))))
|
||||
|
||||
(defmacro org-roam-with-file (file keep-buf-p &rest body)
|
||||
"Execute BODY within FILE.
|
||||
If FILE is nil, execute BODY in the current buffer.
|
||||
Kills the buffer if KEEP-BUF-P is nil, and FILE is not yet visited."
|
||||
(declare (indent 2) (debug t))
|
||||
`(let* (new-buf
|
||||
(auto-mode-alist nil)
|
||||
(find-file-hook nil)
|
||||
(buf (or (and (not ,file)
|
||||
(current-buffer)) ;If FILE is nil, use current buffer
|
||||
(find-buffer-visiting ,file) ; If FILE is already visited, find buffer
|
||||
(progn
|
||||
(setq new-buf t)
|
||||
(find-file-noselect ,file)))) ; Else, visit FILE and return buffer
|
||||
res)
|
||||
(with-current-buffer buf
|
||||
(unless (derived-mode-p 'org-mode)
|
||||
(delay-mode-hooks
|
||||
(let ((org-inhibit-startup t)
|
||||
(org-agenda-files nil))
|
||||
(org-mode)
|
||||
(hack-local-variables))))
|
||||
(setq res (progn ,@body))
|
||||
(unless (and new-buf (not ,keep-buf-p))
|
||||
(save-buffer)))
|
||||
(if (and new-buf (not ,keep-buf-p))
|
||||
(when (find-buffer-visiting ,file)
|
||||
(kill-buffer (find-buffer-visiting ,file))))
|
||||
res))
|
||||
|
||||
;;; Buffer utilities
|
||||
(defmacro org-roam-with-temp-buffer (file &rest body)
|
||||
"Execute BODY within a temp buffer.
|
||||
Like `with-temp-buffer', but propagates `org-roam-directory'.
|
||||
If FILE, set `default-directory' to FILE's directory and insert its contents."
|
||||
(declare (indent 1) (debug t))
|
||||
(let ((current-org-roam-directory (make-symbol "current-org-roam-directory")))
|
||||
`(let ((,current-org-roam-directory org-roam-directory))
|
||||
(with-temp-buffer
|
||||
(let ((org-roam-directory ,current-org-roam-directory))
|
||||
(delay-mode-hooks (org-mode))
|
||||
(when ,file
|
||||
(insert-file-contents ,file)
|
||||
(setq-local default-directory (file-name-directory ,file)))
|
||||
,@body)))))
|
||||
|
||||
;;; Formatting
|
||||
(defun org-roam-format-template (template replacer)
|
||||
"Format TEMPLATE with the function REPLACER.
|
||||
The templates are of form ${foo} for variable foo, and
|
||||
${foo=default} for variable foo with default value \"default\".
|
||||
REPLACER takes an argument of the format variable and the default
|
||||
value (possibly nil). Adapted from `s-format'."
|
||||
(let ((saved-match-data (match-data)))
|
||||
(unwind-protect
|
||||
(replace-regexp-in-string
|
||||
"\\${\\([^}]+\\)}"
|
||||
(lambda (md)
|
||||
(let ((var (match-string 1 md))
|
||||
(replacer-match-data (match-data))
|
||||
default-val)
|
||||
(when (string-match "\\(.+\\)=\\(.+\\)" var)
|
||||
(setq default-val (match-string 2 var)
|
||||
var (match-string 1 var)))
|
||||
(unwind-protect
|
||||
(let ((v (progn
|
||||
(set-match-data saved-match-data)
|
||||
(funcall replacer var default-val))))
|
||||
(if v
|
||||
(format (apply #'propertize "%s" (text-properties-at 0 var)) v)
|
||||
(signal 'org-roam-format-resolve md)))
|
||||
(set-match-data replacer-match-data))))
|
||||
(if (functionp template)
|
||||
(funcall template)
|
||||
template)
|
||||
;; Need literal to make sure it works
|
||||
t t)
|
||||
(set-match-data saved-match-data))))
|
||||
|
||||
;;; Fontification
|
||||
(defvar org-ref-buffer-hacked)
|
||||
|
||||
(defun org-roam-fontify-like-in-org-mode (s)
|
||||
"Fontify string S like in Org mode.
|
||||
Like `org-fontify-like-in-org-mode', but supports `org-ref'."
|
||||
;; NOTE: pretend that the temporary buffer created by `org-fontify-like-in-org-mode' to
|
||||
;; fontify a `cite:' reference has been hacked by org-ref, whatever that means;
|
||||
;;
|
||||
;; `org-ref-cite-link-face-fn', which is used to supply a face for `cite:' links, calls
|
||||
;; `hack-dir-local-variables' rationalizing that `bibtex-completion' would throw some warnings
|
||||
;; otherwise. This doesn't seem to be the case and calling this function just before
|
||||
;; `org-font-lock-ensure' (alias of `font-lock-ensure') actually instead of fixing the alleged
|
||||
;; warnings messes the things so badly that `font-lock-ensure' crashes with error and doesn't let
|
||||
;; org-roam to proceed further. I don't know what's happening there exactly but disabling this hackery
|
||||
;; fixes the crashing. Fortunately, org-ref provides the `org-ref-buffer-hacked' switch, which we use
|
||||
;; here to make it believe that the buffer was hacked.
|
||||
;;
|
||||
;; This is a workaround for `cite:' links and does not have any effect on other ref types.
|
||||
;;
|
||||
;; `org-ref-buffer-hacked' is a buffer-local variable, therefore we inline
|
||||
;; `org-fontify-like-in-org-mode' here
|
||||
(with-temp-buffer
|
||||
(insert s)
|
||||
(let ((org-ref-buffer-hacked t))
|
||||
(org-mode)
|
||||
(setq-local org-fold-core-style 'overlays)
|
||||
(font-lock-ensure)
|
||||
(buffer-string))))
|
||||
|
||||
;;; Org-mode utilities
|
||||
;;;; Motions
|
||||
(defun org-roam-up-heading-or-point-min ()
|
||||
"Fixed version of Org's `org-up-heading-or-point-min'."
|
||||
(ignore-errors (org-back-to-heading t))
|
||||
(let ((p (point)))
|
||||
(if (< 1 (funcall outline-level))
|
||||
(progn
|
||||
(org-up-heading-safe)
|
||||
(when (= (point) p)
|
||||
(goto-char (point-min))))
|
||||
(unless (bobp) (goto-char (point-min))))))
|
||||
|
||||
;;;; Keywords
|
||||
(defun org-roam-get-keyword (name &optional file bound)
|
||||
"Return keyword property NAME from an org FILE.
|
||||
FILE defaults to current file.
|
||||
Only scans up to BOUND bytes of the document."
|
||||
(unless bound
|
||||
(setq bound 1024))
|
||||
(if file
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file nil 0 bound)
|
||||
(org-roam--get-keyword name))
|
||||
(org-roam--get-keyword name bound)))
|
||||
|
||||
(defun org-roam--get-keyword (name &optional bound)
|
||||
"Return keyword property NAME in current buffer.
|
||||
If BOUND, scan up to BOUND bytes of the buffer."
|
||||
(save-excursion
|
||||
(let ((re (format "^#\\+%s:[ \t]*\\([^\n]+\\)" (upcase name))))
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward re bound t)
|
||||
(buffer-substring-no-properties (match-beginning 1) (match-end 1))))))
|
||||
|
||||
(defun org-roam-end-of-meta-data (&optional full)
|
||||
"Like `org-end-of-meta-data', but supports file-level metadata.
|
||||
|
||||
When FULL is non-nil but not t, skip planning information,
|
||||
properties, clocking lines and logbook drawers.
|
||||
|
||||
When optional argument FULL is t, skip everything above, and also
|
||||
skip keywords."
|
||||
(org-back-to-heading-or-point-min t)
|
||||
(when (org-at-heading-p) (forward-line))
|
||||
;; Skip planning information.
|
||||
(when (looking-at-p org-planning-line-re) (forward-line))
|
||||
;; Skip property drawer.
|
||||
(when (looking-at org-property-drawer-re)
|
||||
(goto-char (match-end 0))
|
||||
(forward-line))
|
||||
;; When FULL is not nil, skip more.
|
||||
(when (and full (not (org-at-heading-p)))
|
||||
(catch 'exit
|
||||
(let ((end (save-excursion (outline-next-heading) (point)))
|
||||
(re (concat "[ \t]*$" "\\|" org-clock-line-re)))
|
||||
(while (not (eobp))
|
||||
(cond ;; Skip clock lines.
|
||||
((looking-at-p re) (forward-line))
|
||||
;; Skip logbook drawer.
|
||||
((looking-at-p org-logbook-drawer-re)
|
||||
(if (re-search-forward "^[ \t]*:END:[ \t]*$" end t)
|
||||
(forward-line)
|
||||
(throw 'exit t)))
|
||||
((looking-at-p org-drawer-regexp)
|
||||
(if (re-search-forward "^[ \t]*:END:[ \t]*$" end t)
|
||||
(forward-line)
|
||||
(throw 'exit t)))
|
||||
;; When FULL is t, skip keywords too.
|
||||
((and (eq full t)
|
||||
(looking-at-p org-keyword-regexp))
|
||||
(forward-line))
|
||||
(t (throw 'exit t))))))))
|
||||
|
||||
(defun org-roam-set-keyword (key value)
|
||||
"Set keyword KEY to VALUE.
|
||||
If the property is already set, it's value is replaced."
|
||||
(org-with-point-at 1
|
||||
(let ((case-fold-search t))
|
||||
(if (re-search-forward (concat "^#\\+" key ":\\(.*\\)") (point-max) t)
|
||||
(if (string-blank-p value)
|
||||
(kill-whole-line)
|
||||
(replace-match (concat " " value) 'fixedcase nil nil 1))
|
||||
(org-roam-end-of-meta-data 'drawers)
|
||||
(if (save-excursion (end-of-line) (eobp))
|
||||
(progn
|
||||
(end-of-line)
|
||||
(insert "\n"))
|
||||
(forward-line)
|
||||
(beginning-of-line))
|
||||
(insert "#+" key ": " value "\n")))))
|
||||
|
||||
(defun org-roam-erase-keyword (keyword)
|
||||
"Erase the line where the KEYWORD is, setting line from the top of the file."
|
||||
(let ((case-fold-search t))
|
||||
(org-with-point-at 1
|
||||
(when (re-search-forward (concat "^#\\+" keyword ":") nil t)
|
||||
(beginning-of-line)
|
||||
(delete-region (point) (line-end-position))
|
||||
(delete-char 1)))))
|
||||
|
||||
;;;; Properties
|
||||
(defun org-roam-add-property (val prop)
|
||||
"Add VAL value to PROP property for the node at point.
|
||||
Both, VAL and PROP are strings."
|
||||
(org-roam-property-add prop val))
|
||||
|
||||
(defun org-roam-remove-property (prop &optional val)
|
||||
"Remove VAL value from PROP property for the node at point.
|
||||
Both VAL and PROP are strings.
|
||||
|
||||
If VAL is not specified, user is prompted to select a value."
|
||||
(org-roam-property-remove prop val))
|
||||
|
||||
(defun org-roam-property-add (prop val)
|
||||
"Add VAL value to PROP property for the node at point.
|
||||
Both, VAL and PROP are strings."
|
||||
(let* ((p (org-entry-get (point) prop))
|
||||
(lst (when p (split-string-and-unquote p)))
|
||||
(lst (if (memq val lst) lst (cons val lst)))
|
||||
(lst (seq-uniq lst)))
|
||||
(org-set-property prop (combine-and-quote-strings lst))
|
||||
val))
|
||||
|
||||
(defun org-roam-property-remove (prop &optional val)
|
||||
"Remove VAL value from PROP property for the node at point.
|
||||
Both VAL and PROP are strings.
|
||||
|
||||
If VAL is not specified, user is prompted to select a value."
|
||||
(let* ((p (org-entry-get (point) prop))
|
||||
(lst (when p (split-string-and-unquote p)))
|
||||
(prop-to-remove (or val (completing-read "Remove: " lst)))
|
||||
(lst (delete prop-to-remove lst)))
|
||||
(if lst
|
||||
(org-set-property prop (combine-and-quote-strings lst))
|
||||
(org-delete-property prop))
|
||||
prop-to-remove))
|
||||
|
||||
;;; Refs
|
||||
(defun org-roam-org-ref-path-to-keys (path)
|
||||
"Return a list of keys given an org-ref cite: PATH.
|
||||
Accounts for both v2 and v3."
|
||||
(cond ((fboundp 'org-ref-parse-cite-path)
|
||||
(mapcar (lambda (cite) (plist-get cite :key))
|
||||
(plist-get (org-ref-parse-cite-path path) :references)))
|
||||
((fboundp 'org-ref-split-and-strip-string)
|
||||
(org-ref-split-and-strip-string path))))
|
||||
|
||||
;;; Logs
|
||||
(defvar org-roam-verbose)
|
||||
(defun org-roam-message (format-string &rest args)
|
||||
"Pass FORMAT-STRING and ARGS to `message' when `org-roam-verbose' is t."
|
||||
(when org-roam-verbose
|
||||
(apply #'message `(,(concat "(org-roam) " format-string) ,@args))))
|
||||
|
||||
;;; Diagnostics
|
||||
;; TODO Update this to also get commit hash
|
||||
;;;###autoload
|
||||
(defun org-roam-version (&optional message)
|
||||
"Return `org-roam' version.
|
||||
Interactively, or when MESSAGE is non-nil, show in the echo area."
|
||||
(interactive)
|
||||
(let* ((toplib (or load-file-name buffer-file-name))
|
||||
gitdir topdir version)
|
||||
(unless (and toplib (equal (file-name-nondirectory toplib) "org-roam-utils.el"))
|
||||
(setq toplib (locate-library "org-roam-utils.el")))
|
||||
(setq toplib (and toplib (org-roam--straight-chase-links toplib)))
|
||||
(when toplib
|
||||
(setq topdir (file-name-directory toplib)
|
||||
gitdir (expand-file-name ".git" topdir)))
|
||||
(when (file-exists-p gitdir)
|
||||
(setq version
|
||||
(let ((default-directory topdir))
|
||||
(shell-command-to-string "git describe --tags --dirty --always"))))
|
||||
(unless version
|
||||
(setq version (with-temp-buffer
|
||||
(insert-file-contents-literally (locate-library "org-roam.el"))
|
||||
(goto-char (point-min))
|
||||
(save-match-data
|
||||
(if (re-search-forward "\\(?:;; Version: \\([^z-a]*?$\\)\\)" nil nil)
|
||||
(substring-no-properties (match-string 1))
|
||||
"N/A")))))
|
||||
(if (or message (called-interactively-p 'interactive))
|
||||
(message "%s" version)
|
||||
version)))
|
||||
|
||||
(defun org-roam--straight-chase-links (filename)
|
||||
"Chase links in FILENAME until a name that is not a link.
|
||||
|
||||
This is the same as `file-chase-links', except that it also
|
||||
handles fake symlinks that are created by the package manager
|
||||
straight.el on Windows.
|
||||
|
||||
See <https://github.com/raxod502/straight.el/issues/520>."
|
||||
(when (and (bound-and-true-p straight-symlink-emulation-mode)
|
||||
(fboundp 'straight-chase-emulated-symlink))
|
||||
(when-let ((target (straight-chase-emulated-symlink filename)))
|
||||
(unless (eq target 'broken)
|
||||
(setq filename target))))
|
||||
(file-chase-links filename))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-diagnostics ()
|
||||
"Collect and print info for `org-roam' issues."
|
||||
(interactive)
|
||||
(with-current-buffer (switch-to-buffer-other-window (get-buffer-create "*org-roam diagnostics*"))
|
||||
(erase-buffer)
|
||||
(insert (propertize "Copy info below this line into issue:\n" 'face '(:weight bold)))
|
||||
(insert (format "- Emacs: %s\n" (emacs-version)))
|
||||
(insert (format "- Framework: %s\n"
|
||||
(condition-case _
|
||||
(completing-read "I'm using the following Emacs framework:"
|
||||
'("Doom" "Spacemacs" "N/A" "I don't know"))
|
||||
(quit "N/A"))))
|
||||
(insert (format "- Org: %s\n" (org-version nil 'full)))
|
||||
(insert (format "- Org-roam: %s" (org-roam-version)))
|
||||
(insert (format "- sqlite-connector: %s"
|
||||
(if-let ((conn (org-roam-db--get-connection)))
|
||||
(eieio-object-class conn)
|
||||
"not connected")))))
|
||||
|
||||
(provide 'org-roam-utils)
|
||||
;;; org-roam-utils.el ends here
|
2036
org-roam.el
2036
org-roam.el
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@
|
||||
#+roam_alias: "a1" "a 2"
|
||||
#+title: t1
|
@ -1,3 +1,6 @@
|
||||
:PROPERTIES:
|
||||
:ID: 440795d0-70c1-4165-993d-aebd5eef7a24
|
||||
:END:
|
||||
#+title: Bar
|
||||
|
||||
This is file bar. Bar links to [[file:nested/bar.org][Nested Bar]].
|
||||
[[id:884b2341-b7fe-434d-848c-5282c0727861][Foo]]
|
||||
|
@ -1 +0,0 @@
|
||||
#+title: Base
|
@ -1 +0,0 @@
|
||||
#+roam_key: cite:mitsuha2007
|
8
tests/roam-files/demoteable.org
Normal file
8
tests/roam-files/demoteable.org
Normal file
@ -0,0 +1,8 @@
|
||||
:PROPERTIES:
|
||||
:ID: 97bf31cf-dfee-45d8-87a5-2ae0dabc4734
|
||||
:END:
|
||||
#+title: Demoteable
|
||||
|
||||
* Demoteable h1
|
||||
|
||||
** Demoteable child
|
19
tests/roam-files/family.org
Normal file
19
tests/roam-files/family.org
Normal file
@ -0,0 +1,19 @@
|
||||
:PROPERTIES:
|
||||
:ID: 998b2341-b7fe-434d-848c-5282c0727870
|
||||
:END:
|
||||
#+title: Family
|
||||
|
||||
* Grand-Parent
|
||||
:PROPERTIES:
|
||||
:ID: 77a90980-1994-464e-901f-7e3d3df07fd3
|
||||
:END:
|
||||
|
||||
** Parent
|
||||
:PROPERTIES:
|
||||
:ID: 0fa5bb3e-3d8c-4966-8bc9-78d32e505d69
|
||||
:END:
|
||||
|
||||
*** Child
|
||||
:PROPERTIES:
|
||||
:ID: 5fb4fdc5-b6d2-4f75-8d54-e60053e467ec
|
||||
:END:
|
@ -1,8 +1,4 @@
|
||||
:PROPERTIES:
|
||||
:ID: 884b2341-b7fe-434d-848c-5282c0727861
|
||||
:END:
|
||||
#+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]]
|
||||
|
@ -1,14 +0,0 @@
|
||||
#+TITLE: Headline
|
||||
|
||||
* Headline 1
|
||||
:PROPERTIES:
|
||||
:ID: e84d0630-efad-4017-9059-5ef917908823
|
||||
:END:
|
||||
|
||||
* No headline here
|
||||
Oops.
|
||||
|
||||
* Headline 2
|
||||
:PROPERTIES:
|
||||
:ID: 801b58eb-97e2-435f-a33e-ff59a2f0c213
|
||||
:END:
|
0
tests/roam-files/markdown.md
Normal file
0
tests/roam-files/markdown.md
Normal file
@ -1,2 +0,0 @@
|
||||
#+roam_key: https://www.orgroam.com/
|
||||
#+roam_key: cite:orgroam2020
|
@ -1,3 +0,0 @@
|
||||
#+title: Nested Bar
|
||||
|
||||
This file is nested, 1 level deeper. It links to both [[file:../foo.org][Foo]] and [[file:foo.org][Nested Foo]].
|
@ -1 +0,0 @@
|
||||
#+title: Deeply Nested File
|
@ -1,3 +0,0 @@
|
||||
#+title: Nested Foo
|
||||
|
||||
This file has no links.
|
@ -1,5 +0,0 @@
|
||||
no title in this file :O
|
||||
|
||||
links to itself, with no title: [[file:no-title.org][no-title]]
|
||||
|
||||
* Headline title
|
3
tests/roam-files/promoteable.org
Normal file
3
tests/roam-files/promoteable.org
Normal file
@ -0,0 +1,3 @@
|
||||
* Promoteable h1
|
||||
|
||||
** Promoteable child
|
7
tests/roam-files/ref_with_space.org
Normal file
7
tests/roam-files/ref_with_space.org
Normal file
@ -0,0 +1,7 @@
|
||||
:PROPERTIES:
|
||||
:ROAM_REFS: "http://site.net/docs/01. introduction - hello world.html"
|
||||
:ID: 5b9a7400-f59c-4ef9-acbb-045b69af98f1
|
||||
:END:
|
||||
#+title: ref with space
|
||||
* Note
|
||||
hello
|
7
tests/roam-files/roam-exclude.org
Normal file
7
tests/roam-files/roam-exclude.org
Normal file
@ -0,0 +1,7 @@
|
||||
:PROPERTIES:
|
||||
:ID: 53fadc75-f48e-461e-be06-44a1e88b2abe
|
||||
:ROAM_EXCLUDE: t
|
||||
:END:
|
||||
#+TITLE: Excluded by Org-roam
|
||||
|
||||
This node is excluded by declaring ~ROAM_EXCLUDE: t~.
|
@ -1,3 +0,0 @@
|
||||
#+title: Tagless File
|
||||
|
||||
This file has no tags, and should not yield any tags on extracting via ~#+roam_tags~.
|
@ -1,4 +0,0 @@
|
||||
#+roam_tags: "t1" "t2 with space" t3
|
||||
#+title: Tags
|
||||
|
||||
This file is used to test functionality for =(org-roam--extract-tags)=
|
@ -1 +0,0 @@
|
||||
#+roam_alias: "roam" "alias"
|
@ -1,4 +0,0 @@
|
||||
#+title: TITLE PROP
|
||||
#+roam_alias: "roam" "alias"
|
||||
|
||||
* Headline
|
@ -1 +0,0 @@
|
||||
* Headline
|
@ -1 +0,0 @@
|
||||
#+title: Title
|
@ -1,3 +0,0 @@
|
||||
#+title: Unlinked
|
||||
|
||||
Nothing links here :(
|
@ -1 +0,0 @@
|
||||
#+roam_key: https://google.com/
|
5
tests/roam-files/with-alias.org
Normal file
5
tests/roam-files/with-alias.org
Normal file
@ -0,0 +1,5 @@
|
||||
:PROPERTIES:
|
||||
:ID: 57ff3ce7-5bda-4825-8fca-c09f523e87ba
|
||||
:ROAM_ALIASES: Batman
|
||||
:END:
|
||||
#+title: Bruce Wayne
|
16
tests/roam-files/with-times.org
Normal file
16
tests/roam-files/with-times.org
Normal file
@ -0,0 +1,16 @@
|
||||
:PROPERTIES:
|
||||
:ID: 9a20ca6c-5555-41c9-a039-ac38bf59c7a9
|
||||
:END:
|
||||
#+title: With Times
|
||||
|
||||
* Scheduled heading
|
||||
SCHEDULED: <2024-07-16 Tue>
|
||||
:PROPERTIES:
|
||||
:ID: a523c198-4cb4-44d2-909c-a0e3258089cd
|
||||
:END:
|
||||
|
||||
* Deadline heading
|
||||
DEADLINE: <2024-07-17 Tue>
|
||||
:PROPERTIES:
|
||||
:ID: 3ab84701-d1c1-463f-b5c6-715e6ff5a0bf
|
||||
:END:
|
63
tests/test-org-roam-capture.el
Normal file
63
tests/test-org-roam-capture.el
Normal file
@ -0,0 +1,63 @@
|
||||
;;; test-org-roam-capture.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(describe "org-roam-capture--fill-template"
|
||||
(it "fills template without newline"
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo")
|
||||
:to-equal "foo"))
|
||||
|
||||
(it "fills template ensuring newline"
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo" 'ensure-newline)
|
||||
:to-equal "foo\n"))
|
||||
|
||||
(it "fills template with newline"
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo\n")
|
||||
:to-equal "foo\n"))
|
||||
|
||||
(it "fills template with two newlines"
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo\n\n")
|
||||
:to-equal "foo\n\n")
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo\n\t\n")
|
||||
:to-equal "foo\n\t\n"))
|
||||
|
||||
(it "fills template without deleting newlines in its body"
|
||||
(expect
|
||||
(org-roam-capture--fill-template "foo\n\n\nbar\n\n")
|
||||
:to-equal "foo\n\n\nbar\n\n"))
|
||||
|
||||
(it "expands templates when it's a function"
|
||||
(expect
|
||||
(org-roam-capture--fill-template (lambda () "foo"))
|
||||
:to-equal "foo")))
|
||||
|
||||
(provide 'test-org-roam-capture)
|
||||
|
||||
;;; test-org-roam-capture.el ends here
|
120
tests/test-org-roam-db.el
Normal file
120
tests/test-org-roam-db.el
Normal file
@ -0,0 +1,120 @@
|
||||
;;; test-org-roam-db.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(defvar root-directory default-directory)
|
||||
|
||||
(describe "org-roam-db-get-scheduled-time"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location)
|
||||
(cd root-directory))
|
||||
|
||||
(it "should get scheduled time for current heading node"
|
||||
(org-roam-id-open "a523c198-4cb4-44d2-909c-a0e3258089cd" nil)
|
||||
(expect (org-roam-db-get-scheduled-time) :to-equal "2024-07-16T00:00:00")))
|
||||
|
||||
(describe "org-roam-db-get-deadline-time"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location)
|
||||
(cd root-directory))
|
||||
|
||||
(it "should get deadline time for current heading node"
|
||||
(org-roam-id-open "3ab84701-d1c1-463f-b5c6-715e6ff5a0bf" nil)
|
||||
(expect (org-roam-db-get-deadline-time) :to-equal "2024-07-17T00:00:00")))
|
||||
|
||||
(describe "org-roam-db--file-hash"
|
||||
(it "computes the SHA1 of file"
|
||||
(expect (org-roam-db--file-hash "tests/roam-files/family.org")
|
||||
:to-equal
|
||||
"c4ebf8918c1533df72e4d182cbf1bbd90f776b3b")))
|
||||
|
||||
(describe "org-roam-db-sync"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "has the correct number of files"
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from files]))
|
||||
:to-equal
|
||||
9))
|
||||
|
||||
(it "has the correct number of nodes"
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from nodes]))
|
||||
:to-equal
|
||||
12))
|
||||
|
||||
(it "has the correct number of links"
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links]))
|
||||
:to-equal
|
||||
1))
|
||||
|
||||
(it "respects ROAM_EXCLUDE"
|
||||
;; The excluded node has ID "53fadc75-f48e-461e-be06-44a1e88b2abe"
|
||||
(expect (mapcar #'car (org-roam-db-query [:select id :from nodes]))
|
||||
:to-have-same-items-as
|
||||
'("884b2341-b7fe-434d-848c-5282c0727861"
|
||||
"440795d0-70c1-4165-993d-aebd5eef7a24"
|
||||
"5b9a7400-f59c-4ef9-acbb-045b69af98f1"
|
||||
"0fa5bb3e-3d8c-4966-8bc9-78d32e505d69"
|
||||
"5fb4fdc5-b6d2-4f75-8d54-e60053e467ec"
|
||||
"77a90980-1994-464e-901f-7e3d3df07fd3"
|
||||
"57ff3ce7-5bda-4825-8fca-c09f523e87ba"
|
||||
"998b2341-b7fe-434d-848c-5282c0727870"
|
||||
"a523c198-4cb4-44d2-909c-a0e3258089cd"
|
||||
"3ab84701-d1c1-463f-b5c6-715e6ff5a0bf"
|
||||
"9a20ca6c-5555-41c9-a039-ac38bf59c7a9"
|
||||
"97bf31cf-dfee-45d8-87a5-2ae0dabc4734")))
|
||||
|
||||
(it "reads ref in quotes correctly"
|
||||
(expect (mapcar #'car (org-roam-db-query [:select [ref] :from refs]))
|
||||
:to-have-same-items-as
|
||||
'("//site.net/docs/01. introduction - hello world.html"))))
|
||||
|
||||
(provide 'test-org-roam-db)
|
||||
|
||||
;;; test-org-roam-db.el ends here
|
79
tests/test-org-roam-id.el
Normal file
79
tests/test-org-roam-id.el
Normal file
@ -0,0 +1,79 @@
|
||||
;;; test-org-roam-id.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(defvar root-directory default-directory)
|
||||
|
||||
(describe "org-roam-id-at-point"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location)
|
||||
(cd root-directory))
|
||||
|
||||
(it "returns the correct node ids"
|
||||
(find-file "tests/roam-files/family.org" nil)
|
||||
(expect (org-roam-id-at-point) :to-equal "998b2341-b7fe-434d-848c-5282c0727870")
|
||||
|
||||
(search-forward "Grand")
|
||||
(expect (org-roam-id-at-point) :to-equal "77a90980-1994-464e-901f-7e3d3df07fd3")
|
||||
|
||||
(search-forward "Child")
|
||||
(expect (org-roam-id-at-point) :to-equal "5fb4fdc5-b6d2-4f75-8d54-e60053e467ec")))
|
||||
|
||||
(describe "org-roam-id-find"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "finds nothing for non-existing node"
|
||||
(expect (org-roam-id-find "non-existing") :to-equal nil))
|
||||
|
||||
(it "finds the correct file node"
|
||||
(let ((location (org-roam-id-find "884b2341-b7fe-434d-848c-5282c0727861")))
|
||||
(expect (car location) :to-match ".*/tests/roam-files/foo.org")
|
||||
(expect (cdr location) :to-equal 1)))
|
||||
|
||||
(it "finds the correct heading node"
|
||||
(let ((location (org-roam-id-find "0fa5bb3e-3d8c-4966-8bc9-78d32e505d69")))
|
||||
(expect (car location) :to-match ".*/tests/roam-files/family.org")
|
||||
(expect (cdr location) :to-equal 156))))
|
||||
|
||||
(provide 'test-org-roam-id)
|
||||
|
||||
;;; test-org-roam-id.el ends here
|
39
tests/test-org-roam-mode.el
Normal file
39
tests/test-org-roam-mode.el
Normal file
@ -0,0 +1,39 @@
|
||||
;;; test-org-roam-mode.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(describe "org-roam-unlinked-references--rg-command"
|
||||
(before-each
|
||||
;; the space in the directory is on purpose
|
||||
(setq org-roam-directory "/tmp/org roam"))
|
||||
|
||||
(it "returns the correct rg command for unlinked references"
|
||||
(expect (org-roam-unlinked-references--rg-command '("foo" "bar"))
|
||||
:to-equal
|
||||
"rg --follow --only-matching --vimgrep --pcre2 --ignore-case --glob \"*.org\" --glob \"*.org.gpg\" --glob \"*.org.age\" '\\[([^[]]++|(?R))*\\]|(\\bfoo\\b)|(\\bbar\\b)' /tmp/org\\ roam")))
|
||||
|
||||
(provide 'test-org-roam-mode)
|
||||
|
||||
;;; test-org-roam-mode.el ends here
|
164
tests/test-org-roam-node.el
Normal file
164
tests/test-org-roam-node.el
Normal file
@ -0,0 +1,164 @@
|
||||
;;; test-org-roam-node.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(defvar root-directory default-directory)
|
||||
|
||||
(describe "org-roam-node-from-id"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "returns nil for unknown id"
|
||||
(expect (org-roam-node-from-id "non-existing") :to-equal nil))
|
||||
|
||||
(it "returns correct node from id"
|
||||
(let ((node (org-roam-node-from-id "884b2341-b7fe-434d-848c-5282c0727861")))
|
||||
(expect (org-roam-node-title node) :to-equal "Foo"))))
|
||||
|
||||
(describe "org-roam-node-from-title-or-alias"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "returns nil for unknown title"
|
||||
(expect (org-roam-node-from-title-or-alias "non-existing") :to-equal nil))
|
||||
|
||||
(it "returns correct node from title"
|
||||
(let ((node (org-roam-node-from-title-or-alias "Foo")))
|
||||
(expect (org-roam-node-title node) :to-equal "Foo")))
|
||||
|
||||
(it "returns correct node from alias"
|
||||
(let ((node (org-roam-node-from-title-or-alias "Batman")))
|
||||
(expect (org-roam-node-title node) :to-equal "Bruce Wayne")))
|
||||
|
||||
(it "returns correct node from alias with nocase"
|
||||
(let ((node (org-roam-node-from-title-or-alias "batman" t)))
|
||||
(expect (org-roam-node-title node) :to-equal "Bruce Wayne"))))
|
||||
|
||||
(describe "org-roam-demote-entire-buffer"
|
||||
(after-each
|
||||
(cd root-directory))
|
||||
|
||||
(it "demotes an entire org buffer"
|
||||
(find-file "tests/roam-files/demoteable.org" nil)
|
||||
(org-roam-demote-entire-buffer)
|
||||
(expect (buffer-substring-no-properties (point) (point-max))
|
||||
:to-equal "* Demoteable\n:PROPERTIES:\n:ID: 97bf31cf-dfee-45d8-87a5-2ae0dabc4734\n:END:\n\n** Demoteable h1\n\n*** Demoteable child\n")))
|
||||
|
||||
(describe "org-roam--h1-count"
|
||||
(after-each
|
||||
(cd root-directory))
|
||||
|
||||
(it "returns the correct number of level-1 headings"
|
||||
(find-file "tests/roam-files/foo.org" nil)
|
||||
(expect (org-roam--h1-count) :to-equal 0)
|
||||
|
||||
(cd root-directory)
|
||||
|
||||
(find-file "tests/roam-files/family.org" nil)
|
||||
(expect (org-roam--h1-count) :to-equal 1)))
|
||||
|
||||
(describe "org-roam--buffer-promoteable-p"
|
||||
(after-each
|
||||
(cd root-directory))
|
||||
|
||||
(it "should check if buffer is promoteable"
|
||||
(find-file "tests/roam-files/foo.org" nil)
|
||||
(expect (org-roam--buffer-promoteable-p) :to-equal nil)
|
||||
|
||||
(cd root-directory)
|
||||
|
||||
(find-file "tests/roam-files/family.org" nil)
|
||||
(expect (org-roam--buffer-promoteable-p) :to-equal nil)
|
||||
|
||||
(cd root-directory)
|
||||
|
||||
(find-file "tests/roam-files/promoteable.org" nil)
|
||||
(expect (org-roam--buffer-promoteable-p) :to-equal t)))
|
||||
|
||||
(describe "org-roam--get-titles"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "returns the list of titles and aliases"
|
||||
(expect (org-roam--get-titles)
|
||||
:to-have-same-items-as
|
||||
`("Bar" "Batman" "Bruce Wayne" "Child" "Deadline heading" "Demoteable" "Family"
|
||||
"Foo" "Grand-Parent" "Parent" "ref with space" "Scheduled heading" "With Times"))))
|
||||
|
||||
|
||||
(describe "org-roam-alias"
|
||||
(before-all
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(org-roam-db-sync))
|
||||
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location)
|
||||
(cd root-directory))
|
||||
|
||||
(it "adds an alias to a node"
|
||||
(cd root-directory)
|
||||
(find-file "tests/roam-files/foo.org" nil)
|
||||
(org-roam-alias-add "qux")
|
||||
(expect (buffer-substring-no-properties (point) (point-max))
|
||||
:to-equal ":PROPERTIES:\n:ID: 884b2341-b7fe-434d-848c-5282c0727861\n:ROAM_ALIASES: qux\n:END:\n#+title: Foo\n"))
|
||||
|
||||
(it "removes an alias from a node"
|
||||
(cd root-directory)
|
||||
(find-file "tests/roam-files/with-alias.org" nil)
|
||||
(org-roam-alias-remove "Batman")
|
||||
(expect (buffer-substring-no-properties (point) (point-max))
|
||||
:to-equal ":PROPERTIES:\n:ID: 57ff3ce7-5bda-4825-8fca-c09f523e87ba\n:END:\n#+title: Bruce Wayne\n")))
|
||||
|
||||
(provide 'test-org-roam-node)
|
||||
|
||||
;;; test-org-roam-node.el ends here
|
@ -1,53 +0,0 @@
|
||||
;;; test-org-roam-perf.el --- Performance Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(defconst test-org-roam-perf-zip-url "https://github.com/org-roam/test-org-files/archive/master.zip"
|
||||
"Path to zip for test org-roam files.")
|
||||
|
||||
(defun test-org-roam-perf--abs-path (file-path)
|
||||
"Get absolute FILE-PATH from `org-roam-directory'."
|
||||
(expand-file-name file-path org-roam-directory))
|
||||
|
||||
(defun test-org-roam-perf--init ()
|
||||
"."
|
||||
(let* ((temp-loc (expand-file-name (make-temp-name "test-org-files-") temporary-file-directory))
|
||||
(zip-file-loc (concat temp-loc ".zip"))
|
||||
(_ (url-copy-file test-org-roam-perf-zip-url zip-file-loc))
|
||||
(_ (shell-command (format "mkdir -p %s && unzip -j -qq %s -d %s" temp-loc zip-file-loc temp-loc))))
|
||||
(setq org-roam-directory temp-loc)))
|
||||
|
||||
(describe "Cache Build"
|
||||
(it "cache build from scratch time to be acceptable"
|
||||
(test-org-roam-perf--init)
|
||||
(pcase (benchmark-run 1 (org-roam-db-build-cache t))
|
||||
(`(,time ,gcs ,time-in-gc)
|
||||
(message "Elapsed time: %fs (%fs in %d GCs)" time time-in-gc gcs)
|
||||
(expect time :to-be-less-than 110))))
|
||||
(it "builds quickly without change"
|
||||
(pcase (benchmark-run 1 (org-roam-db-build-cache))
|
||||
(`(,time ,gcs ,time-in-gc)
|
||||
(message "Elapsed time: %fs (%fs in %d GCs)" time time-in-gc gcs)
|
||||
(expect time :to-be-less-than 5)))))
|
64
tests/test-org-roam-utils.el
Normal file
64
tests/test-org-roam-utils.el
Normal file
@ -0,0 +1,64 @@
|
||||
;;; test-org-roam-utils.el --- Tests for Org-roam -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Jethro Kuan
|
||||
|
||||
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||
;; Package-Requires: ((buttercup))
|
||||
|
||||
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
|
||||
(describe "org-roam-whitespace-content"
|
||||
(it "extracts whitespace correctly"
|
||||
(expect
|
||||
(org-roam-whitespace-content "foo")
|
||||
:to-equal "")
|
||||
(expect
|
||||
(org-roam-whitespace-content "foo\n")
|
||||
:to-equal "\n")
|
||||
(expect
|
||||
(org-roam-whitespace-content "foo\n\t\n")
|
||||
:to-equal "\n\t\n")))
|
||||
|
||||
(describe "org-roam-db--file-title"
|
||||
(it "supports normal titles"
|
||||
(expect
|
||||
(with-temp-buffer
|
||||
(insert "#+title:normal title")
|
||||
(org-roam-db--file-title))
|
||||
:to-equal "normal title"))
|
||||
(it "supports multi-line titles"
|
||||
(expect
|
||||
(with-temp-buffer
|
||||
(insert "#+title: title:\n#+title: separated by newline")
|
||||
(org-roam-db--file-title))
|
||||
:to-equal "title: separated by newline"))
|
||||
(it "supports file-name based titles"
|
||||
(progn
|
||||
(setq org-roam-directory temporary-file-directory
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org"))
|
||||
(with-temp-buffer
|
||||
(write-file (expand-file-name "test file.org" org-roam-directory))
|
||||
(org-roam-db--file-title)))
|
||||
:to-equal "test file"))
|
||||
|
||||
(provide 'test-org-roam-utils)
|
||||
|
||||
;;; test-org-roam-utils.el ends here
|
@ -23,341 +23,59 @@
|
||||
|
||||
(require 'buttercup)
|
||||
(require 'org-roam)
|
||||
(require 'dash)
|
||||
|
||||
(defun test-org-roam--abs-path (file-path)
|
||||
"Get absolute FILE-PATH from `org-roam-directory'."
|
||||
(expand-file-name file-path org-roam-directory))
|
||||
(defvar root-directory default-directory)
|
||||
|
||||
(defun test-org-roam--find-file (path)
|
||||
"PATH."
|
||||
(let ((path (test-org-roam--abs-path path)))
|
||||
(make-directory (file-name-directory path) t)
|
||||
(find-file path)))
|
||||
(describe "org-roam-file-p"
|
||||
(it "checks if given file respects criteria"
|
||||
(setq org-roam-directory "/non-existent")
|
||||
(expect (org-roam-file-p "tests/roam-files/family.org") :to-equal nil)
|
||||
|
||||
(defvar test-org-roam-directory (expand-file-name "tests/roam-files")
|
||||
"Directory containing org-roam test org files.")
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files"))
|
||||
(expect (org-roam-file-p "tests/roam-files/family.org") :to-equal t)
|
||||
(expect (org-roam-file-p "tests/roam-files/markdown.md") :to-equal nil)
|
||||
|
||||
(defun test-org-roam--init ()
|
||||
"."
|
||||
(let ((original-dir test-org-roam-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)
|
||||
(sleep-for 2)))
|
||||
(setq org-roam-file-exclude-regexp (regexp-quote "family.org"))
|
||||
(expect (org-roam-file-p "tests/roam-files/family.org") :to-equal nil)))
|
||||
|
||||
(defun test-org-roam--teardown ()
|
||||
(org-roam-mode -1)
|
||||
(delete-file org-roam-db-location)
|
||||
(org-roam-db--close))
|
||||
(describe "org-roam-buffer-p"
|
||||
(it "checks if current buffer respects criteria"
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-file-exclude-regexp nil)
|
||||
(find-file "tests/roam-files/family.org" nil)
|
||||
(expect (org-roam-buffer-p) :to-equal t)
|
||||
(cd root-directory)))
|
||||
|
||||
(describe "Ref extraction"
|
||||
(before-all
|
||||
(test-org-roam--init))
|
||||
|
||||
(after-all
|
||||
(test-org-roam--teardown))
|
||||
|
||||
(cl-flet
|
||||
((test (fn file)
|
||||
(let* ((fname (test-org-roam--abs-path file))
|
||||
(buf (find-file-noselect fname)))
|
||||
(with-current-buffer buf
|
||||
;; Unlike tag extraction, it doesn't make sense to
|
||||
;; pass a filename.
|
||||
(funcall fn)))))
|
||||
;; Enable "cite:" link parsing
|
||||
(org-link-set-parameters "cite")
|
||||
(it "extracts web keys"
|
||||
(expect (test #'org-roam--extract-ref
|
||||
"web_ref.org")
|
||||
:to-equal
|
||||
'("website" . "//google.com/")))
|
||||
(it "extracts cite keys"
|
||||
(expect (test #'org-roam--extract-ref
|
||||
"cite_ref.org")
|
||||
:to-equal
|
||||
'("cite" . "mitsuha2007")))
|
||||
(it "extracts all keys"
|
||||
(expect (test #'org-roam--extract-refs
|
||||
"multiple-refs.org")
|
||||
:to-have-same-items-as
|
||||
'(("cite" . "orgroam2020")
|
||||
("website" . "//www.orgroam.com/"))))))
|
||||
|
||||
(describe "Title extraction"
|
||||
:var (org-roam-title-sources)
|
||||
(before-all
|
||||
(test-org-roam--init))
|
||||
|
||||
(after-all
|
||||
(test-org-roam--teardown))
|
||||
|
||||
(cl-flet
|
||||
((test (fn file)
|
||||
(let ((buf (find-file-noselect
|
||||
(test-org-roam--abs-path file))))
|
||||
(with-current-buffer buf
|
||||
(funcall fn)))))
|
||||
(it "extracts title from title property"
|
||||
(expect (test #'org-roam--extract-titles-title
|
||||
"titles/title.org")
|
||||
:to-equal
|
||||
'("Title"))
|
||||
(expect (test #'org-roam--extract-titles-title
|
||||
"titles/aliases.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-title
|
||||
"titles/headline.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-title
|
||||
"titles/combination.org")
|
||||
:to-equal
|
||||
'("TITLE PROP")))
|
||||
|
||||
(it "extracts alias"
|
||||
(expect (test #'org-roam--extract-titles-alias
|
||||
"titles/title.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-alias
|
||||
"titles/aliases.org")
|
||||
:to-equal
|
||||
'("roam" "alias"))
|
||||
(expect (test #'org-roam--extract-titles-alias
|
||||
"titles/headline.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-alias
|
||||
"titles/combination.org")
|
||||
:to-equal
|
||||
'("roam" "alias")))
|
||||
|
||||
(it "extracts headlines"
|
||||
(expect (test #'org-roam--extract-titles-alias
|
||||
"titles/title.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-headline
|
||||
"titles/aliases.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-titles-headline
|
||||
"titles/headline.org")
|
||||
:to-equal
|
||||
'("Headline"))
|
||||
(expect (test #'org-roam--extract-titles-headline
|
||||
"titles/combination.org")
|
||||
:to-equal
|
||||
'("Headline")))
|
||||
|
||||
(describe "uses org-roam-title-sources correctly"
|
||||
(it "'((title headline) alias)"
|
||||
(expect (let ((org-roam-title-sources '((title headline) alias)))
|
||||
(test #'org-roam--extract-titles
|
||||
"titles/combination.org"))
|
||||
:to-equal
|
||||
'("TITLE PROP" "roam" "alias")))
|
||||
(it "'((headline title) alias)"
|
||||
(expect (let ((org-roam-title-sources '((headline title) alias)))
|
||||
(test #'org-roam--extract-titles
|
||||
"titles/combination.org"))
|
||||
:to-equal
|
||||
'("Headline" "roam" "alias")))
|
||||
(it "'(headline alias title)"
|
||||
(expect (let ((org-roam-title-sources '(headline alias title)))
|
||||
(test #'org-roam--extract-titles
|
||||
"titles/combination.org"))
|
||||
:to-equal
|
||||
'("Headline" "roam" "alias" "TITLE PROP"))))))
|
||||
|
||||
(describe "Tag extraction"
|
||||
:var (org-roam-tag-sources)
|
||||
(before-all
|
||||
(test-org-roam--init))
|
||||
|
||||
(after-all
|
||||
(test-org-roam--teardown))
|
||||
|
||||
(cl-flet
|
||||
((test (fn file)
|
||||
(let* ((fname (test-org-roam--abs-path file))
|
||||
(buf (find-file-noselect fname)))
|
||||
(with-current-buffer buf
|
||||
(funcall fn fname)))))
|
||||
(it "extracts from prop"
|
||||
(expect (test #'org-roam--extract-tags-prop
|
||||
"tags/tag.org")
|
||||
:to-equal
|
||||
'("t1" "t2 with space" "t3"))
|
||||
(expect (test #'org-roam--extract-tags-prop
|
||||
"tags/no_tag.org")
|
||||
:to-equal
|
||||
nil))
|
||||
|
||||
(it "extracts from all directories"
|
||||
(expect (test #'org-roam--extract-tags-all-directories
|
||||
"base.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-tags-all-directories
|
||||
"tags/tag.org")
|
||||
:to-equal
|
||||
'("tags"))
|
||||
(expect (test #'org-roam--extract-tags-all-directories
|
||||
"nested/deeply/deeply_nested_file.org")
|
||||
:to-equal
|
||||
'("nested" "deeply")))
|
||||
|
||||
(it "extracts from last directory"
|
||||
(expect (test #'org-roam--extract-tags-last-directory
|
||||
"base.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-tags-last-directory
|
||||
"tags/tag.org")
|
||||
:to-equal
|
||||
'("tags"))
|
||||
(expect (test #'org-roam--extract-tags-last-directory
|
||||
"nested/deeply/deeply_nested_file.org")
|
||||
:to-equal
|
||||
'("deeply")))
|
||||
|
||||
(it "extracts from first directory"
|
||||
(expect (test #'org-roam--extract-tags-first-directory
|
||||
"base.org")
|
||||
:to-equal
|
||||
nil)
|
||||
(expect (test #'org-roam--extract-tags-first-directory
|
||||
"tags/tag.org")
|
||||
:to-equal
|
||||
'("tags"))
|
||||
(expect (test #'org-roam--extract-tags-first-directory
|
||||
"nested/deeply/deeply_nested_file.org")
|
||||
:to-equal
|
||||
'("nested")))
|
||||
|
||||
(describe "uses org-roam-tag-sources correctly"
|
||||
(it "'(prop)"
|
||||
(expect (let ((org-roam-tag-sources '(prop)))
|
||||
(test #'org-roam--extract-tags
|
||||
"tags/tag.org"))
|
||||
:to-equal
|
||||
'("t1" "t2 with space" "t3")))
|
||||
(it "'(prop all-directories)"
|
||||
(expect (let ((org-roam-tag-sources '(prop all-directories)))
|
||||
(test #'org-roam--extract-tags
|
||||
"tags/tag.org"))
|
||||
:to-equal
|
||||
'("t1" "t2 with space" "t3" "tags"))))))
|
||||
|
||||
(describe "ID extraction"
|
||||
(before-all
|
||||
(test-org-roam--init))
|
||||
|
||||
(after-all
|
||||
(test-org-roam--teardown))
|
||||
|
||||
(cl-flet
|
||||
((test (fn file)
|
||||
(let* ((fname (test-org-roam--abs-path file))
|
||||
(buf (find-file-noselect fname)))
|
||||
(with-current-buffer buf
|
||||
(funcall fn fname)))))
|
||||
(it "extracts ids"
|
||||
(expect (test #'org-roam--extract-ids
|
||||
"headlines/headline.org")
|
||||
:to-have-same-items-as
|
||||
`(["e84d0630-efad-4017-9059-5ef917908823" ,(test-org-roam--abs-path "headlines/headline.org") 1]
|
||||
["801b58eb-97e2-435f-a33e-ff59a2f0c213" ,(test-org-roam--abs-path "headlines/headline.org") 1])))))
|
||||
|
||||
(describe "Test roam links"
|
||||
(it ""
|
||||
(expect (org-roam-link--split-path "")
|
||||
:to-equal
|
||||
'(title "" "" nil)))
|
||||
(it "title"
|
||||
(expect (org-roam-link--split-path "title")
|
||||
:to-equal
|
||||
'(title "title" "" nil)))
|
||||
(it "title*"
|
||||
(expect (org-roam-link--split-path "title*")
|
||||
:to-equal
|
||||
'(title+headline "title" "" 5)))
|
||||
(it "title*headline"
|
||||
(expect (org-roam-link--split-path "title*headline")
|
||||
:to-equal
|
||||
'(title+headline "title" "headline" 5)))
|
||||
(it "*headline"
|
||||
(expect (org-roam-link--split-path "*headline")
|
||||
:to-equal
|
||||
'(headline "" "headline" 0))))
|
||||
|
||||
;;; Tests
|
||||
(xdescribe "org-roam-db-build-cache"
|
||||
(describe "org-roam-list-files"
|
||||
(before-each
|
||||
(test-org-roam--init))
|
||||
(setq org-roam-directory (expand-file-name "tests/roam-files")
|
||||
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
|
||||
org-roam-file-extensions '("org")
|
||||
org-roam-file-exclude-regexp nil))
|
||||
|
||||
(after-each
|
||||
(test-org-roam--teardown))
|
||||
(after-all
|
||||
(org-roam-db--close)
|
||||
(delete-file org-roam-db-location))
|
||||
|
||||
(it "initializes correctly"
|
||||
;; Cache
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from files])) :to-be 8)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links])) :to-be 5)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from titles])) :to-be 8)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from titles
|
||||
:where titles :is-null])) :to-be 1)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from refs])) :to-be 1)
|
||||
(it "gets files correctly"
|
||||
(expect (length (org-roam-list-files)) :to-equal 9))
|
||||
|
||||
;; Links
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links
|
||||
:where (= source $s1)]
|
||||
(test-org-roam--abs-path "foo.org"))) :to-be 1)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links
|
||||
:where (= source $s1)]
|
||||
(test-org-roam--abs-path "nested/bar.org"))) :to-be 2)
|
||||
(it "respects org-roam-file-extensions"
|
||||
(setq org-roam-file-extensions '("md"))
|
||||
(expect (length (org-roam-list-files)) :to-equal 1)
|
||||
(setq org-roam-file-extensions '("org" "md"))
|
||||
(expect (length (org-roam-list-files)) :to-equal 10))
|
||||
|
||||
;; Links -- File-to
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links
|
||||
:where (= dest $s1)]
|
||||
(test-org-roam--abs-path "nested/foo.org"))) :to-be 1)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links
|
||||
:where (= dest $s1)]
|
||||
(test-org-roam--abs-path "nested/bar.org"))) :to-be 1)
|
||||
(expect (caar (org-roam-db-query [:select (funcall count) :from links
|
||||
:where (= dest $s1)]
|
||||
(test-org-roam--abs-path "unlinked.org"))) :to-be 0)
|
||||
;; TODO Test titles
|
||||
(expect (org-roam-db-query [:select * :from titles])
|
||||
(it "respects org-roam-file-exclude-regexp"
|
||||
(setq org-roam-file-exclude-regexp (regexp-quote "foo.org"))
|
||||
(expect (length (org-roam-list-files)) :to-equal 8)))
|
||||
|
||||
(describe "org-roam--list-files-search-globs"
|
||||
|
||||
(it "returns the correct list of globs"
|
||||
(expect (org-roam--list-files-search-globs org-roam-file-extensions)
|
||||
:to-have-same-items-as
|
||||
(list (list (test-org-roam--abs-path "alias.org")
|
||||
(list "t1" "a1" "a 2"))
|
||||
(list (test-org-roam--abs-path "bar.org")
|
||||
(list "Bar"))
|
||||
(list (test-org-roam--abs-path "foo.org")
|
||||
(list "Foo"))
|
||||
(list (test-org-roam--abs-path "nested/bar.org")
|
||||
(list "Nested Bar"))
|
||||
(list (test-org-roam--abs-path "nested/foo.org")
|
||||
(list "Nested Foo"))
|
||||
(list (test-org-roam--abs-path "no-title.org")
|
||||
(list "Headline title"))
|
||||
(list (test-org-roam--abs-path "web_ref.org") nil)
|
||||
(list (test-org-roam--abs-path "unlinked.org")
|
||||
(list "Unlinked"))))
|
||||
|
||||
(expect (org-roam-db-query [:select * :from refs])
|
||||
:to-have-same-items-as
|
||||
(list (list "https://google.com/" (test-org-roam--abs-path "web_ref.org") "website")))
|
||||
|
||||
;; Expect rebuilds to be really quick (nothing changed)
|
||||
(expect (org-roam-db-build-cache)
|
||||
:to-equal
|
||||
(list :files 0 :links 0 :tags 0 :titles 0 :refs 0 :deleted 0))))
|
||||
'("\"*.org\"" "\"*.org.gpg\"" "\"*.org.age\""))))
|
||||
|
||||
(provide 'test-org-roam)
|
||||
|
||||
|
Reference in New Issue
Block a user