(feature) add templating functionality via org-roam-template (#165)

This allows users to customize the filename, and the content of the template.
This commit is contained in:
Jethro Kuan
2020-02-23 17:11:49 +08:00
committed by GitHub
parent d2843b816f
commit c54c206694
2 changed files with 78 additions and 62 deletions

View File

@@ -69,10 +69,37 @@ Org files in all of its main commands (`org-roam-insert`,
`org-roam-find-file`). Hence, having any unique file name is a decent `org-roam-find-file`). Hence, having any unique file name is a decent
option, and the default workflow uses the timestamp as the filename. option, and the default workflow uses the timestamp as the filename.
The format of the filename is controlled by the function Org-roam provides templating functionality via `org-roam-templates`.
`org-roam-file-name-function`, which defaults to a format like `org-roam-templates` maps a template string key to a template. Each
`YYYYMMDDHHMMSS_title_here.org`. You may choose to define your own template consists of two parts: (1) a function that takes the title,
function to change this. and generates a filename. (2) the template content. The templated
content accepts two special fields: `${title}` and `${slug}`, which
are substituted with the title and slug respectively. Org-roam ships
with the default template, which inserts the title of the note.
Here's an example of customizing templates:
```emacs-lisp
(defun jethro/org-roam-title-private (title)
(let ((timestamp (format-time-string "%Y%m%d%H%M%S" (current-time)))
(slug (org-roam--title-to-slug title)))
(format "private-%s_%s" timestamp slug)))
(setq org-roam-templates
(list (list "default" (list :file #'org-roam--file-name-timestamp-title
:content "#+SETUPFILE:./hugo_setup.org
#+HUGO_SECTION: zettels
#+HUGO_SLUG: ${slug}
#+TITLE: ${title}"))
(list "private" (list :file #'jethro/org-roam-title-private
:content "#+TITLE: ${title}"))))
```
Here, I define a file-name function `jethro/org-roam-title-private`,
which forms titles like `private-20200202000000-note_name`. The
content string is simply the title. For the default template, I have
extended it to include more boilerplate content for publishing
purposes.
If you wish to be prompted to change the file name on creation, set If you wish to be prompted to change the file name on creation, set
`org-roam-filename-noconfirm` to `nil`: `org-roam-filename-noconfirm` to `nil`:

View File

