[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/denote 6da2d7b2e4 11/12: Merge pull request #210 from j
From: |
ELPA Syncer |
Subject: |
[elpa] externals/denote 6da2d7b2e4 11/12: Merge pull request #210 from jeanphilippegg/slug-functions |
Date: |
Thu, 4 Jan 2024 06:57:54 -0500 (EST) |
branch: externals/denote
commit 6da2d7b2e4a8b01065c8f2f96e01d14cd04a3f76
Merge: 15736c648e 63d25e0cfd
Author: Protesilaos Stavrou <info@protesilaos.com>
Commit: GitHub <noreply@github.com>
Merge pull request #210 from jeanphilippegg/slug-functions
Slug functions
---
README.org | 171 ++++++++++++-----------------
denote.el | 305 ++++++++++++++++++++++++++++-----------------------
tests/denote-test.el | 55 ++++------
3 files changed, 261 insertions(+), 270 deletions(-)
diff --git a/README.org b/README.org
index c18b9a9e6e..02aa3d7863 100644
--- a/README.org
+++ b/README.org
@@ -1341,7 +1341,7 @@ directories.
Every note produced by Denote follows this pattern by default
([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]):
-: DATE--TITLE__KEYWORDS.EXTENSION
+: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
The =DATE= field represents the date in year-month-day format followed
by the capital letter =T= (for "time") and the current time in
@@ -1349,9 +1349,19 @@ 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.
+File names can include a string of alphanumeric characters in the
+=SIGNATURE= field. Signatures have no clearly defined purpose and are up
+to the user to define. One use-case is to use them to establish
+sequential relations between files (e.g. 1, 1a, 1b, 1b1, 1b2, ...).
+
+Signatures are an optional extension to Denote's file-naming scheme.
+They can be added to newly created files on demand, with the command
+~denote-signature~, or by modifying the value of the user option
+~denote-prompts~.
+
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
+([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name
components]]). An entry about "Economics
in the Euro Area" produces an =economics-in-the-euro-area= string for
the =TITLE= of the file name.
@@ -1360,12 +1370,12 @@ underscore (the separator is inserted automatically).
Each keyword is
a string provided by the user at the relevant prompt which broadly
describes the contents of the entry.
-Each of the keywords is a single word, with multiple keywords
-providing the multi-dimensionality needed for advanced searches
-through Denote files. Users who need to compose a keyword out of
-multiple words are encouraged to apply a letter casing convention such
-as camelCase/CamelCase and set the ~denote-file-name-letter-casing~
-user option accordingly ([[#h:6ae1ab8c-5e36-4216-8e93-f37f4447582c][Contol the
letter casing of file names]]).
+Each of the keywords is a single word, with multiple keywords providing
+the multi-dimensionality needed for advanced searches through Denote
+files. Users who need to compose a keyword out of multiple words such
+as camelCase/CamelCase and are encouraged to use the
+~denote-file-name-slug-functions~ user option accordingly
+([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name
components]]).
#+vindex: denote-file-type
The =EXTENSION= is the file type. By default, it is =.org= (~org-mode~)
@@ -1391,23 +1401,6 @@ invoking =M-x re-builder=).
[[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme
for searching or filtering]].
-As an optional extension to the above, file names can include a string
-of alphanumeric characters in the =SIGNATURE= field. Signatures have
-no clearly defined purpose and are up to the user to define. One
-use-case is to use them to establish sequential relations between
-files (e.g. 1, 1a, 1b, 1b1, 1b2, ...). A full file name with a
-signature looks like this:
-
-: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
-
-The =SIGNATURE= field is anchored by the equals sign and thus retains
-the aforementioned searching/anchoring feature of =--= and =__=.
-
-Signatures are an optional extension to Denote's file-naming scheme.
-They can be added to newly created files on demand, with the command
-~denote-signature~, or by modifying the value of the user option
-~denote-prompts~.
-
The ~denote-prompts~ can be configured in such ways to yield the
following file name permutations:
@@ -1427,90 +1420,70 @@ scheme we apply guarantees that a listing is readable
in a variety of
contexts. The Denote file-naming scheme is, in essence, an effective,
low-tech invention.
-** Sluggified title, keywords, and signature
+** Sluggification of file name components
:PROPERTIES:
:CUSTOM_ID: h:ae8b19a1-7f67-4258-96b3-370a72c43f4e
:END:
-Denote has to be highly opinionated about which characters can be used
-in file names and the file's front matter in order to enforce its
-file-naming scheme. The variable ~denote-excluded-punctuation-regexp~
-holds the relevant value. In simple terms:
+Files names can contain any character that the file system
+permits. Denote imposes a few additional restrictions:
+
++ The tokens "==", =__= and =--= are interpreted by Denote and should
+ appear only once. =_= is the separator for keywords.
+
++ The dot character is not allowed in a note's file name, except to
+ indicate the extension.
+
+By default, Denote enforce other rules to file names through the user
+option ~denote-file-name-slug-functions~.
+
+These rules are applied to file names by default:
-+ What we count as "illegal characters" are converted into hyphens.
++ What we count as "illegal characters" are removed. The variable
+ ~denote-excluded-punctuation-regexp~ holds the relevant value.
+ 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 removed, meaning that =hello-world= becomes =helloworld=.
- This is because hyphens in keywords do not work everywhere, such as
- in Org.
++ Spaces or other delimiters are removed from keywords, meaning that
+ =hello-world= becomes =helloworld=. This is because hyphens in
+ keywords do not work everywhere, such as in Org.
+ 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]]).
-Consider a =helloWorld= or =HelloWorld= convention for those cases
-where you would want to have a hyphen between consistuent words of a
-keyword.
++ All file name components are downcased. Consider a =helloWorld= or
+ =HelloWorld= convention for those cases where you would want to have a
+ hyphen between consistuent words of a keyword.
-** Contol the letter casing of file names
-:PROPERTIES:
-:CUSTOM_ID: h:6ae1ab8c-5e36-4216-8e93-f37f4447582c
-:END:
-
-#+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.
+#+vindex: denote-file-name-slug-functions
+The user option ~denote-file-name-slug-functions~ can be used to control
+the sluggification of the components of file names
([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming
+scheme]]). The default method is explained in the previous section.
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))
+'((title . denote-sluggify-title)
+ (signature . denote-sluggify-signature)
+ (keyword . denote-sluggify-keyword))
#+end_example
What these cons cells of =(COMPONENT . METHOD)= are:
- The =COMPONENT= is an unquoted symbol among =title=, =signature=,
- =keywords=, which refers 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.
-
-As an example, we can downcase the title, but preserve the letter
-casing of the signature and keyword components with this:
+ =keyword=, which refers to the corresponding component of the file
+ name.
-#+begin_src emacs-lisp
-(setq denote-file-name-letter-casing
- '((title . downcase)
- (signature . verbatim)
- (keywords . verbatim)
- (t . downcase)))
-#+end_src
+- The =METHOD= is the function to be used to format the given component.
+ This function should take a string as its parameter and return the
+ string formatted for the file name. In the case of the `keyword'
+ component, the function receives a SINGLE string representing a single
+ keyword and return it formatted for the file name. Joining the
+ keywords together is handled by Denote. Note that the `keyword'
+ function is also applied to the keywords of the front matter.
** Features of the file-naming scheme for searching or filtering
:PROPERTIES:
@@ -3856,10 +3829,10 @@ might change them without further notice.
#+findex: denote-sluggify
+ Function ~denote-sluggify~ :: Make =STR= an appropriate slug for
- file names and related
([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggified title and keywords]]).
+ file names and related
([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name
components]]).
-#+findex: denote-sluggify-and-join
-+ Function ~denote-sluggify-and-join~ :: Sluggify =STR= while joining
+#+findex: denote-sluggify-keyword
++ Function ~denote-sluggify-keyword~ :: Sluggify =STR= while joining
separate words.
#+findex: denote-desluggify
@@ -3869,11 +3842,11 @@ might change them without further notice.
#+findex: denote-sluggify-signature
+ Function ~denote-sluggify-signature~ :: Make =STR= an appropriate
- slug for signatures ([[#h:6ae1ab8c-5e36-4216-8e93-f37f4447582c][Contol the
letter casing of file names]]).
+ slug for signatures
([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name
components]]).
#+findex: denote-sluggify-keywords
+ Function ~denote-sluggify-keywords~ :: Sluggify =KEYWORDS=, which is
- a list of strings ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggified
title and keywords]]).
+ a list of strings ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification
of file name components]]).
#+findex: denote-filetype-heuristics
+ Function ~denote-filetype-heuristics~ :: Return likely file type of
@@ -3889,8 +3862,8 @@ might change them without further notice.
#+findex: denote-format-file-name
+ Function ~denote-format-file-name~ :: Format file name. =DIR-PATH=,
- =ID=, =KEYWORDS=, =TITLE-SLUG=, =EXTENSION= and =SIGNATURE-SLUG= are
- expected to be supplied by ~denote~ or equivalent command.
+ =ID=, =KEYWORDS=, =TITLE=, =EXTENSION= and =SIGNATURE= are expected to
+ be supplied by ~denote~ or equivalent command.
=DIR-PATH= is a string pointing to a directory. It ends with a
forward slash (the function ~denote-directory~ makes sure this is
@@ -3907,11 +3880,9 @@ might change them without further notice.
nil value, in which case the relevant file name component is not
added to the base file name.
- =TITLE-SLUG= and =SIGNATURE-SLUG= are strings which, in principle,
- are sluggified before passed as arguments here (per
- ~denote-sluggify~ and ~denote-sluggify-signature~). They can be an
- empty string or a nil value, in which case their respective file
- name component is not added to the base file name.
+ =TITLE= and =SIGNATURE= are strings. They can be an empty string, in
+ which case their respective file name component is not added to the
+ base file name.
=EXTENSION= is a string that contains a dot followed by the file
type extension. It can be an empty string or a nil value, in which
@@ -4004,15 +3975,13 @@ might change them without further notice.
filter the candidates per the given regular expression.
#+findex: denote-keywords-prompt
-+ Function ~denote-keywords-prompt~ :: Prompt for one or more
- keywords. Read entries as separate when they are demarcated by the
++ Function ~denote-keywords-prompt~ :: Prompt for one or more keywords.
+ Read entries as separate when they are demarcated by the
~crm-separator~, which typically is a comma. With optional
=PROMPT-TEXT=, use it to prompt the user for keywords. Else use a
generic prompt. With optional =INITIAL-KEYWORDS= use them as the
- initial minibuffer text. Process the return value with
- ~denote-keywords-sort~ and sort with ~string-collate-lessp~ if the
- user option ~denote-sort-keywords~ is non-nil. [ The optional
- =INITIAL-KEYWORDS= argument is part of {{{development-version}}}. ]
+ initial minibuffer text. [ The optional =INITIAL-KEYWORDS= argument is
+ part of {{{development-version}}}. ]
#+findex: denote-title-prompt
+ Function ~denote-title-prompt~ :: Prompt for title string. With
diff --git a/denote.el b/denote.el
index 23c7df9be5..04b0750afc 100644
--- a/denote.el
+++ b/denote.el
@@ -154,7 +154,7 @@ directory and also checks if a safe local value should be
used."
'("emacs" "philosophy" "politics" "economics")
"List of strings with predefined keywords for `denote'.
Also see user options: `denote-infer-keywords',
-`denote-sort-keywords', `denote-file-name-letter-casing'."
+`denote-sort-keywords', `denote-file-name-slug-functions'."
:group 'denote
:package-version '(denote . "0.1.0")
:type '(repeat string))
@@ -501,49 +501,56 @@ and `denote-link-after-creating-with-command'."
:link '(info-link "(denote) Choose which commands to prompt for")
:type '(repeat symbol))
-(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.
+(defvar denote-file-name-slug-functions
+ '((title . denote-sluggify-title)
+ (signature . denote-sluggify-signature)
+ (keyword . denote-sluggify-keyword))
+ "Specify the method Denote uses to format the components of the file name.
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 refers 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)))
- :link '(info-link "(denote) Contol the letter casing of file names")
- :package-version '(denote . "2.1.0"))
+ `keyword' (notice the absence of `s', see below), which
+ refers to the corresponding component of the file name.
+
+- The METHOD is the function to be used to format the given
+ component. This function should take a string as its parameter
+ and return the string formatted for the file name. In the case
+ of the `keyword' component, the function receives a SINGLE
+ string representing a single keyword and return it formatted
+ for the file name. Joining the keywords together is handled by
+ Denote.
+
+Note that the `keyword' function is also applied to the keywords
+of the front matter.
+
+By default, if a function is not specified for a component, we
+use `denote-sluggify-title', `denote-sluggify-keyword' and
+`denote-sluggify-signature'.")
+
+(make-obsolete
+ 'denote-file-name-letter-casing
+ 'denote-file-name-slug-functions
+ "3.0.0")
+
+(defvar denote-file-name-deslug-functions
+ '((title . denote-desluggify-title)
+ (signature . denote-desluggify-signature)
+ (keyword . denote-desluggify-keyword))
+ "Specify the method Denote uses to reverse the process of `denote-sluggify'.
+
+Since `denote-sluggify' is destructive, this is just an attempt
+to get back a more human-friendly component. This is useful when
+you want to retrieve a title or signature from the file name and
+display it as the default input in commands such as
+`denote-rename-file'.
+
+See the documentation of `denote-file-name-slug-functions'.
+
+By default, if a function is not specified for a component, we
+use `denote-desluggify-title', `denote-desluggify-keyword' and
+`denote-desluggify-signature'.")
;;;; Main variables
@@ -556,13 +563,13 @@ The note's ID is derived from the date and time of its
creation.")
(defconst denote-id-regexp "\\([0-9]\\{8\\}\\)\\(T[0-9]\\{6\\}\\)"
"Regular expression to match `denote-id-format'.")
-(defconst denote-signature-regexp "==\\([[:alnum:][:nonascii:]=]*\\)"
+(defconst denote-signature-regexp "==\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$"
"Regular expression to match the SIGNATURE field in a file name.")
-(defconst denote-title-regexp "--\\([[:alnum:][:nonascii:]-]*\\)"
+(defconst denote-title-regexp "--\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$"
"Regular expression to match the TITLE field in a file name.")
-(defconst denote-keywords-regexp "__\\([[:alnum:][:nonascii:]_-]*\\)"
+(defconst denote-keywords-regexp "__\\([^.]*?\\)\\(--.*\\|__.*\\|\\..*\\)*$"
"Regular expression to match the KEYWORDS field in a file name.")
(defconst denote-excluded-punctuation-regexp
"[][{}!@#$%^&*()=+'\"?,.\|;:~`‘’“”/]*"
@@ -618,6 +625,21 @@ as the aforementioned variables."
(setq str (replace-regexp-in-string regexp "" str))))
str)
+(defun denote--slug-no-punct-for-signature (str &optional extra-characters)
+ "Remove punctuation (except = signs) from STR.
+
+This works the same way as `denote--slug-no-punct', except that =
+signs are not removed from STR.
+
+EXTRA-CHARACTERS is an optional string. See
+`denote--slug-no-punct' for its documentation."
+ (dolist (regexp (list denote-excluded-punctuation-regexp
+ denote-excluded-punctuation-extra-regexp
+ extra-characters))
+ (when (stringp regexp)
+ (setq str (replace-regexp-in-string (string-replace "=" "" regexp) ""
str))))
+ str)
+
(defun denote--slug-hyphenate (str)
"Replace spaces and underscores with hyphens in STR.
Also replace multiple hyphens with a single one and remove any
@@ -628,25 +650,23 @@ leading and trailing hyphen."
"-\\{2,\\}" "-"
(replace-regexp-in-string "_\\|\s+" "-" str))))
-(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)
+(defun denote-sluggify (component str)
"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'.
+Apply the function specified in `denote-file-name-slug-function'
+to COMPONENT which is one of `title', `signature', `keyword'."
+ (let ((slug-function (alist-get component denote-file-name-slug-functions)))
+ (cond ((eq component 'title)
+ (funcall (or slug-function #'denote-sluggify-title) str))
+ ((eq component 'keyword)
+ (funcall (or slug-function #'denote-sluggify-keyword) str))
+ ((eq component 'signature)
+ (funcall (or slug-function #'denote-sluggify-signature) str)))))
-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))))
+(make-obsolete
+ 'denote-letter-case
+ 'denote-sluggify
+ "3.0.0")
(defun denote--slug-put-equals (str)
"Replace spaces and underscores with equals signs in STR.
@@ -658,35 +678,64 @@ any leading and trailing signs."
"=\\{2,\\}" "="
(replace-regexp-in-string "_\\|\s+" "=" str))))
+(defun denote-sluggify-title (str)
+ "Make STR an appropriate slug for title."
+ (downcase (denote--slug-hyphenate (denote--slug-no-punct str))))
+
(defun denote-sluggify-signature (str)
- "Make STR an appropriate slug for signatures.
-Perform letter casing according to `denote-file-name-letter-casing'."
- (denote-letter-case 'signature (denote--slug-put-equals
(denote--slug-no-punct str "-+"))))
+ "Make STR an appropriate slug for signature."
+ (downcase (denote--slug-put-equals (denote--slug-no-punct-for-signature str
"-+"))))
-(defun denote-sluggify-and-join (str)
+(defun denote-sluggify-keyword (str)
"Sluggify STR while joining separate words."
- (denote-letter-case
- 'keywords
+ (downcase
(replace-regexp-in-string
"-" ""
(denote--slug-hyphenate (denote--slug-no-punct str)))))
+(make-obsolete
+ 'denote-sluggify-and-join
+ 'denote-sluggify-keyword
+ "3.0.0")
+
(defun denote-sluggify-keywords (keywords)
"Sluggify KEYWORDS, which is a list of strings."
- (mapcar #'denote-sluggify-and-join keywords))
-
-;; TODO 2023-05-22: Review name of `denote-desluggify' to signify what
-;; the doc string warns about.
-(defun denote-desluggify (str)
+ (mapcar (lambda (keyword)
+ (denote-sluggify 'keyword keyword))
+ keywords))
+
+(defun denote-desluggify (component str)
+ "Attempt to reverse the process of `denote-sluggify' for STR on COMPONENT.
+
+Apply the function specified in `denote-file-name-deslug-function'
+to COMPONENT which is one of `title', `signature', `keyword'."
+ (let ((deslug-function (alist-get component
denote-file-name-deslug-functions)))
+ (cond ((eq component 'title)
+ (funcall (or deslug-function #'denote-desluggify-title) str))
+ ((eq component 'keyword)
+ (funcall (or deslug-function #'denote-desluggify-keyword) str))
+ ((eq component 'signature)
+ (funcall (or deslug-function #'denote-desluggify-signature) str)))))
+
+(defun denote-desluggify-title (str)
"Upcase first char in STR and dehyphenate STR, inverting `denote-sluggify'.
The intent of this function is to be used on individual strings,
such as the TITLE component of a Denote file name, but not on the
-entire file name. Put differently, it does not work with
-signatures and keywords."
+entire file name."
(let ((str (replace-regexp-in-string "-" " " str)))
(aset str 0 (upcase (aref str 0)))
str))
+;; NOTE 2024-01-01: This is not used for now.
+(defun denote-desluggify-signature (str)
+ "Reverse of `denote-sluggify-signature' for STR."
+ str)
+
+;; NOTE 2023-12-25: This is not used for now.
+(defun denote-desluggify-keyword (str)
+ "Reverse of `denote-sluggify-keyword' for STR."
+ str)
+
(defun denote--file-empty-p (file)
"Return non-nil if FILE is empty."
(zerop (or (file-attribute-size (file-attributes file)) 0)))
@@ -722,8 +771,7 @@ For our purposes, a note must not be a directory, must
satisfy
(defun denote-file-has-signature-p (file)
"Return non-nil if FILE has a Denote identifier."
- (string-match-p denote-signature-regexp
- (file-name-nondirectory file)))
+ (denote-retrieve-filename-signature file))
(make-obsolete 'denote-file-directory-p nil "2.0.0")
@@ -957,11 +1005,8 @@ PATH must be a Denote-style file name where keywords are
prefixed
with an underscore.
If PATH has no such keywords, return nil."
- (let* ((file-name (file-name-nondirectory path))
- (kws (when (string-match denote-keywords-regexp file-name)
- (match-string-no-properties 1 file-name))))
- (when kws
- (split-string kws "_"))))
+ (when-let ((kws (denote-retrieve-filename-keywords path)))
+ (split-string kws "_")))
(defun denote--inferred-keywords ()
"Extract keywords from `denote-directory-files'.
@@ -1014,13 +1059,8 @@ With optional PROMPT-TEXT, use it to prompt the user for
keywords. Else use a generic prompt. With optional
INITIAL-KEYWORDS use them as the initial minibuffer text.
-Process the return value with `denote-keywords-sort' and sort
-with `string-collate-lessp' if the user option
-`denote-sort-keywords' is non-nil.
-
Return an empty list if the minibuffer input is empty."
- (denote-keywords-sort
- (denote--keywords-crm (denote-keywords) prompt-text initial-keywords)))
+ (denote--keywords-crm (denote-keywords) prompt-text initial-keywords))
(defun denote-keywords-sort (keywords)
"Sort KEYWORDS if `denote-sort-keywords' is non-nil.
@@ -1038,10 +1078,7 @@ KEYWORDS is a list of strings, per
`denote-keywords-prompt'."
"Combine KEYWORDS list of strings into a single string.
Keywords are separated by the underscore character, per the
Denote file-naming scheme."
- (mapconcat
- (lambda (k)
- (denote-letter-case 'keywords k))
- keywords "_"))
+ (string-join keywords "_"))
(defun denote--keywords-add-to-history (keywords)
"Append KEYWORDS to `denote--keyword-history'."
@@ -1516,7 +1553,7 @@ that internally)."
((not (string-blank-p title))))
title
(if-let ((title (denote-retrieve-filename-title file)))
- (denote-desluggify title)
+ (denote-desluggify 'title title)
(file-name-base file))))
(defun denote--retrieve-location-in-xrefs (identifier)
@@ -1544,9 +1581,9 @@ See `denote--retrieve-locations-in-xrefs'."
;;;;; Common helpers for new notes
-(defun denote-format-file-name (dir-path id keywords title-slug extension
signature-slug)
+(defun denote-format-file-name (dir-path id keywords title extension signature)
"Format file name.
-DIR-PATH, ID, KEYWORDS, TITLE-SLUG, EXTENSION and SIGNATURE-SLUG are
+DIR-PATH, ID, KEYWORDS, TITLE, EXTENSION and SIGNATURE are
expected to be supplied by `denote' or equivalent command.
DIR-PATH is a string pointing to a directory. It ends with a
@@ -1564,11 +1601,9 @@ by `denote-keywords-combine'. KEYWORDS can be an empty
list or
a nil value, in which case the relevant file name component is
not added to the base file name.
-TITLE-SLUG and SIGNATURE-SLUG are strings which, in principle,
-are sluggified before passed as arguments here (per
-`denote-sluggify' and `denote-sluggify-signature'). They can be
-an empty string or a nil value, in which case their respective
-file name component is not added to the base file name.
+TITLE and SIGNATURE are strings. They can be an empty string, in
+which case their respective file name component is not added to
+the base file name.
EXTENSION is a string that contains a dot followed by the file
type extension. It can be an empty string or a nil value, in
@@ -1587,12 +1622,12 @@ which case it is not added to the base file name."
((not (string-match-p denote-id-regexp id))
(error "ID `%s' does not match `denote-id-regexp'" id)))
(let ((file-name (concat dir-path id)))
- (when (and signature-slug (not (string-empty-p signature-slug)))
- (setq file-name (concat file-name "==" signature-slug)))
- (when (and title-slug (not (string-empty-p title-slug)))
- (setq file-name (concat file-name "--" title-slug)))
+ (when (not (string-empty-p signature))
+ (setq file-name (concat file-name "==" (denote-sluggify 'signature
signature))))
+ (when (not (string-empty-p title))
+ (setq file-name (concat file-name "--" (denote-sluggify 'title title))))
(when keywords
- (setq file-name (concat file-name "__" (denote-keywords-combine
keywords))))
+ (setq file-name (concat file-name "__" (denote-keywords-combine
(denote-sluggify-keywords keywords)))))
(concat file-name extension)))
(defun denote--format-front-matter-title (title file-type)
@@ -1601,9 +1636,9 @@ which case it is not added to the base file name."
(defun denote--format-front-matter-keywords (keywords file-type)
"Format KEYWORDS according to FILE-TYPE for the file's front matter.
-Apply `denote-letter-case' to KEYWORDS."
- (let ((kw (denote-sluggify-keywords keywords)))
- (funcall (denote--keywords-value-function file-type) kw)))
+Apply `denote-sluggify' to KEYWORDS."
+ (let ((kws (denote-sluggify-keywords keywords)))
+ (funcall (denote--keywords-value-function file-type) kws)))
(defun denote--format-front-matter (title date keywords id filetype)
"Front matter for new notes.
@@ -1621,11 +1656,7 @@ values of `denote-file-type'."
Use ID, TITLE, KEYWORDS, FILE-TYPE and SIGNATURE to construct
path to DIR."
(denote-format-file-name
- dir id
- (denote-sluggify-keywords keywords)
- (denote-sluggify title 'title)
- (denote--file-extension file-type)
- (denote-sluggify-signature signature)))
+ dir id keywords title (denote--file-extension file-type) signature))
;; Adapted from `org-hugo--org-date-time-to-rfc3339' in the `ox-hugo'
;; package: <https://github.com/kaushalmodi/ox-hugo>.
@@ -1814,9 +1845,7 @@ When called from Lisp, all arguments are optional.
(append args nil)))
(let* ((title (or title ""))
(file-type (denote--valid-file-type (or file-type denote-file-type)))
- (kws (if (called-interactively-p 'interactive)
- keywords
- (denote-keywords-sort keywords)))
+ (kws (denote-keywords-sort keywords))
(date (if (or (null date) (string-empty-p date))
(current-time)
(denote--valid-date date)))
@@ -2492,15 +2521,16 @@ file-naming scheme."
(format "Rename `%s' with keywords (empty to remove)" file-in-prompt)
(denote-convert-file-name-keywords-to-crm (or
(denote-retrieve-filename-keywords file) "")))
(denote-signature-prompt
- (string-replace "=" " " (or (denote-retrieve-filename-signature file)
""))
+ (or (denote-retrieve-filename-signature file) "")
(format "Rename `%s' with signature (empty to remove)" file-in-prompt))
current-prefix-arg)))
(let* ((dir (file-name-directory file))
(id (or (denote-retrieve-filename-identifier file)
(denote-create-unique-file-identifier file
(denote--get-all-used-ids) ask-date)))
+ (keywords (denote-keywords-sort keywords))
(extension (denote-get-file-extension file))
(file-type (denote-filetype-heuristics file))
- (new-name (denote-format-file-name dir id keywords (denote-sluggify
title 'title) extension (denote-sluggify-signature signature)))
+ (new-name (denote-format-file-name dir id keywords title extension
signature))
(max-mini-window-height denote-rename-max-mini-window-height))
(when (or denote-rename-no-confirm (denote-rename-file-prompt file
new-name))
(denote-rename-file-and-buffer file new-name)
@@ -2531,14 +2561,15 @@ the changes made to the file: perform them outright."
(title (denote-title-prompt
(denote--retrieve-title-or-filename file file-type)
(format "Rename `%s' with title (empty to remove)"
file-in-prompt)))
- (keywords (denote-keywords-prompt
- (format "Rename `%s' with keywords (empty to
remove)" file-in-prompt)
- (denote-convert-file-name-keywords-to-crm (or
(denote-retrieve-filename-keywords file) ""))))
+ (keywords (denote-keywords-sort
+ (denote-keywords-prompt
+ (format "Rename `%s' with keywords (empty to
remove)" file-in-prompt)
+ (denote-convert-file-name-keywords-to-crm (or
(denote-retrieve-filename-keywords file) "")))))
(signature (denote-signature-prompt
- (string-replace "=" " " (or
(denote-retrieve-filename-signature file) ""))
+ (or (denote-retrieve-filename-signature file) "")
(format "Rename `%s' with signature (empty to
remove)" file-in-prompt)))
(extension (denote-get-file-extension file))
- (new-name (denote-format-file-name dir id keywords
(denote-sluggify title 'title) extension (denote-sluggify-signature
signature))))
+ (new-name (denote-format-file-name dir id keywords 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)
@@ -2566,8 +2597,8 @@ Specifically, do the following:
- retain the file's existing name and make it the TITLE field,
per Denote's file-naming scheme;
-- `denote-letter-case' and sluggify the TITLE, according to our
- conventions (check the user option `denote-file-name-letter-casing');
+- sluggify the TITLE, according to our conventions (check the
+ user option `denote-file-name-slug-functions');
- prepend an identifier to the TITLE;
@@ -2590,18 +2621,19 @@ Specifically, do the following:
(declare (interactive-only t))
(interactive nil dired-mode)
(if-let ((marks (dired-get-marked-files)))
- (let ((keywords (denote-keywords-prompt "Rename marked files with
keywords, overwriting existing (empty to ignore/remove)"))
+ (let ((keywords (denote-keywords-sort
+ (denote-keywords-prompt "Rename marked files with
keywords, overwriting existing (empty to ignore/remove)")))
(used-ids (unless (seq-every-p #'denote-file-has-identifier-p
marks)
(denote--get-all-used-ids))))
(dolist (file marks)
(let* ((dir (file-name-directory file))
(id (or (denote-retrieve-filename-identifier file)
(denote-create-unique-file-identifier file used-ids)))
- (signature (string-replace "=" " " (or
(denote-retrieve-filename-signature file) "")))
+ (signature (or (denote-retrieve-filename-signature file) ""))
(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 'title) extension (denote-sluggify-signature
signature))))
+ (new-name (denote-format-file-name dir id keywords 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)
@@ -2639,12 +2671,11 @@ does internally."
(if-let ((file-type (denote-filetype-heuristics file))
(title (denote-retrieve-front-matter-title-value file file-type))
(id (denote-retrieve-filename-identifier file)))
- (let* ((sluggified-title (denote-sluggify title 'title))
- (keywords (denote-retrieve-front-matter-keywords-value file
file-type))
- (signature (string-replace "=" " " (or
(denote-retrieve-filename-signature file) "")))
+ (let* ((keywords (denote-retrieve-front-matter-keywords-value file
file-type))
+ (signature (or (denote-retrieve-filename-signature file) ""))
(extension (denote-get-file-extension file))
(dir (file-name-directory file))
- (new-name (denote-format-file-name dir id keywords
sluggified-title extension (denote-sluggify-signature signature))))
+ (new-name (denote-format-file-name dir id keywords title
extension signature)))
(when (or auto-confirm
(denote-rename-file-prompt file new-name))
(denote-rename-file-and-buffer file new-name)
@@ -2708,7 +2739,7 @@ relevant front matter."
(list
(buffer-file-name)
(denote-title-prompt)
- (denote-keywords-prompt)))
+ (denote-keywords-sort (denote-keywords-prompt))))
(when-let ((denote-file-is-writable-and-supported-p file)
(id (denote-retrieve-filename-identifier file))
(file-type (denote-filetype-heuristics file)))
@@ -2751,8 +2782,7 @@ of the file. This needs to be done manually."
(signature (or (denote-retrieve-filename-signature file) ""))
(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 'title)
new-extension signature))
+ (new-name (denote-format-file-name dir id keywords title
new-extension signature))
(max-mini-window-height denote-rename-max-mini-window-height))
(when (and (not (eq old-extension new-extension))
(denote-rename-file-prompt file new-name))
@@ -2828,17 +2858,17 @@ and seconds."
:group 'denote-faces
:package-version '(denote . "2.1.0"))
-;; For character classes, evaluate: (info "(elisp) Char Classes")
(defvar denote-faces--file-name-regexp
- (concat "\\(?1:[0-9]\\{8\\}\\)\\(?10:T\\)\\(?2:[0-9]\\{6\\}\\)"
- "\\(?:\\(?3:==\\)\\(?4:[[:alnum:][:nonascii:]=]*?\\)\\)?"
- "\\(?:\\(?5:--\\)\\(?6:[[:alnum:][:nonascii:]-]*?\\)\\)?"
- "\\(?:\\(?7:__\\)\\(?8:[[:alnum:][:nonascii:]_-]*?\\)\\)?"
+ (concat "\\(?11:[\t\s]+\\|.*/\\)?"
+ "\\(?1:[0-9]\\{8\\}\\)\\(?10:T\\)\\(?2:[0-9]\\{6\\}\\)"
+ "\\(?:\\(?3:==\\)\\(?4:[^.]*?\\)\\)?"
+ "\\(?:\\(?5:--\\)\\(?6:[^.]*?\\)\\)?"
+ "\\(?:\\(?7:__\\)\\(?8:[^.]*?\\)\\)?"
"\\(?9:\\..*\\)?$")
"Regexp of file names for fontification.")
(defconst denote-faces-file-name-keywords
- `((,(concat "\\(?11:[\t\s]+\\|.*/\\)?" denote-faces--file-name-regexp)
+ `((,denote-faces--file-name-regexp
(11 'denote-faces-subdirectory nil t)
(1 'denote-faces-date)
(10 'denote-faces-time-delimiter nil t)
@@ -3969,6 +3999,7 @@ Consult the manual for template samples."
(id (denote--find-first-unused-id
(format-time-string denote-id-format date)
(denote--get-all-used-ids)))
+ (keywords (denote-keywords-sort keywords))
(directory (if (denote--dir-in-denote-directory-p subdirectory)
(file-name-as-directory subdirectory)
(denote-directory)))
diff --git a/tests/denote-test.el b/tests/denote-test.el
index f61d9b9b68..07b15bd019 100644
--- a/tests/denote-test.el
+++ b/tests/denote-test.el
@@ -59,7 +59,7 @@ leading and trailing hyphen."
(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 " ___ !~!!$%^ This iS a tEsT ++ ?? ")
+ (should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ")
"this-is-a-test")))
(ert-deftest denote-test--denote--slug-put-equals ()
@@ -79,13 +79,13 @@ 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 denote-test--denote-sluggify-and-join ()
- "Test that `denote-sluggify-and-join' sluggifies the string while joining
words.
+(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-and-join "--- ___ !~!!$%^ This iS a - tEsT
++ ?? ")
+ (should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++
?? ")
"thisisatest")))
(ert-deftest denote-test--denote-sluggify-keywords ()
@@ -98,8 +98,8 @@ The function also account for the value of the user option
(ert-deftest denote-test--denote-desluggify ()
"Test that `denote-desluggify' upcases first character and de-hyphenates
string."
- (should (equal (denote-desluggify "this-is-a-test") "This is a test"))
- (should (null (equal (denote-desluggify "this=is=a=test") "This is a
test"))))
+ (should (equal (denote-desluggify 'title "this-is-a-test") "This is a test"))
+ (should (null (equal (denote-desluggify 'title "this=is=a=test") "This is a
test"))))
(ert-deftest denote-test--denote--file-empty-p ()
"Test that `denote--file-empty-p' returns non-nil on empty file."
@@ -275,56 +275,56 @@ Extend what we do in
`denote-test--denote-file-type-extensions'."
(should-error (denote-format-file-name
nil
id
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
""
id
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ 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
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
(denote-directory)
nil
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
(denote-directory)
""
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
(denote-directory)
"0123456"
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
""))
(should (equal (denote-format-file-name
(denote-directory)
id
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
"")
"/tmp/test-denote/20231128T055311--some-test__one_two.org"))
@@ -341,19 +341,10 @@ Extend what we do in
`denote-test--denote-file-type-extensions'."
(should (equal (denote-format-file-name
(denote-directory)
id
- nil
- nil
- (denote--file-extension 'org)
- "")
- "/tmp/test-denote/20231128T055311.org"))
-
- (should (equal (denote-format-file-name
- (denote-directory)
- id
- (denote-sluggify-keywords kws)
- (denote-sluggify title)
+ kws
+ title
(denote--file-extension 'org)
- (denote-sluggify-signature "sig"))
+ "sig")
"/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
(ert-deftest denote-test--denote-get-file-extension ()
- [elpa] externals/denote 4ec5bfbd7b 01/12: Make denote-rename-file prompts more consistent, (continued)
- [elpa] externals/denote 4ec5bfbd7b 01/12: Make denote-rename-file prompts more consistent, ELPA Syncer, 2024/01/04
- [elpa] externals/denote c41d3427f8 05/12: Sort keywords outside denote-keywords-sort, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 63d25e0cfd 08/12: Update README section on the file naming scheme, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 7011c3894a 04/12: Fix denote-directory-files, ELPA Syncer, 2024/01/04
- [elpa] externals/denote c76eba3708 07/12: Refactor slug functions and denote-letter-case, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 083862d829 06/12: Use new denote--slug-no-punct-for-signature in denote-sluggify-signature, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 4731e0ab0f 12/12: Merge pull request #217 from jeanphilippegg/denote-region, ELPA Syncer, 2024/01/04
- [elpa] externals/denote b5ef3f5481 02/12: Simplify denote-convert-file-name-keywords-to-crm, ELPA Syncer, 2024/01/04
- [elpa] externals/denote ca1c534ac9 09/12: Make denote-region obey denote-prompts, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 15736c648e 10/12: Merge pull request #208 from jeanphilippegg/denote-rename-file, ELPA Syncer, 2024/01/04
- [elpa] externals/denote 6da2d7b2e4 11/12: Merge pull request #210 from jeanphilippegg/slug-functions,
ELPA Syncer <=
- [elpa] externals/denote 0fde4498e5 03/12: Simplify denote-keywords-prompt, ELPA Syncer, 2024/01/04