mirror of
https://github.com/org-roam/org-roam
synced 2025-08-29 14:33:29 -05:00
(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:
@@ -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`:
|
||||||
|
105
org-roam.el
105
org-roam.el
@@ -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"
|
||||||
|
Reference in New Issue
Block a user