@@ -100,10 +100,6 @@ If nil, always ask for filename."
:type 'boolean :type 'boolean
:group 'org-roam) :group 'org-roam)
(defcustom org-roam-autopopulate-title t "Whether to autopopulate the title."
:type 'boolean
:group 'org-roam)
(defcustom org-roam-buffer-width 0.33 "Width of `org-roam' buffer." (defcustom org-roam-buffer-width 0.33 "Width of `org-roam' buffer."
:type 'number :type 'number
:group 'org-roam) :group 'org-roam)
@@ -210,7 +206,7 @@ If called interactively, then PARENTS is non-nil."
"Return all org-roam files." "Return all org-roam files."
(org-roam--find-files (file-truename org-roam-directory))) (org-roam--find-files (file-truename org-roam-directory)))
(defun org-roam--make-new-file-path (id &optional absolute) (defun org-roam--new-file-path (id &optional absolute)
"Make new file path from identifier `ID'. "Make new file path from identifier `ID'.
If `ABSOLUTE', return an absolute file-path. Else, return a relative file-path." If `ABSOLUTE', return an absolute file-path. Else, return a relative file-path."
@@ -253,42 +249,35 @@ It uses TITLE and the current timestamp to form a unique title."
(format "%s_%s" timestamp slug))) (format "%s_%s" timestamp slug)))
;;; Creating org-roam files ;;; Creating org-roam files
(defun org-roam--populate-title (file &optional title) (defvar org-roam-templates
"Populate title line for FILE using TITLE, if provided. (list (list "default" (list :file #'org-roam--file-name-timestamp-title
If not provided, derive the title from the file name." :content "#+TITLE: ${title}")))
(let ((title (or title "Templates to insert for new files in org-roam.")
(-> file
(file-name-nondirectory)
(file-name-sans-extension)
(split-string "_")
(string-join " ")
(s-titleize)))))
(write-region
(concat
"#+TITLE: "
title
"\n\n")
nil file nil)))
(defun org-roam--make-file (file-path &optional title) (defun org-roam--make-new-file (title &optional template-key)
"Create an org-roam file at FILE-PATH, optionally setting the TITLE attribute." (unless org-roam-templates
(if (file-exists-p file-path) (user-error "No templates defined"))
(error (format "Aborting, file already exists at %s" file-path)) (let (template)
(make-empty-file file-path t) (if template-key
(if org-roam-autopopulate-title (setq template (cadr (assoc template-key org-roam-templates)))
(org-roam--populate-title file-path title)) (if (= (length org-roam-templates) 1)
(save-excursion (setq template (car org-roam-templates))
(with-current-buffer (find-file-noselect file-path) (setq template
(org-roam--update-cache))))) (cadr (assoc (completing-read "Template: " org-roam-templates)
org-roam-templates)))))
(defun org-roam--new-file-named (slug) (let (file-name-fn file-path)
"Create a new file named `SLUG'. (fset 'file-name-fn (plist-get template :file))
`SLUG' is the short file name, without a path or a file extension." (setq file-path (org-roam--new-file-path (file-name-fn title) t))
(interactive "sNew filename (without extension): ") (if (file-exists-p file-path)
(let ((file-path (org-roam--make-new-file-path slug t))) file-path
(unless (file-exists-p file-path) (make-empty-file file-path t)
(org-roam--make-file file-path)) (write-region
(find-file file-path))) (s-format (plist-get template :content)
'aget
(list (cons "title" title)
(cons "slug" (org-roam--title-to-slug title))))
nil file-path nil)
file-path))))
(defun org-roam--get-new-id (title) (defun org-roam--get-new-id (title)
"Return a new ID, given the note TITLE." "Return a new ID, given the note TITLE."
@@ -297,16 +286,11 @@ If not provided, derive the title from the file name."
proposed-slug proposed-slug
(read-string "Enter ID (without extension): " (read-string "Enter ID (without extension): "
proposed-slug))) proposed-slug)))
(file-path (org-roam--make-new-file-path new-slug t))) (file-path (org-roam--new-file-path new-slug t)))
(if (file-exists-p file-path) (if (file-exists-p file-path)
(user-error "There's already a file at %s") (user-error "There's already a file at %s")
new-slug))) new-slug)))
(defun org-roam-new-file ()
"Quickly create a new file, using the current timestamp."
(interactive)
(org-roam--new-file-named (format-time-string "%Y%m%d%H%M%S" (current-time))))
;;; Inserting org-roam links ;;; Inserting org-roam links
(defun org-roam-insert (prefix) (defun org-roam-insert (prefix)
"Find an org-roam file, and insert a relative org link to it at point. "Find an org-roam file, and insert a relative org link to it at point.
@@ -326,14 +310,12 @@ If PREFIX, downcase the title before insertion."
(title (completing-read "File: " completions nil nil region-text)) (title (completing-read "File: " completions nil nil region-text))
(region-or-title (or region-text title)) (region-or-title (or region-text title))
(absolute-file-path (or (cadr (assoc title completions)) (absolute-file-path (or (cadr (assoc title completions))
(org-roam--make-new-file-path (org-roam--get-new-id title) t))) (org-roam--make-new-file title)))
(current-file-path (-> (or (buffer-base-buffer) (current-file-path (-> (or (buffer-base-buffer)
(current-buffer)) (current-buffer))
(buffer-file-name) (buffer-file-name)
(file-truename) (file-truename)
(file-name-directory)))) (file-name-directory))))
(unless (file-exists-p absolute-file-path)
(org-roam--make-file absolute-file-path title))
(when region ;; Remove previously selected text. (when region ;; Remove previously selected text.
(goto-char (car region)) (goto-char (car region))
(delete-char (- (cdr region) (car region)))) (delete-char (- (cdr region) (car region))))
@@ -353,10 +335,7 @@ If PREFIX, downcase the title before insertion."
(org-roam--find-all-files))) (org-roam--find-all-files)))
(title-or-slug (completing-read "File: " completions)) (title-or-slug (completing-read "File: " completions))
(absolute-file-path (or (cadr (assoc title-or-slug completions)) (absolute-file-path (or (cadr (assoc title-or-slug completions))
(org-roam--make-new-file-path (org-roam--make-new-file title-or-slug))))
(org-roam--get-new-id title-or-slug) t))))
(unless (file-exists-p absolute-file-path)
(org-roam--make-file absolute-file-path title-or-slug))
(find-file absolute-file-path))) (find-file absolute-file-path)))
(defun org-roam--get-roam-buffers () (defun org-roam--get-roam-buffers ()
@@ -462,21 +441,31 @@ This is equivalent to removing the node from the graph."
(org-roam--maybe-update-buffer :redisplay t))) (org-roam--maybe-update-buffer :redisplay t)))
;;; Org-roam daily notes ;;; Org-roam daily notes
(defun org-roam--file-for-time (time)
"Create and find file for TIME."
(let* ((org-roam-templates (list (list "daily" (list :file (lambda (title) title)
:content "#+TITLE: ${title}")))))
(org-roam--make-new-file (format-time-string "%Y-%m-%d" time) "daily")))
(defun org-roam-today () (defun org-roam-today ()
"Create the file for today." "Create and find file for today."
(interactive) (interactive)
(org-roam--new-file-named (format-time-string "%Y-%m-%d" (current-time)))) (let ((path (org-roam--file-for-time (current-time))))
(find-file path)))
(defun org-roam-tomorrow () (defun org-roam-tomorrow ()
"Create the file for tomorrow." "Create and find the file for tomorrow."
(interactive) (interactive)
(org-roam--new-file-named (format-time-string "%Y-%m-%d" (time-add 86400 (current-time))))) (let ((path (org-roam--file-for-time (time-add 86400 (current-time)))))
(find-file path)))
(defun org-roam-date () (defun org-roam-date ()
"Create the file for any date using the calendar." "Create the file for any date using the calendar."
(interactive) (interactive)
(let ((time (org-read-date nil 'to-time nil "Date: "))) (let ((time (org-read-date nil 'to-time nil "Date: ")))
(org-roam--new-file-named (format-time-string "%Y-%m-%d" time)))) (let ((path (org-roam--file-for-time time)))
(find-file path))))
;;; Org-roam buffer ;;; Org-roam buffer
(define-derived-mode org-roam-backlinks-mode org-mode "Backlinks" (define-derived-mode org-roam-backlinks-mode org-mode "Backlinks"