Skip to content

A custom identifier that is a simple incrementing number scheme

This is a sample implementation of custom Denote identifiers (Define a completely custom identifier scheme).

In its simplest form, a custom Denote identifier can be an integer that is incremented each time a new file name is produced. In the following code block, my~denote-get-identifier is the function we bind to the variable denote-get-identifier-function, which then makes Denote use the custom identifier scheme.

emacs-lisp
(setq denote-get-identifier-function #'my-denote-get-identifier)

(defun my-denote--get-all-used-ids ()
  (when-let* ((ids (make-hash-table :test 'equal))
              (files (denote-directory-files nil nil nil nil :has-identifier))
              (file-names (mapcar #'file-name-nondirectory files))
              (names (append file-names (denote--buffer-file-names))))
    (dolist (name names)
      (when-let* ((id (denote-retrieve-filename-identifier name)))
        (puthash id t ids)))
    ids))

(defun my-denote-get-identifier (initial-identifier _date)
  (let ((identifiers (or denote-used-identifiers (my-denote--get-all-used-ids))))
    (cond
     ((and initial-identifier (not (gethash initial-identifier identifiers)))
      initial-identifier)
     (t
      (number-to-string
       (let ((keys nil))
         ;; NOTE 2025-09-19: The `string-to-number' is done with the
         ;; assumption that this is always an integer.  It will break
         ;; with the default Denote identifier scheme or, indeed,
         ;; anything that is not strictly numeric.
         (when identifiers (maphash (lambda (key value) (push (string-to-number key) keys)) identifiers))
         (if keys
             (+ (apply #'max keys) 1)
           1)))))))



;; And this is for testing purposes

;; (let ((denote-directory "/tmp/"))
;;   (my-denote-get-identifier nil nil))
;;
;; (let* ((denote-used-identifiers (make-hash-table :test #'equal))
;;        (_ (progn
;;             (puthash "1" 1 denote-used-identifiers)
;;             (puthash "2" 2 denote-used-identifiers)
;;             (puthash "3" 3 denote-used-identifiers))))
;;   (my-denote-get-identifier "1" nil))

An alternative for the above:

emacs-lisp
(defun my-denote--find-first-unused-id-as-number (id)
  (while (gethash id denote-used-identifiers)
    (setq id (number-to-string (1+ (string-to-number id)))))
  id)

(defun my-denote-generate-identifier-as-number (initial-identifier _date)
  (let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
    (cond (;; Always use the supplied initial-identifier if possible,
           ;; regardless of format.
           (and initial-identifier
                (not (gethash initial-identifier denote-used-identifiers)))
           initial-identifier)
          (;; If the supplied initial-identifier is already used, but
           ;; it has the right format, make is unique.
           (and initial-identifier
                (string-match-p "[1-9][0-9]*" initial-identifier))
           (my-denote--find-first-unused-id-as-number initial-identifier))
          (;; Else, the supplied initial-identifier is nil or it is
           ;; already used or it does not match the supplied
           ;; format. Ignore it and generate a valid identifier with
           ;; the right format.
           t
           (my-denote--find-first-unused-id-as-number "1")))))