Skip to content

Commit

Permalink
Merge pull request #65 from jeanphilippegg/reorganization
Browse files Browse the repository at this point in the history
Cleanup of denote.el
  • Loading branch information
protesilaos committed Jul 29, 2022
2 parents f698ce0 + b8b365b commit 4fd7e0d
Showing 1 changed file with 119 additions and 123 deletions.
242 changes: 119 additions & 123 deletions denote.el
Expand Up @@ -424,8 +424,6 @@ FILE must be an absolute path."
(string-match-p denote--id-regexp (buffer-name)))
(string-prefix-p (denote-directory) (expand-file-name default-directory))))

;;;; Keywords

(defun denote--directory-files-recursively (directory)
"Return expanded files in DIRECTORY recursively."
(mapcar
Expand Down Expand Up @@ -466,6 +464,8 @@ names that are relative to the variable `denote-directory'."
f))
(denote--directory-files))))

;;;; Keywords

(defun denote--extract-keywords-from-path (path)
"Extract keywords from PATH."
(let* ((file-name (file-name-nondirectory path))
Expand Down Expand Up @@ -712,28 +712,90 @@ should be valid for note creation."
(setq denote-last-buffer buffer)
(setq denote-last-front-matter header)))

(defvar denote--title-history nil
"Minibuffer history of `denote--title-prompt'.")

(defun denote--title-prompt (&optional default-title)
"Read file title for `denote'.
Optional DEFAULT-TITLE is used as the default value."
(let ((format (if default-title
(format "File title [%s]: " default-title)
"File title: ")))
(setq denote-last-title
(read-string format nil 'denote--title-history default-title))))

;;;;; The `denote' command

(defun denote--dir-in-denote-directory-p (directory)
"Return DIRECTORY if in variable `denote-directory', else nil."
(when-let* ((dir directory)
((string-prefix-p (expand-file-name (denote-directory))
(expand-file-name dir))))
dir))

(defun denote--file-type-symbol (filetype)
"Return FILETYPE as a symbol."
(cond
((stringp filetype)
(intern filetype))
((symbolp filetype)
filetype)
(t (user-error "`%s' is not a symbol or string" filetype))))

(defun denote--date-add-current-time (date)
"Add current time to DATE, if necessary.
The idea is to turn 2020-01-15 into 2020-01-15 16:19 so that the
hour and minute component is not left to 00:00.
This reduces the burden on the user who would otherwise need to
input that value in order to avoid the error of duplicate
identifiers.
It also addresses a difference between Emacs 28 and Emacs 29
where the former does not read dates without a time component."
(if (<= (length date) 10)
(format "%s %s" date (format-time-string "%H:%M:%S" (current-time)))
date))

(defun denote--valid-date (date)
"Return DATE if parsed by `date-to-time', else signal error."
(let ((datetime (denote--date-add-current-time date)))
(date-to-time datetime)))

(defun denote--buffer-file-names ()
"Return file names of active buffers."
(mapcar
(lambda (name)
(file-name-nondirectory name))
(seq-filter
(lambda (name) (denote--only-note-p name))
(delq nil
(mapcar
(lambda (buf)
(buffer-file-name buf))
(buffer-list))))))

;; This should only be relevant for `denote-date', otherwise the
;; identifier is always unique (we trust that no-one writes multiple
;; notes within fractions of a second).
(defun denote--id-exists-p (identifier)
"Return non-nil if IDENTIFIER already exists."
(let ((current-buffer-name (when (buffer-file-name)
(file-name-nondirectory (buffer-file-name)))))
(or (seq-some (lambda (file)
(string-match-p (concat "\\`" identifier) file))
(delete current-buffer-name (denote--buffer-file-names)))
(delete current-buffer-name
(denote--directory-files-matching-regexp
(concat "\\`" identifier))))))

(defun denote--barf-duplicate-id (identifier)
"Throw a user-error if IDENTIFIER already exists else return t."
(if (denote--id-exists-p identifier)
(user-error "`%s' already exists; aborting new note creation" identifier)
t))

(defun denote--subdirs ()
"Return list of subdirectories in variable `denote-directory'."
(seq-remove
(lambda (filename)
;; TODO 2022-07-03: Generalise for all VC backends. Which ones?
;;
;; TODO 2022-07-03: Maybe it makes sense to also allow the user to
;; specify a blocklist of directories that should always be
;; excluded?
(or (string-match-p "\\.git" filename)
(not (file-directory-p filename))))
(directory-files-recursively (denote-directory) ".*" t t)))

;;;;; The `denote' command and its prompts

;;;###autoload
(defun denote (&optional title keywords file-type subdirectory date)
"Create a new note with the appropriate metadata and file name.
Expand Down Expand Up @@ -781,9 +843,18 @@ When called from Lisp, all arguments are optional.
(denote--prepare-note (or title "") keywords date id directory file-type)
(denote--keywords-add-to-history keywords)))

(defalias 'denote-create-note (symbol-function 'denote))
(defvar denote--title-history nil
"Minibuffer history of `denote--title-prompt'.")

;;;;; The `denote-type' command
(defun denote--title-prompt (&optional default-title)
"Read file title for `denote'.
Optional DEFAULT-TITLE is used as the default value."
(let ((format (if default-title
(format "File title [%s]: " default-title)
"File title: ")))
(setq denote-last-title
(read-string format nil 'denote--title-history default-title))))

(defvar denote--file-type-history nil
"Minibuffer history of `denote--file-type-prompt'.")
Expand All @@ -797,30 +868,6 @@ here for clarity."
"Select file type: " '(org markdown-yaml markdown-toml text) nil t
nil 'denote--file-type-history))

(defun denote--file-type-symbol (filetype)
"Return FILETYPE as a symbol."
(cond
((stringp filetype)
(intern filetype))
((symbolp filetype)
filetype)
(t (user-error "`%s' is not a symbol or string" filetype))))

;;;###autoload
(defun denote-type ()
"Create note while prompting for a file type.
This is the equivalent to calling `denote' when `denote-prompts'
is set to \\='(file-type title keywords)."
(declare (interactive-only t))
(interactive)
(let ((denote-prompts '(file-type title keywords)))
(call-interactively #'denote)))

(defalias 'denote-create-note-using-type (symbol-function 'denote-type))

;;;;; The `denote-date' command

(defvar denote--date-history nil
"Minibuffer history of `denote--date-prompt'.")

Expand All @@ -830,58 +877,41 @@ is set to \\='(file-type title keywords)."
"DATE and TIME for note (e.g. 2022-06-16 14:30): "
nil 'denote--date-history))

(defun denote--date-add-current-time (date)
"Add current time to DATE, if necessary.
The idea is to turn 2020-01-15 into 2020-01-15 16:19 so that the
hour and minute component is not left to 00:00.
(defvar denote--subdir-history nil
"Minibuffer history of `denote-subdirectory'.")

This reduces the burden on the user who would otherwise need to
input that value in order to avoid the error of duplicate
identifiers.
(defun denote--subdirs-completion-table (dirs)
"Match DIRS as a completion table."
(let* ((def (car denote--subdir-history))
(table (denote--completion-table 'file dirs))
(prompt (if def
(format "Select subdirectory [%s]: " def)
"Select subdirectory: ")))
(completing-read prompt table nil t nil 'denote--subdir-history def)))

It also addresses a difference between Emacs 28 and Emacs 29
where the former does not read dates without a time component."
(if (<= (length date) 10)
(format "%s %s" date (format-time-string "%H:%M:%S" (current-time)))
date))
(defun denote--subdirs-prompt ()
"Handle user input on choice of subdirectory."
(let* ((root (directory-file-name (denote-directory)))
(subdirs (denote--subdirs))
(dirs (push root subdirs)))
(denote--subdirs-completion-table dirs)))

(defun denote--valid-date (date)
"Return DATE if parsed by `date-to-time', else signal error."
(let ((datetime (denote--date-add-current-time date)))
(date-to-time datetime)))
;;;;; Convenience functions

(defun denote--buffer-file-names ()
"Return file names of active buffers."
(mapcar
(lambda (name)
(file-name-nondirectory name))
(seq-filter
(lambda (name) (denote--only-note-p name))
(delq nil
(mapcar
(lambda (buf)
(buffer-file-name buf))
(buffer-list))))))
(defalias 'denote-create-note (symbol-function 'denote))

;; This should only be relevant for `denote-date', otherwise the
;; identifier is always unique (we trust that no-one writes multiple
;; notes within fractions of a second).
(defun denote--id-exists-p (identifier)
"Return non-nil if IDENTIFIER already exists."
(let ((current-buffer-name (when (buffer-file-name)
(file-name-nondirectory (buffer-file-name)))))
(or (seq-some (lambda (file)
(string-match-p (concat "\\`" identifier) file))
(delete current-buffer-name (denote--buffer-file-names)))
(delete current-buffer-name
(denote--directory-files-matching-regexp
(concat "\\`" identifier))))))
;;;###autoload
(defun denote-type ()
"Create note while prompting for a file type.
(defun denote--barf-duplicate-id (identifier)
"Throw a user-error if IDENTIFIER already exists else return t."
(if (denote--id-exists-p identifier)
(user-error "`%s' already exists; aborting new note creation" identifier)
t))
This is the equivalent to calling `denote' when `denote-prompts'
is set to \\='(file-type title keywords)."
(declare (interactive-only t))
(interactive)
(let ((denote-prompts '(file-type title keywords)))
(call-interactively #'denote)))

(defalias 'denote-create-note-using-type (symbol-function 'denote-type))

;;;###autoload
(defun denote-date ()
Expand All @@ -899,40 +929,6 @@ is set to \\='(date title keywords)."

(defalias 'denote-create-note-using-date (symbol-function 'denote-date))

;;;;; The `denote-subdirectory' command

(defvar denote--subdir-history nil
"Minibuffer history of `denote-subdirectory'.")

(defun denote--subdirs ()
"Return list of subdirectories in variable `denote-directory'."
(seq-remove
(lambda (filename)
;; TODO 2022-07-03: Generalise for all VC backends. Which ones?
;;
;; TODO 2022-07-03: Maybe it makes sense to also allow the user to
;; specify a blocklist of directories that should always be
;; excluded?
(or (string-match-p "\\.git" filename)
(not (file-directory-p filename))))
(directory-files-recursively (denote-directory) ".*" t t)))

(defun denote--subdirs-completion-table (dirs)
"Match DIRS as a completion table."
(let* ((def (car denote--subdir-history))
(table (denote--completion-table 'file dirs))
(prompt (if def
(format "Select subdirectory [%s]: " def)
"Select subdirectory: ")))
(completing-read prompt table nil t nil 'denote--subdir-history def)))

(defun denote--subdirs-prompt ()
"Handle user input on choice of subdirectory."
(let* ((root (directory-file-name (denote-directory)))
(subdirs (denote--subdirs))
(dirs (push root subdirs)))
(denote--subdirs-completion-table dirs)))

;;;###autoload
(defun denote-subdirectory ()
"Create note while prompting for a subdirectory.
Expand Down

0 comments on commit 4fd7e0d

Please sign in to comment.