[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/denote 4fa3433402 1/3: Add user option denote-file-name
From: |
ELPA Syncer |
Subject: |
[elpa] externals/denote 4fa3433402 1/3: Add user option denote-file-name-letter-casing |
Date: |
Wed, 11 Oct 2023 09:58:05 -0400 (EDT) |
branch: externals/denote
commit 4fa343340250a0a0e23d3133d2f20ce25f0f46b7
Author: Protesilaos Stavrou <info@protesilaos.com>
Commit: Protesilaos Stavrou <info@protesilaos.com>
Add user option denote-file-name-letter-casing
---
README.org | 64 +++++++++++++++++++++++++++++----
denote.el | 118 +++++++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 153 insertions(+), 29 deletions(-)
diff --git a/README.org b/README.org
index 03b3ad7894..ad574802da 100644
--- a/README.org
+++ b/README.org
@@ -1091,7 +1091,8 @@ the following:
- retains the file's existing name and make it the =TITLE= field, per
Denote's file-naming scheme;
-- downcases and sluggifies the =TITLE=, per our conventions;
+- sluggifies the =TITLE= and adjusts its letter casing, per our
+ conventions ([[*Contol the letter casing of file names][Contol the letter
casing of file names]]);
- prepends an identifier to the =TITLE=;
@@ -1268,8 +1269,9 @@ hour-minute-second notation. The presentation is compact:
=20220531T091625=. The =DATE= serves as the unique identifier of each
note and, as such, is also known as the file's ID or identifier.
-The =TITLE= field is the title of the note, as provided by the user. It
-automatically gets downcased and hyphenated. An entry about "Economics
+The =TITLE= field is the title of the note, as provided by the user.
+It automatically gets downcased by default and is also hyphenated
+([[#h:6ae1ab8c-5e36-4216-8e93-f37f4447582c][Contol the letter casing of file
names]]). An entry about "Economics
in the Euro Area" produces an =economics-in-the-euro-area= string for
the =TITLE= of the file name.
@@ -1363,15 +1365,65 @@ holds the relevant value. In simple terms:
+ What we count as "illegal characters" are converted into hyphens.
-+ Input for a file title is hyphenated and downcased. The original
- value is preserved in the note's contents
([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]).
++ Input for a file title is hyphenated. The original value is
+ preserved in the note's contents
([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]).
+ Keywords should not have spaces or other delimiters. If they do, they
- are converted into hyphens. Keywords are always downcased.
+ are converted into hyphens.
+ Signatures are like the above, but use the equals sign instead of
hyphens.
+All file name components are downcases by default, though users can
+configure this behaviour ([[#h:6ae1ab8c-5e36-4216-8e93-f37f4447582c][Contol
the letter casing of file names]]).
+
+** Contol the letter casing of file names
+:PROPERTIES:
+:CUSTOM_ID: h:6ae1ab8c-5e36-4216-8e93-f37f4447582c
+:END:
+
+[ Part of {{{development-version}}}. ]
+
+#+vindex: denote-file-name-letter-casing
+The user option ~denote-file-name-letter-casing~ controls the letter
+casing of the individual components of file names
([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+The default method is to downcase everything.
+
+The value of this user option is an alist where each element is a cons
+cell of the form =(COMPONENT . METHOD)=. For example, here is the
+default value:
+
+#+begin_example emacs-lisp
+'((title . downcase)
+ (signature . downcase)
+ (keywords . downcase)
+ (t . downcase))
+#+end_example
+
+What these cons cells of =(COMPONENT . METHOD)= are:
+
+- The =COMPONENT= is an unquoted symbol among =title=, =signature=,
+ =keywords=, which refer to the corresponding component of the file
+ name. The special ~t~ =COMPONENT= is a fallback value in case the
+ others are not specified.
+
+- The =METHOD= is the letter casing scheme, which is an unquoted
+ symbol of either =downcase= or =verbatim=. A nil value has the same
+ meaning as =downcase=. Other non-nil =METHOD= types are reserved
+ for possible future use.
+
+ The =downcase= =METHOD= converts user input for the given
+ =COMPONENT= into lower case. The benefit of this approach (which is
+ the default behaviour) is that file names remain consistent over the
+ long-term. The user never needs to account for varying letter
+ casing while working with them.
+
+ The =verbatim= =METHOD= means that Denote will not affect the letter
+ casing of user input when generating the given file name =COMPONENT=.
+ Conventions like CamelCase or camelCase are respected. The user
+ thus assumes responsibility to keep file names in a good state over
+ the long term.
+
** Features of the file-naming scheme for searching or filtering
:PROPERTIES:
:CUSTOM_ID: h:1a953736-86c2-420b-b566-fb22c97df197
diff --git a/denote.el b/denote.el
index 6ad86f641f..ee5954c8fe 100644
--- a/denote.el
+++ b/denote.el
@@ -603,9 +603,68 @@ leading and trailing hyphen."
"-\\{2,\\}" "-"
(replace-regexp-in-string "_\\|\s+" "-" str))))
-(defun denote-sluggify (str)
- "Make STR an appropriate slug for file names and related."
- (downcase (denote--slug-hyphenate (denote--slug-no-punct str))))
+(defcustom denote-file-name-letter-casing
+ '((title . downcase)
+ (signature . downcase)
+ (keywords . downcase)
+ (t . downcase))
+ "Specify the method Denote uses to affect the letter casing of file names.
+
+The value is an alist where each element is a cons cell of the
+form (COMPONENT . METHOD).
+
+- The COMPONENT is an unquoted symbol among `title', `signature',
+ `keywords', which refer to the corresponding component of the
+ file name. The special t COMPONENT is a fallback value in case
+ the others are not specified.
+
+- The METHOD is the letter casing scheme, which is an unquoted
+ symbol of either `downcase' or `verbatim'. A nil value has the
+ same meaning as `downcase'. Other non-nil METHOD types are
+ reserved for possible future use.
+
+ The `downcase' METHOD converts user input for the given
+ COMPONENT into lower case. The benefit of this approach (which
+ is the default behaviour) is that file names remain consistent
+ over the long-term. The user never needs to account for
+ varying letter casing while working with them.
+
+ The `verbatim' METHOD means that Denote will not affect the
+ letter casing of user input when generating the given file name
+ COMPONENT. As such, conventions like CamelCase or camelCase
+ are respected. The user thus assumes responsibility to keep
+ file names in a good state over the long term."
+ :group 'denote
+ :type '(alist
+ :key (choice :tag "File name component"
+ (const :tag "The --TITLE component of the file name"
title)
+ (const :tag "The ==SIGNATURE component of the file
name" signature)
+ (const :tag "The __KEYWORDS component of the file name"
keywords)
+ (const :tag "Fallback for any unspecified file name
component" t))
+ :value (choice :tag "Letter casing method"
+ (const :tag "Downcase file names (default)" downcase)
+ (const :tag "Accept file name inputs verbatim"
verbatim)))
+ :package-version '(denote . "2.1.0"))
+
+(defun denote-letter-case (component args)
+ "Apply letter casing specified by COMPONENT to ARGS.
+COMPONENT is a symbol representing a file name component, as
+described in the user option `denote-file-name-letter-casing'."
+ (if (or (eq (alist-get component denote-file-name-letter-casing) 'verbatim)
+ (eq (alist-get t denote-file-name-letter-casing) 'verbatim))
+ args
+ (funcall #'downcase args)))
+
+(defun denote-sluggify (str &optional component)
+ "Make STR an appropriate slug for file name COMPONENT.
+
+COMPONENT is a symbol used to retrieve the letter casing method
+corresponding to the file name field is references. COMPONENT is
+described in the user option `denote-file-name-letter-casing'.
+
+A nil value of COMPONENT has the same meaning as applying
+`downcase' to STR."
+ (denote-letter-case component (denote--slug-hyphenate (denote--slug-no-punct
str))))
(define-obsolete-function-alias
'denote--sluggify
@@ -622,13 +681,21 @@ any leading and trailing signs."
"=\\{2,\\}" "="
(replace-regexp-in-string "_\\|\s+" "=" str))))
-(defun denote-sluggify-signature (str)
- "Make STR an appropriate slug for signatures."
- (downcase (denote--slug-put-equals (denote--slug-no-punct str))))
+(defun denote-sluggify-signature (str &optional component)
+ "Make STR an appropriate slug for signatures.
+
+COMPONENT is a symbol used to retrieve the letter casing method
+corresponding to the file name field is references. COMPONENT is
+described in the user option `denote-file-name-letter-casing'.
+
+A nil value of COMPONENT has the same meaning as applying
+`downcase' to STR."
+ (denote-letter-case component (denote--slug-put-equals
(denote--slug-no-punct str))))
(defun denote-sluggify-and-join (str)
"Sluggify STR while joining separate words."
- (downcase
+ (denote-letter-case
+ 'keywords
(replace-regexp-in-string
"-" ""
(denote--slug-hyphenate (denote--slug-no-punct str)))))
@@ -641,11 +708,12 @@ any leading and trailing signs."
(defun denote-sluggify-keywords (keywords)
"Sluggify KEYWORDS, which is a list of strings."
(if (listp keywords)
- (mapcar
- (if denote-allow-multi-word-keywords
- #'denote-sluggify
- #'denote-sluggify-and-join)
- keywords)
+ ;; FIXME 2023-10-09: What to do with `denote-allow-multi-word-keywords
+ (mapcar
+ (if denote-allow-multi-word-keywords
+ #'denote-sluggify
+ #'denote-sluggify-and-join)
+ keywords)
(error "`%s' is not a list" keywords)))
(define-obsolete-function-alias
@@ -1024,7 +1092,10 @@ KEYWORDS is a list of strings, per
`denote-keywords-prompt'."
(defun denote--keywords-combine (keywords)
"Format KEYWORDS output of `denote-keywords-prompt'."
- (mapconcat #'downcase keywords "_"))
+ (mapconcat
+ (lambda (k)
+ (denote-letter-case 'keywords k))
+ keywords "_"))
(defun denote--keywords-add-to-history (keywords)
"Append KEYWORDS to `denote--keyword-history'."
@@ -1544,8 +1615,8 @@ are expected to be supplied by `denote' or equivalent
command."
(defun denote--format-front-matter-keywords (keywords file-type)
"Format KEYWORDS according to FILE-TYPE for the file's front matter.
-Apply `downcase' to KEYWORDS."
- (let ((kw (mapcar #'downcase (denote-sluggify-keywords keywords))))
+Apply `denote-letter-case' to KEYWORDS."
+ (let ((kw (denote-sluggify-keywords keywords)))
(funcall (denote--keywords-value-function file-type) kw)))
(make-obsolete-variable 'denote-text-front-matter-delimiter nil "0.6.0")
@@ -1568,10 +1639,10 @@ construct path to DIR."
(denote-format-file-name
dir id
(denote-sluggify-keywords keywords)
- (denote-sluggify title)
+ (denote-sluggify title 'title)
(denote--file-extension file-type)
(when signature
- (denote-sluggify-signature signature))))
+ (denote-sluggify-signature signature 'signature))))
;; Adapted from `org-hugo--org-date-time-to-rfc3339' in the `ox-hugo'
;; package: <https://github.com/kaushalmodi/ox-hugo>.
@@ -2420,7 +2491,7 @@ files)."
(extension (denote-get-file-extension file))
(file-type (denote-filetype-heuristics file))
(new-name (denote-format-file-name
- dir id keywords (denote-sluggify title) extension
signature))
+ dir id keywords (denote-sluggify title 'title) extension
signature))
(max-mini-window-height 0.33)) ; allow minibuffer to be resized
(when (denote-rename-file-prompt file new-name)
(denote-rename-file-and-buffer file new-name)
@@ -2462,7 +2533,7 @@ of the file. This needs to be done manually."
(old-extension (denote-get-file-extension file))
(new-extension (denote--file-extension new-file-type))
(new-name (denote-format-file-name
- dir id keywords (denote-sluggify title) new-extension))
+ dir id keywords (denote-sluggify title 'title)
new-extension))
(max-mini-window-height 0.33)) ; allow minibuffer to be resized
(when (and (not (eq old-extension new-extension))
(denote-rename-file-prompt file new-name))
@@ -2480,7 +2551,8 @@ Specifically, do the following:
- retain the file's existing name and make it the TITLE field,
per Denote's file-naming scheme;
-- downcase and sluggify the TITLE, per our conventions;
+- `denote-letter-case' and sluggify the TITLE, per our
+ conventions (check the user option `denote-file-name-letter-casing');
- prepend an identifier to the TITLE;
@@ -2520,7 +2592,7 @@ Specifically, do the following:
(file-type (denote-filetype-heuristics file))
(title (denote--retrieve-title-or-filename file file-type))
(extension (denote-get-file-extension file))
- (new-name (denote-format-file-name dir id keywords
(denote-sluggify title) extension signature)))
+ (new-name (denote-format-file-name dir id keywords
(denote-sluggify title 'title) extension signature)))
(denote-rename-file-and-buffer file new-name)
(when (denote-file-is-writable-and-supported-p new-name)
(if (denote--edit-front-matter-p new-name file-type)
@@ -2573,7 +2645,7 @@ proceed with the renaming."
;; want to throw an exception if any is nil.
dir id
(or (denote-retrieve-keywords-value file file-type) nil)
- (denote-sluggify title) extension
+ (denote-sluggify title 'title) extension
(or (denote-retrieve-filename-signature file) nil))))
(when (or auto-confirm
(denote-rename-file-prompt file new-name))
@@ -2627,7 +2699,7 @@ their respective front matter."
(keywords (denote-retrieve-keywords-value file file-type))
(extension (denote-get-file-extension file))
(new-name (denote-format-file-name
- dir id keywords (denote-sluggify title) extension
signature)))
+ dir id keywords (denote-sluggify title 'title)
extension signature)))
(denote-rename-file-and-buffer file new-name)))
(revert-buffer))
(user-error "No marked files; aborting")))