Simplify org-roam-insert and org-roam-find-file (#62)

* Simplify org-roam-insert and org-roam-find-file

See #59.

* Add docs for org-roam automatic filenaming

* Update installation instructions
This commit is contained in:
Jethro Kuan 2020-02-13 00:25:45 +08:00 committed by GitHub
parent a2a448d7d2
commit 791c059200
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 85 deletions

51
doc/configuration.md Normal file
View file

@ -0,0 +1,51 @@
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`.
## 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.

View file

@ -10,7 +10,6 @@ The recommended method is using [use-package][use-package] and
:straight (:host github :repo "jethrokuan/org-roam") :straight (:host github :repo "jethrokuan/org-roam")
: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)
@ -31,5 +30,9 @@ git clone https://github.com/jethrokuan/org-roam/ ~/.emacs.d/elisp/org-roam
(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

View file

@ -7,6 +7,7 @@ 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
markdown_extensions: markdown_extensions:

View file

@ -33,26 +33,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 +43,9 @@ 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.")
(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)
@ -133,13 +118,13 @@ If called interactively, then PARENTS is non-nil."
(f-child-of-p (file-truename (buffer-file-name (current-buffer))) (f-child-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
(user-error "The Org-Roam caches aren't built! Please run org-roam--build-cache-async"))
nil)))
(defun org-roam--find-files (dir) (defun org-roam--find-files (dir)
"Return all org-roam files in `DIR'." "Return all org-roam files in `DIR'."
@ -177,20 +162,15 @@ 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))
@ -232,9 +212,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,58 +234,34 @@ 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)))) (file-path (or (cadr (assoc title completions))
(when (not id) (org-roam--get-new-id title))))
(if org-roam-timestamped-files
(setq id (org-roam--get-new-id)))
(read-string "Enter new file id: " (org-roam--title-to-id title)))
(let ((file-path (org-roam--get-file-path id)))
(unless (file-exists-p file-path)
(org-roam--make-file file-path title))
(insert (format "[[%s][%s]]"
(concat "file:" 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) (unless (file-exists-p file-path)
(org-roam--make-file file-path)) (org-roam--make-file file-path title))
(insert (format "[[%s][%s]]" (insert (format "[[%s][%s]]"
(concat "file:" file-path) (concat "file:" file-path)
(format org-roam-link-id-format id))))) (format org-roam-link-title-format title)))))
;;; 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)))) (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)
(read-string "Enter new file id: "
(org-roam--title-to-id title-or-id)))))
(setq file-path (org-roam--get-file-path id t))))
(unless (file-exists-p file-path) (unless (file-exists-p file-path)
(org-roam--make-file file-path title-or-id)) (org-roam--make-file file-path title-or-slug))
(find-file file-path))) (find-file file-path)))
;;; Building the org-roam cache (asynchronously) ;;; Building the org-roam cache (asynchronously)
@ -536,7 +503,7 @@ This is equivalent to removing the node from the graph."
(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 +520,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)
@ -657,15 +624,15 @@ This needs to be quick/infrequent, because this is run at
(mapcar (lambda (file) (mapcar (lambda (file)
(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)) (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 "}")