mirror of
https://github.com/org-roam/org-roam
synced 2025-08-03 12:27:23 -05:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
5eb1a87123 | |||
914bbe3b53 | |||
01130b49e1 | |||
684ab67952 | |||
60eeb3985a | |||
a6cdc77980 | |||
9cd12a4f11 | |||
e00538f909 | |||
270995b2d4 | |||
1cfd71f5a8 | |||
ede33d7411 | |||
7817116403 | |||
efd2072070 | |||
791c059200 |
69
.github/workflows/test.yml
vendored
Normal file
69
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# * test.yml --- Test Emacs packages using makem.sh on GitHub Actions
|
||||||
|
|
||||||
|
# https://github.com/alphapapa/makem.sh
|
||||||
|
|
||||||
|
# Based on Steve Purcell's examples at
|
||||||
|
# <https://github.com/purcell/setup-emacs/blob/master/.github/workflows/test.yml>,
|
||||||
|
# <https://github.com/purcell/package-lint/blob/master/.github/workflows/test.yml>.
|
||||||
|
|
||||||
|
# * License:
|
||||||
|
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
# * Code:
|
||||||
|
|
||||||
|
name: "CI"
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
emacs_version:
|
||||||
|
- 26.3
|
||||||
|
- snapshot
|
||||||
|
steps:
|
||||||
|
- uses: purcell/setup-emacs@master
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.emacs_version }}
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Initialize sandbox
|
||||||
|
run: |
|
||||||
|
SANDBOX_DIR=$(mktemp -d) || exit 1
|
||||||
|
echo ::set-env name=SANDBOX_DIR::$SANDBOX_DIR
|
||||||
|
./makem.sh -vv --sandbox $SANDBOX_DIR --install-deps --install-linters
|
||||||
|
|
||||||
|
# The "all" rule is not used, because it treats compilation warnings
|
||||||
|
# as failures, so linting and testing are run as separate steps.
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
continue-on-error: true
|
||||||
|
run: ./makem.sh -vv --sandbox $SANDBOX_DIR lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
if: always() # Run test even if linting fails.
|
||||||
|
run: ./makem.sh -vv --sandbox $SANDBOX_DIR test
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# eval: (outline-minor-mode)
|
||||||
|
# End:
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/.sandbox/
|
37
CHANGELOG.md
Normal file
37
CHANGELOG.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.1 (2020-02-15)
|
||||||
|
|
||||||
|
Mostly a documentation/cleanup release.
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
* [#62][gh-62] Add the options `org-roam-use-timestamps-as-filename` and `org-roam-file-format`, more in documentation.
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
* [#62][gh-62] The ID (file-name) workflow is no longer first-class, but a fallback when titles don't exist.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
* [#66][gh-66], [#68][gh-68]: Improved the quality of the package in preparation of submission to MELPA
|
||||||
|
* [#73][gh-73]: Added CI to the project via Github Issues (Thanks [@alphapapa](https://github.com/alphapapa/) for scripts and setup)
|
||||||
|
* [#69][gh-69], [#72][gh-72], [#75][gh-75]: Major cleanup and de-duplication of code
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
* [#67][gh-67]: Fixed `org-roam--make-file` not creating files with extensions
|
||||||
|
* [#71][gh-71], [#78][gh-78]: Fixed `org-roam-insert` not inserting correct paths
|
||||||
|
* [#82][gh-82]: Fixed nested Org-roam files not being detected as part of Org-roam
|
||||||
|
|
||||||
|
[gh-62]: https://github.com/jethrokuan/org-roam/pull/66
|
||||||
|
[gh-66]: https://github.com/jethrokuan/org-roam/pull/66
|
||||||
|
[gh-67]: https://github.com/jethrokuan/org-roam/pull/67
|
||||||
|
[gh-68]: https://github.com/jethrokuan/org-roam/pull/68
|
||||||
|
[gh-69]: https://github.com/jethrokuan/org-roam/pull/69
|
||||||
|
[gh-71]: https://github.com/jethrokuan/org-roam/pull/71
|
||||||
|
[gh-72]: https://github.com/jethrokuan/org-roam/pull/72
|
||||||
|
[gh-73]: https://github.com/jethrokuan/org-roam/pull/73
|
||||||
|
[gh-75]: https://github.com/jethrokuan/org-roam/pull/75
|
||||||
|
[gh-78]: https://github.com/jethrokuan/org-roam/pull/78
|
||||||
|
[gh-82]: https://github.com/jethrokuan/org-roam/pull/82
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# eval: (auto-fill-mode -1)
|
||||||
|
# End:
|
34
CONTRIBUTING.md
Normal file
34
CONTRIBUTING.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
If you discover issues, have ideas for improvements or new features, please
|
||||||
|
report them to the [issue tracker][1] of the repository or submit a pull
|
||||||
|
request. Please, try to follow these guidelines when you do so.
|
||||||
|
|
||||||
|
## Issue reporting
|
||||||
|
|
||||||
|
* Check that the issue has not already been reported.
|
||||||
|
* Check that the issue has not already been fixed in the latest code
|
||||||
|
(a.k.a. `develop`).
|
||||||
|
* Be clear, concise and precise in your description of the problem.
|
||||||
|
* Open an issue with a descriptive title and a summary in grammatically correct,
|
||||||
|
complete sentences.
|
||||||
|
* Include any relevant code to the issue summary.
|
||||||
|
* If you're reporting performance issues it'd be nice if you added some profiling data (Emacs has a built-in profiler).
|
||||||
|
|
||||||
|
## Pull requests
|
||||||
|
|
||||||
|
* Read [how to properly contribute to open source projects on Github][2].
|
||||||
|
* Use a topic branch to easily amend a pull request later, if necessary.
|
||||||
|
* Write [good commit messages][3].
|
||||||
|
* Mention related tickets in the commit messages (e.g. `[Fix #N] Add missing autoload cookies`)
|
||||||
|
* Update the [changelog][5].
|
||||||
|
* Use the same coding conventions as the rest of the project.
|
||||||
|
* Verify your Emacs Lisp code with `checkdoc` (<kbd>C-c ? d</kbd>).
|
||||||
|
* Open a [pull request][4] that relates to *only* one subject with a clear title
|
||||||
|
and description in grammatically correct, complete sentences.
|
||||||
|
|
||||||
|
[1]: https://github.com/jethrokuan/org-roam/issues
|
||||||
|
[2]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request
|
||||||
|
[3]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||||
|
[4]: https://help.github.com/articles/using-pull-requests
|
||||||
|
[5]: https://github.com/jethrokuan/org-roam/blob/master/CHANGELOG.md
|
56
Makefile
Normal file
56
Makefile
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# * makem.sh/Makefile --- Script to aid building and testing Emacs Lisp packages
|
||||||
|
|
||||||
|
# This Makefile is from the makem.sh repo: <https://github.com/alphapapa/makem.sh>.
|
||||||
|
|
||||||
|
# * Arguments
|
||||||
|
|
||||||
|
# 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)
|
62
README.md
62
README.md
@ -1,28 +1,26 @@
|
|||||||
|
[![License GPL 3][badge-license]](http://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
[](https://org-roam.readthedocs.io/en/latest/?badge=latest)
|
[](https://org-roam.readthedocs.io/en/latest/?badge=latest)
|
||||||
|
[](https://img.shields.io/github/v/release/jethrokuan/org-roam)
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
Org-roam is a rudimentary [Roam][roamresearch] replica built around
|
Org-roam is a rudimentary [Roam][roamresearch] replica built around
|
||||||
the all-powerful [Org-mode][org].
|
the all-powerful [Org-mode][org].
|
||||||
|
|
||||||
Like Roam, Org-roam offers a powerful and effortless non-hierarchical
|
Like Roam, Org-roam offers a powerful and effortless non-hierarchical
|
||||||
note-taking approach. With Org-roam, notes flow naturally, making
|
note-taking approach. With Org-roam, notes flow naturally, making
|
||||||
note-taking fun and easy.
|
note-taking fun and easy. Org-roam *enables* a note-taking workflow that
|
||||||
|
is not fluid with vanilla Org-mode (more in [this blog
|
||||||
|
post](https://blog.jethro.dev/posts/how_to_take_smart_notes_org/)).
|
||||||
|
|
||||||
|
|
||||||
The goal of the project is to implement core features of Roam around
|
The goal of the project is to implement core features of Roam around
|
||||||
Org-mode, and eventually introduce features enabled by the Emacs
|
Org-mode, and eventually introduce features enabled by the Emacs
|
||||||
ecosystem.
|
ecosystem.
|
||||||
|
|
||||||
For more documentation, see [the documentation page](https://org-roam.readthedocs.io/en/latest/).
|
Visit [the documentation
|
||||||
|
page](https://org-roam.readthedocs.io/en/latest/) for a tutorial and
|
||||||
## Understanding Roam
|
more links.
|
||||||
|
|
||||||
To understand more about Roam, I recommend the following links:
|
|
||||||
|
|
||||||
- [Building a second brain in
|
|
||||||
Roam](https://reddit.com/r/RoamResearch/comments/eho7de/building_a_second_brain_in_roamand_why_you_might)
|
|
||||||
- [Roam: Why I Love It and How I Use
|
|
||||||
It](https://www.nateliason.com/blog/roam)
|
|
||||||
|
|
||||||
## Project Status
|
|
||||||
|
|
||||||
As of February 2020, it is in a very early stage of development.
|
As of February 2020, it is in a very early stage of development.
|
||||||
|
|
||||||
@ -37,16 +35,52 @@ used to navigate to the respective files.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The recommended method is using use-package and straight, or a similar package manager.
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(use-package org-roam
|
||||||
|
:after org
|
||||||
|
:hook
|
||||||
|
((org-mode . org-roam-mode)
|
||||||
|
(after-init . org-roam--build-cache-async) ;; optional!
|
||||||
|
)
|
||||||
|
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
|
||||||
|
:custom
|
||||||
|
(org-roam-directory "/path/to/org-files/")
|
||||||
|
:bind
|
||||||
|
("C-c n l" . org-roam)
|
||||||
|
("C-c n t" . org-roam-today)
|
||||||
|
("C-c n f" . org-roam-find-file)
|
||||||
|
("C-c n i" . org-roam-insert)
|
||||||
|
("C-c n g" . org-roam-show-graph))
|
||||||
|
```
|
||||||
|
|
||||||
|
For more detailed installation instructions, please see [the
|
||||||
|
installation
|
||||||
|
documentation](https://org-roam.readthedocs.io/en/develop/installation/).
|
||||||
|
|
||||||
## Knowledge Bases using Org-Roam
|
## Knowledge Bases using Org-Roam
|
||||||
|
|
||||||
- [Jethro Kuan](https://braindump.jethro.dev/)
|
- [Jethro Kuan](https://braindump.jethro.dev/)
|
||||||
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
|
([Source](https://github.com/jethrokuan/braindump/tree/master/org))
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
A changelog is being maintained [here](CHANGELOG.md)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
To report bugs and suggest new feature use the issue tracker. If you
|
To report bugs and suggest new feature use the issue tracker. If you
|
||||||
have some code which you would like to be merged, then open a pull
|
have some code which you would like to be merged, then open a pull
|
||||||
request. Please also see CONTRIBUTING.md.
|
request. Please also see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © Jethro Kuan and contributors. Distributed under the GNU
|
||||||
|
General Public License, Version 3
|
||||||
|
|
||||||
[roamresearch]: https://www.roamresearch.com/
|
[roamresearch]: https://www.roamresearch.com/
|
||||||
[org]: https://orgmode.org/
|
[org]: https://orgmode.org/
|
||||||
|
[badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg
|
||||||
|
116
doc/configuration.md
Normal file
116
doc/configuration.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
To ensure that Org-roam remains manageable, the number of
|
||||||
|
configuration options is deliberately kept small. However, we have
|
||||||
|
attempted to accommodate as many usage styles as possible.
|
||||||
|
|
||||||
|
In this section, we'll go over the main customization options
|
||||||
|
available to Org-Roam. This section is *crucial*. We need to exploit
|
||||||
|
the flexibility of Emacs, and mould our tools exactly to our liking.
|
||||||
|
|
||||||
|
All of Org-roam's customization options can be viewed via `M-x
|
||||||
|
customize-group org-roam`.
|
||||||
|
|
||||||
|
## Setting the Org-roam Directory
|
||||||
|
|
||||||
|
Perhaps the single most important variable to set is
|
||||||
|
`org-roam-directory`. Set `org-roam-directory` to the folder
|
||||||
|
containing all your Org files:
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(setq org-roam-directory "/path/to/org/")
|
||||||
|
```
|
||||||
|
|
||||||
|
Every Org file, at any level of nesting, within `/path/to/org/` is
|
||||||
|
considered part of the Org-roam ecosystem.
|
||||||
|
|
||||||
|
## Org-roam Buffer
|
||||||
|
|
||||||
|
The Org-roam buffer defaults to popping up from the right. You may
|
||||||
|
choose to set it to pop up from the left with `(setq
|
||||||
|
org-roam-buffer-position 'left)`.
|
||||||
|
|
||||||
|
The Org-roam buffer name can also be renamed: e.g. `(setq
|
||||||
|
org-roam-buffer "*my-buffer-name*")`.
|
||||||
|
|
||||||
|
The Org-roam buffer width is adjustable via `org-roam-buffer-width`.
|
||||||
|
The value of `org-roam-buffer-width` set as a percentage of the total
|
||||||
|
frame width. For example:
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(setq org-roam-buffer-width 0.4)
|
||||||
|
```
|
||||||
|
|
||||||
|
Will result in the Org-roam buffer taking up 40% of the screen width.
|
||||||
|
I have found this to be a good number.
|
||||||
|
|
||||||
|
## Org-roam Links
|
||||||
|
|
||||||
|
By default, links are inserted with the title as the link description.
|
||||||
|
This can make them hard to distinguish from external links. If you
|
||||||
|
wish, you may choose add special indicators for Org-roam links by
|
||||||
|
tweaking `org-roam-link-title-format`, for example:
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(setq org-roam-link-title-format "R:%s")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Org-roam Files
|
||||||
|
|
||||||
|
These customization options revolve around the Org files created and
|
||||||
|
managed by Org-roam.
|
||||||
|
|
||||||
|
### Automatically Creating Files Using Timestamp
|
||||||
|
|
||||||
|
A common hassle is ensuring that files are uniquely named within the
|
||||||
|
Org-roam directory. Org-roam's default workflow utilizes the title of
|
||||||
|
Org files in all of its main commands (`org-roam-insert`,
|
||||||
|
`org-roam-find-file`). Hence, having any unique file name is a decent
|
||||||
|
option, and the default workflow uses the timestamp as the filename.
|
||||||
|
|
||||||
|
The format of the filename is specified by the string
|
||||||
|
`org-roam-file-format`, which defaults to `"%Y%m%d%H%M%S"`. To see
|
||||||
|
valid specifications, see the help (`C-h f`) for `format-time-string`.
|
||||||
|
|
||||||
|
There are several reasons for keeping filenames meaningful. For
|
||||||
|
example, one may wish to publish the Org files, and some publishing
|
||||||
|
methods such as Org-publish use the file names as slugs for the URLs.
|
||||||
|
|
||||||
|
If you wish to maintain manual control of filenames, set
|
||||||
|
`org-roam-use-timestamp-as-filename` to `nil`:
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(setq org-roam-use-timestamp-as-filename nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
When this setting is turned off, the user is instead manually prompted
|
||||||
|
for a filename. It is then the user's responsibility to ensure that
|
||||||
|
the file names are unique.
|
||||||
|
|
||||||
|
### Autopopulating Titles
|
||||||
|
|
||||||
|
The default workflow uses the title of the Org file in several
|
||||||
|
commands. The title is specified via the `#+TITLE:` attribute,
|
||||||
|
typically near the top of the file. The option
|
||||||
|
`org-roam-autopopulate-title` defaults to `t`. When true, the title
|
||||||
|
attribute is automatically inserted into the files created via
|
||||||
|
Org-roam commands. Setting it to `nil` will disable this behaviour.
|
||||||
|
|
||||||
|
|
||||||
|
## Org-roam Graph Viewer
|
||||||
|
|
||||||
|
Org-roam generates an SVG image using
|
||||||
|
[Graphviz](https://graphviz.org/). To setup graph navigation, see the
|
||||||
|
[Graph Setup](graph_setup.md) page.
|
||||||
|
|
||||||
|
Org-roam tries its best to locate the Graphviz executable from your
|
||||||
|
PATH, but if it fails to do so, you may set it manually:
|
||||||
|
|
||||||
|
```
|
||||||
|
(setq org-roam-graphviz-executable "/path/to/dot")
|
||||||
|
```
|
||||||
|
|
||||||
|
Org-roam also attempts to use Firefox (located on PATH) to view the
|
||||||
|
SVG, you may choose to set it to any compatible program:
|
||||||
|
|
||||||
|
```
|
||||||
|
(setq org-roam-graph-viewer "/path/to/image-viewer")
|
||||||
|
```
|
@ -1,10 +1,9 @@
|
|||||||
## Ecosystem
|
|
||||||
|
|
||||||
A number of packages work well combined with Org-Roam:
|
A number of packages work well combined with Org-Roam:
|
||||||
|
|
||||||
### Deft
|
## Deft
|
||||||
[Deft](https://jblevins.org/projects/deft/) provides a nice
|
|
||||||
interface for browsing and filtering org-roam notes.
|
[Deft][deft] provides a nice interface for browsing and filtering
|
||||||
|
org-roam notes.
|
||||||
|
|
||||||
```
|
```
|
||||||
(use-package deft
|
(use-package deft
|
||||||
@ -19,7 +18,12 @@ interface for browsing and filtering org-roam notes.
|
|||||||
(deft-use-filename-as-title t))
|
(deft-use-filename-as-title t))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Org-journal
|
The Deft interface can slow down quickly when the number of files get
|
||||||
|
huge. [Notdeft][notdeft] is a fork of Deft that uses an external
|
||||||
|
search engine and indexer.
|
||||||
|
|
||||||
|
## Org-journal
|
||||||
|
|
||||||
[Org-journal](https://github.com/bastibe/org-journal) is a more
|
[Org-journal](https://github.com/bastibe/org-journal) is a more
|
||||||
powerful alternative to the simple function `org-roam-today`. It
|
powerful alternative to the simple function `org-roam-today`. It
|
||||||
provides better journaling capabilities, and a nice calendar interface
|
provides better journaling capabilities, and a nice calendar interface
|
||||||
@ -35,3 +39,59 @@ to see all dated entries.
|
|||||||
(org-journal-dir "/path/to/org-roam-files/")
|
(org-journal-dir "/path/to/org-roam-files/")
|
||||||
(org-journal-date-format "%A, %d %B %Y"))
|
(org-journal-date-format "%A, %d %B %Y"))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Note-taking Add-ons
|
||||||
|
|
||||||
|
These are some plugins that make note-taking in Org-mode more
|
||||||
|
enjoyable.
|
||||||
|
|
||||||
|
### Org-download
|
||||||
|
|
||||||
|
[Org-download][org-download] lets you screenshot and yank images from
|
||||||
|
the web into your notes:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(use-package org-download
|
||||||
|
:after org
|
||||||
|
:bind
|
||||||
|
(:map org-mode-map
|
||||||
|
(("s-Y" . org-download-screenshot)
|
||||||
|
("s-y" . org-download-yank))))
|
||||||
|
```
|
||||||
|
|
||||||
|
### mathpix.el
|
||||||
|
|
||||||
|
[mathpix.el][mathpix-el] uses [Mathpix's](https://mathpix.com/) API to convert clips into
|
||||||
|
latex equations:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(use-package mathpix.el
|
||||||
|
:straight (:host github :repo "jethrokuan/mathpix.el")
|
||||||
|
:custom ((mathpix-app-id "app-id")
|
||||||
|
(mathpix-app-key "app-key"))
|
||||||
|
:bind
|
||||||
|
("C-x m" . mathpix-screenshot))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Org-noter / Interleave
|
||||||
|
|
||||||
|
[Org-noter][org-noter] and [Interleave][interleave] are both projects
|
||||||
|
that allow synchronised annotation of documents (PDF, EPUB etc.)
|
||||||
|
within Org-mode.
|
||||||
|
|
||||||
|
### Org-ref
|
||||||
|
|
||||||
|
[Org-ref][org-ref] does citation and bibliography management in
|
||||||
|
Org-mode, and a great tool for scientific notes.
|
||||||
|
|
||||||
|
[deft]: https://jblevins.org/projects/deft/
|
||||||
|
[notdeft]: https://github.com/hasu/notdeft
|
||||||
|
[org-download]: https://github.com/abo-abo/org-download
|
||||||
|
[mathpix-el]: https://github.com/jethrokuan/mathpix.el
|
||||||
|
[org-noter]: https://github.com/weirdNox/org-noter
|
||||||
|
[interleave]: https://github.com/rudolfochrist/interleave
|
||||||
|
[org-ref]: https://github.com/jkitchin/org-ref
|
||||||
|
39
doc/graph_setup.md
Normal file
39
doc/graph_setup.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
The setup is similar to that of org-protocol. Here `roam://` links are
|
||||||
|
defined, and need to be associated with an application.
|
||||||
|
|
||||||
|
The gist of the setup is setting up a Bash script to trim off the
|
||||||
|
`roam://` prefix from the link, causing the desktop application to
|
||||||
|
call `emacsclient path/to/org-roam-file.org`.
|
||||||
|
|
||||||
|
## Setting Up for Linux
|
||||||
|
|
||||||
|
Create a desktop application. I place mine in
|
||||||
|
`~/.local/share/applications/roam.desktop`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=Org-Roam Client
|
||||||
|
Exec=/home/jethro/.local/bin/launch_emacs %u
|
||||||
|
Icon=emacs-icon
|
||||||
|
Type=Application
|
||||||
|
Terminal=false
|
||||||
|
MimeType=x-scheme-handler/roam
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the `Exec` key is set to a bash script poorly named
|
||||||
|
`launch_emacs`. You can set it to whatever you want.
|
||||||
|
|
||||||
|
Create the corresponding bash script, and make it executable. Here's
|
||||||
|
how it looks like:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
emacsclient "${1#*:}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, associate `roam://` links with the desktop application by
|
||||||
|
running in your shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xdg-mime default roam.desktop x-scheme-handler/roam
|
||||||
|
```
|
BIN
doc/images/mathpix.gif
Normal file
BIN
doc/images/mathpix.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 781 KiB |
BIN
doc/images/org-download.gif
Normal file
BIN
doc/images/org-download.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 MiB |
BIN
doc/images/org-roam-find-file.gif
Normal file
BIN
doc/images/org-roam-find-file.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 578 KiB |
BIN
doc/images/org-roam-insert-filetag.gif
Normal file
BIN
doc/images/org-roam-insert-filetag.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 774 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.2 MiB |
@ -6,11 +6,13 @@ The recommended method is using [use-package][use-package] and
|
|||||||
```
|
```
|
||||||
(use-package org-roam
|
(use-package org-roam
|
||||||
:after org
|
:after org
|
||||||
:hook (org-mode . org-roam-mode)
|
:hook
|
||||||
:straight (:host github :repo "jethrokuan/org-roam")
|
((org-mode . org-roam-mode)
|
||||||
|
(after-init . org-roam--build-cache-async) ;; optional!
|
||||||
|
)
|
||||||
|
:straight (:host github :repo "jethrokuan/org-roam" :branch "develop")
|
||||||
:custom
|
:custom
|
||||||
(org-roam-directory "/path/to/org-files/")
|
(org-roam-directory "/path/to/org-files/")
|
||||||
(org-roam-link-representation 'title) ;; or keep it as 'id
|
|
||||||
:bind
|
:bind
|
||||||
("C-c n l" . org-roam)
|
("C-c n l" . org-roam)
|
||||||
("C-c n t" . org-roam-today)
|
("C-c n t" . org-roam-today)
|
||||||
@ -26,10 +28,34 @@ directory and add it to your load path:
|
|||||||
git clone https://github.com/jethrokuan/org-roam/ ~/.emacs.d/elisp/org-roam
|
git clone https://github.com/jethrokuan/org-roam/ ~/.emacs.d/elisp/org-roam
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
(use-package org-roam
|
||||||
|
:after org
|
||||||
|
:load-path "elisp/"
|
||||||
|
:hook
|
||||||
|
((org-mode . org-roam-mode)
|
||||||
|
(after-init . org-roam--build-cache-async) ;; optional!
|
||||||
|
)
|
||||||
|
:custom
|
||||||
|
(org-roam-directory "/path/to/org-files/")
|
||||||
|
:bind
|
||||||
|
("C-c n l" . org-roam)
|
||||||
|
("C-c n t" . org-roam-today)
|
||||||
|
("C-c n f" . org-roam-find-file)
|
||||||
|
("C-c n i" . org-roam-insert)
|
||||||
|
("C-c n g" . org-roam-show-graph))
|
||||||
|
```
|
||||||
|
|
||||||
|
Or without use-package:
|
||||||
|
|
||||||
```
|
```
|
||||||
(add-to-list 'load-path "./elisp")
|
(add-to-list 'load-path "./elisp")
|
||||||
(require 'org-roam)
|
(require 'org-roam)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There are a number of important configuration options, that greatly
|
||||||
|
affect the Roam workflow. Do look through them at the
|
||||||
|
[Configuration](configuration.md) page.
|
||||||
|
|
||||||
[use-package]: https://github.com/jwiegley/use-package
|
[use-package]: https://github.com/jwiegley/use-package
|
||||||
[straight]: https://github.com/raxod502/straight.el
|
[straight]: https://github.com/raxod502/straight.el
|
||||||
|
20
doc/notetaking_workflow.md
Normal file
20
doc/notetaking_workflow.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
## Recommended Books
|
||||||
|
- [How to Take Smart Notes][1]
|
||||||
|
|
||||||
|
## Articles
|
||||||
|
- [How to Take Smart Notes in Org-mode - Jethro Kuan][7]
|
||||||
|
- [The Zettelkasten Method - LessWrong 2.0][3]
|
||||||
|
- [Building a second brain in Roam][4]
|
||||||
|
- [Roam: Why I Love It and How I Use It][5]
|
||||||
|
- [Adam Keesling's Twitter Thread][6]
|
||||||
|
|
||||||
|
## What to Do With Your Notes
|
||||||
|
- [How to Use Roam to Outline a New Article in Under 20 Minutes][2]
|
||||||
|
|
||||||
|
[1]: https://www.goodreads.com/book/show/34507927-how-to-take-smart-notes?ac=1&from_search=true&qid=6L8iEE1FIA&rank=1
|
||||||
|
[2]: https://www.youtube.com/watch?v=RvWic15iXjk
|
||||||
|
[3]: https://www.lesswrong.com/posts/NfdHG6oHBJ8Qxc26s/the-zettelkasten-method-1
|
||||||
|
[4]: https://reddit.com/r/RoamResearch/comments/eho7de/building_a_second_brain_in_roamand_why_you_might
|
||||||
|
[5]: https://www.nateliason.com/blog/roam
|
||||||
|
[6]: https://twitter.com/adam_keesling/status/1196864424725774336?s=20
|
||||||
|
[7]: https://blog.jethro.dev/posts/how_to_take_smart_notes_org/
|
73
doc/tour.md
73
doc/tour.md
@ -1,20 +1,69 @@
|
|||||||
### A Tour of Org-Roam
|
Org-roam was built to support a workflow that was not possible with
|
||||||
|
vanilla Org-mode. This flow is modelled after the [Zettelkasten
|
||||||
|
method][zettelkasten], and many of [Roam Research][roam]'s workflows.
|
||||||
|
Understanding this flow is crucial! Org-roam doesn't auto-magically
|
||||||
|
make your note-taking better -- it's changing the note-taking workflow
|
||||||
|
that does.
|
||||||
|
|
||||||
All of this starts from the note. A note is just a simple `.org` file
|
To understand more the methods and madness, the [Note-Taking
|
||||||
in the directory. Any org file in the directory is considered part of
|
Workflow][appendix:ntw] page contains a page of useful references.
|
||||||
the org-roam ecosystem. Notes are quickly linked together (and created
|
I've also written [a post][jethro-blog-post] about how I use Org-roam.
|
||||||
if necessary) using `org-roam-insert`.
|
|
||||||
|
|
||||||

|
Without further ado, let's begin!
|
||||||
|
|
||||||
Org-roam tracks all of these file links, and builds a cache
|
## Building the Cache
|
||||||
asynchronously in the background. This cache is used to populate the
|
|
||||||
backlinks buffer, which shows files that link to the current file, as
|
Assuming you've set `org-roam-directory` appropriately, running `M-x
|
||||||
well as some preview contents:
|
org-roam--build-cache-async` should build up the caches that will
|
||||||
|
allow you to begin using Org-roam. I do this on startup:
|
||||||
|
|
||||||
|
```emacs-lisp
|
||||||
|
(add-hook 'after-init-hook 'org-roam--build-cache-async)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Finding a Note
|
||||||
|
|
||||||
|
`org-roam-find-file` shows you the list of notes you currently have in
|
||||||
|
Org-roam. Selecting the title will bring you to the corresponding
|
||||||
|
note. Entering a title of a note that does not yet exist will create a
|
||||||
|
new note with that title.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Inserting Links
|
||||||
|
|
||||||
|
Within your Org-roam notes, you are encouraged to liberally insert
|
||||||
|
links to existing (or new) Org-roam notes with `org-roam-insert`.
|
||||||
|
Entering a non-existent title will also create a new note with that
|
||||||
|
title.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It is crucial for good usage of Org-roam to insert links liberally,
|
||||||
|
where you want them the notes to resurface!
|
||||||
|
|
||||||
|
## The Org-roam Buffer
|
||||||
|
|
||||||
|
All of Org-roam's operations are designed such that the built cache is
|
||||||
|
a consistent view of the inter-connectivity between your notes. The
|
||||||
|
Org-roam buffer shows backlinks: i.e. the files that link to the
|
||||||
|
currently viewed file, along with some surrounding context. The
|
||||||
|
Org-roam buffer will always show the backlinks for the current
|
||||||
|
Org-roam file in view.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
These file links also form a graph. The generated graph is navigable
|
## Exporting the Graph
|
||||||
in Emacs.
|
|
||||||
|
It's also possible to export the links as a graph, using graphviz. The
|
||||||
|
generated graph is navigable in Emacs, but requires some additional
|
||||||
|
setup, which I describe in the [Graph Appendix][appendix:graph-setup]
|
||||||
|
page.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
[zettelkasten]: https://zettelkasten.de/
|
||||||
|
[appendix:ntw]: notetaking_workflow.md
|
||||||
|
[appendix:graph-setup]: graph_setup.md
|
||||||
|
[roam]: https://www.roamresearch.com/
|
||||||
|
[jethro-blog-post]: https://blog.jethro.dev/posts/how_to_take_smart_notes_org/
|
||||||
|
@ -7,8 +7,11 @@ nav:
|
|||||||
- Home: index.md
|
- Home: index.md
|
||||||
- A Tour of Org-Roam: tour.md
|
- A Tour of Org-Roam: tour.md
|
||||||
- Installation: installation.md
|
- Installation: installation.md
|
||||||
|
- Configuration: configuration.md
|
||||||
- Ecosystem: ecosystem.md
|
- Ecosystem: ecosystem.md
|
||||||
- Similar Packages: comparison.md
|
- Similar Packages: comparison.md
|
||||||
|
- "Appendix: Note-taking Workflow": notetaking_workflow.md
|
||||||
|
- "Appendix: Graph Setup": graph_setup.md
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- admonition
|
- admonition
|
||||||
- pymdownx.betterem:
|
- pymdownx.betterem:
|
||||||
|
121
org-roam-utils.el
Normal file
121
org-roam-utils.el
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
;;; org-roam-utils.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 0.1.0
|
||||||
|
;; Package-Requires: ((emacs "26.1"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
;; any later version.
|
||||||
|
;;
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
;;
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library is an attempt at injecting Roam functionality into Org-mode.
|
||||||
|
;; This is achieved primarily through building caches for forward links,
|
||||||
|
;; backward links, and file titles.
|
||||||
|
;;
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'org)
|
||||||
|
(require 'org-element)
|
||||||
|
(require 'subr-x)
|
||||||
|
(require 'cl-lib)
|
||||||
|
|
||||||
|
(defun org-roam--find-files (dir)
|
||||||
|
"Return all `org-roam' files in `DIR'."
|
||||||
|
(if (file-exists-p dir)
|
||||||
|
(let ((files (directory-files dir t "." t))
|
||||||
|
(dir-ignore-regexp (concat "\\(?:"
|
||||||
|
"\\."
|
||||||
|
"\\|\\.\\."
|
||||||
|
"\\)$"))
|
||||||
|
result)
|
||||||
|
(dolist (file files)
|
||||||
|
(cond
|
||||||
|
((file-directory-p file)
|
||||||
|
(when (not (string-match dir-ignore-regexp file))
|
||||||
|
(setq result (append (org-roam--find-files file) result))))
|
||||||
|
((and (file-readable-p file)
|
||||||
|
(string= (file-name-extension file) "org"))
|
||||||
|
(setq result (cons (file-truename file) result)))))
|
||||||
|
result)))
|
||||||
|
|
||||||
|
(defun org-roam--parse-content (&optional file-path)
|
||||||
|
"Parse the current buffer, and return a list of items for processing."
|
||||||
|
(org-element-map (org-element-parse-buffer) 'link
|
||||||
|
(lambda (link)
|
||||||
|
(let ((type (org-element-property :type link))
|
||||||
|
(path (org-element-property :path link))
|
||||||
|
(start (org-element-property :begin link)))
|
||||||
|
(when (and (string= type "file")
|
||||||
|
(string= (file-name-extension path) "org"))
|
||||||
|
(goto-char start)
|
||||||
|
(let* ((element (org-element-at-point))
|
||||||
|
(content (or (org-element-property :raw-value element)
|
||||||
|
(buffer-substring
|
||||||
|
(or (org-element-property :content-begin element)
|
||||||
|
(org-element-property :begin element))
|
||||||
|
(or (org-element-property :content-end element)
|
||||||
|
(org-element-property :end element))))))
|
||||||
|
(list :from (or file-path
|
||||||
|
(file-truename (buffer-file-name (current-buffer))))
|
||||||
|
:to (file-truename (expand-file-name path org-roam-directory))
|
||||||
|
:content (string-trim content))))))))
|
||||||
|
|
||||||
|
(cl-defun org-roam--insert-item (item &key forward backward)
|
||||||
|
"Insert ITEM into FORWARD and BACKWARD cache.
|
||||||
|
|
||||||
|
ITEM is of the form: (:from from-path :to to-path :content preview-content)."
|
||||||
|
(pcase-let ((`(:from ,p-from :to ,p-to :content ,content) item))
|
||||||
|
;; Build forward-links
|
||||||
|
(let ((links (gethash p-from forward)))
|
||||||
|
(if links
|
||||||
|
(puthash p-from
|
||||||
|
(if (member p-to links)
|
||||||
|
links
|
||||||
|
(cons p-to links)) forward)
|
||||||
|
(puthash p-from (list p-to) forward)))
|
||||||
|
;; Build backward-links
|
||||||
|
(let ((contents-hash (gethash p-to backward)))
|
||||||
|
(if contents-hash
|
||||||
|
(if-let ((contents-list (gethash p-from contents-hash)))
|
||||||
|
(let ((updated (cons content contents-list)))
|
||||||
|
(puthash p-from updated contents-hash)
|
||||||
|
(puthash p-to contents-hash backward))
|
||||||
|
(progn
|
||||||
|
(puthash p-from (list content) contents-hash)
|
||||||
|
(puthash p-to contents-hash backward)))
|
||||||
|
(let ((contents-hash (make-hash-table :test #'equal)))
|
||||||
|
(puthash p-from (list content) contents-hash)
|
||||||
|
(puthash p-to contents-hash backward))))))
|
||||||
|
|
||||||
|
(defun org-roam--extract-title ()
|
||||||
|
"Extract the title from `BUFFER'."
|
||||||
|
(org-element-map
|
||||||
|
(org-element-parse-buffer)
|
||||||
|
'keyword
|
||||||
|
(lambda (kw)
|
||||||
|
(when (string= (org-element-property :key kw) "TITLE")
|
||||||
|
(org-element-property :value kw)))
|
||||||
|
:first-match t))
|
||||||
|
|
||||||
|
(provide 'org-roam-utils)
|
||||||
|
|
||||||
|
;;; org-roam-utils.el ends here
|
421
org-roam.el
421
org-roam.el
@ -1,9 +1,39 @@
|
|||||||
;;; org-roam.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
;;; org-roam.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 0.1.0
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (async "1.9.4"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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:
|
;;; Commentary:
|
||||||
;;
|
;;
|
||||||
|
;; This library is an attempt at injecting Roam functionality into Org-mode.
|
||||||
|
;; This is achieved primarily through building caches for forward links,
|
||||||
|
;; backward links, and file titles.
|
||||||
|
;;
|
||||||
|
;;
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(eval-when-compile (require 'cl-lib))
|
(eval-when-compile (require 'cl-lib))
|
||||||
(require 'dash)
|
(require 'dash)
|
||||||
(require 'org-element)
|
(require 'org-element)
|
||||||
@ -11,19 +41,24 @@
|
|||||||
(require 'subr-x)
|
(require 'subr-x)
|
||||||
(require 's)
|
(require 's)
|
||||||
(require 'f)
|
(require 'f)
|
||||||
|
(require 'org-roam-utils)
|
||||||
|
|
||||||
;;; Customizations
|
;;; Customizations
|
||||||
(defgroup org-roam nil
|
(defgroup org-roam nil
|
||||||
"Roam Research replica in Org-mode."
|
"Roam Research replica in Org-mode."
|
||||||
:group 'org
|
:group 'org
|
||||||
:prefix "org-roam-")
|
:prefix "org-roam-"
|
||||||
|
:link '(url-link :tag "Github" "https://github.com/jethrokuan/org-roam")
|
||||||
|
:link '(url-link :tag "Online Manual" "https://org-roam.readthedocs.io/"))
|
||||||
|
|
||||||
(defcustom org-roam-directory (expand-file-name "~/org-roam/")
|
(defcustom org-roam-directory (expand-file-name "~/org-roam/")
|
||||||
"Org-roam directory."
|
"Path to Org-roam files.
|
||||||
:type 'directory
|
|
||||||
|
All Org files, at any level of nesting, is considered part of the Org-roam."
|
||||||
|
:type 'directoy
|
||||||
:group 'org-roam)
|
:group 'org-roam)
|
||||||
|
|
||||||
(defcustom org-roam-position 'right
|
(defcustom org-roam-buffer-position 'right
|
||||||
"Position of `org-roam' buffer.
|
"Position of `org-roam' buffer.
|
||||||
|
|
||||||
Valid values are
|
Valid values are
|
||||||
@ -33,26 +68,8 @@ Valid values are
|
|||||||
(const right))
|
(const right))
|
||||||
:group 'org-roam)
|
:group 'org-roam)
|
||||||
|
|
||||||
(defcustom org-roam-link-representation 'id
|
(defcustom org-roam-file-format "%Y%m%d%H%M%S"
|
||||||
"The value used to represent an org-roam link.
|
"The timestamp format to use filenames."
|
||||||
|
|
||||||
Valid values are
|
|
||||||
* file,
|
|
||||||
* title."
|
|
||||||
:type '(choice (const id)
|
|
||||||
(const title))
|
|
||||||
:group 'org-roam)
|
|
||||||
|
|
||||||
(defcustom org-roam-timestamped-files nil
|
|
||||||
"Whether to use timestamps to generate unique filenames."
|
|
||||||
:type 'boolean
|
|
||||||
:group 'org-roam)
|
|
||||||
|
|
||||||
(defcustom org-roam-timestamp-format "%Y-%m-%d%H%M%S"
|
|
||||||
"The timestamp format to use filenames.")
|
|
||||||
|
|
||||||
(defcustom org-roam-link-id-format "§%s"
|
|
||||||
"The format string used when inserting org-roam links that use id."
|
|
||||||
:type 'string
|
:type 'string
|
||||||
:group 'org-roam)
|
:group 'org-roam)
|
||||||
|
|
||||||
@ -61,6 +78,11 @@ Valid values are
|
|||||||
:type 'string
|
:type 'string
|
||||||
:group 'org-roam)
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-use-timestamp-as-filename t
|
||||||
|
"Whether to use timestamp as a file name. If not true, prompt for a file name each time."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
(defcustom org-roam-autopopulate-title t "Whether to autopopulate the title."
|
(defcustom org-roam-autopopulate-title t "Whether to autopopulate the title."
|
||||||
:type 'boolean
|
:type 'boolean
|
||||||
:group 'org-roam)
|
:group 'org-roam)
|
||||||
@ -130,35 +152,16 @@ If called interactively, then PARENTS is non-nil."
|
|||||||
(defun org-roam--org-roam-file-p ()
|
(defun org-roam--org-roam-file-p ()
|
||||||
"Return t if file is part of org-roam system, false otherwise."
|
"Return t if file is part of org-roam system, false otherwise."
|
||||||
(and (buffer-file-name (current-buffer))
|
(and (buffer-file-name (current-buffer))
|
||||||
(f-child-of-p (file-truename (buffer-file-name (current-buffer)))
|
(f-descendant-of-p (file-truename (buffer-file-name (current-buffer)))
|
||||||
org-roam-directory)))
|
org-roam-directory)))
|
||||||
|
|
||||||
(defun org-roam--get-title (file)
|
(defun org-roam--get-title-from-cache (file)
|
||||||
"Return title of `FILE'.
|
"Return title of `FILE' from the cache."
|
||||||
|
|
||||||
It first tries the cache. If the cache does not contain the file,
|
|
||||||
it will return the title by loading the file."
|
|
||||||
(or (gethash file org-roam-titles-cache)
|
(or (gethash file org-roam-titles-cache)
|
||||||
(org-roam--extract-file-title file)))
|
(progn
|
||||||
|
(unless org-roam-cache-initialized
|
||||||
(defun org-roam--find-files (dir)
|
(user-error "The Org-Roam caches aren't built! Please run org-roam--build-cache-async"))
|
||||||
"Return all org-roam files in `DIR'."
|
nil)))
|
||||||
(if (file-exists-p dir)
|
|
||||||
(let ((files (directory-files dir t "." t))
|
|
||||||
(dir-ignore-regexp (concat "\\(?:"
|
|
||||||
"\\."
|
|
||||||
"\\|\\.\\."
|
|
||||||
"\\)$"))
|
|
||||||
result)
|
|
||||||
(dolist (file files)
|
|
||||||
(cond
|
|
||||||
((file-directory-p file)
|
|
||||||
(when (not (string-match dir-ignore-regexp file))
|
|
||||||
(setq result (append (org-roam--find-files file) result))))
|
|
||||||
((and (file-readable-p file)
|
|
||||||
(string= (file-name-extension file) "org"))
|
|
||||||
(setq result (cons (file-truename file) result)))))
|
|
||||||
result)))
|
|
||||||
|
|
||||||
(defun org-roam--find-all-files ()
|
(defun org-roam--find-all-files ()
|
||||||
"Return all org-roam files."
|
"Return all org-roam files."
|
||||||
@ -177,26 +180,22 @@ If `ABSOLUTE', return the absolute file-path. Else, return the relative file-pat
|
|||||||
(file-relative-name absolute-file-path
|
(file-relative-name absolute-file-path
|
||||||
(file-truename org-roam-directory)))))
|
(file-truename org-roam-directory)))))
|
||||||
|
|
||||||
(defun org-roam--get-id (file-path)
|
(defun org-roam--get-title-or-slug (file-path)
|
||||||
"Convert `FILE-PATH' to the org-roam id."
|
"Convert `FILE-PATH' to the file title, if it exists. Else, return the path."
|
||||||
(file-name-sans-extension
|
(or (org-roam--get-title-from-cache file-path)
|
||||||
(file-relative-name
|
(-> file-path
|
||||||
(file-truename file-path)
|
(file-relative-name (file-truename org-roam-directory))
|
||||||
(file-truename org-roam-directory))))
|
(file-name-sans-extension))))
|
||||||
|
|
||||||
(defun org-roam--get-title-or-id (file-path)
|
(defun org-roam--title-to-slug (title)
|
||||||
"Convert `FILE-PATH' to the file title, if it exists. Else, return the id."
|
"Convert TITLE to a filename-suitable slug."
|
||||||
(or (org-roam--get-title file-path)
|
|
||||||
(org-roam--get-id file-path)))
|
|
||||||
|
|
||||||
(defun org-roam--title-to-id (title)
|
|
||||||
"Convert TITLE to id."
|
|
||||||
(let* ((s (s-downcase title))
|
(let* ((s (s-downcase title))
|
||||||
(s (replace-regexp-in-string "[^a-zA-Z0-9_ ]" "" s))
|
(s (replace-regexp-in-string "[^a-zA-Z0-9_ ]" "" s))
|
||||||
(s (s-split " " s))
|
(s (s-split " " s))
|
||||||
(s (s-join "_" s)))
|
(s (s-join "_" s)))
|
||||||
s))
|
s))
|
||||||
|
|
||||||
|
|
||||||
;;; Creating org-roam files
|
;;; Creating org-roam files
|
||||||
(defun org-roam--populate-title (file &optional title)
|
(defun org-roam--populate-title (file &optional title)
|
||||||
"Populate title line for FILE using TITLE, if provided.
|
"Populate title line for FILE using TITLE, if provided.
|
||||||
@ -218,7 +217,7 @@ If not provided, derive the title from the file name."
|
|||||||
(defun org-roam--make-file (file-path &optional title)
|
(defun org-roam--make-file (file-path &optional title)
|
||||||
"Create an org-roam file at FILE-PATH, optionally setting the TITLE attribute."
|
"Create an org-roam file at FILE-PATH, optionally setting the TITLE attribute."
|
||||||
(if (file-exists-p file-path)
|
(if (file-exists-p file-path)
|
||||||
(error (format "Aborting, file already exists at " file-path))
|
(error (format "Aborting, file already exists at %s" file-path))
|
||||||
(if org-roam-autopopulate-title
|
(if org-roam-autopopulate-title
|
||||||
(org-roam--populate-title file-path title)
|
(org-roam--populate-title file-path title)
|
||||||
(make-empty-file file-path))))
|
(make-empty-file file-path))))
|
||||||
@ -232,9 +231,20 @@ If not provided, derive the title from the file name."
|
|||||||
(org-roam--make-file file-path))
|
(org-roam--make-file file-path))
|
||||||
(find-file file-path)))
|
(find-file file-path)))
|
||||||
|
|
||||||
(defun org-roam--get-new-id ()
|
(defun org-roam--get-new-id (&optional title)
|
||||||
"Return a new ID, generated from the current time."
|
"Return a new ID, generated from the current time.
|
||||||
(format-time-string org-roam-timestamp-format (current-time)))
|
|
||||||
|
Optionally pass it the title, for a smart file name."
|
||||||
|
(if org-roam-use-timestamp-as-filename
|
||||||
|
(format-time-string org-roam-file-format (current-time))
|
||||||
|
(let* ((slug (read-string "Enter ID (without extension): "
|
||||||
|
(if title
|
||||||
|
(org-roam--title-to-slug title)
|
||||||
|
"")))
|
||||||
|
(file-path (org-roam--get-file-path slug t)))
|
||||||
|
(if (file-exists-p file-path)
|
||||||
|
(user-error "There's already a file at %s")
|
||||||
|
slug))))
|
||||||
|
|
||||||
(defun org-roam-new-file ()
|
(defun org-roam-new-file ()
|
||||||
"Quickly create a new file, using the current timestamp."
|
"Quickly create a new file, using the current timestamp."
|
||||||
@ -243,59 +253,40 @@ If not provided, derive the title from the file name."
|
|||||||
|
|
||||||
;;; Inserting org-roam links
|
;;; Inserting org-roam links
|
||||||
(defun org-roam-insert ()
|
(defun org-roam-insert ()
|
||||||
"Insert an org-roam link."
|
"Find an org-roam file, and insert a relative org link to it at point."
|
||||||
(interactive)
|
(interactive)
|
||||||
(pcase org-roam-link-representation
|
|
||||||
('id (org-roam--insert-id))
|
|
||||||
('title (org-roam--insert-title))))
|
|
||||||
|
|
||||||
(defun org-roam--insert-title ()
|
|
||||||
"Find `ID', and insert a relative org link to it at point."
|
|
||||||
(let* ((completions (mapcar (lambda (file)
|
(let* ((completions (mapcar (lambda (file)
|
||||||
(list (org-roam--get-title-or-id file)
|
(list (org-roam--get-title-or-slug file)
|
||||||
(org-roam--get-id file)))
|
file))
|
||||||
(org-roam--find-all-files)))
|
(org-roam--find-all-files)))
|
||||||
(title (completing-read "File: " completions))
|
(title (completing-read "File: " completions))
|
||||||
(id (cadr (assoc title completions))))
|
(absolute-file-path (or (cadr (assoc title completions))
|
||||||
(when (not id)
|
(org-roam--get-file-path (org-roam--get-new-id title) t)))
|
||||||
(if org-roam-timestamped-files
|
(current-file-path (-> (current-buffer)
|
||||||
(setq id (org-roam--get-new-id)))
|
(buffer-file-name)
|
||||||
(read-string "Enter new file id: " (org-roam--title-to-id title)))
|
(file-truename)
|
||||||
(let ((file-path (org-roam--get-file-path id)))
|
(file-name-directory))))
|
||||||
(unless (file-exists-p file-path)
|
(unless (file-exists-p absolute-file-path)
|
||||||
(org-roam--make-file file-path title))
|
(org-roam--make-file absolute-file-path title))
|
||||||
(insert (format "[[%s][%s]]"
|
(insert (format "[[%s][%s]]"
|
||||||
(concat "file:" file-path)
|
(concat "file:" (file-relative-name absolute-file-path
|
||||||
(format org-roam-link-title-format title))))))
|
current-file-path))
|
||||||
|
(format org-roam-link-title-format title)))))
|
||||||
(defun org-roam--insert-id ()
|
|
||||||
"Find `ID', and insert a relative org link to it at point."
|
|
||||||
(let* ((id (completing-read "File: " (mapcar #'org-roam--get-id (org-roam--find-all-files))))
|
|
||||||
(file-path (org-roam--get-file-path id)))
|
|
||||||
(unless (file-exists-p file-path)
|
|
||||||
(org-roam--make-file file-path))
|
|
||||||
(insert (format "[[%s][%s]]"
|
|
||||||
(concat "file:" file-path)
|
|
||||||
(format org-roam-link-id-format id)))))
|
|
||||||
|
|
||||||
;;; Finding org-roam files
|
;;; Finding org-roam files
|
||||||
(defun org-roam-find-file ()
|
(defun org-roam-find-file ()
|
||||||
"Find and open an org-roam file."
|
"Find and open an org-roam file."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let* ((completions (mapcar (lambda (file)
|
(let* ((completions (mapcar (lambda (file)
|
||||||
(list (org-roam--get-title-or-id file) file))
|
(list (org-roam--get-title-or-slug file) file))
|
||||||
(org-roam--find-all-files)))
|
(org-roam--find-all-files)))
|
||||||
(title-or-id (completing-read "File: " completions))
|
(title-or-slug (completing-read "File: " completions))
|
||||||
(file-path (cadr (assoc title-or-id completions))))
|
(absolute-file-path (or (cadr (assoc title-or-slug completions))
|
||||||
(unless file-path
|
(org-roam--get-file-path
|
||||||
(let ((id (if org-roam-timestamped-files
|
(org-roam--get-new-id title-or-slug) t))))
|
||||||
(org-roam--get-new-id)
|
(unless (file-exists-p absolute-file-path)
|
||||||
(read-string "Enter new file id: "
|
(org-roam--make-file absolute-file-path title-or-slug))
|
||||||
(org-roam--title-to-id title-or-id)))))
|
(find-file absolute-file-path)))
|
||||||
(setq file-path (org-roam--get-file-path id t))))
|
|
||||||
(unless (file-exists-p file-path)
|
|
||||||
(org-roam--make-file file-path title-or-id))
|
|
||||||
(find-file file-path)))
|
|
||||||
|
|
||||||
;;; Building the org-roam cache (asynchronously)
|
;;; Building the org-roam cache (asynchronously)
|
||||||
(defun org-roam--build-cache-async ()
|
(defun org-roam--build-cache-async ()
|
||||||
@ -303,101 +294,30 @@ If not provided, derive the title from the file name."
|
|||||||
(interactive)
|
(interactive)
|
||||||
(async-start
|
(async-start
|
||||||
`(lambda ()
|
`(lambda ()
|
||||||
(require 'org)
|
(setq load-path ',load-path)
|
||||||
(require 'org-element)
|
(package-initialize)
|
||||||
(require 'subr-x) ; temp-fix
|
(require 'org-roam-utils)
|
||||||
(require 'cl-lib)
|
|
||||||
,(async-inject-variables "org-roam-directory")
|
,(async-inject-variables "org-roam-directory")
|
||||||
(let ((backward-links (make-hash-table :test #'equal))
|
(let ((backward-links (make-hash-table :test #'equal))
|
||||||
(forward-links (make-hash-table :test #'equal))
|
(forward-links (make-hash-table :test #'equal))
|
||||||
(file-titles (make-hash-table :test #'equal)))
|
(file-titles (make-hash-table :test #'equal)))
|
||||||
(cl-labels ((org-roam--find-files
|
(let* ((org-roam-files (org-roam--find-files org-roam-directory))
|
||||||
(dir)
|
(file-items (mapcar (lambda (file)
|
||||||
(if (file-exists-p dir)
|
|
||||||
(let ((files (directory-files dir t "." t))
|
|
||||||
(dir-ignore-regexp (concat "\\(?:"
|
|
||||||
"\\."
|
|
||||||
"\\|\\.\\."
|
|
||||||
"\\)$"))
|
|
||||||
result)
|
|
||||||
(dolist (file files)
|
|
||||||
(cond
|
|
||||||
((file-directory-p file)
|
|
||||||
(when (not (string-match dir-ignore-regexp file))
|
|
||||||
(setq result (append (org-roam--find-files file) result))))
|
|
||||||
((and (file-readable-p file)
|
|
||||||
(string= (file-name-extension file) "org"))
|
|
||||||
(setq result (cons (file-truename file) result)))))
|
|
||||||
result)))
|
|
||||||
(org-roam--parse-content
|
|
||||||
(file)
|
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert-file-contents file)
|
(insert-file-contents file)
|
||||||
(with-current-buffer (current-buffer)
|
(org-roam--parse-content file))) org-roam-files)))
|
||||||
(org-element-map (org-element-parse-buffer) 'link
|
(dolist (items file-items)
|
||||||
(lambda (link)
|
(dolist (item items)
|
||||||
(let ((type (org-element-property :type link))
|
(org-roam--insert-item
|
||||||
(path (org-element-property :path link))
|
item
|
||||||
(start (org-element-property :begin link)))
|
:forward forward-links
|
||||||
(when (and (string= type "file")
|
:backward backward-links)))
|
||||||
(string= (file-name-extension path) "org"))
|
|
||||||
(goto-char start)
|
|
||||||
(let* ((element (org-element-at-point))
|
|
||||||
(content (or (org-element-property :raw-value element)
|
|
||||||
(buffer-substring
|
|
||||||
(or (org-element-property :content-begin element)
|
|
||||||
(org-element-property :begin element))
|
|
||||||
(or (org-element-property :content-end element)
|
|
||||||
(org-element-property :end element))))))
|
|
||||||
(list :from file
|
|
||||||
:to (file-truename (expand-file-name path org-roam-directory))
|
|
||||||
:content (string-trim content))))))))))
|
|
||||||
(org-roam--process-items
|
|
||||||
(items)
|
|
||||||
(mapcar
|
|
||||||
(lambda (item)
|
|
||||||
(pcase-let ((`(:from ,p-from :to ,p-to :content ,content) item))
|
|
||||||
;; Build forward-links
|
|
||||||
(let ((links (gethash p-from forward-links)))
|
|
||||||
(if links
|
|
||||||
(puthash p-from
|
|
||||||
(if (member p-to links)
|
|
||||||
links
|
|
||||||
(cons p-to links)) forward-links)
|
|
||||||
(puthash p-from (list p-to) forward-links)))
|
|
||||||
;; Build backward-links
|
|
||||||
(let ((contents-hash (gethash p-to backward-links)))
|
|
||||||
(if contents-hash
|
|
||||||
(if-let ((contents-list (gethash p-from contents-hash)))
|
|
||||||
(let ((updated (cons content contents-list)))
|
|
||||||
(puthash p-from updated contents-hash)
|
|
||||||
(puthash p-to contents-hash backward-links))
|
|
||||||
(progn
|
|
||||||
(puthash p-from (list content) contents-hash)
|
|
||||||
(puthash p-to contents-hash backward-links)))
|
|
||||||
(let ((contents-hash (make-hash-table :test #'equal)))
|
|
||||||
(puthash p-from (list content) contents-hash)
|
|
||||||
(puthash p-to contents-hash backward-links))))))
|
|
||||||
items))
|
|
||||||
(org-roam--extract-title
|
|
||||||
(buffer)
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(org-element-map
|
|
||||||
(org-element-parse-buffer)
|
|
||||||
'keyword
|
|
||||||
(lambda (kw)
|
|
||||||
(when (string= (org-element-property :key kw) "TITLE")
|
|
||||||
(org-element-property :value kw)))
|
|
||||||
:first-match t))))
|
|
||||||
(let ((org-roam-files (org-roam--find-files org-roam-directory)))
|
|
||||||
(mapcar #'org-roam--process-items
|
|
||||||
(mapcar #'org-roam--parse-content org-roam-files))
|
|
||||||
(mapcar (lambda (file)
|
(mapcar (lambda (file)
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert-file-contents file)
|
(insert-file-contents file)
|
||||||
(when-let ((title (org-roam--extract-title (current-buffer))))
|
(when-let ((title (org-roam--extract-title)))
|
||||||
(puthash file title file-titles))))
|
(puthash file title file-titles))))
|
||||||
org-roam-files)))
|
org-roam-files))
|
||||||
(list
|
(list
|
||||||
:forward forward-links
|
:forward forward-links
|
||||||
:backward backward-links
|
:backward backward-links
|
||||||
@ -409,63 +329,11 @@ If not provided, derive the title from the file name."
|
|||||||
(setq org-roam-cache-initialized t)
|
(setq org-roam-cache-initialized t)
|
||||||
(message "Org-roam cache built!"))))
|
(message "Org-roam cache built!"))))
|
||||||
|
|
||||||
(defun org-roam--insert-item (item)
|
(defun org-roam--clear-cache ()
|
||||||
"Insert `ITEM' into org-roam caches.
|
"Remove any related links to the file.
|
||||||
|
|
||||||
`ITEM' is of the form: (:from from-path :to to-path :content preview-content)
|
|
||||||
|
|
||||||
Before calling this function, `org-roam-cache' should be already populated."
|
|
||||||
(pcase-let ((`(:from ,p-from :to ,p-to :content ,content) item))
|
|
||||||
;; Build forward-links
|
|
||||||
(let ((links (gethash p-from org-roam-forward-links-cache)))
|
|
||||||
(if links
|
|
||||||
(puthash p-from
|
|
||||||
(if (member p-to links)
|
|
||||||
links
|
|
||||||
(cons p-to links)) org-roam-forward-links-cache)
|
|
||||||
(puthash p-from (list p-to) org-roam-forward-links-cache)))
|
|
||||||
;; Build backward-links
|
|
||||||
(let ((contents-hash (gethash p-to org-roam-backward-links-cache)))
|
|
||||||
(if contents-hash
|
|
||||||
(if-let ((contents-list (gethash p-from contents-hash)))
|
|
||||||
(let ((updated (cons content contents-list)))
|
|
||||||
(puthash p-from updated contents-hash)
|
|
||||||
(puthash p-to contents-hash org-roam-backward-links-cache))
|
|
||||||
(progn
|
|
||||||
(puthash p-from (list content) contents-hash)
|
|
||||||
(puthash p-to contents-hash org-roam-backward-links-cache)))
|
|
||||||
(let ((contents-hash (make-hash-table :test #'equal)))
|
|
||||||
(puthash p-from (list content) contents-hash)
|
|
||||||
(puthash p-to contents-hash org-roam-backward-links-cache))))))
|
|
||||||
|
|
||||||
(defun org-roam--parse-content ()
|
|
||||||
"Parse the current buffer, and return a list of items for processing."
|
|
||||||
(with-current-buffer (current-buffer)
|
|
||||||
(org-element-map (org-element-parse-buffer) 'link
|
|
||||||
(lambda (link)
|
|
||||||
(let ((type (org-element-property :type link))
|
|
||||||
(path (org-element-property :path link))
|
|
||||||
(start (org-element-property :begin link)))
|
|
||||||
(when (and (string= type "file")
|
|
||||||
(string= (file-name-extension path) "org"))
|
|
||||||
(goto-char start)
|
|
||||||
(let* ((element (org-element-at-point))
|
|
||||||
(content (or (org-element-property :raw-value element)
|
|
||||||
(buffer-substring
|
|
||||||
(or (org-element-property :content-begin element)
|
|
||||||
(org-element-property :begin element))
|
|
||||||
(or (org-element-property :content-end element)
|
|
||||||
(org-element-property :end element))))))
|
|
||||||
(list :from (file-truename (buffer-file-name (current-buffer)))
|
|
||||||
:to (file-truename (expand-file-name path org-roam-directory))
|
|
||||||
:content (string-trim content)))))))))
|
|
||||||
|
|
||||||
(defun org-roam--clear-cache-for-buffer (buffer)
|
|
||||||
"Remove any related links to the file for `BUFFER'.
|
|
||||||
|
|
||||||
This is equivalent to removing the node from the graph."
|
This is equivalent to removing the node from the graph."
|
||||||
(with-current-buffer (current-buffer)
|
(let ((file (file-truename (buffer-file-name (current-buffer)))))
|
||||||
(let ((file (file-truename (buffer-file-name buffer))))
|
|
||||||
;; Step 1: Remove all existing links for file
|
;; Step 1: Remove all existing links for file
|
||||||
(when-let ((forward-links (gethash file org-roam-forward-links-cache)))
|
(when-let ((forward-links (gethash file org-roam-forward-links-cache)))
|
||||||
;; Delete backlinks to file
|
;; Delete backlinks to file
|
||||||
@ -476,25 +344,28 @@ This is equivalent to removing the node from the graph."
|
|||||||
;; Clean out forward links
|
;; Clean out forward links
|
||||||
(remhash file org-roam-forward-links-cache))
|
(remhash file org-roam-forward-links-cache))
|
||||||
;; Step 2: Remove from the title cache
|
;; Step 2: Remove from the title cache
|
||||||
(remhash file org-roam-titles-cache))))
|
(remhash file org-roam-titles-cache)))
|
||||||
|
|
||||||
(defun org-roam--update-cache-title (buffer)
|
(defun org-roam--update-cache-title ()
|
||||||
"Inserts the `TITLE' of file in buffer into the cache."
|
"Insert the title of the current buffer into the cache."
|
||||||
(when-let ((title (org-roam--extract-title buffer)))
|
(when-let ((title (org-roam--extract-title)))
|
||||||
(puthash (file-truename (buffer-file-name buffer))
|
(puthash (file-truename (buffer-file-name (current-buffer)))
|
||||||
title
|
title
|
||||||
org-roam-titles-cache)))
|
org-roam-titles-cache)))
|
||||||
|
|
||||||
(defun org-roam--update-cache ()
|
(defun org-roam--update-cache ()
|
||||||
"Update org-roam caches for the current buffer file."
|
"Update org-roam caches for the current buffer file."
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(org-roam--clear-cache-for-buffer (current-buffer))
|
(org-roam--clear-cache)
|
||||||
;; Insert into title cache
|
;; Insert into title cache
|
||||||
(org-roam--update-cache-title (current-buffer))
|
(org-roam--update-cache-title)
|
||||||
;; Insert new items
|
;; Insert new items
|
||||||
(let ((items (org-roam--parse-content)))
|
(let ((items (org-roam--parse-content)))
|
||||||
(dolist (item items)
|
(dolist (item items)
|
||||||
(org-roam--insert-item item)))
|
(org-roam--insert-item
|
||||||
|
item
|
||||||
|
:forward org-roam-forward-links-cache
|
||||||
|
:backward org-roam-backward-links-cache)))
|
||||||
;; Rerender buffer
|
;; Rerender buffer
|
||||||
(org-roam--maybe-update-buffer :redisplay t)))
|
(org-roam--maybe-update-buffer :redisplay t)))
|
||||||
|
|
||||||
@ -516,27 +387,10 @@ This is equivalent to removing the node from the graph."
|
|||||||
(org-roam--new-file-named (format-time-string "%Y-%m-%d" time))))
|
(org-roam--new-file-named (format-time-string "%Y-%m-%d" time))))
|
||||||
|
|
||||||
;;; Org-roam buffer updates
|
;;; Org-roam buffer updates
|
||||||
(defun org-roam--extract-title (buffer)
|
|
||||||
"Extract the title from `BUFFER'."
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(org-element-map
|
|
||||||
(org-element-parse-buffer)
|
|
||||||
'keyword
|
|
||||||
(lambda (kw)
|
|
||||||
(when (string= (org-element-property :key kw) "TITLE")
|
|
||||||
(org-element-property :value kw)))
|
|
||||||
:first-match t)))
|
|
||||||
|
|
||||||
(defun org-roam--extract-file-title (file)
|
|
||||||
"Extract the title from `FILE'."
|
|
||||||
(with-temp-buffer
|
|
||||||
(insert-file-contents file)
|
|
||||||
(org-roam--extract-title (current-buffer))))
|
|
||||||
|
|
||||||
(defun org-roam-update (file-path)
|
(defun org-roam-update (file-path)
|
||||||
"Show the backlinks for given org file for file at `FILE-PATH'."
|
"Show the backlinks for given org file for file at `FILE-PATH'."
|
||||||
(org-roam--ensure-cache-built)
|
(org-roam--ensure-cache-built)
|
||||||
(let ((buffer-title (org-roam--get-title-or-id file-path)))
|
(let ((buffer-title (org-roam--get-title-or-slug file-path)))
|
||||||
(with-current-buffer org-roam-buffer
|
(with-current-buffer org-roam-buffer
|
||||||
(let ((inhibit-read-only t))
|
(let ((inhibit-read-only t))
|
||||||
(erase-buffer)
|
(erase-buffer)
|
||||||
@ -553,7 +407,7 @@ This is equivalent to removing the node from the graph."
|
|||||||
(maphash (lambda (file-from contents)
|
(maphash (lambda (file-from contents)
|
||||||
(insert (format "** [[file:%s][%s]]\n"
|
(insert (format "** [[file:%s][%s]]\n"
|
||||||
file-from
|
file-from
|
||||||
(org-roam--get-title-or-id file-from)))
|
(org-roam--get-title-or-slug file-from)))
|
||||||
(dolist (content contents)
|
(dolist (content contents)
|
||||||
(insert (concat (propertize (s-trim (s-replace "\n" " " content))
|
(insert (concat (propertize (s-trim (s-replace "\n" " " content))
|
||||||
'font-lock-face 'org-block)
|
'font-lock-face 'org-block)
|
||||||
@ -586,11 +440,11 @@ Valid states are 'visible, 'exists and 'none."
|
|||||||
(enlarge-window-horizontally (- w (window-width))))))))
|
(enlarge-window-horizontally (- w (window-width))))))))
|
||||||
|
|
||||||
(defun org-roam--setup-buffer ()
|
(defun org-roam--setup-buffer ()
|
||||||
"Setup the `org-roam' buffer at the `org-roam-position'."
|
"Setup the `org-roam' buffer at the `org-roam-buffer-position'."
|
||||||
(let ((window (get-buffer-window)))
|
(let ((window (get-buffer-window)))
|
||||||
(-> (get-buffer-create org-roam-buffer)
|
(-> (get-buffer-create org-roam-buffer)
|
||||||
(display-buffer-in-side-window
|
(display-buffer-in-side-window
|
||||||
`((side . ,org-roam-position)))
|
`((side . ,org-roam-buffer-position)))
|
||||||
(select-window))
|
(select-window))
|
||||||
(org-roam--set-width
|
(org-roam--set-width
|
||||||
(round (* (frame-width)
|
(round (* (frame-width)
|
||||||
@ -654,19 +508,17 @@ This needs to be quick/infrequent, because this is run at
|
|||||||
(org-roam--ensure-cache-built)
|
(org-roam--ensure-cache-built)
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert "digraph {\n")
|
(insert "digraph {\n")
|
||||||
(mapcar (lambda (file)
|
(dolist (file (org-roam--find-all-files))
|
||||||
(insert
|
(insert
|
||||||
(format " \"%s\" [URL=\"roam://%s\"];\n"
|
(format " \"%s\" [URL=\"roam://%s\"];\n"
|
||||||
(org-roam--get-id file)
|
(org-roam--get-title-or-slug file)
|
||||||
file)))
|
file)))
|
||||||
(org-roam--find-all-files))
|
|
||||||
(maphash
|
(maphash
|
||||||
(lambda (from-link to-links)
|
(lambda (from-link to-links)
|
||||||
(dolist (to-link to-links)
|
(dolist (to-link to-links)
|
||||||
(insert (format " \"%s\" -> \"%s\";\n"
|
(insert (format " \"%s\" -> \"%s\";\n"
|
||||||
(org-roam--get-id from-link)
|
(org-roam--get-title-or-slug from-link)
|
||||||
(org-roam--get-id to-link))))
|
(org-roam--get-title-or-slug to-link)))))
|
||||||
)
|
|
||||||
org-roam-forward-links-cache)
|
org-roam-forward-links-cache)
|
||||||
(insert "}")
|
(insert "}")
|
||||||
(buffer-string)))
|
(buffer-string)))
|
||||||
@ -687,7 +539,6 @@ This needs to be quick/infrequent, because this is run at
|
|||||||
(call-process org-roam-graphviz-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
|
(call-process org-roam-graphviz-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
|
||||||
(call-process org-roam-graph-viewer nil 0 nil temp-graph)))
|
(call-process org-roam-graph-viewer nil 0 nil temp-graph)))
|
||||||
|
|
||||||
|
|
||||||
(provide 'org-roam)
|
(provide 'org-roam)
|
||||||
|
|
||||||
;;; org-roam.el ends here
|
;;; org-roam.el ends here
|
||||||
|
@ -5,6 +5,5 @@ pkgs.mkShell {
|
|||||||
name = "docs";
|
name = "docs";
|
||||||
buildInput = with pkgs; [
|
buildInput = with pkgs; [
|
||||||
mkdocs
|
mkdocs
|
||||||
python3Packages.alabaster
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
35
tests/test-org-roam.el
Normal file
35
tests/test-org-roam.el
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
;;; test-org-roam.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:
|
||||||
|
|
||||||
|
;;;; Requirements
|
||||||
|
|
||||||
|
(require 'buttercup)
|
||||||
|
(require 'org-roam)
|
||||||
|
|
||||||
|
;;; Tests
|
||||||
|
(describe "Org-roam cache"
|
||||||
|
(it "Mock Test"
|
||||||
|
(expect t :to-be t)))
|
Reference in New Issue
Block a user