mirror of
https://github.com/protesilaos/denote.git
synced 2026-09-14 01:06:24 -04:00
Compare commits
No commits in common. "main" and "3.0.7" have entirely different histories.
2087
CHANGELOG.org
2087
CHANGELOG.org
File diff suppressed because it is too large
Load diff
4
COPYING
4
COPYING
|
|
@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
|
|||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 2021 Protesilaos
|
||||
Copyright (C) 2021 Protesilaos Stavrou
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) 2021 Protesilaos
|
||||
<program> Copyright (C) 2021 Protesilaos Stavrou
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
# denote: Simple notes with an efficient file-naming scheme
|
||||
|
||||
New to Denote? Start reading this from the manual:
|
||||
<https://protesilaos.com/emacs/denote#h:c54bedb4-5377-4dbd-853c-5870ace6eb33>.
|
||||
|
||||
* * *
|
||||
|
||||
Denote is a simple note-taking tool for Emacs. It is based on the idea
|
||||
that notes should follow a predictable and descriptive file-naming
|
||||
scheme. The file name must offer a clear indication of what the note is
|
||||
|
|
|
|||
4792
README.org
4792
README.org
File diff suppressed because it is too large
Load diff
252
denote-journal-extras.el
Normal file
252
denote-journal-extras.el
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
;;; denote-journal-extras.el --- Convenience functions for daily journaling -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This is a set of optional convenience functions that used to be
|
||||
;; provided in the Denote manual. They facilitate the use of Denote
|
||||
;; for daily journaling.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote)
|
||||
|
||||
(defgroup denote-journal-extras nil
|
||||
"Denote for daily journaling."
|
||||
:group 'denote
|
||||
:link '(info-link "(denote) Top")
|
||||
:link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
|
||||
|
||||
(defcustom denote-journal-extras-directory
|
||||
(expand-file-name "journal" denote-directory)
|
||||
"Directory for storing daily journal entries.
|
||||
This can either be the same as the variable `denote-directory' or
|
||||
a subdirectory of it.
|
||||
|
||||
A value of nil means to use the variable `denote-directory'.
|
||||
Journal entries will thus be in a flat listing together with all
|
||||
other notes. They can still be retrieved easily by searching for
|
||||
the `denote-journal-extras-keyword'."
|
||||
:group 'denote-journal-extras
|
||||
:type '(choice (directory :tag "Provide directory path (is created if missing)")
|
||||
(const :tag "Use the `denote-directory'" nil)))
|
||||
|
||||
(defcustom denote-journal-extras-keyword "journal"
|
||||
"Single word keyword to tag journal entries.
|
||||
It is used by `denote-journal-extras-new-entry' to add a keyword
|
||||
to the newly created file."
|
||||
:group 'denote-journal-extras
|
||||
:type 'string)
|
||||
|
||||
(defcustom denote-journal-extras-title-format 'day-date-month-year-24h
|
||||
"Date format to construct the title with `denote-journal-extras-new-entry'.
|
||||
The value is either a symbol or an arbitrary string that is
|
||||
passed to `format-time-string' (consult its documentation for the
|
||||
technicalities).
|
||||
|
||||
Acceptable symbols and their corresponding styles are:
|
||||
|
||||
| Symbol | Style |
|
||||
|-------------------------+-----------------------------------|
|
||||
| day | Monday |
|
||||
| day-date-month-year | Monday 19 September 2023 |
|
||||
| day-date-month-year-24h | Monday 19 September 2023 20:49 |
|
||||
| day-date-month-year-12h | Monday 19 September 2023 08:49 PM |
|
||||
|
||||
With a nil value, make `denote-journal-extras-new-entry' prompt
|
||||
for a title."
|
||||
:group 'denote-journal-extras
|
||||
:type '(choice
|
||||
(const :tag "Prompt for title with `denote-journal-extras-new-entry'" nil)
|
||||
(const :tag "Monday"
|
||||
:doc "The `format-time-string' is: %A"
|
||||
day)
|
||||
(const :tag "Monday 19 September 2023"
|
||||
:doc "The `format-time-string' is: %A %e %B %Y"
|
||||
day-date-month-year)
|
||||
(const :tag "Monday 19 September 2023 20:49"
|
||||
:doc "The `format-time-string' is: %A %e %B %Y %H:%M"
|
||||
day-date-month-year-24h)
|
||||
(const :tag "Monday 19 September 2023 08:49 PM"
|
||||
:doc "The `format-time-string' is: %A %e %B %Y %I:%M %^p"
|
||||
day-date-month-year-12h)
|
||||
(string :tag "Custom string with `format-time-string' specifiers")))
|
||||
|
||||
(defcustom denote-journal-extras-hook nil
|
||||
"Normal hook called after `denote-journal-extras-new-entry'.
|
||||
Use this to, for example, set a timer after starting a new
|
||||
journal entry (refer to the `tmr' package on GNU ELPA)."
|
||||
:group 'denote-journal-extras
|
||||
:type 'hook)
|
||||
|
||||
(defun denote-journal-extras-directory ()
|
||||
"Make the variable `denote-journal-extras-directory' and its parents."
|
||||
(if-let (((stringp denote-journal-extras-directory))
|
||||
(directory (file-name-as-directory (expand-file-name denote-journal-extras-directory))))
|
||||
(progn
|
||||
(when (not (file-directory-p denote-journal-extras-directory))
|
||||
(make-directory directory :parents))
|
||||
directory)
|
||||
(denote-directory)))
|
||||
|
||||
(defun denote-journal-extras-daily--title-format (&optional date)
|
||||
"Return present date in `denote-journal-extras-title-format' or prompt for title.
|
||||
With optional DATE, use it instead of the present date. DATE has
|
||||
the same format as that returned by `current-time'."
|
||||
(format-time-string
|
||||
(if (and denote-journal-extras-title-format
|
||||
(stringp denote-journal-extras-title-format))
|
||||
denote-journal-extras-title-format
|
||||
(pcase denote-journal-extras-title-format
|
||||
('day "%A")
|
||||
('day-date-month-year "%A %e %B %Y")
|
||||
('day-date-month-year-24h "%A %e %B %Y %H:%M")
|
||||
('day-date-month-year-12h "%A %e %B %Y %I:%M %^p")
|
||||
(_ (denote-title-prompt (format-time-string "%F" date)))))
|
||||
date))
|
||||
|
||||
(defun denote-journal-extras--get-template ()
|
||||
"Return template that has `journal' key in `denote-templates'.
|
||||
If no template with `journal' key exists but `denote-templates'
|
||||
is non-nil, prompt the user for a template among
|
||||
`denote-templates'. Else return nil.
|
||||
|
||||
Also see `denote-journal-extras-new-entry'."
|
||||
(if-let ((template (alist-get 'journal denote-templates)))
|
||||
template
|
||||
(when denote-templates
|
||||
(denote-template-prompt))))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-journal-extras-new-entry (&optional date)
|
||||
"Create a new journal entry in variable `denote-journal-extras-directory'.
|
||||
Use `denote-journal-extras-keyword' as a keyword for the newly
|
||||
created file. Set the title of the new entry according to the
|
||||
value of the user option `denote-journal-extras-title-format'.
|
||||
|
||||
With optional DATE as a prefix argument, prompt for a date. If
|
||||
`denote-date-prompt-use-org-read-date' is non-nil, use the Org
|
||||
date selection module.
|
||||
|
||||
When called from Lisp DATE is a string and has the same format as
|
||||
that covered in the documentation of the `denote' function. It
|
||||
is internally processed by `denote-parse-date'."
|
||||
(interactive (list (when current-prefix-arg (denote-date-prompt))))
|
||||
(let ((internal-date (denote-parse-date date))
|
||||
(denote-directory (denote-journal-extras-directory)))
|
||||
(denote
|
||||
(denote-journal-extras-daily--title-format internal-date)
|
||||
`(,denote-journal-extras-keyword)
|
||||
nil nil date
|
||||
(denote-journal-extras--get-template))
|
||||
(run-hooks 'denote-journal-extras-hook)))
|
||||
|
||||
(defun denote-journal-extras--entry-today (&optional date)
|
||||
"Return list of files matching a journal for today or optional DATE.
|
||||
DATE has the same format as that returned by `denote-parse-date'."
|
||||
(let* ((identifier (format "%sT[0-9]\\{6\\}" (format-time-string "%Y%m%d" date)))
|
||||
(files (denote-directory-files identifier))
|
||||
(keyword (concat "_" (regexp-quote denote-journal-extras-keyword))))
|
||||
(seq-filter
|
||||
(lambda (file)
|
||||
(string-match-p keyword file))
|
||||
files)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-journal-extras-new-or-existing-entry (&optional date)
|
||||
"Locate an existing journal entry or create a new one.
|
||||
A journal entry is one that has `denote-journal-extras-keyword' as
|
||||
part of its file name.
|
||||
|
||||
If there are multiple journal entries for the current date,
|
||||
prompt for one using minibuffer completion. If there is only
|
||||
one, visit it outright. If there is no journal entry, create one
|
||||
by calling `denote-journal-extra-new-entry'.
|
||||
|
||||
With optional DATE as a prefix argument, prompt for a date. If
|
||||
`denote-date-prompt-use-org-read-date' is non-nil, use the Org
|
||||
date selection module.
|
||||
|
||||
When called from Lisp, DATE is a string and has the same format
|
||||
as that covered in the documentation of the `denote' function.
|
||||
It is internally processed by `denote-parse-date'."
|
||||
(interactive
|
||||
(list
|
||||
(when current-prefix-arg
|
||||
(denote-date-prompt))))
|
||||
(let* ((internal-date (denote-parse-date date))
|
||||
(files (denote-journal-extras--entry-today internal-date)))
|
||||
(cond
|
||||
((length> files 1)
|
||||
(find-file (completing-read "Select journal entry: " files nil :require-match)))
|
||||
(files
|
||||
(find-file (car files)))
|
||||
(t
|
||||
(denote-journal-extras-new-entry date)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-journal-extras-link-or-create-entry (&optional date id-only)
|
||||
"Use `denote-link' on journal entry, creating it if necessary.
|
||||
A journal entry is one that has `denote-journal-extras-keyword' as
|
||||
part of its file name.
|
||||
|
||||
If there are multiple journal entries for the current date,
|
||||
prompt for one using minibuffer completion. If there is only
|
||||
one, link to it outright. If there is no journal entry, create one
|
||||
by calling `denote-journal-extra-new-entry' and link to it.
|
||||
|
||||
With optional DATE as a prefix argument, prompt for a date. If
|
||||
`denote-date-prompt-use-org-read-date' is non-nil, use the Org
|
||||
date selection module.
|
||||
|
||||
When called from Lisp, DATE is a string and has the same format
|
||||
as that covered in the documentation of the `denote' function.
|
||||
It is internally processed by `denote-parse-date'.
|
||||
|
||||
With optional ID-ONLY as a prefix argument create a link that
|
||||
consists of just the identifier. Else try to also include the
|
||||
file's title. This has the same meaning as in `denote-link'."
|
||||
(interactive
|
||||
(pcase current-prefix-arg
|
||||
('(16) (list (denote-date-prompt) :id-only))
|
||||
('(4) (list (denote-date-prompt)))))
|
||||
(let* ((internal-date (denote-parse-date date))
|
||||
(files (denote-journal-extras--entry-today internal-date))
|
||||
(path))
|
||||
(cond
|
||||
((length> files 1)
|
||||
(setq path (completing-read "Select journal entry: " files nil :require-match)))
|
||||
(files
|
||||
(setq path (car files)))
|
||||
(t
|
||||
(save-window-excursion
|
||||
(denote-journal-extras-new-entry date)
|
||||
(save-buffer)
|
||||
(setq path (buffer-file-name)))))
|
||||
(denote-link path
|
||||
(denote-filetype-heuristics (buffer-file-name))
|
||||
(denote--link-get-description path)
|
||||
id-only)))
|
||||
|
||||
(provide 'denote-journal-extras)
|
||||
;;; denote-journal-extras.el ends here
|
||||
70
denote-org-dblock.el
Normal file
70
denote-org-dblock.el
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
;;; denote-org-dblock.el --- Compatibility alieases for Denote Org Dynamic blocks -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Authors: Elias Storms <elias.storms@gmail.com>,
|
||||
;; Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Deprecated-since: 3.0.0
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This file defines compatibility aliases for Org dynamic blocks set
|
||||
;; up by Denote. The new source of these is the denote-org-extras.el.
|
||||
;;
|
||||
;; Below is the old commentary.
|
||||
;;
|
||||
;; * * *
|
||||
;;
|
||||
;; This file defines Org dynamic blocks using the facility described
|
||||
;; in the Org manual. Evaluate this:
|
||||
;;
|
||||
;; (info "(org) Dynamic Blocks")
|
||||
;;
|
||||
;; The dynamic blocks defined herein are documented at length in the
|
||||
;; Denote manual. See the following node and its subsections:
|
||||
;;
|
||||
;; (info "(denote) Use Org dynamic blocks")
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote-org-extras)
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'denote-org-dblock-insert-links
|
||||
'denote-org-extras-dblock-insert-links
|
||||
"3.0.0")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'denote-org-dblock-insert-backlinks
|
||||
'denote-org-extras-dblock-insert-backlinks
|
||||
"3.0.0")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'denote-org-dblock-insert-files
|
||||
'denote-org-extras-dblock-insert-files
|
||||
"3.0.0")
|
||||
|
||||
(display-warning
|
||||
'denote
|
||||
"`denote-org-dblock.el' is obsolete; use `denote-org-extras.el'"
|
||||
:warning)
|
||||
|
||||
(provide 'denote-org-dblock)
|
||||
;;; denote-org-dblock.el ends here
|
||||
590
denote-org-extras.el
Normal file
590
denote-org-extras.el
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
;;; denote-org-extras.el --- Denote extensions for Org mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; WORK-IN-PROGRESS
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote)
|
||||
(require 'denote-sort)
|
||||
(require 'org)
|
||||
|
||||
;;;; Link to file and heading
|
||||
|
||||
(defun denote-org-extras--get-outline (file)
|
||||
"Return `outline-regexp' headings and line numbers of FILE."
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(let ((outline-regexp (format "^\\(?:%s\\)" (or (bound-and-true-p outline-regexp) "[*\^L]+")))
|
||||
candidates)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (if (bound-and-true-p outline-search-function)
|
||||
(funcall outline-search-function)
|
||||
(re-search-forward outline-regexp nil t))
|
||||
(push
|
||||
;; NOTE 2024-01-20: The -5 (minimum width) is a
|
||||
;; sufficiently high number to keep the alignment
|
||||
;; consistent in most cases. Larger files will simply
|
||||
;; shift the heading text in minibuffer, but this is not an
|
||||
;; issue anymore.
|
||||
(format "%-5s %s"
|
||||
(line-number-at-pos (point))
|
||||
(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
|
||||
candidates)
|
||||
(goto-char (1+ (line-end-position)))))
|
||||
(if candidates
|
||||
(nreverse candidates)
|
||||
(user-error "No outline")))))
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'denote-org-extras--outline-prompt
|
||||
'denote-org-extras-outline-prompt
|
||||
"3.1.0")
|
||||
|
||||
(defun denote-org-extras-outline-prompt (&optional file)
|
||||
"Prompt for outline among headings retrieved by `denote-org-extras--get-outline'.
|
||||
With optional FILE use the outline of it, otherwise use that of
|
||||
the current file."
|
||||
(let ((current-file (or file buffer-file-name)))
|
||||
(completing-read
|
||||
(format "Select heading inside `%s': "
|
||||
(propertize (file-name-nondirectory current-file) 'face 'denote-faces-prompt-current-name))
|
||||
(denote--completion-table-no-sort 'imenu (denote-org-extras--get-outline current-file))
|
||||
nil :require-match)))
|
||||
|
||||
(defun denote-org-extras--get-heading-and-id-from-line (line file)
|
||||
"Return heading text and CUSTOM_ID from the given LINE in FILE."
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- line))
|
||||
(cons (denote-link-ol-get-heading) (denote-link-ol-get-id)))))
|
||||
|
||||
(defun denote-org-extras-format-link-with-heading (file heading-id description)
|
||||
"Prepare link to FILE with HEADING-ID using DESCRIPTION."
|
||||
(when (region-active-p)
|
||||
(setq description (buffer-substring-no-properties (region-beginning) (region-end)))
|
||||
(denote--delete-active-region-content))
|
||||
(format "[[denote:%s::#%s][%s]]"
|
||||
(denote-retrieve-filename-identifier file)
|
||||
heading-id
|
||||
description))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-link-to-heading ()
|
||||
"Link to file and then specify a heading to extend the link to.
|
||||
|
||||
The resulting link has the following pattern:
|
||||
|
||||
[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]].
|
||||
|
||||
Because only Org files can have links to individual headings,
|
||||
limit the list of possible files to those which include the .org
|
||||
file extension (remember that Denote works with many file types,
|
||||
per the user option `denote-file-type').
|
||||
|
||||
The user option `denote-org-extras-store-link-to-heading'
|
||||
determined whether the `org-store-link' function can save a link
|
||||
to the current heading. Such links look the same as those of
|
||||
this command, though the functionality defined herein is
|
||||
independent of it.
|
||||
|
||||
To only link to a file, use the `denote-link' command.
|
||||
|
||||
Also see `denote-org-extras-backlinks-for-heading'."
|
||||
(declare (interactive-only t))
|
||||
(interactive nil org-mode)
|
||||
(unless (derived-mode-p 'org-mode)
|
||||
(user-error "Links to headings only work between Org files"))
|
||||
(when-let ((file (denote-file-prompt ".*\\.org"))
|
||||
(file-text (denote--link-get-description file))
|
||||
(heading (denote-org-extras-outline-prompt file))
|
||||
(line (string-to-number (car (split-string heading "\t"))))
|
||||
(heading-data (denote-org-extras--get-heading-and-id-from-line line file))
|
||||
(heading-text (car heading-data))
|
||||
(heading-id (cdr heading-data))
|
||||
(description (denote-link-format-heading-description file-text heading-text)))
|
||||
(insert (denote-org-extras-format-link-with-heading file heading-id description))))
|
||||
|
||||
;;;; Heading backlinks
|
||||
|
||||
(defun denote-org-extras--get-file-id-and-heading-id (file)
|
||||
"Return IDENTIFIER::#ORG-HEADING-CUSTOM-ID string for FILE heading at point."
|
||||
(if-let ((heading-id (org-entry-get (point) "CUSTOM_ID")))
|
||||
(concat (denote-retrieve-filename-identifier-with-error file) "::#" heading-id)
|
||||
(error "No CUSTOM_ID for heading at point in file `%s'" file)))
|
||||
|
||||
(defun denote-org-extras--get-backlinks-buffer-name (text)
|
||||
"Format a buffer name for `denote-org-extras-backlinks-for-heading' with TEXT."
|
||||
(format "*Denote HEADING backlinks for %S*" text))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-backlinks-for-heading ()
|
||||
"Produce backlinks for the current heading.
|
||||
This otherwise has the same behaviour as `denote-backlinks'---refer to
|
||||
that for the details.
|
||||
|
||||
Also see `denote-org-extras-link-to-heading'."
|
||||
(interactive)
|
||||
(when-let ((heading-id (denote-org-extras--get-file-id-and-heading-id buffer-file-name))
|
||||
(heading-text (substring-no-properties (denote-link-ol-get-heading))))
|
||||
(denote-link--prepare-backlinks heading-id ".*\\.org" (denote-org-extras--get-backlinks-buffer-name heading-text))))
|
||||
|
||||
;;;; Extract subtree into its own note
|
||||
|
||||
(defun denote-org-extras--get-heading-date ()
|
||||
"Try to return a timestamp for the current Org heading.
|
||||
This can be used as the value for the DATE argument of the
|
||||
`denote' command."
|
||||
(when-let ((pos (point))
|
||||
(timestamp (or (org-entry-get pos "DATE")
|
||||
(org-entry-get pos "CREATED")
|
||||
(org-entry-get pos "CLOSED"))))
|
||||
(date-to-time timestamp)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-extract-org-subtree ()
|
||||
"Create new Denote note using the current Org subtree as input.
|
||||
Remove the subtree from its current file and move its contents
|
||||
into a new Denote file (a subtree is a heading with all of its
|
||||
contents, including subheadings).
|
||||
|
||||
Take the text of the subtree's top level heading and use it as
|
||||
the title of the new note.
|
||||
|
||||
If the heading has any tags, use them as the keywords of the new
|
||||
note. If the Org file has any #+filetags use them as well (Org's
|
||||
filetags are inherited by the headings). If none of these are
|
||||
true and the user option `denote-prompts' includes an entry for
|
||||
keywords, then prompt for keywords. Else do not include any
|
||||
keywords.
|
||||
|
||||
If the heading has a PROPERTIES drawer, retain it for further
|
||||
review.
|
||||
|
||||
If the heading's PROPERTIES drawer includes a DATE or CREATED
|
||||
property, or there exists a CLOSED statement with a timestamp
|
||||
value, use that to derive the date (or date and time) of the new
|
||||
note (if there is only a date, the time is taken as 00:00). If
|
||||
more than one of these is present, the order of preference is
|
||||
DATE, then CREATED, then CLOSED. If none of these is present,
|
||||
use the current time. If the `denote-prompts' includes an entry
|
||||
for a date, then prompt for a date at this stage (also see
|
||||
`denote-date-prompt-use-org-read-date').
|
||||
|
||||
For the rest, consult the value of the user option
|
||||
`denote-prompts' in the following scenaria:
|
||||
|
||||
- Optionally prompt for a subdirectory, otherwise produce the new
|
||||
note in the variable `denote-directory'.
|
||||
|
||||
- Optionally prompt for a file signature, otherwise do not use
|
||||
one.
|
||||
|
||||
Make the new note an Org file regardless of the value of
|
||||
`denote-file-type'."
|
||||
(interactive nil org-mode)
|
||||
(unless (derived-mode-p 'org-mode)
|
||||
(user-error "Headings can only be extracted from Org files"))
|
||||
(if-let ((text (org-get-entry))
|
||||
(heading (denote-link-ol-get-heading)))
|
||||
(let ((tags (org-get-tags))
|
||||
(date (denote-org-extras--get-heading-date))
|
||||
subdirectory
|
||||
signature)
|
||||
(dolist (prompt denote-prompts)
|
||||
(pcase prompt
|
||||
('keywords (when (not tags)
|
||||
(setq tags (denote-keywords-prompt))))
|
||||
('subdirectory (setq subdirectory (denote-subdirectory-prompt)))
|
||||
('date (when (not date) (setq date (denote-date-prompt))))
|
||||
('signature (setq signature (denote-signature-prompt)))))
|
||||
(delete-region (org-entry-beginning-position)
|
||||
(save-excursion (org-end-of-subtree t) (point)))
|
||||
(denote heading tags 'org subdirectory date nil signature)
|
||||
(insert text))
|
||||
(user-error "No subtree to extract; aborting")))
|
||||
|
||||
;;;; Convert links from `:denote' to `:file' and vice versa
|
||||
|
||||
;; TODO 2024-02-28: Do we need to convert between other link types? I
|
||||
;; think not, since the `denote:' type is modelled after the `file:'
|
||||
;; one.
|
||||
(defun denote-org-extras--get-link-type-regexp (type)
|
||||
"Return regexp for Org link TYPE.
|
||||
TYPE is a symbol of either `file' or `denote'.
|
||||
|
||||
The regexp consists of four groups. Group 1 is the link type, 2
|
||||
is the target, 3 is the target's search terms, and 4 is the
|
||||
description."
|
||||
(let ((group-1))
|
||||
(pcase type
|
||||
('denote (setq group-1 "denote"))
|
||||
('file (setq group-1 "file"))
|
||||
(_ (error "`%s' is an unknown link type" type)))
|
||||
(format "\\[\\[\\(?1:%s:\\)\\(?:\\(?2:.*?\\)\\(?3:::.*\\)?\\]\\|\\]\\)\\(?4:\\[\\(?:.*?\\)\\]\\)?\\]" group-1)))
|
||||
|
||||
(defun denote-org-extras--get-path (id)
|
||||
"Return file path to ID according to `org-link-file-path-type'."
|
||||
(if (or (eq org-link-file-path-type 'adaptive)
|
||||
(eq org-link-file-path-type 'relative))
|
||||
(denote-get-relative-path-by-id id)
|
||||
(denote-get-path-by-id id)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-convert-links-to-file-type ()
|
||||
"Convert denote: links to file: links in the current Org buffer.
|
||||
Ignore all other link types. Also ignore links that do not
|
||||
resolve to a file in the variable `denote-directory'."
|
||||
(interactive nil org-mode)
|
||||
(if (derived-mode-p 'org-mode)
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward (denote-org-extras--get-link-type-regexp 'denote) nil :no-error)
|
||||
(let* ((id (match-string-no-properties 2))
|
||||
(search (or (match-string-no-properties 3) ""))
|
||||
(desc (or (match-string-no-properties 4) ""))
|
||||
(file (save-match-data (denote-org-extras--get-path id))))
|
||||
(when id
|
||||
(let ((new-text (if desc
|
||||
(format "[[file:%s%s]%s]" file search desc)
|
||||
(format "[[file:%s%s]]" file search))))
|
||||
(replace-match new-text :fixed-case :literal)))))
|
||||
;; TODO 2024-02-28: notify how many changed.
|
||||
(message "Converted `denote:' links to `file:' links"))
|
||||
(user-error "The current file is not using Org mode")))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-convert-links-to-denote-type ()
|
||||
"Convert file: links to denote: links in the current Org buffer.
|
||||
Ignore all other link types. Also ignore file: links that do not
|
||||
point to a file with a Denote file name."
|
||||
(interactive nil org-mode)
|
||||
(if (derived-mode-p 'org-mode)
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward (denote-org-extras--get-link-type-regexp 'file) nil :no-error)
|
||||
(let* ((file (match-string-no-properties 2))
|
||||
(search (or (match-string-no-properties 3) ""))
|
||||
(desc (or (match-string-no-properties 4) ""))
|
||||
(id (save-match-data (denote-retrieve-filename-identifier file))))
|
||||
(when id
|
||||
(let ((new-text (if desc
|
||||
(format "[[denote:%s%s]%s]" id search desc)
|
||||
(format "[[denote:%s%s]]" id search))))
|
||||
(replace-match new-text :fixed-case :literal)))))
|
||||
;; TODO 2024-02-28: notify how many changed.
|
||||
(message "Converted as `file:' links to `denote:' links"))
|
||||
(user-error "The current file is not using Org mode")))
|
||||
|
||||
;;;; Org dynamic blocks
|
||||
|
||||
;; NOTE 2024-01-22 12:26:13 +0200: The following is copied from the
|
||||
;; now-deleted denote-org-dblock.el. Its original author was Elias
|
||||
;; Storms <elias.storms@gmail.com>, with substantial contributions and
|
||||
;; further developments by me (Protesilaos).
|
||||
|
||||
;; This section defines Org dynamic blocks using the facility described
|
||||
;; in the Org manual. Evaluate this:
|
||||
;;
|
||||
;; (info "(org) Dynamic Blocks")
|
||||
;;
|
||||
;; The dynamic blocks defined herein are documented at length in the
|
||||
;; Denote manual. See the following node and its subsections:
|
||||
;;
|
||||
;; (info "(denote) Use Org dynamic blocks")
|
||||
|
||||
;;;;; Common helper functions
|
||||
|
||||
(defun denote-org-extras-dblock--files (files-matching-regexp &optional sort-by-component reverse)
|
||||
"Return list of FILES-MATCHING-REGEXP in variable `denote-directory'.
|
||||
SORT-BY-COMPONENT and REVERSE have the same meaning as
|
||||
`denote-sort-files'. If both are nil, do not try to perform any
|
||||
sorting.
|
||||
|
||||
Also see `denote-org-extras-dblock--files-missing-only'."
|
||||
(cond
|
||||
((and sort-by-component reverse)
|
||||
(denote-sort-get-directory-files files-matching-regexp sort-by-component reverse :omit-current))
|
||||
(sort-by-component
|
||||
(denote-sort-get-directory-files files-matching-regexp sort-by-component nil :omit-current))
|
||||
(reverse
|
||||
(denote-sort-get-directory-files files-matching-regexp :no-component-specified reverse :omit-current))
|
||||
(t
|
||||
(denote-directory-files files-matching-regexp :omit-current))))
|
||||
|
||||
(defun denote-org-extras-dblock--get-missing-links (regexp)
|
||||
"Return list of missing links to all notes matching REGEXP.
|
||||
Missing links are those for which REGEXP does not have a match in
|
||||
the current buffer."
|
||||
(let ((found-files (denote-directory-files regexp :omit-current))
|
||||
(linked-files (denote-link--expand-identifiers denote-org-link-in-context-regexp)))
|
||||
(if-let ((final-files (seq-difference found-files linked-files)))
|
||||
final-files
|
||||
(message "All links matching `%s' are present" regexp)
|
||||
'())))
|
||||
|
||||
(defun denote-org-extras-dblock--files-missing-only (files-matching-regexp &optional sort-by-component reverse)
|
||||
"Return list of missing links to FILES-MATCHING-REGEXP.
|
||||
SORT-BY-COMPONENT and REVERSE have the same meaning as
|
||||
`denote-sort-files'. If both are nil, do not try to perform any
|
||||
sorting.
|
||||
|
||||
Also see `denote-org-extras-dblock--files'."
|
||||
(denote-sort-files
|
||||
(denote-org-extras-dblock--get-missing-links files-matching-regexp)
|
||||
sort-by-component
|
||||
reverse))
|
||||
|
||||
;;;;; Dynamic block to insert links
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-dblock-insert-links (regexp)
|
||||
"Create Org dynamic block to insert Denote links matching REGEXP."
|
||||
(interactive
|
||||
(list
|
||||
(denote-files-matching-regexp-prompt))
|
||||
org-mode)
|
||||
(org-create-dblock (list :name "denote-links"
|
||||
:regexp regexp
|
||||
:sort-by-component nil
|
||||
:reverse-sort nil
|
||||
:id-only nil))
|
||||
(org-update-dblock))
|
||||
|
||||
;; NOTE 2024-03-30: This is how the autoload is done in org.el.
|
||||
;;;###autoload
|
||||
(eval-after-load 'org
|
||||
'(progn
|
||||
(org-dynamic-block-define "denote-links" 'denote-org-extras-dblock-insert-links)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-dblock-write:denote-links (params)
|
||||
"Function to update `denote-links' Org Dynamic blocks.
|
||||
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
|
||||
(let* ((regexp (plist-get params :regexp))
|
||||
(rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp))
|
||||
(sort (plist-get params :sort-by-component))
|
||||
(reverse (plist-get params :reverse-sort))
|
||||
(block-name (plist-get params :block-name))
|
||||
(files (denote-org-extras-dblock--files rx sort reverse)))
|
||||
(when block-name (insert "#+name: " block-name "\n"))
|
||||
(denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting)
|
||||
(join-line))) ; remove trailing empty line
|
||||
|
||||
;;;;; Dynamic block to insert missing links
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-dblock-insert-missing-links (regexp)
|
||||
"Create Org dynamic block to insert Denote links matching REGEXP."
|
||||
(interactive
|
||||
(list
|
||||
(denote-files-matching-regexp-prompt))
|
||||
org-mode)
|
||||
(org-create-dblock (list :name "denote-missing-links"
|
||||
:regexp regexp
|
||||
:sort-by-component nil
|
||||
:reverse-sort nil
|
||||
:id-only nil))
|
||||
(org-update-dblock))
|
||||
|
||||
;; NOTE 2024-03-30: This is how the autoload is done in org.el.
|
||||
;;;###autoload
|
||||
(eval-after-load 'org
|
||||
'(progn
|
||||
(org-dynamic-block-define "denote-missing-links" 'denote-org-extras-dblock-insert-links)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-dblock-write:denote-missing-links (params)
|
||||
"Function to update `denote-links' Org Dynamic blocks.
|
||||
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
|
||||
(let* ((regexp (plist-get params :regexp))
|
||||
(rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp))
|
||||
(sort (plist-get params :sort-by-component))
|
||||
(reverse (plist-get params :reverse-sort))
|
||||
(block-name (plist-get params :block-name))
|
||||
(files (denote-org-extras-dblock--files-missing-only rx sort reverse)))
|
||||
(when block-name (insert "#+name: " block-name "\n"))
|
||||
(denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting)
|
||||
(join-line))) ; remove trailing empty line
|
||||
|
||||
;;;;; Dynamic block to insert backlinks
|
||||
|
||||
(defun denote-org-extras-dblock--maybe-sort-backlinks (files sort-by-component reverse)
|
||||
"Sort backlink FILES if SORT-BY-COMPONENT and/or REVERSE is non-nil."
|
||||
(cond
|
||||
((and sort-by-component reverse)
|
||||
(denote-sort-files files sort-by-component reverse))
|
||||
(sort-by-component
|
||||
(denote-sort-files files sort-by-component))
|
||||
(reverse
|
||||
(denote-sort-files files :no-component-specified reverse))
|
||||
(t
|
||||
files)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-dblock-insert-backlinks ()
|
||||
"Create Org dynamic block to insert Denote backlinks to current file."
|
||||
(interactive nil org-mode)
|
||||
(org-create-dblock (list :name "denote-backlinks"
|
||||
:sort-by-component nil
|
||||
:reverse-sort nil
|
||||
:id-only nil))
|
||||
(org-update-dblock))
|
||||
|
||||
;; NOTE 2024-03-30: This is how the autoload is done in org.el.
|
||||
;;;###autoload
|
||||
(eval-after-load 'org
|
||||
'(progn
|
||||
(org-dynamic-block-define "denote-backlinks" 'denote-org-extras-dblock-insert-backlinks)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-dblock-write:denote-backlinks (params)
|
||||
"Function to update `denote-backlinks' Org Dynamic blocks.
|
||||
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
|
||||
(when-let ((files (denote-link-return-backlinks)))
|
||||
(let* ((sort (plist-get params :sort-by-component))
|
||||
(reverse (plist-get params :reverse-sort))
|
||||
(files (denote-org-extras-dblock--maybe-sort-backlinks files sort reverse)))
|
||||
(denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting)
|
||||
(join-line)))) ; remove trailing empty line
|
||||
|
||||
;;;;; Dynamic block to insert entire file contents
|
||||
|
||||
(defun denote-org-extras-dblock--get-file-contents (file &optional no-front-matter add-links)
|
||||
"Insert the contents of FILE.
|
||||
With optional NO-FRONT-MATTER as non-nil, try to remove the front
|
||||
matter from the top of the file. If NO-FRONT-MATTER is a number,
|
||||
remove that many lines starting from the top. If it is any other
|
||||
non-nil value, delete from the top until the first blank line.
|
||||
|
||||
With optional ADD-LINKS as non-nil, first insert a link to the
|
||||
file and then insert its contents. In this case, format the
|
||||
contents as a typographic list. If ADD-LINKS is `id-only', then
|
||||
insert links as `denote-link' does when supplied with an ID-ONLY
|
||||
argument."
|
||||
(when (denote-file-is-note-p file)
|
||||
(with-temp-buffer
|
||||
(when add-links
|
||||
(insert
|
||||
(format "- %s\n\n"
|
||||
(denote-format-link
|
||||
file
|
||||
(denote--link-get-description file)
|
||||
'org
|
||||
(eq add-links 'id-only)))))
|
||||
(let ((beginning-of-contents (point)))
|
||||
(insert-file-contents file)
|
||||
(when no-front-matter
|
||||
(delete-region
|
||||
(if (natnump no-front-matter)
|
||||
(progn (forward-line no-front-matter) (line-beginning-position))
|
||||
(1+ (re-search-forward "^$" nil :no-error 1)))
|
||||
beginning-of-contents))
|
||||
(when add-links
|
||||
(indent-region beginning-of-contents (point-max) 2)))
|
||||
(buffer-string))))
|
||||
|
||||
(defvar denote-org-extras-dblock-file-contents-separator
|
||||
(concat "\n\n" (make-string 50 ?-) "\n\n\n")
|
||||
"Fallback separator used by `denote-org-extras-dblock-add-files'.")
|
||||
|
||||
(defun denote-org-extras-dblock--separator (separator)
|
||||
"Return appropriate value of SEPARATOR for `denote-org-extras-dblock-add-files'."
|
||||
(cond
|
||||
((null separator) "")
|
||||
((stringp separator) separator)
|
||||
(t denote-org-extras-dblock-file-contents-separator)))
|
||||
|
||||
(defun denote-org-extras-dblock-add-files (regexp &optional separator no-front-matter add-links sort-by-component reverse)
|
||||
"Insert files matching REGEXP.
|
||||
|
||||
Seaprate them with the optional SEPARATOR. If SEPARATOR is nil,
|
||||
use the `denote-org-extras-dblock-file-contents-separator'.
|
||||
|
||||
If optional NO-FRONT-MATTER is non-nil try to remove the front
|
||||
matter from the top of the file. Do it by finding the first
|
||||
blank line, starting from the top of the buffer.
|
||||
|
||||
If optional ADD-LINKS is non-nil, first insert a link to the file
|
||||
and then insert its contents. In this case, format the contents
|
||||
as a typographic list.
|
||||
|
||||
If optional SORT-BY-COMPONENT is a symbol among `denote-sort-components',
|
||||
sort files matching REGEXP by the corresponding Denote file name
|
||||
component. If the symbol is not among `denote-sort-components',
|
||||
fall back to the default identifier-based sorting.
|
||||
|
||||
If optional REVERSE is non-nil reverse the sort order."
|
||||
(let* ((files (denote-org-extras-dblock--files regexp sort-by-component reverse))
|
||||
(files-contents (mapcar
|
||||
(lambda (file) (denote-org-extras-dblock--get-file-contents file no-front-matter add-links))
|
||||
files)))
|
||||
(insert (string-join files-contents (denote-org-extras-dblock--separator separator)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-org-extras-dblock-insert-files (regexp sort-by-component)
|
||||
"Create Org dynamic block to insert Denote files matching REGEXP.
|
||||
Sort the files according to SORT-BY-COMPONENT, which is a symbol
|
||||
among `denote-sort-components'."
|
||||
(interactive
|
||||
(list
|
||||
(denote-files-matching-regexp-prompt)
|
||||
(denote-sort-component-prompt))
|
||||
org-mode)
|
||||
(org-create-dblock (list :name "denote-files"
|
||||
:regexp regexp
|
||||
:sort-by-component sort-by-component
|
||||
:reverse-sort nil
|
||||
:no-front-matter nil
|
||||
:file-separator nil
|
||||
:add-links nil))
|
||||
(org-update-dblock))
|
||||
|
||||
;; NOTE 2024-03-30: This is how the autoload is done in org.el.
|
||||
;;;###autoload
|
||||
(eval-after-load 'org
|
||||
'(progn
|
||||
(org-dynamic-block-define "denote-files" 'denote-org-extras-dblock-insert-files)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-dblock-write:denote-files (params)
|
||||
"Function to update `denote-files' Org Dynamic blocks.
|
||||
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
|
||||
(let* ((regexp (plist-get params :regexp))
|
||||
(rx (if (listp regexp) (macroexpand `(rx ,regexp)) regexp))
|
||||
(sort (plist-get params :sort-by-component))
|
||||
(reverse (plist-get params :reverse-sort))
|
||||
(block-name (plist-get params :block-name))
|
||||
(separator (plist-get params :file-separator))
|
||||
(no-f-m (plist-get params :no-front-matter))
|
||||
(add-links (plist-get params :add-links)))
|
||||
(when block-name (insert "#+name: " block-name "\n"))
|
||||
(when rx (denote-org-extras-dblock-add-files rx separator no-f-m add-links sort reverse)))
|
||||
(join-line)) ; remove trailing empty line
|
||||
|
||||
|
||||
(provide 'denote-org-extras)
|
||||
;;; denote-org-extras.el ends here
|
||||
159
denote-rename-buffer.el
Normal file
159
denote-rename-buffer.el
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
;;; denote-rename-buffer.el --- Rename Denote buffers to be shorter and easier to read -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Rename Denote buffers to be shorter and easier to read. Enable
|
||||
;; `denote-rename-buffer-mode' to automatically rename the buffer of a
|
||||
;; Denote file. The renaming function is specified in the user option
|
||||
;; `denote-rename-buffer-function'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote)
|
||||
|
||||
(defgroup denote-rename-buffer nil
|
||||
"Rename Denote buffers to be shorter and easier to read."
|
||||
:group 'denote
|
||||
:link '(info-link "(denote) Top")
|
||||
:link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
|
||||
|
||||
(defcustom denote-rename-buffer-format "%t"
|
||||
"The format of the buffer name `denote-rename-buffer' should use.
|
||||
Thie value is a string that treats specially the following
|
||||
specifiers:
|
||||
|
||||
- The %t is the Denote TITLE of the file.
|
||||
- The %i is the Denote IDENTIFIER of the file.
|
||||
- The %d is the same as %i (DATE mnemonic).
|
||||
- The %s is the Denote SIGNATURE of the file.
|
||||
- The %k is the Denote KEYWORDS of the file.
|
||||
- The %% is a literal percent sign.
|
||||
|
||||
In addition, the following flags are available for each of the specifiers:
|
||||
|
||||
- 0 :: Pad to the width, if given, with zeros instead of spaces.
|
||||
- - :: Pad to the width, if given, on the right instead of the left.
|
||||
- < :: Truncate to the width and precision, if given, on the left.
|
||||
- > :: Truncate to the width and precision, if given, on the right.
|
||||
- ^ :: Convert to upper case.
|
||||
- _ :: Convert to lower case.
|
||||
|
||||
When combined all together, the above are written thus:
|
||||
|
||||
%<flags><width><precision>SPECIFIER-CHARACTER
|
||||
|
||||
Any other string it taken as-is. Users may want, for example, to
|
||||
include some text that makes Denote buffers stand out, such as
|
||||
a [D] prefix."
|
||||
:type 'string
|
||||
:package-version '(denote . "2.1.0")
|
||||
:group 'denote-rename-buffer)
|
||||
|
||||
(defcustom denote-rename-buffer-function #'denote-rename-buffer
|
||||
"Symbol of function that is called to rename the Denote file buffer.
|
||||
The default `denote-rename-buffer' function uses the pattern
|
||||
described in `denote-rename-buffer-format'.
|
||||
|
||||
Users can set this variable to an arbitrary function that does
|
||||
something else. The function is called without arguments from
|
||||
the `find-file-hook' and `denote-after-new-note-hook'.
|
||||
|
||||
A nil value for this variable means that the title of the Denote
|
||||
buffer will be used, if available."
|
||||
:type '(choice
|
||||
(const :tag "Rename using the `denote-rename-buffer-format'" denote-rename-buffer)
|
||||
(function :tag "Use a custom renaming function"))
|
||||
:package-version '(denote . "2.1.0")
|
||||
:group 'denote-rename-buffer)
|
||||
|
||||
(defun denote-rename-buffer--format (buffer)
|
||||
"Parse the BUFFER through the `denote-rename-buffer-format'."
|
||||
(when-let ((file (buffer-file-name buffer))
|
||||
(type (denote-filetype-heuristics file)))
|
||||
(string-trim
|
||||
(format-spec denote-rename-buffer-format
|
||||
(list (cons ?t (cond
|
||||
((denote-retrieve-front-matter-title-value file type))
|
||||
((denote-retrieve-filename-title file))
|
||||
(t "")))
|
||||
(cons ?i (or (denote-retrieve-filename-identifier file) ""))
|
||||
(cons ?d (or (denote-retrieve-filename-identifier file) ""))
|
||||
(cons ?s (or (denote-retrieve-filename-signature file) ""))
|
||||
(cons ?k (if-let ((kws (denote-retrieve-front-matter-keywords-value file type)))
|
||||
(denote-keywords-combine kws)
|
||||
(or (denote-retrieve-filename-keywords file) "")))
|
||||
(cons ?% "%"))
|
||||
'delete))))
|
||||
|
||||
(defun denote-rename-buffer (&optional buffer)
|
||||
"Rename current buffer or optional BUFFER with `denote-rename-buffer-format'.
|
||||
The symbol of this function is the default value of the user
|
||||
option `denote-rename-buffer-function' and is thus used by the
|
||||
`denote-rename-buffer-mode'."
|
||||
(when-let ((file (buffer-file-name buffer))
|
||||
((denote-file-has-identifier-p file))
|
||||
(new-name (denote-rename-buffer--format (or buffer (current-buffer))))
|
||||
((not (string-blank-p new-name))))
|
||||
(rename-buffer new-name :unique)))
|
||||
|
||||
(make-obsolete
|
||||
'denote-rename-buffer-with-title
|
||||
'denote-rename-buffer
|
||||
"2.1.0")
|
||||
|
||||
(make-obsolete
|
||||
'denote-rename-buffer-with-identifier
|
||||
'denote-rename-buffer
|
||||
"2.1.0")
|
||||
|
||||
(defun denote-rename-buffer--fallback (&optional buffer)
|
||||
"Fallback to rename BUFFER or `current-buffer'.
|
||||
This is called if `denote-rename-buffer-rename-function' is nil."
|
||||
(let ((denote-rename-buffer-format "%t"))
|
||||
(denote-rename-buffer buffer)))
|
||||
|
||||
(defun denote-rename-buffer-rename-function-or-fallback ()
|
||||
"Call `denote-rename-buffer-function' or its fallback to rename with title.
|
||||
Add this to `find-file-hook' and `denote-after-new-note-hook'."
|
||||
(funcall (or denote-rename-buffer-function #'denote-rename-buffer--fallback)))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode denote-rename-buffer-mode
|
||||
"Automatically rename Denote buffers to be easier to read.
|
||||
A buffer is renamed upon visiting the underlying file. This
|
||||
means that existing buffers are not renamed until they are
|
||||
visited again in a new buffer (files are visited with the command
|
||||
`find-file' or related)."
|
||||
:global t
|
||||
(if denote-rename-buffer-mode
|
||||
(progn
|
||||
(add-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback)
|
||||
(add-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback)
|
||||
(add-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback))
|
||||
(remove-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback)
|
||||
(remove-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback)
|
||||
(remove-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback)))
|
||||
|
||||
(provide 'denote-rename-buffer)
|
||||
;;; denote-rename-buffer.el ends here
|
||||
107
denote-silo-extras.el
Normal file
107
denote-silo-extras.el
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
;;; denote-silo-extras.el --- Convenience functions for using Denote in multiple silos -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This is a set of convenience functions that used to be provided in
|
||||
;; the Denote manual. A "silo" is a `denote-directory' that is
|
||||
;; self-contained. Users can maintain multiple silos. Consult the
|
||||
;; manual for the details. With the Denote package installed,
|
||||
;; evaluate the following to read the relevant node:
|
||||
;;
|
||||
;; (info "(denote) Maintain separate directory silos for notes")
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote)
|
||||
|
||||
(defgroup denote-silo-extras nil
|
||||
"Make it easier to use Denote across Silos."
|
||||
:group 'denote
|
||||
:link '(info-link "(denote) Top")
|
||||
:link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
|
||||
|
||||
(defcustom denote-silo-extras-directories
|
||||
`(,denote-directory)
|
||||
"List of file paths pointing to Denote silos.
|
||||
Each file path points to a directory, which takes the same value
|
||||
as the variable `denote-directory'."
|
||||
:group 'denote-silo-extras
|
||||
:link '(info-link "(denote) Maintain separate directories for notes")
|
||||
:type '(repeat directory))
|
||||
|
||||
(defvar denote-silo-extras-directory-history nil
|
||||
"Minibuffer history for `denote-silo-extras-directory-prompt'.")
|
||||
|
||||
(defalias 'denote-silo-extras--directory-history 'denote-silo-extras-directory-history
|
||||
"Compatibility alias for `denote-silo-extras-directory-history'.")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'denote-silo-extras--directory-prompt
|
||||
'denote-silo-extras-directory-prompt
|
||||
"3.1.0")
|
||||
|
||||
(defun denote-silo-extras-directory-prompt ()
|
||||
"Prompt for directory among `denote-silo-extras-directories'."
|
||||
(let ((default (car denote-silo-extras-directory-history)))
|
||||
(completing-read
|
||||
(format-prompt "Select a silo" default)
|
||||
(denote--completion-table 'file denote-silo-extras-directories)
|
||||
nil :require-match nil 'denote-silo-extras-directory-history)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-silo-extras-create-note (silo)
|
||||
"Select SILO and run `denote' in it.
|
||||
SILO is a file path from `denote-silo-extras-directories'.
|
||||
|
||||
When called from Lisp, SILO is a file system path to a directory."
|
||||
(interactive (list (denote-silo-extras-directory-prompt)))
|
||||
(let ((denote-directory silo))
|
||||
(call-interactively #'denote)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-silo-extras-open-or-create (silo)
|
||||
"Select SILO and run `denote-open-or-create' in it.
|
||||
SILO is a file path from `denote-silo-extras-directories'.
|
||||
|
||||
When called from Lisp, SILO is a file system path to a directory."
|
||||
(interactive (list (denote-silo-extras-directory-prompt)))
|
||||
(let ((denote-directory silo))
|
||||
(call-interactively #'denote-open-or-create)))
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-silo-extras-select-silo-then-command (silo command)
|
||||
"Select SILO and run Denote COMMAND in it.
|
||||
SILO is a file path from `denote-silo-extras-directories', while
|
||||
COMMAND is one among `denote-silo-extras-commands'.
|
||||
|
||||
When called from Lisp, SILO is a file system path to a directory."
|
||||
(interactive
|
||||
(list
|
||||
(denote-silo-extras-directory-prompt)
|
||||
(denote-command-prompt)))
|
||||
(let ((denote-directory silo))
|
||||
(call-interactively command)))
|
||||
|
||||
(provide 'denote-silo-extras)
|
||||
;;; denote-silo-extras.el ends here
|
||||
201
denote-sort.el
Normal file
201
denote-sort.el
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
;;; denote-sort.el --- Sort Denote files based on a file name component -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Sort Denote files based on their file name components, namely, the
|
||||
;; signature, title, or keywords.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'denote)
|
||||
|
||||
(defgroup denote-sort nil
|
||||
"Sort Denote files based on a file name component."
|
||||
:group 'denote
|
||||
:link '(info-link "(denote) Top")
|
||||
:link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
|
||||
|
||||
(defvar denote-sort-comparison-function #'string-collate-lessp
|
||||
"String comparison function used by `denote-sort-files' subroutines.")
|
||||
|
||||
(defvar denote-sort-components '(title keywords signature identifier)
|
||||
"List of sorting keys applicable for `denote-sort-files' and related.")
|
||||
|
||||
;; NOTE 2023-12-04: We can have compound sorting algorithms such as
|
||||
;; title+signature, but I want to keep this simple for the time being.
|
||||
;; Let us first hear from users to understand if there is a real need
|
||||
;; for such a feature.
|
||||
(defmacro denote-sort--define-lessp (component)
|
||||
"Define function to sort by COMPONENT."
|
||||
(let ((retrieve-fn (intern (format "denote-retrieve-filename-%s" component))))
|
||||
`(defun ,(intern (format "denote-sort-%s-lessp" component)) (file1 file2)
|
||||
,(format
|
||||
"Return smallest between FILE1 and FILE2 based on their %s.
|
||||
The comparison is done with `denote-sort-comparison-function' between the
|
||||
two title values."
|
||||
component)
|
||||
(let* ((one (,retrieve-fn file1))
|
||||
(two (,retrieve-fn file2))
|
||||
(one-empty-p (or (null one) (string-empty-p one)))
|
||||
(two-empty-p (or (null two) (string-empty-p two))))
|
||||
(cond
|
||||
(one-empty-p nil)
|
||||
((and (not one-empty-p) two-empty-p) one)
|
||||
(t (funcall denote-sort-comparison-function one two)))))))
|
||||
|
||||
;; TODO 2023-12-04: Subject to the above NOTE, we can also sort by
|
||||
;; directory and by file length.
|
||||
(denote-sort--define-lessp title)
|
||||
(denote-sort--define-lessp keywords)
|
||||
(denote-sort--define-lessp signature)
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-sort-files (files component &optional reverse)
|
||||
"Returned sorted list of Denote FILES.
|
||||
|
||||
With COMPONENT as a symbol among `denote-sort-components',
|
||||
sort files based on the corresponding file name component.
|
||||
|
||||
With COMPONENT as a nil value keep the original date-based
|
||||
sorting which relies on the identifier of each file name.
|
||||
|
||||
With optional REVERSE as a non-nil value, reverse the sort order."
|
||||
(let* ((files-to-sort (copy-sequence files))
|
||||
(sort-fn (when component
|
||||
(pcase component
|
||||
('title #'denote-sort-title-lessp)
|
||||
('keywords #'denote-sort-keywords-lessp)
|
||||
('signature #'denote-sort-signature-lessp))))
|
||||
(sorted-files (if sort-fn (sort files sort-fn) files-to-sort)))
|
||||
(if reverse
|
||||
(reverse sorted-files)
|
||||
sorted-files)))
|
||||
|
||||
(defun denote-sort-get-directory-files (files-matching-regexp sort-by-component &optional reverse omit-current)
|
||||
"Return sorted list of files in variable `denote-directory'.
|
||||
|
||||
With FILES-MATCHING-REGEXP as a string limit files to those
|
||||
matching the given regular expression.
|
||||
|
||||
With SORT-BY-COMPONENT as a symbol among `denote-sort-components',
|
||||
pass it to `denote-sort-files' to sort by the corresponding file
|
||||
name component.
|
||||
|
||||
With optional REVERSE as a non-nil value, reverse the sort order.
|
||||
|
||||
With optional OMIT-CURRENT, do not include the current file in
|
||||
the list."
|
||||
(denote-sort-files
|
||||
(denote-directory-files files-matching-regexp omit-current)
|
||||
sort-by-component
|
||||
reverse))
|
||||
|
||||
(defun denote-sort-get-links (files-matching-regexp sort-by-component current-file-type id-only &optional reverse)
|
||||
"Return sorted typographic list of links for FILES-MATCHING-REGEXP.
|
||||
|
||||
With FILES-MATCHING-REGEXP as a string, match files stored in the
|
||||
variable `denote-directory'.
|
||||
|
||||
With SORT-BY-COMPONENT as a symbol among `denote-sort-components',
|
||||
sort FILES-MATCHING-REGEXP by the given Denote file name
|
||||
component. If SORT-BY-COMPONENT is nil or an unknown non-nil
|
||||
value, default to the identifier-based sorting.
|
||||
|
||||
With CURRENT-FILE-TYPE as a symbol among those specified in
|
||||
`denote-file-type' (or the `car' of each element in
|
||||
`denote-file-types'), format the link accordingly. With a nil or
|
||||
unknown non-nil value, default to the Org notation.
|
||||
|
||||
With ID-ONLY as a non-nil value, produce links that consist only
|
||||
of the identifier, thus deviating from CURRENT-FILE-TYPE.
|
||||
|
||||
With optional REVERSE as a non-nil value, reverse the sort order."
|
||||
(denote-link--prepare-links
|
||||
(denote-sort-get-directory-files files-matching-regexp sort-by-component reverse)
|
||||
current-file-type
|
||||
id-only))
|
||||
|
||||
(defvar denote-sort-component-history nil
|
||||
"Minibuffer history of `denote-sort-component-prompt'.")
|
||||
|
||||
(defalias 'denote-sort--component-hist 'denote-sort-component-history
|
||||
"Compatibility alias for `denote-sort-component-history'.")
|
||||
|
||||
(defun denote-sort-component-prompt ()
|
||||
"Prompt `denote-sort-files' for sorting key among `denote-sort-components'."
|
||||
(let ((default (car denote-sort-component-history)))
|
||||
(intern
|
||||
(completing-read
|
||||
(format-prompt "Sort by file name component" default)
|
||||
denote-sort-components nil :require-match
|
||||
nil 'denote-sort-component-history default))))
|
||||
|
||||
(defvar-local denote-sort--dired-buffer nil
|
||||
"Buffer object of current `denote-sort-dired'.")
|
||||
|
||||
;;;###autoload
|
||||
(defun denote-sort-dired (files-matching-regexp sort-by-component reverse)
|
||||
"Produce Dired dired-buffer with sorted files from variable `denote-directory'.
|
||||
When called interactively, prompt for FILES-MATCHING-REGEXP,
|
||||
SORT-BY-COMPONENT, and REVERSE.
|
||||
|
||||
1. FILES-MATCHING-REGEXP limits the list of Denote files to
|
||||
those matching the provided regular expression.
|
||||
|
||||
2. SORT-BY-COMPONENT sorts the files by their file name
|
||||
component (one among `denote-sort-components').
|
||||
|
||||
3. REVERSE is a boolean to reverse the order when it is a non-nil value.
|
||||
|
||||
When called from Lisp, the arguments are a string, a keyword, and
|
||||
a non-nil value, respectively."
|
||||
(interactive
|
||||
(list
|
||||
(denote-files-matching-regexp-prompt)
|
||||
(denote-sort-component-prompt)
|
||||
(y-or-n-p "Reverse sort? ")))
|
||||
(if-let ((default-directory (denote-directory))
|
||||
(files (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse))
|
||||
;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
|
||||
;; buffer-name produces an error if the regexp contains a
|
||||
;; wildcard for a directory. I can reproduce this in emacs
|
||||
;; -Q and am not sure if it is a bug. Anyway, I will report
|
||||
;; it upstream, but even if it is fixed we cannot use it
|
||||
;; for now (whatever fix will be available for Emacs 30+).
|
||||
(denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp sort-by-component))
|
||||
(buffer-name (format "Denote sort by `%s' at %s" sort-by-component (format-time-string "%T"))))
|
||||
(let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name files)))))
|
||||
(setq denote-sort--dired-buffer dired-buffer)
|
||||
(with-current-buffer dired-buffer
|
||||
(setq-local revert-buffer-function
|
||||
(lambda (&rest _)
|
||||
(kill-buffer dired-buffer)
|
||||
(denote-sort-dired files-matching-regexp sort-by-component reverse))))
|
||||
;; Because of the above NOTE, I am printing a message. Not
|
||||
;; what I want, but it is better than nothing...
|
||||
(message denote-sort-dired-buffer-name))
|
||||
(message "No matching files for: %s" files-matching-regexp)))
|
||||
|
||||
(provide 'denote-sort)
|
||||
;;; denote-sort.el ends here
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
;;; denote-test.el --- Unit tests for Denote -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023-2026 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://github.com/protesilaos/denote
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Maintainer: Denote Development <~protesilaos/denote@lists.sr.ht>
|
||||
;; URL: https://git.sr.ht/~protesilaos/denote
|
||||
;; Mailing-List: https://lists.sr.ht/~protesilaos/denote
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
|
|
@ -23,182 +24,136 @@
|
|||
|
||||
;;; Commentary:
|
||||
|
||||
;; Tests for Denote. Note that we are using Shorthands in this file,
|
||||
;; so the "dt-" prefix really is "denote-test-". Evaluate the
|
||||
;; following to learn more:
|
||||
;;
|
||||
;; (info "(elisp) Shorthands")
|
||||
;; WORK-IN-PROGRESS
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
|
||||
;;;; Tests for denote.el
|
||||
|
||||
(require 'denote)
|
||||
|
||||
(ert-deftest dt-denote-directories--get-paths ()
|
||||
"Test that `denote-directories--get-paths' returns a list of paths."
|
||||
(let ((path "~/Documents/notes"))
|
||||
(should (equal
|
||||
(denote-directories--get-paths path)
|
||||
(list (file-name-as-directory (expand-file-name path))))))
|
||||
(let ((paths (list "~/Documents/notes" "~/Documents/books")))
|
||||
(should (equal
|
||||
(denote-directories--get-paths paths)
|
||||
(mapcar
|
||||
(lambda (path)
|
||||
(file-name-as-directory (expand-file-name path)))
|
||||
paths)))
|
||||
(should (seq-every-p
|
||||
(lambda (path)
|
||||
(string-suffix-p "/" path))
|
||||
(denote-directories--get-paths paths)))
|
||||
(should-not (null (denote-directories--get-paths paths))))
|
||||
(should-error (denote-directories--get-paths 'hello))
|
||||
(should-error (denote-directories--get-paths '(hello)))
|
||||
(should-error (denote-directories--get-paths '(1 2 3))))
|
||||
|
||||
(ert-deftest dt-denote--make-denote-directory ()
|
||||
(ert-deftest denote-test--denote--make-denote-directory ()
|
||||
"Test that `denote--make-denote-directory' creates the directory."
|
||||
(let* ((names (list "denote-test-made-directory-1" "denote-test-made-directory-2"))
|
||||
(directories (mapcar
|
||||
(lambda (directory)
|
||||
(file-name-as-directory (expand-file-name directory temporary-file-directory)))
|
||||
names)))
|
||||
(should-not (denote-directories--make-paths directories))
|
||||
(should (seq-every-p #'file-name-directory directories))
|
||||
(dolist (directory directories)
|
||||
(delete-directory directory))))
|
||||
(should (null (denote--make-denote-directory))))
|
||||
|
||||
(ert-deftest dt-denote-directories ()
|
||||
"Test that `denote-directories' returns list of directories.
|
||||
It does so by expanding the value of the user option `denote-directory'.
|
||||
(ert-deftest denote-test--denote-directory ()
|
||||
"Test that variable `denote-directory' returns an absolute directory name."
|
||||
(let ((path (denote-directory)))
|
||||
(should (and (file-directory-p path)
|
||||
(file-name-absolute-p path)))))
|
||||
|
||||
Also see `denote-test--denote--make-denote-directory',
|
||||
`denote-test---denote-directories--get-paths'."
|
||||
(let ((denote-directory "/tmp/denote-test-notes"))
|
||||
(should
|
||||
(equal
|
||||
(denote-directories)
|
||||
(list (file-name-as-directory (expand-file-name denote-directory)))))
|
||||
(should (and (file-directory-p denote-directory)
|
||||
(file-name-absolute-p denote-directory))))
|
||||
(let ((denote-directory (list "/tmp/denote-test-notes-1" "/tmp/denote-test-notes-2")))
|
||||
(should
|
||||
(equal
|
||||
(denote-directories)
|
||||
(mapcar
|
||||
(lambda (directory)
|
||||
(file-name-as-directory (expand-file-name directory)))
|
||||
denote-directory)))
|
||||
(should (and (seq-every-p #'file-directory-p denote-directory)
|
||||
(seq-every-p #'file-name-absolute-p denote-directory)))))
|
||||
(ert-deftest denote-test--denote--slug-no-punct ()
|
||||
"Test that `denote--slug-no-punct' removes punctuation from the string.
|
||||
Concretely, replace with spaces anything that matches the
|
||||
`denote-excluded-punctuation-regexp' and
|
||||
`denote-excluded-punctuation-extra-regexp'."
|
||||
(should (equal (denote--slug-no-punct "This is !@# test")
|
||||
"This is test")))
|
||||
|
||||
(ert-deftest dt-denote-sluggify-title ()
|
||||
"Test that `denote-sluggify-title' removes punctuation from the string.
|
||||
Concretely, remove anything specified in `denote-sluggify-title'."
|
||||
(should (equal (denote-sluggify-title "this-is-!@#test") "this-is-test")))
|
||||
|
||||
(ert-deftest dt-denote-slug-keep-only-ascii ()
|
||||
"Test that `denote-slug-keep-only-ascii' removes non-ASCII characters."
|
||||
(should (equal
|
||||
(denote-slug-keep-only-ascii "There are no-ASCII : characters | here 😀")
|
||||
"There are no-ASCII characters here ")))
|
||||
|
||||
(ert-deftest dt-denote-slug-hyphenate ()
|
||||
"Test that `denote-slug-hyphenate' hyphenates the string.
|
||||
(ert-deftest denote-test--denote--slug-hyphenate ()
|
||||
"Test that `denote--slug-hyphenate' hyphenates the string.
|
||||
Also replace multiple hyphens with a single one and remove any
|
||||
leading and trailing hyphen."
|
||||
(should (equal (denote-slug-hyphenate "__ This is a test __ ") "This-is-a-test")))
|
||||
(should (equal (denote--slug-hyphenate "__ This is a test __ ")
|
||||
"This-is-a-test")))
|
||||
|
||||
(ert-deftest dt-denote-sluggify ()
|
||||
(ert-deftest denote-test--denote-sluggify ()
|
||||
"Test that `denote-sluggify' sluggifies the string.
|
||||
To sluggify is to (i) downcase, (ii) hyphenate, (iii) de-punctuate, and (iv) remove spaces from the string."
|
||||
(should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ") "this-is-a-test")))
|
||||
(should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ")
|
||||
"this-is-a-test")))
|
||||
|
||||
(ert-deftest Ddenote--slug-put-equals ()
|
||||
"Test that `denote-slug-put-equals' replaces spaces/underscores with =.
|
||||
(ert-deftest denote-test--denote--slug-put-equals ()
|
||||
"Test that `denote--slug-put-equals' replaces spaces/underscores with =.
|
||||
Otherwise do the same as what is described in
|
||||
`denote-test--denote-slug-hyphenate'.
|
||||
`denote-test--denote--slug-hyphenate'.
|
||||
|
||||
The use of the equals sign is for the SIGNATURE field of the
|
||||
Denote file name."
|
||||
(should (equal (denote-slug-put-equals "__ This is a test __ ") "This=is=a=test")))
|
||||
(should (equal (denote--slug-put-equals "__ This is a test __ ")
|
||||
"This=is=a=test")))
|
||||
|
||||
(ert-deftest dt-denote-sluggify-signature ()
|
||||
(ert-deftest denote-test--denote-sluggify-signature ()
|
||||
"Test that `denote-sluggify-signature' sluggifies the string for file signatures.
|
||||
This is like `denote-test--denote-sluggify', except that it also
|
||||
accounts for what we describe in `denote-test--denote-slug-put-equals'."
|
||||
(should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ") "this=is=a=test")))
|
||||
accounts for what we describe in `denote-test--denote--slug-put-equals'."
|
||||
(should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ")
|
||||
"this=is=a=test")))
|
||||
|
||||
(ert-deftest dt-denote-sluggify-keyword ()
|
||||
(ert-deftest denote-test--denote-sluggify-keyword ()
|
||||
"Test that `denote-sluggify-keyword' sluggifies the string while joining words.
|
||||
In this context, to join words is to elimitate any space or
|
||||
delimiter between them.
|
||||
|
||||
Otherwise, this is like `denote-test--denote-sluggify'."
|
||||
(should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ") "thisisatest")))
|
||||
(should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ")
|
||||
"thisisatest")))
|
||||
|
||||
(ert-deftest dt-denote-sluggify-keywords ()
|
||||
(ert-deftest denote-test--denote-sluggify-keywords ()
|
||||
"Test that `denote-sluggify-keywords' sluggifies a list of strings.
|
||||
The function also account for the value of the user option
|
||||
`denote-allow-multi-word-keywords'."
|
||||
(should (equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __")) '("oneone" "two" "three"))))
|
||||
(should
|
||||
(equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __"))
|
||||
'("oneone" "two" "three"))))
|
||||
|
||||
(ert-deftest dt-denote-slug-remove-accents ()
|
||||
"Test that `denote-slug-remove-accents' removed diacretics."
|
||||
(should (string= (denote-slug-remove-accents "ê") "e"))
|
||||
(should (string= (denote-slug-remove-accents "ñ") "n")))
|
||||
|
||||
(ert-deftest dt-denote--file-empty-p ()
|
||||
(ert-deftest denote-test--denote--file-empty-p ()
|
||||
"Test that `denote--file-empty-p' returns non-nil on empty file."
|
||||
(let ((file (make-temp-file "denote-test")))
|
||||
(should (denote--file-empty-p file))
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(insert "Hello world, this is not empty anymore!")
|
||||
(save-buffer))
|
||||
(should-not (denote--file-empty-p file))
|
||||
(delete-file file)))
|
||||
;; (should (null (denote--file-empty-p user-init-file))
|
||||
(should (let ((file (make-temp-file "denote-test")))
|
||||
(prog1
|
||||
(denote--file-empty-p file)
|
||||
(delete-file file)))))
|
||||
|
||||
(ert-deftest dt-denote-file-has-denoted-filename-p ()
|
||||
"Test that `denote-file-has-denoted-filename-p' validates a Denote file name."
|
||||
(should (denote-file-has-denoted-filename-p "20230522T154900--test__keyword.txt"))
|
||||
(should-not (denote-file-has-denoted-filename-p "hello")))
|
||||
(ert-deftest denote-test--denote-file-is-note-p ()
|
||||
"Test that `denote-file-is-note-p' checks that files is a Denote note.
|
||||
For our purposes, a note must note be a directory, must satisfy
|
||||
`file-regular-p', its path must be part of the variable
|
||||
`denote-directory', it must have a Denote identifier in its name,
|
||||
and use one of the extensions implied by `denote-file-type'."
|
||||
(should (let* ((tmp (temporary-file-directory))
|
||||
(denote-directory tmp)
|
||||
(file (concat tmp "20230522T154900--test__keyword.txt")))
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(write-file file))
|
||||
(prog1
|
||||
(denote-file-is-note-p file)
|
||||
(delete-file file)))))
|
||||
|
||||
(ert-deftest dt-denote-file-has-identifier-p ()
|
||||
(ert-deftest denote-test--denote-file-has-identifier-p ()
|
||||
"Test that `denote-file-has-identifier-p' checks for a Denote identifier."
|
||||
(should (denote-file-has-identifier-p "20230522T154900--test__keyword.txt"))
|
||||
(should-not (denote-file-has-identifier-p "T154900--test__keyword.txt")))
|
||||
(should (null (denote-file-has-identifier-p "T154900--test__keyword.txt"))))
|
||||
|
||||
(ert-deftest dt-denote-file-has-signature-p ()
|
||||
(ert-deftest denote-test--denote-file-has-signature-p ()
|
||||
"Test that `denote-file-has-signature-p' checks for a Denote signature."
|
||||
(should (denote-file-has-signature-p "20230522T154900==sig--test__keyword.txt"))
|
||||
(should-not (denote-file-has-signature-p "20230522T154900--test__keyword.txt")))
|
||||
(should (null (denote-file-has-signature-p "20230522T154900--test__keyword.txt"))))
|
||||
|
||||
(ert-deftest dt-denote-file-has-supported-extension-p ()
|
||||
(ert-deftest denote-test--denote-file-has-supported-extension-p ()
|
||||
"Test that `denote-file-has-supported-extension-p' matches a supported extension."
|
||||
(let ((extensions (denote-file-type-extensions-with-encryption)))
|
||||
(should
|
||||
(member
|
||||
(file-name-extension "20230522T154900==sig--test__keyword.txt" :period)
|
||||
extensions))
|
||||
(should-not
|
||||
(member
|
||||
(file-name-extension "20230522T154900==sig--test__keyword" :period)
|
||||
extensions))))
|
||||
(should
|
||||
(member
|
||||
(file-name-extension "20230522T154900==sig--test__keyword.txt" :period)
|
||||
(denote-file-type-extensions-with-encryption)))
|
||||
(should
|
||||
(null
|
||||
(member
|
||||
(file-name-extension "20230522T154900==sig--test__keyword" :period)
|
||||
(denote-file-type-extensions-with-encryption)))))
|
||||
|
||||
(ert-deftest dt-denote-file-type-extensions ()
|
||||
(ert-deftest denote-test--denote-file-type-extensions ()
|
||||
"Test that `denote-file-type-extensions' returns file extensions.
|
||||
We check for the common file type extensions, though the user can set
|
||||
`denote-file-types' to nil and handle things on their own or, anyhow,
|
||||
modify that variable."
|
||||
We check for the common file type extensions, though the user can
|
||||
theoretically set `denote-file-types' to nil and handle things on
|
||||
their own. We do not have to test for that scenario, because
|
||||
such a user will be redefining large parts of Denote's behaviour
|
||||
with regard to file types."
|
||||
(let ((extensions (denote-file-type-extensions)))
|
||||
(should (or (member ".md" extensions)
|
||||
(member ".org" extensions)
|
||||
(member ".txt" extensions)))))
|
||||
|
||||
(ert-deftest dt-denote-file-type-extensions-with-encryption ()
|
||||
(ert-deftest denote-test--denote-file-type-extensions-with-encryption ()
|
||||
"Test that `denote-file-type-extensions-with-encryption' covers encryption.
|
||||
Extend what we do in `denote-test--denote-file-type-extensions'."
|
||||
(let ((extensions (denote-file-type-extensions-with-encryption)))
|
||||
|
|
@ -212,272 +167,371 @@ Extend what we do in `denote-test--denote-file-type-extensions'."
|
|||
(member ".org.age" extensions)
|
||||
(member ".txt.age" extensions)))))
|
||||
|
||||
(ert-deftest dt-denote--format-front-matter ()
|
||||
"Test that `denote--format-front-matter' formats front matter correctly.
|
||||
To make the test reproducible, set `denote-date-format' to a value that
|
||||
does not involve the time zone."
|
||||
(let ((denote-date-format "%Y-%m-%d")
|
||||
(denote-front-matter-components-present-even-if-empty-value '(title keywords signature date identifier)))
|
||||
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") '("") "" "" 'text)
|
||||
(string-join
|
||||
'("title: "
|
||||
"date: 2024-01-01"
|
||||
"tags: "
|
||||
"identifier: "
|
||||
"signature: "
|
||||
"---------------------------\n\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'text)
|
||||
(string-join
|
||||
'("title: Some test"
|
||||
"date: 2023-06-05"
|
||||
"tags: one two"
|
||||
"identifier: 20230605T102234"
|
||||
"signature: sig"
|
||||
"---------------------------\n\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'org)
|
||||
(string-join
|
||||
'("#+title: "
|
||||
"#+date: 2024-01-01"
|
||||
"#+filetags: "
|
||||
"#+identifier: "
|
||||
"#+signature: "
|
||||
"\n")
|
||||
"\n")))
|
||||
(should (equal
|
||||
(denote--format-front-matter
|
||||
"Some test" (date-to-time "2023-06-05") '("one" "two")
|
||||
"20230605T102234" "sig" 'org)
|
||||
(string-join
|
||||
'("#+title: Some test"
|
||||
"#+date: 2023-06-05"
|
||||
"#+filetags: :one:two:"
|
||||
"#+identifier: 20230605T102234"
|
||||
"#+signature: sig"
|
||||
"\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-yaml)
|
||||
(string-join
|
||||
'("---"
|
||||
"title: \"\""
|
||||
"date: 2024-01-01"
|
||||
"tags: []"
|
||||
"identifier: \"\""
|
||||
"signature: \"\""
|
||||
"---"
|
||||
"\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'markdown-yaml)
|
||||
(string-join
|
||||
'("---"
|
||||
"title: \"Some test\""
|
||||
"date: 2023-06-05"
|
||||
"tags: [\"one\", \"two\"]"
|
||||
"identifier: \"20230605T102234\""
|
||||
"signature: \"sig\""
|
||||
"---"
|
||||
"\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-toml)
|
||||
(string-join
|
||||
'("+++"
|
||||
"title = \"\""
|
||||
"date = 2024-01-01"
|
||||
"tags = []"
|
||||
"identifier = \"\""
|
||||
"signature = \"\""
|
||||
"+++"
|
||||
"\n")
|
||||
"\n")))
|
||||
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'markdown-toml)
|
||||
(string-join
|
||||
'("+++"
|
||||
"title = \"Some test\""
|
||||
"date = 2023-06-05"
|
||||
"tags = [\"one\", \"two\"]"
|
||||
"identifier = \"20230605T102234\""
|
||||
"signature = \"sig\""
|
||||
"+++"
|
||||
"\n")
|
||||
"\n")))))
|
||||
(ert-deftest denote-test--denote-surround-with-quotes ()
|
||||
"Test that `denote-surround-with-quotes' returns a string in quotes."
|
||||
(should (and (equal (denote-surround-with-quotes "test") "\"test\"")
|
||||
(equal (denote-surround-with-quotes "") "\"\"")
|
||||
(equal (denote-surround-with-quotes nil) "\"\"")
|
||||
(equal (denote-surround-with-quotes 'wrong) "\"\"")
|
||||
(equal (denote-surround-with-quotes '(wrong)) "\"\""))))
|
||||
|
||||
(ert-deftest dt-denote-format-file-name ()
|
||||
(ert-deftest denote-test--denote--format-front-matter ()
|
||||
"Test that `denote--format-front-matter' formats front matter correctly."
|
||||
(should (and (equal (denote--format-front-matter "" "" '("") "" 'text)
|
||||
(mapconcat #'identity
|
||||
'("title: "
|
||||
"date: "
|
||||
"tags: "
|
||||
"identifier: "
|
||||
"---------------------------\n\n")
|
||||
"\n"))
|
||||
|
||||
(equal
|
||||
(denote--format-front-matter
|
||||
"Some test" "2023-06-05" '("one" "two")
|
||||
"20230605T102234" 'text)
|
||||
(mapconcat #'identity
|
||||
'("title: Some test"
|
||||
"date: 2023-06-05"
|
||||
"tags: one two"
|
||||
"identifier: 20230605T102234"
|
||||
"---------------------------\n\n")
|
||||
"\n"))))
|
||||
|
||||
(should (and (equal (denote--format-front-matter "" "" nil "" 'org)
|
||||
(mapconcat #'identity
|
||||
'("#+title: "
|
||||
"#+date: "
|
||||
"#+filetags: "
|
||||
"#+identifier: "
|
||||
"\n")
|
||||
"\n"))
|
||||
|
||||
(equal
|
||||
(denote--format-front-matter
|
||||
"Some test" "2023-06-05" '("one" "two")
|
||||
"20230605T102234" 'org)
|
||||
(mapconcat #'identity
|
||||
'("#+title: Some test"
|
||||
"#+date: 2023-06-05"
|
||||
"#+filetags: :one:two:"
|
||||
"#+identifier: 20230605T102234"
|
||||
"\n")
|
||||
"\n"))))
|
||||
|
||||
(should (and (equal (denote--format-front-matter "" "" nil "" 'markdown-yaml)
|
||||
(mapconcat #'identity
|
||||
'("---"
|
||||
"title: \"\""
|
||||
"date: "
|
||||
"tags: []"
|
||||
"identifier: \"\""
|
||||
"---"
|
||||
"\n")
|
||||
"\n"))
|
||||
|
||||
(equal
|
||||
(denote--format-front-matter
|
||||
"Some test" "2023-06-05" '("one" "two")
|
||||
"20230605T102234" 'markdown-yaml)
|
||||
(mapconcat #'identity
|
||||
'("---"
|
||||
"title: \"Some test\""
|
||||
"date: 2023-06-05"
|
||||
"tags: [\"one\", \"two\"]"
|
||||
"identifier: \"20230605T102234\""
|
||||
"---"
|
||||
"\n")
|
||||
"\n"))))
|
||||
|
||||
(should (and (equal (denote--format-front-matter "" "" nil "" 'markdown-toml)
|
||||
(mapconcat #'identity
|
||||
'("+++"
|
||||
"title = \"\""
|
||||
"date = "
|
||||
"tags = []"
|
||||
"identifier = \"\""
|
||||
"+++"
|
||||
"\n")
|
||||
"\n"))
|
||||
|
||||
(equal
|
||||
(denote--format-front-matter
|
||||
"Some test" "2023-06-05" '("one" "two")
|
||||
"20230605T102234" 'markdown-toml)
|
||||
(mapconcat #'identity
|
||||
'("+++"
|
||||
"title = \"Some test\""
|
||||
"date = 2023-06-05"
|
||||
"tags = [\"one\", \"two\"]"
|
||||
"identifier = \"20230605T102234\""
|
||||
"+++"
|
||||
"\n")
|
||||
"\n")))))
|
||||
|
||||
(ert-deftest denote-test--denote-format-file-name ()
|
||||
"Test that `denote-format-file-name' returns all expected paths."
|
||||
(let* ((title "Some test")
|
||||
(id (format-time-string denote-date-identifier-format (denote-valid-date-p "2023-11-28 05:53:11")))
|
||||
(id (format-time-string denote-id-format (denote-valid-date-p "2023-11-28 05:53:11")))
|
||||
(denote-directory "/tmp/test-denote")
|
||||
(dir (car (denote-directories)))
|
||||
(ext (denote--file-extension 'org))
|
||||
(kws '("one" "two")))
|
||||
(should-error (denote-format-file-name nil id kws title ext ""))
|
||||
(should-error (denote-format-file-name "" id kws title ext ""))
|
||||
;; NOTE that `denote-directory' is the `let' bound value without the suffix
|
||||
(should-error (denote-format-file-name denote-directory id kws title ext ""))
|
||||
(should-error (denote-format-file-name dir "" nil "" ext ""))
|
||||
(should (equal (denote-format-file-name dir nil kws title ext "") "/tmp/test-denote/--some-test__one_two.org"))
|
||||
(should (equal (denote-format-file-name dir "" kws title ext "") "/tmp/test-denote/--some-test__one_two.org"))
|
||||
(should (equal (denote-format-file-name dir "0123456" kws title ext "") "/tmp/test-denote/@@0123456--some-test__one_two.org"))
|
||||
(should (equal (denote-format-file-name dir id kws title ext "") "/tmp/test-denote/20231128T055311--some-test__one_two.org"))
|
||||
(should (equal (denote-format-file-name dir id nil "" ext "") "/tmp/test-denote/20231128T055311.org"))
|
||||
(should (equal (denote-format-file-name dir id nil nil ext nil) "/tmp/test-denote/20231128T055311.org"))
|
||||
(should (equal (denote-format-file-name dir id kws title ext "sig") "/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
|
||||
(should-error (denote-format-file-name
|
||||
nil
|
||||
id
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(ert-deftest dt-denote-get-file-extension ()
|
||||
(should-error (denote-format-file-name
|
||||
""
|
||||
id
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(should-error (denote-format-file-name
|
||||
denote-directory ; notice this is the `let' bound value without the suffix
|
||||
id
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(should-error (denote-format-file-name
|
||||
(denote-directory)
|
||||
nil
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(should-error (denote-format-file-name
|
||||
(denote-directory)
|
||||
""
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(should-error (denote-format-file-name
|
||||
(denote-directory)
|
||||
"0123456"
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
""))
|
||||
|
||||
(should (equal (denote-format-file-name
|
||||
(denote-directory)
|
||||
id
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
"")
|
||||
"/tmp/test-denote/20231128T055311--some-test__one_two.org"))
|
||||
|
||||
(should (equal (denote-format-file-name
|
||||
(denote-directory)
|
||||
id
|
||||
nil
|
||||
""
|
||||
(denote--file-extension 'org)
|
||||
"")
|
||||
"/tmp/test-denote/20231128T055311.org"))
|
||||
|
||||
(should (equal (denote-format-file-name
|
||||
(denote-directory)
|
||||
id
|
||||
nil
|
||||
nil
|
||||
(denote--file-extension 'org)
|
||||
nil)
|
||||
"/tmp/test-denote/20231128T055311.org"))
|
||||
|
||||
(should (equal (denote-format-file-name
|
||||
(denote-directory)
|
||||
id
|
||||
kws
|
||||
title
|
||||
(denote--file-extension 'org)
|
||||
"sig")
|
||||
"/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
|
||||
|
||||
(ert-deftest denote-test--denote-get-file-extension ()
|
||||
"Test that `denote-get-file-extension' gets the correct file extension."
|
||||
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") ""))
|
||||
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org"))
|
||||
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg"))
|
||||
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age")))
|
||||
(should (and (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") "")
|
||||
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org")
|
||||
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg")
|
||||
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age"))))
|
||||
|
||||
(ert-deftest dt-denote-get-file-extension-sans-encryption ()
|
||||
(ert-deftest denote-test--denote-get-file-extension-sans-encryption ()
|
||||
"Test that `denote-get-file-extension-sans-encryption' gets the file extension without encryption."
|
||||
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") ""))
|
||||
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org"))
|
||||
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org"))
|
||||
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org")))
|
||||
(should (and (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") "")
|
||||
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org")
|
||||
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org")
|
||||
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org"))))
|
||||
|
||||
;; TODO 2026-04-06: For the `denote-file-type'.
|
||||
|
||||
(ert-deftest dt-denote-filetype-heuristics ()
|
||||
(ert-deftest denote-test--denote-filetype-heuristics ()
|
||||
"Test that `denote-filetype-heuristics' gets the correct file type."
|
||||
(should-not (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing"))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md")
|
||||
(caar (denote--file-types-with-extension ".md"))))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg")
|
||||
(caar (denote--file-types-with-extension ".md"))))
|
||||
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age")
|
||||
(caar (denote--file-types-with-extension ".md")))))
|
||||
(should (and (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing") nil)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text)
|
||||
;; NOTE 2023-10-11: It returns `markdown-yaml' as a fallback. In
|
||||
;; an actual file, it reads the file contents to determine what
|
||||
;; it is and can return `markdown-toml'. In principle, we should
|
||||
;; be testing this here, though I prefer to keep things simple.
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md") 'markdown-yaml)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg") 'markdown-yaml)
|
||||
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age") 'markdown-yaml))))
|
||||
|
||||
(ert-deftest dt-denote-retrieve-filename-identifier ()
|
||||
(ert-deftest denote-test--denote-get-identifier ()
|
||||
"Test that `denote-get-identifier' returns an identifier."
|
||||
(should (and (equal (denote-get-identifier) (format-time-string denote-id-format (current-time)))
|
||||
(equal (denote-get-identifier "2024-02-01 10:34") "20240201T103400")
|
||||
(equal (denote-get-identifier 1705644188) "20240119T080308")
|
||||
(equal (denote-get-identifier '(26026 4251)) "20240119T080307")))
|
||||
(should-error (denote-get-identifier "Invalid date")))
|
||||
|
||||
(ert-deftest denote-test--denote-retrieve-filename-identifier ()
|
||||
"Test that `denote-retrieve-filename-identifier' returns only the identifier."
|
||||
(should (null (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing.org")))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "20240610T194654"))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "20240610T194654"))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "20240610T194654"))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "20240610T194654"))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "20240610T194654"))
|
||||
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "20240610T194654")))
|
||||
(should (and (null
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing.org"))
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
|
||||
"20240610T194654")
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
|
||||
"20240610T194654")
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
|
||||
"20240610T194654")
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
|
||||
"20240610T194654")
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"20240610T194654")
|
||||
(equal
|
||||
(denote-retrieve-filename-identifier "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"20240610T194654"))))
|
||||
|
||||
(ert-deftest dt-denote-retrieve-filename-title ()
|
||||
(ert-deftest denote-test--denote-retrieve-filename-title ()
|
||||
"Test that `denote-retrieve-filename-title' returns only the title."
|
||||
(should (null (denote-retrieve-filename-title "/path/to/testing/20240610T194654__denote_testing.org")))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "this-is-a-test-reordered"))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "this-is-a-test-reordered"))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "this-is-a-test-reordered"))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "this-is-a-test-reordered"))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "this-is-a-test-reordered"))
|
||||
(should (equal (denote-retrieve-filename-title "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "this-is-a-test-reordered")))
|
||||
(should (and (null
|
||||
(denote-retrieve-filename-title "/path/to/testing/20240610T194654__denote_testing.org"))
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
|
||||
"this-is-a-test-reordered")
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
|
||||
"this-is-a-test-reordered")
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
|
||||
"this-is-a-test-reordered")
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
|
||||
"this-is-a-test-reordered")
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"this-is-a-test-reordered")
|
||||
(equal
|
||||
(denote-retrieve-filename-title "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"this-is-a-test-reordered"))))
|
||||
|
||||
(ert-deftest dt-denote-retrieve-filename-keywords ()
|
||||
(ert-deftest denote-test--denote-retrieve-filename-keywords ()
|
||||
"Test that `denote-retrieve-filename-keywords' returns only the keywords."
|
||||
(should (null (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered.org")))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "denote_testing"))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "denote_testing"))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "denote_testing"))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "denote_testing"))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "denote_testing"))
|
||||
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "denote_testing")))
|
||||
(should (and (null
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered.org"))
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
|
||||
"denote_testing")
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
|
||||
"denote_testing")
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
|
||||
"denote_testing")
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
|
||||
"denote_testing")
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"denote_testing")
|
||||
(equal
|
||||
(denote-retrieve-filename-keywords "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"denote_testing"))))
|
||||
|
||||
(ert-deftest dt-denote-retrieve-filename-signature ()
|
||||
(ert-deftest denote-test--denote-retrieve-filename-signature ()
|
||||
"Test that `denote-retrieve-filename-signature' returns only the signature."
|
||||
(should (null (denote-retrieve-filename-signature "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")))
|
||||
(should (equal (denote-retrieve-filename-signature "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "signature"))
|
||||
(should (equal (denote-retrieve-filename-signature "/path/to/testing/--this-is-a-test-reordered==signature__denote_testing@@20240610T194654.org") "signature"))
|
||||
(should (equal (denote-retrieve-filename-signature "/path/to/testing/__denote_testing--this-is-a-test-reordered==signature@@20240610T194654.org") "signature"))
|
||||
(should (equal (denote-retrieve-filename-signature "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered==signature.org") "signature"))
|
||||
(should (equal (denote-retrieve-filename-signature "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "signature")))
|
||||
(should (and (null
|
||||
(denote-retrieve-filename-signature "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org"))
|
||||
(equal
|
||||
(denote-retrieve-filename-signature "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
|
||||
"signature")
|
||||
(equal
|
||||
(denote-retrieve-filename-signature "/path/to/testing/--this-is-a-test-reordered==signature__denote_testing@@20240610T194654.org")
|
||||
"signature")
|
||||
(equal
|
||||
(denote-retrieve-filename-signature "/path/to/testing/__denote_testing--this-is-a-test-reordered==signature@@20240610T194654.org")
|
||||
"signature")
|
||||
(equal
|
||||
(denote-retrieve-filename-signature "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered==signature.org")
|
||||
"signature")
|
||||
(equal
|
||||
(denote-retrieve-filename-signature "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
|
||||
"signature"))))
|
||||
|
||||
(ert-deftest dt-denote-date-identifier-p ()
|
||||
"Test that `denote-date-identifier-p' works for Denote identifiers."
|
||||
(should (denote-date-identifier-p "20240901T090910"))
|
||||
(should-not (denote-date-identifier-p "20240901T090910-not-identifier-format")))
|
||||
;;;; denote-journal-extras.el
|
||||
|
||||
(ert-deftest dt-denote-id-to-date ()
|
||||
"Test that `denote-id-to-date' returns the date from an identifier."
|
||||
(should (equal (denote-id-to-date "20240901T090910") "2024-09-01"))
|
||||
(should-error (denote-id-to-date "20240901T090910-not-identifier-format")))
|
||||
(require 'denote-journal-extras)
|
||||
|
||||
(ert-deftest dt-denote--date-convert ()
|
||||
"Test that `denote--date-convert' works with dates."
|
||||
(should (equal (denote--date-convert '(26454 45206 461174 657000) :list) '(26454 45206 461174 657000)))
|
||||
(should (equal (denote--date-convert '(26454 45206 461174 657000) :string) "2024-12-09 10:55:50"))
|
||||
(should (equal (denote--date-convert nil :string) ""))
|
||||
(should (equal (denote--date-convert nil :list) nil))
|
||||
(should-error (denote--date-convert '(26454 45206 461174 657000) :not-valid-type))
|
||||
(should-error (denote--date-convert nil :not-valid-type)))
|
||||
(ert-deftest denote-test--denote-journal-extras-daily--title-format ()
|
||||
"Make sure that `denote-journal-extras-daily--title-format' yields the desired format."
|
||||
(should (and
|
||||
;; These three should prompt, but I am here treating the
|
||||
;; prompt as if already returned a string. The test for
|
||||
;; the `denote-title-prompt' can be separate.
|
||||
(stringp
|
||||
(cl-letf (((symbol-function 'denote-title-prompt) #'identity)
|
||||
(denote-journal-extras-title-format nil))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
;; TODO 2026-02-23: Add support for Windows paths. But I need somebody who has a Windows machine...
|
||||
(ert-deftest dt-denote--get-common-root-directory ()
|
||||
"Test that `denote--get-common-root-directory' returns the right path."
|
||||
(should (string=
|
||||
(denote--get-common-root-directory
|
||||
'("/home/prot/Documents/notes/"
|
||||
"/home/prot/Documents/notes/attachments"))
|
||||
"/home/prot/Documents/notes/"))
|
||||
(should (string=
|
||||
(denote--get-common-root-directory
|
||||
'("/home/prot/Documents/"
|
||||
"/home/prot/Documents/notes/attachments"))
|
||||
"/home/prot/Documents/"))
|
||||
(should (string=
|
||||
(denote--get-common-root-directory
|
||||
'("/home/prot/Books/"
|
||||
"/home/prot/Documents/notes/"))
|
||||
"/home/prot/"))
|
||||
(should (string=
|
||||
(denote--get-common-root-directory
|
||||
'("/tmp/notes/"
|
||||
"/home/prot/Documents/notes/"))
|
||||
"/"))
|
||||
(should (string=
|
||||
(denote--get-common-root-directory
|
||||
'("/home/prot/Documents/notes/"))
|
||||
"/home/prot/Documents/notes/"))
|
||||
(should-error (denote--get-common-root-directory '("~/Documents/notes/"))))
|
||||
(stringp
|
||||
(cl-letf (((symbol-function 'denote-title-prompt) #'identity)
|
||||
(denote-journal-extras-title-format t))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
(ert-deftest dt-denote-query--keywords-as-regexp ()
|
||||
"Test that `denote-query--keywords-as-regexp' works as intended."
|
||||
(should (string= (denote-query--keywords-as-regexp '("one" "two")) "_\\(?:one\\|two\\)"))
|
||||
(should-error (denote-query--keywords-as-regexp "one"))
|
||||
(should-error (denote-query--keywords-as-regexp '("one" 1))))
|
||||
(stringp
|
||||
(cl-letf (((symbol-function 'denote-title-prompt) #'identity)
|
||||
(denote-journal-extras-title-format :some-arbitrary-keyword))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
(ert-deftest dt-denote-file-type ()
|
||||
"Test that function `denote-file-type' does what it is meant to."
|
||||
(let* ((create-file-fn (lambda (name extension)
|
||||
(expand-file-name
|
||||
(format "denote-test-file-type-%s.%s" name extension)
|
||||
(temporary-file-directory))))
|
||||
(modify-file-fn (lambda (file contents)
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(erase-buffer)
|
||||
(insert contents)
|
||||
(save-buffer))))
|
||||
(yaml-file (funcall create-file-fn "yaml" "md"))
|
||||
(toml-file (funcall create-file-fn "toml" "md"))
|
||||
(org-file (funcall create-file-fn "org" "org")))
|
||||
(progn
|
||||
(funcall modify-file-fn yaml-file denote-yaml-front-matter)
|
||||
(should (eq (denote-file-type yaml-file) 'markdown-yaml)))
|
||||
(progn
|
||||
(funcall modify-file-fn toml-file denote-toml-front-matter)
|
||||
(should (eq (denote-file-type toml-file) 'markdown-toml)))
|
||||
(progn
|
||||
(funcall modify-file-fn org-file denote-org-front-matter)
|
||||
(should (eq (denote-file-type org-file) 'org)))
|
||||
(should-not (denote-file-type "test"))
|
||||
(let ((denote-file-types nil))
|
||||
(should-not (denote-file-type "test.md")))))
|
||||
;; And these return the following values
|
||||
(string-match-p
|
||||
"\\<.*?\\>"
|
||||
(let ((denote-journal-extras-title-format 'day))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
(string-match-p
|
||||
"\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\}"
|
||||
(let ((denote-journal-extras-title-format 'day-date-month-year))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
(string-match-p
|
||||
"\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\} [0-9]\\{,2\\}:[0-9]\\{,2\\} \\<.*?\\>"
|
||||
(let ((denote-journal-extras-title-format 'day-date-month-year-12h))
|
||||
(denote-journal-extras-daily--title-format)))
|
||||
|
||||
(string-match-p
|
||||
"\\<.*?\\> [0-9]\\{,2\\} \\<.*?\\> [0-9]\\{,4\\} [0-9]\\{,2\\}:[0-9]\\{,2\\}"
|
||||
(let ((denote-journal-extras-title-format 'day-date-month-year-24h))
|
||||
(denote-journal-extras-daily--title-format))))))
|
||||
|
||||
(provide 'denote-test)
|
||||
;;; denote-test.el ends here
|
||||
|
||||
;; Local Variables:
|
||||
;; read-symbol-shorthands: (("dt" . "denote-test-"))
|
||||
;; End:
|
||||
|
|
|
|||
Loading…
Reference in a new issue