[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/denote 6e9473cd4f 09/11: Merge branch 'alphanumeric-seq
From: |
ELPA Syncer |
Subject: |
[elpa] externals/denote 6e9473cd4f 09/11: Merge branch 'alphanumeric-sequence-extension' |
Date: |
Sun, 12 Jan 2025 06:58:06 -0500 (EST) |
branch: externals/denote
commit 6e9473cd4f98edf66fbaafe6a7e815fdb4e4f4e3
Merge: 913571e79e a717f46eee
Author: Protesilaos Stavrou <info@protesilaos.com>
Commit: Protesilaos Stavrou <info@protesilaos.com>
Merge branch 'alphanumeric-sequence-extension'
---
denote-sequence.el | 388 ++++++++++++++++++++++++++++++++++++++++-----------
tests/denote-test.el | 165 ++++++++++++++++++----
2 files changed, 444 insertions(+), 109 deletions(-)
diff --git a/denote-sequence.el b/denote-sequence.el
index cfdefd32b9..8301caeb5d 100644
--- a/denote-sequence.el
+++ b/denote-sequence.el
@@ -47,19 +47,6 @@
;;; Code:
-;; NOTE 2024-12-25: Right now I am hardcoding the = as a field
-;; separator inside of the Denote signature. This is the default
-;; behaviour, though we provide the `denote-file-name-slug-functions'
-;; which, in principle, make the separator anything the user wants.
-;; If we can accommodate such open-endedness, then I am happy to make
-;; the relevant changes, but I prefer to keep it restricted at this
-;; early stage.
-;;
-;; Similarly, I am not giving the option for Luhmann-style sequences
-;; that include numbers and letters. Ours consist only of numbers,
-;; since (i) it is simpler and (ii) we already have the field
-;; separator to give a sufficient sense of place.
-
;; TODO 2025-01-08: Test whether the built-in hierarchy.el can be used
;; to present the sequences in a nice way. What do we need and how
;; exactly do we use that library.
@@ -72,66 +59,272 @@
:link '(info-link "(denote) Sequence notes")
:link '(url-link :tag "homepage" "https://protesilaos.com/emacs/denote";))
-(defconst denote-sequence-regexp "=?[0-9]+"
- "Pattern of a sequence.")
+(defconst denote-sequence-schemes '(numeric alphanumeric)
+ "Symbols representing sequence schemes.")
+
+(defcustom denote-sequence-scheme 'numeric
+ "Sequencing scheme to establish file hierarchies.
+The value is the symbol `numeric' or `alphanumeric'.
+
+Numeric sequences (the default) are the easier to understand but also
+are the longest. Each level of depth in the hierarchy is delimited by
+an equals sign: the 1=1=2 thus refers to the second child of the first
+child of parent 1. Each level of depth can be a number of any length,
+like 1=40=2=20.
+
+Alphanumeric sequences are more compact than numeric ones. Their depth
+is derived via the alternation from numbers to latin characters, such
+that 1a2 refers to the second child of the first child of parent 1.
+Because they alternate between numbers and letters, they do not use the
+equals sign. When a number cannot be represented by a single letter,
+two or more are used instead, such as the number 51 corresponding to
+zx (z is 26 and x is 25)."
+ :group 'denote-sequence
+ :type '(choice (const :tag "Numeric like 1=1=2" numeric)
+ (const :tag "Alphanumeric like 1a2" alphanumeric)))
+
+(defconst denote-sequence-numeric-regexp "=?[0-9]+"
+ "Pattern of a numeric sequence.")
+
+(defconst denote-sequence-alphanumeric-regexp "\\([0-9]+\\)\\([[:alpha:]]+\\)?"
+ "Pattern of an alphanumeric sequence.")
(defconst denote-sequence-types '(parent child sibling)
"Types of sequence.")
-(defun denote-sequence-p (sequence)
- "Return SEQUENCE string if it matches `denote-sequence-regexp'."
- (when (and (string-match-p denote-sequence-regexp sequence)
+(defun denote-sequence-numeric-p (sequence)
+ "Return SEQUENCE if it is numeric per `denote-sequence-scheme'."
+ (when (and (string-match-p denote-sequence-numeric-regexp sequence)
(not (string-match-p "[a-zA-Z]" sequence))
(not (string-suffix-p "=" sequence)))
sequence))
+(defun denote-sequence-alphanumeric-p (sequence)
+ "Return SEQUENCE if it is alphanumeric per `denote-sequence-scheme'."
+ (when (and (string-match-p denote-sequence-alphanumeric-regexp sequence)
+ (string-match-p "\\`[0-9]+" sequence)
+ (not (string-match-p "=" sequence)))
+ sequence))
+
+(defun denote-sequence-p (sequence)
+ "Return SEQUENCE string is of a supported scheme.
+Also see `denote-sequence-numeric-p' and `denote-sequence-alphanumeric-p'."
+ (when (or (denote-sequence-numeric-p sequence)
+ (denote-sequence-alphanumeric-p sequence))
+ sequence))
+
(defun denote-sequence-with-error-p (sequence)
- "Return SEQUENCE string if it matches `denote-sequence-regexp'."
+ "Return SEQUENCE string if it matches `denote-sequence-numeric-regexp'."
(or (denote-sequence-p sequence)
(error "The sequence `%s' does not pass `denote-sequence-p'" sequence)))
+(defun denote-sequence--numeric-partial-p (string)
+ "Return non-nil if STRING likely is part of a numeric sequence."
+ (and (string-match-p "[0-9]+" string)
+ (not (string-match-p "[[:alpha:][:punct:]]" string))))
+
+(defun denote-sequence--alphanumeric-partial-p (string)
+ "Return non-nil if STRING likely is part of an alphanumeric sequence."
+ (and (string-match-p "[a-z]+" string)
+ (not (string-match-p "[0-9[:punct:]]+" string))))
+
+(defun denote-sequence-and-scheme-p (sequence &optional partial)
+ "Return the sequencing scheme of SEQUENCE, per `denote-sequence-scheme'.
+Return a cons cell of the form (sequence . scheme), where the `car' is
+SEQUENCE and the `cdr' is its sequencing scheme as a symbol among
+`denote-sequence-schemes'.
+
+With optional PARTIAL as a non-nil value, assume SEQUENCE to be a string
+that only represents part of a sequence, which itself consists entirely
+of numbers or letters.
+
+Produce an error if the sequencing scheme cannot be established."
+ (cond
+ ((or (and partial (denote-sequence--alphanumeric-partial-p sequence))
+ (denote-sequence-alphanumeric-p sequence))
+ (cons sequence 'alphanumeric))
+ ((or (and partial (denote-sequence--numeric-partial-p sequence))
+ (denote-sequence-numeric-p sequence))
+ (cons sequence 'numeric))
+ (t (error "The sequence `%s' does not pass `denote-sequence-p'" sequence))))
+
+(defun denote-sequence--scheme-of-strings (strings)
+ "Return the sequencing scheme of STRINGS, per `denote-sequence-scheme'."
+ (if (seq-find (lambda (string) (string-match-p "[[:alpha:]]" string))
strings)
+ 'alphanumeric
+ 'numeric))
+
(defun denote-sequence-file-p (file)
"Return non-nil if Denote signature of FILE is a sequence.
-A sequence is string that matches `denote-sequence-regexp'."
+A sequence is string that matches `denote-sequence-numeric-regexp'."
(when-let* ((signature (denote-retrieve-filename-signature file)))
(denote-sequence-p signature)))
+(defun denote-sequence-join (strings scheme)
+ "Join STRINGS to form a sequence according to SCHEME.
+SCHEME is a symbol among `denote-sequence-schemes'. Return resulting
+sequence if it conforms with `denote-sequence-p'."
+ (pcase scheme
+ ('numeric (mapconcat #'identity strings "="))
+ ('alphanumeric (apply #'concat strings))))
+
+(defun denote-sequence-split (sequence &optional partial)
+ "Split the SEQUENCE string into a list.
+SEQUENCE conforms with `denote-sequence-p'. If PARTIAL is non-nil, it
+has the same meaning as in `denote-sequence-and-scheme-p'."
+ (pcase-let* ((`(,sequence . ,scheme) (denote-sequence-and-scheme-p sequence
partial)))
+ (pcase scheme
+ ('numeric
+ (split-string sequence "=" t))
+ ('alphanumeric
+ (let ((strings nil)
+ (start 0))
+ (while (string-match denote-sequence-alphanumeric-regexp sequence
start)
+ (push (match-string 1 sequence) strings)
+ (when-let* ((two (match-string 2 sequence)))
+ (push two strings)
+ (setq start (match-end 2)))
+ (setq start (match-end 1)))
+ (if strings
+ (nreverse strings)
+ (split-string sequence "" :omit-nulls)))))))
+
+(defun denote-sequence--alpha-to-number (string)
+ "Convert STRING of alphabetic characters to its numeric equivalent."
+ (let* ((strings (denote-sequence-split string :partial))
+ (numbers (mapcar
+ (lambda (string)
+ (let ((num (- (string-to-char string) 96)))
+ (cond
+ ((and (> num 0) (<= num 26))
+ num)
+ (t
+ (let ((times (/ num 26)))
+ (if-let* ((mod (% num 26))
+ ((> mod 0))
+ (suffix (+ mod 96)))
+ (list (* times 26) suffix)
+ (list (* times 26))))))))
+ strings)))
+ (format "%s" (apply #'+ numbers))))
+
+(defun denote-sequence--number-to-alpha (string)
+ "Convert STRING of numbers to its alphabetic equivalent."
+ (let ((num (string-to-number string)))
+ (cond
+ ((= num 0)
+ (char-to-string (+ num 97)))
+ ((and (> num 0) (<= num 26))
+ (char-to-string (+ num 96)))
+ (t
+ (let ((times (/ num 26)))
+ (if-let* ((mod (% num 26))
+ ((> mod 0))
+ (prefix (make-string times ?z))
+ (suffix (char-to-string (+ mod 96))))
+ (concat prefix suffix)
+ (make-string times ?z)))))))
+
+(defun denote-sequence--alpha-to-number-complete (sequence)
+ "Like `denote-sequence--alpha-to-number' but for the complete SEQUENCE."
+ (if (denote-sequence-numeric-p sequence)
+ sequence
+ (let* ((parts (denote-sequence-split sequence))
+ (converted-parts (mapcar
+ (lambda (string)
+ (if (denote-sequence--numeric-partial-p string)
+ string
+ (denote-sequence--alpha-to-number string)))
+ parts)))
+ (denote-sequence-join converted-parts 'numeric))))
+
+(defun denote-sequence--number-to-alpha-complete (sequence)
+ "Like `denote-sequence--number-to-alpha' but for the complete SEQUENCE."
+ (if (denote-sequence-alphanumeric-p sequence)
+ sequence
+ (let* ((parts (denote-sequence-split sequence))
+ (odd-is-numeric 0)
+ (converted-parts (mapcar
+ (lambda (string)
+ (setq odd-is-numeric (+ odd-is-numeric 1))
+ (cond
+ ((= (% odd-is-numeric 2) 1)
+ string)
+ ((denote-sequence--alphanumeric-partial-p
string)
+ string)
+ (t
+ (denote-sequence--number-to-alpha string))))
+ parts)))
+ (denote-sequence-join converted-parts 'alphanumeric))))
+
+(defun denote-sequence-convert (string &optional string-is-sequence)
+ "Convert STRING to its counterpart sequencing scheme.
+If STRING-IS-SEQUENCE then assume STRING to be a complete sequence, in
+which case convert the entirety of it. Also see `denote-sequence-scheme'."
+ (cond
+ ((and string-is-sequence (denote-sequence-alphanumeric-p string))
+ (denote-sequence--alpha-to-number-complete string))
+ ((and string-is-sequence (denote-sequence-numeric-p string))
+ (denote-sequence--number-to-alpha-complete string))
+ ((denote-sequence--alphanumeric-partial-p string)
+ (denote-sequence--alpha-to-number string))
+ ((denote-sequence--numeric-partial-p string)
+ (denote-sequence--number-to-alpha string))
+ (t
+ (if string-is-sequence
+ (error "String `%s' did not pass `denote-sequence-p'" string)
+ (error "The `%s' must not contain both numbers and letters" string)))))
+
+(defun denote-sequence-increment (string)
+ "Increment number represented by STRING and return it as a string.
+STRING is part of a sequence, not the entirety of it."
+ (cond
+ ((denote-sequence--numeric-partial-p string)
+ (number-to-string (+ (string-to-number string) 1)))
+ ((denote-sequence--alphanumeric-partial-p string)
+ (let* ((letters (split-string string "" :omit-nulls))
+ (length-1 (= (length letters) 1))
+ (first (car letters))
+ (reverse (nreverse (copy-sequence letters)))
+ (last (car reverse)))
+ (cond
+ ((and length-1 (string= "z" first))
+ "za")
+ (length-1
+ (char-to-string (+ (string-to-char first) 1)))
+ ((string= "z" last)
+ (apply #'concat (append letters (list "a"))))
+ (t
+ (let ((last last))
+ (apply #'concat
+ (append (butlast letters)
+ (list (char-to-string (+ (string-to-char last)
1))))))))))
+ (t
+ (error "The string `%s' must contain only numbers or letters" string))))
+
+(defun denote-sequence-depth (sequence)
+ "Get the depth of SEQUENCE.
+For example, 1=2=1 and 1b1 are three levels of depth."
+ (length (denote-sequence-split sequence)))
+
(defun denote-sequence--children-implied-p (sequence)
"Return non-nil if SEQUENCE implies children.
This does not actually check if there are children in the variable
-`denote-directory', but only that SEQUENCE contains a =, which means
-that its depth is greater than 1."
- (string-match-p "=" sequence))
-
-(defun denote-sequence--join (list-of-strings)
- "Join LIST-OF-STRINGS to form a sequence.
-Return sequence if it conforms with `denote-sequence-p'."
- (thread-last
- (mapconcat #'identity list-of-strings "=")
- (denote-sequence-with-error-p)))
+`denote-directory', but only that SEQUENCE is greater than 1."
+ (> (denote-sequence-depth sequence) 1))
(defun denote-sequence--get-parent (sequence)
"Return implied parent of SEQUENCE, else nil.
Produce an error if SEQUENCE does not conform with `denote-sequence-p'.
The implied check here has the same meaning as described in
`denote-sequence--children-implied-p'."
- (when (and (denote-sequence-with-error-p sequence)
- (denote-sequence--children-implied-p sequence))
- (thread-last
- (denote-sequence-split sequence)
- (butlast)
- (denote-sequence--join))))
-
-(defun denote-sequence-split (sequence)
- "Split the SEQUENCE string into a list.
-SEQUENCE conforms with `denote-sequence-p'."
- (when (denote-sequence-with-error-p sequence)
- (split-string sequence "=" t)))
-
-(defun denote-sequence-depth (sequence)
- "Get the depth of SEQUENCE.
-For example, 1=2=1 is three levels of depth."
- (length (denote-sequence-split sequence)))
+ (pcase-let* ((`(,sequence . ,scheme) (denote-sequence-and-scheme-p
sequence)))
+ (when (and (denote-sequence-with-error-p sequence)
+ (denote-sequence--children-implied-p sequence))
+ (let ((strings (thread-last
+ (denote-sequence-split sequence)
+ (butlast))))
+ (denote-sequence-join strings scheme)))))
(defun denote-sequence-get-all-files ()
"Return all files in variable `denote-directory' with a sequence.
@@ -188,7 +381,7 @@ With optional SEQUENCES operate on those, else use the
return value of
(string-prefix-p sequence string))
(or sequences (denote-sequence-get-all-sequences))))
-(defun denote-sequence-get-sequences-with-max-depth (depth &optional sequences)
+(defun denote-sequence-get-all-sequences-with-max-depth (depth &optional
sequences)
"Get sequences up to DEPTH (inclusive).
With optional SEQUENCES operate on those, else use the return value of
`denote-sequence-get-all-sequences'."
@@ -197,8 +390,8 @@ With optional SEQUENCES operate on those, else use the
return value of
(lists (seq-filter (lambda (element) (>= depth (length element)))
lists-all)))
(delete-dups
(mapcar
- (lambda (sequence)
- (denote-sequence--join (seq-take sequence depth)))
+ (lambda (strings)
+ (denote-sequence-join (seq-take strings depth)
(denote-sequence--scheme-of-strings strings)))
lists))))
(defun denote-sequence--pad (sequence type)
@@ -236,6 +429,23 @@ TYPE is a symbol among `denote-sequence-types'."
(denote-sequence--pad s1 type)
(denote-sequence--pad s2 type)))))))
+(defun denote-sequence--tail-alphanumeric-p (sequence)
+ "Return non-nil if the last character of SEQUENCE is alphanumeric.
+This is for use in `denote-sequence--get-start'."
+ (denote-sequence--alphanumeric-partial-p (substring sequence -1)))
+
+(defun denote-sequence--get-start (&optional sequence prepend-delimiter)
+ "Return the start of a new sequence.
+With optional SEQUENCE, do so based on the final level of depth therein.
+This is usefule only for the alphanumeric `denote-sequence-scheme'. If
+optional PREPEND-DELIMITER is non-nil, prepend the equals sign to the
+number if `denote-sequence-scheme' is numeric."
+ (pcase denote-sequence-scheme
+ ('numeric (if prepend-delimiter "=1" "1"))
+ ('alphanumeric (if (denote-sequence--tail-alphanumeric-p sequence)
+ "1"
+ "a"))))
+
(defun denote-sequence--get-new-parent (&optional sequences)
"Return a new to increment largest among sequences.
With optional SEQUENCES consider only those, otherwise operate on the
@@ -245,38 +455,50 @@ return value of `denote-sequence-get-all-sequences'."
(first-component (car (denote-sequence-split largest)))
(current-number (string-to-number first-component)))
(number-to-string (+ current-number 1)))
- "1"))
+ (denote-sequence--get-start)))
+
+(defun denote-sequence-filter-scheme (sequences &optional scheme)
+ "Return list of SEQUENCES that are `denote-sequence-scheme' or SCHEME."
+ (let ((predicate (pcase (or scheme denote-sequence-scheme)
+ ('alphanumeric #'denote-sequence-alphanumeric-p)
+ ('numeric #'denote-sequence-numeric-p))))
+ (seq-filter predicate sequences)))
(defun denote-sequence--get-new-child (sequence &optional sequences)
"Return a new child of SEQUENCE.
Optional SEQUENCES has the same meaning as that specified in the
function `denote-sequence-get-all-sequences-with-prefix'."
(if-let* ((depth (+ (denote-sequence-depth sequence) 1))
- (all-unfiltered (denote-sequence-get-all-sequences-with-prefix
sequence sequences)))
+ (all-unfiltered (denote-sequence-get-all-sequences-with-prefix
sequence sequences))
+ (start-child (denote-sequence--get-start sequence
:prepend-delimiter)))
(if (= (length all-unfiltered) 1)
- (format "%s=1" (car all-unfiltered))
- (let* ((all (cond
- ((= (length all-unfiltered) 1) all-unfiltered)
- ((denote-sequence-get-sequences-with-max-depth depth
all-unfiltered))
- (t all-unfiltered)))
- (largest (denote-sequence--get-largest all 'child)))
- (if (denote-sequence--children-implied-p largest)
- (let* ((components (denote-sequence-split largest))
- (butlast (butlast components))
- (last-component (car (nreverse components)))
- (current-number (string-to-number last-component))
- (new-number (number-to-string (+ current-number 1))))
- (denote-sequence--join
- (if butlast
- (append butlast (list new-number))
- (list largest new-number))))
- (format "%s=1" largest))))
- (error "Cannot find sequences given sequence `%s'" sequence)))
+ (format "%s%s" (car all-unfiltered) start-child)
+ (if-let* ((all-schemeless (cond
+ ((= (length all-unfiltered) 1)
all-unfiltered)
+
((denote-sequence-get-all-sequences-with-max-depth depth all-unfiltered))
+ (t all-unfiltered)))
+ (all (denote-sequence-filter-scheme all-schemeless))
+ (largest (denote-sequence--get-largest all 'child)))
+ (if (denote-sequence--children-implied-p largest)
+ (pcase-let* ((`(,largest . ,scheme)
(denote-sequence-and-scheme-p largest))
+ (components (denote-sequence-split largest))
+ (butlast (butlast components))
+ (last-component (car (nreverse components)))
+ (new-number (denote-sequence-increment
last-component)))
+ (denote-sequence-join
+ (if butlast
+ (append butlast (list new-number))
+ (list largest new-number))
+ scheme))
+ (format "%s%s" largest start-child))
+ (format "%s%s" sequence start-child)))
+ (error "Cannot find sequences given sequence `%s' using scheme `%s'"
sequence denote-sequence-scheme)))
(defun denote-sequence--get-prefix-for-siblings (sequence)
"Get the prefix of SEQUENCE such that it is possible to find its siblings."
- (when (denote-sequence--children-implied-p sequence)
- (denote-sequence--join (butlast (denote-sequence-split sequence)))))
+ (pcase-let ((`(,sequence . ,scheme) (denote-sequence-and-scheme-p sequence)))
+ (when (denote-sequence--children-implied-p sequence)
+ (denote-sequence-join (butlast (denote-sequence-split sequence))
scheme))))
(defun denote-sequence--get-new-sibling (sequence &optional sequences)
"Return a new sibling SEQUENCE.
@@ -289,20 +511,21 @@ function `denote-sequence-get-all-sequences-with-prefix'."
(denote-sequence--get-prefix-for-siblings
sequence)
sequences)
(denote-sequence-get-all-sequences)))
- (all (denote-sequence-get-sequences-with-max-depth depth
all-unfiltered))
+ (all-schemeless
(denote-sequence-get-all-sequences-with-max-depth depth all-unfiltered))
+ (all (denote-sequence-filter-scheme all-schemeless))
((member sequence all))
(largest (if children-p
(denote-sequence--get-largest all 'sibling)
(denote-sequence--get-largest all 'parent))))
(if children-p
- (let* ((components (denote-sequence-split largest))
- (butlast (butlast components))
- (last-component (car (nreverse components)))
- (current-number (string-to-number last-component))
- (new-number (number-to-string (+ current-number 1))))
- (denote-sequence--join (append butlast (list new-number))))
+ (pcase-let* ((`(,largest . ,scheme) (denote-sequence-and-scheme-p
largest))
+ (components (denote-sequence-split largest))
+ (butlast (butlast components))
+ (last-component (car (nreverse components)))
+ (new-number (denote-sequence-increment
last-component)))
+ (denote-sequence-join (append butlast (list new-number)) scheme))
(number-to-string (+ (string-to-number largest) 1)))
- (error "Cannot find sequences given sequence `%s'" sequence))))
+ (error "Cannot find sequences given sequence `%s' using scheme `%s'"
sequence denote-sequence-scheme))))
(defun denote-sequence-get (type &optional sequence)
"Return a sequence given TYPE among `denote-sequence-types'.
@@ -488,7 +711,10 @@ is ignored."
With optional PROMPT-TEXT use it instead of the generic one."
(read-number
(or prompt-text
- "Get sequences up to this depth (e.g. `1=1=2' is `3' levels of depth):
")))
+ (format "Get sequences up to this depth %s: "
+ (if (eq denote-sequence-scheme 'alphanumeric)
+ "(e.g. `1a2' is `3' levels of depth)"
+ "(e.g. `1=1=2' is `3' levels of depth)")))))
(defun denote-sequence--get-dired-buffer-name (&optional prefix depth)
"Return a string for `denote-sequence-dired' buffer.
diff --git a/tests/denote-test.el b/tests/denote-test.el
index c925f24e1f..f094f6bff1 100644
--- a/tests/denote-test.el
+++ b/tests/denote-test.el
@@ -594,8 +594,9 @@ does not involve the time zone."
;; TODO 2024-12-31: Maybe we can share some state between tests? It
;; is expensive to create those files over and over.
-(ert-deftest dt-denote-sequence--get-new-child ()
- "Make sure `denote-sequence--get-new-child' gets the child of a sequence."
+(ert-deftest dt-denote-sequence--get-new-child-numeric ()
+ "Make sure `denote-sequence--get-new-child' gets the child of a sequence.
+This is done using the numeric `denote-sequence-scheme'."
(let* ((denote-directory (expand-file-name "denote-test"
temporary-file-directory))
(files
(mapcar
@@ -613,24 +614,57 @@ does not involve the time zone."
"20241230T075023==1=1=2--test__testing.txt"
"20241230T075023==1=2--test__testing.txt"
"20241230T075023==1=2=1--test__testing.txt"
- "20241230T075023==2--test__testing.txt"
- "20241230T075023==45--test__testing.txt"
- "20241230T075023==45=1--test__testing.txt")))
+ "20241230T075023==2--test__testing.txt")))
+ (sequences (denote-sequence-get-all-sequences files)))
+ (let ((denote-sequence-scheme 'numeric))
+ (should
+ (and
+ (equal (denote-sequence--get-new-child "1" sequences) "1=3")
+ (equal (denote-sequence--get-new-child "1=1" sequences) "1=1=3")
+ (equal (denote-sequence--get-new-child "1=1=2" sequences) "1=1=2=1")
+ (equal (denote-sequence--get-new-child "1=2" sequences) "1=2=2")
+ (equal (denote-sequence--get-new-child "1=2=1" sequences) "1=2=1=1")
+ (equal (denote-sequence--get-new-child "2" sequences) "2=1")))
+ (should-error (denote-sequence--get-new-child "3" sequences)))
+ (delete-directory denote-directory :delete-contents-as-well)))
+
+(ert-deftest dt-denote-sequence--get-new-child-alphanumeric ()
+ "Make sure `denote-sequence--get-new-child' gets the child of a sequence.
+This is done using the alphanumeric `denote-sequence-scheme'."
+ (let* ((denote-directory (expand-file-name "denote-test"
temporary-file-directory))
+ (files
+ (mapcar
+ (lambda (file)
+ (let ((path (expand-file-name file (denote-directory))))
+ (if (file-exists-p path)
+ path
+ (with-current-buffer (find-file-noselect path)
+ (save-buffer)
+ (kill-buffer (current-buffer)))
+ path)))
+ '("20241230T075004==1--some-new-title__testing.txt"
+ "20241230T075023==1a--child-of-note__testing.txt"
+ "20241230T075023==1a1--test__testing.txt"
+ "20241230T075023==1a2--test__testing.txt"
+ "20241230T075023==1b--test__testing.txt"
+ "20241230T075023==1b1--test__testing.txt"
+ "20241230T075023==2--test__testing.txt")))
(sequences (denote-sequence-get-all-sequences files)))
- (should
- (and
- (equal (denote-sequence--get-new-child "1" sequences) "1=3")
- (equal (denote-sequence--get-new-child "1=1" sequences) "1=1=3")
- (equal (denote-sequence--get-new-child "1=1=2" sequences) "1=1=2=1")
- (equal (denote-sequence--get-new-child "1=2" sequences) "1=2=2")
- (equal (denote-sequence--get-new-child "1=2=1" sequences) "1=2=1=1")
- (equal (denote-sequence--get-new-child "2" sequences) "2=1")
- (equal (denote-sequence--get-new-child "45" sequences) "45=2")))
- (should-error (denote-sequence--get-new-child "3" sequences))
+ (let ((denote-sequence-scheme 'alphanumeric))
+ (should
+ (and
+ (equal (denote-sequence--get-new-child "1" sequences) "1c")
+ (equal (denote-sequence--get-new-child "1a" sequences) "1a3")
+ (equal (denote-sequence--get-new-child "1a2" sequences) "1a2a")
+ (equal (denote-sequence--get-new-child "1b" sequences) "1b2")
+ (equal (denote-sequence--get-new-child "1b1" sequences) "1b1a")
+ (equal (denote-sequence--get-new-child "2" sequences) "2a")))
+ (should-error (denote-sequence--get-new-child "3" sequences)))
(delete-directory denote-directory :delete-contents-as-well)))
-(ert-deftest dt-denote-sequence--get-new-sibling ()
- "Make sure `denote-sequence--get-new-sibling' gets the sibling of a
sequence."
+(ert-deftest dt-denote-sequence--get-new-sibling-numeric ()
+ "Make sure `denote-sequence--get-new-sibling' gets the sibling of a sequence.
+This is done using the numeric `denote-sequence-scheme'."
(let* ((denote-directory (expand-file-name "denote-test"
temporary-file-directory))
(files
(mapcar
@@ -650,18 +684,93 @@ does not involve the time zone."
"20241230T075023==1=2=1--test__testing.txt"
"20241230T075023==2--test__testing.txt")))
(sequences (denote-sequence-get-all-sequences files)))
- (should
- (and
- (equal (denote-sequence--get-new-sibling "1" sequences) "3")
- (equal (denote-sequence--get-new-sibling "1=1" sequences) "1=3")
- (equal (denote-sequence--get-new-sibling "1=1=1" sequences) "1=1=3")
- (equal (denote-sequence--get-new-sibling "1=1=2" sequences) "1=1=3")
- (equal (denote-sequence--get-new-sibling "1=2" sequences) "1=3")
- (equal (denote-sequence--get-new-sibling "1=2=1" sequences) "1=2=2")
- (equal (denote-sequence--get-new-sibling "2" sequences) "3")))
- (should-error (denote-sequence--get-new-sibling "4" sequences))
+ (let ((denote-sequence-scheme 'numeric))
+ (should
+ (and
+ (equal (denote-sequence--get-new-sibling "1" sequences) "3")
+ (equal (denote-sequence--get-new-sibling "1=1" sequences) "1=3")
+ (equal (denote-sequence--get-new-sibling "1=1=1" sequences) "1=1=3")
+ (equal (denote-sequence--get-new-sibling "1=1=2" sequences) "1=1=3")
+ (equal (denote-sequence--get-new-sibling "1=2" sequences) "1=3")
+ (equal (denote-sequence--get-new-sibling "1=2=1" sequences) "1=2=2")
+ (equal (denote-sequence--get-new-sibling "2" sequences) "3")))
+ (should-error (denote-sequence--get-new-sibling "4" sequences)))
(delete-directory denote-directory :delete-contents-as-well)))
-
+
+(ert-deftest dt-denote-sequence--get-new-sibling-alphanumeric ()
+ "Make sure `denote-sequence--get-new-sibling' gets the sibling of a sequence.
+This is done using the alphanumeric `denote-sequence-scheme'."
+ (let* ((denote-directory (expand-file-name "denote-test"
temporary-file-directory))
+ (files
+ (mapcar
+ (lambda (file)
+ (let ((path (expand-file-name file (denote-directory))))
+ (if (file-exists-p path)
+ path
+ (with-current-buffer (find-file-noselect path)
+ (save-buffer)
+ (kill-buffer (current-buffer)))
+ path)))
+ '("20241230T075004==1--some-new-title__testing.txt"
+ "20241230T075023==1a--sibling-of-note__testing.txt"
+ "20241230T075023==1a1--test__testing.txt"
+ "20241230T075023==1a2--test__testing.txt"
+ "20241230T075023==1b--test__testing.txt"
+ "20241230T075023==1b1--test__testing.txt"
+ "20241230T075023==2--test__testing.txt")))
+ (sequences (denote-sequence-get-all-sequences files)))
+ (let ((denote-sequence-scheme 'alphanumeric))
+ (should
+ (and
+ (equal (denote-sequence--get-new-sibling "1" sequences) "3")
+ (equal (denote-sequence--get-new-sibling "1a" sequences) "1c")
+ (equal (denote-sequence--get-new-sibling "1a1" sequences) "1a3")
+ (equal (denote-sequence--get-new-sibling "1a2" sequences) "1a3")
+ (equal (denote-sequence--get-new-sibling "1b" sequences) "1c")
+ (equal (denote-sequence--get-new-sibling "1b1" sequences) "1b2")
+ (equal (denote-sequence--get-new-sibling "2" sequences) "3")))
+ (should-error (denote-sequence--get-new-sibling "4" sequences)))
+ (delete-directory denote-directory :delete-contents-as-well)))
+
+(ert-deftest dt-denote-sequence-split ()
+ "Test that `denote-sequence-split' splits a sequence correctly."
+ (should (and (equal (denote-sequence-split "1") '("1"))
+ (equal (denote-sequence-split "1=1=2") '("1" "1" "2"))
+ (equal (denote-sequence-split "1za5zx") '("1" "za" "5" "zx")))))
+
+(ert-deftest dt-denote-sequence-convert ()
+ "Test that `denote-sequence-convert' converts from alpha to numeric and vice
versa."
+ (should (and (string= (denote-sequence-convert "3") "c")
+ (string= (denote-sequence-convert "18") "r")
+ (string= (denote-sequence-convert "26") "z")
+ (string= (denote-sequence-convert "27") "za")
+ (string= (denote-sequence-convert "130") "zzzzz")
+ (string= (denote-sequence-convert "131") "zzzzza")
+ (string= (denote-sequence-convert "c") "3")
+ (string= (denote-sequence-convert "r") "18")
+ (string= (denote-sequence-convert "z") "26")
+ (string= (denote-sequence-convert "za") "27")
+ (string= (denote-sequence-convert "zzzzz") "130")
+ (string= (denote-sequence-convert "zzzzza") "131")))
+ (should (and (string= (denote-sequence-convert "1=1=2" :string-is-sequence)
"1a2")
+ (string= (denote-sequence-convert "1a2" :string-is-sequence)
"1=1=2")
+ (string= (denote-sequence-convert "1=27=2=55"
:string-is-sequence) "1za2zzc")
+ (string= (denote-sequence-convert "1za2zzc"
:string-is-sequence) "1=27=2=55")
+ (string= (denote-sequence-convert "1=1=2=2=4=1"
:string-is-sequence) "1a2b4a")
+ (string= (denote-sequence-convert "1a2b4a" :string-is-sequence)
"1=1=2=2=4=1")))
+ (should-error (denote-sequence-convert "111=a" :string-is-sequence))
+ (should-error (denote-sequence-convert "a1" :string-is-sequence)))
+
+(ert-deftest dt-denote-sequence-increment ()
+ "Test that `denote-sequence-increment' works with numbers and letters."
+ (should (and (string= (denote-sequence-increment "z") "za")
+ (string= (denote-sequence-increment "ab") "ac")
+ (string= (denote-sequence-increment "az") "aza")
+ (string= (denote-sequence-increment "bbcz") "bbcza")))
+ (should (and (string= (denote-sequence-increment "1") "2")
+ (string= (denote-sequence-increment "10") "11")))
+ (should-error (denote-sequence-increment "1=a")))
+
(provide 'denote-test)
;;; denote-test.el ends here
- [elpa] externals/denote updated (913571e79e -> 4e62c3f2b6), ELPA Syncer, 2025/01/12
- [elpa] externals/denote 14dd0b5d2e 03/11: Make denote-sequence--get-new-child work with alphanumeric sequences, ELPA Syncer, 2025/01/12
- [elpa] externals/denote a0188e7cba 05/11: Expand tests to also cover alphanumeric child sequences, ELPA Syncer, 2025/01/12
- [elpa] externals/denote a4d3d62b26 10/11: Clarify how denote-sequence can be loaded, ELPA Syncer, 2025/01/12
- [elpa] externals/denote 182db0675e 06/11: Expand tests to also cover alphanumeric sibling sequences, ELPA Syncer, 2025/01/12
- [elpa] externals/denote 26f43c8aec 07/11: Make denote-sequence-alphanumeric-p require numeric prefix, ELPA Syncer, 2025/01/12
- [elpa] externals/denote 4997e6b007 02/11: Rename denote-sequence-get-sequences-with-max-depth to include "all", ELPA Syncer, 2025/01/12
- [elpa] externals/denote b642758ff9 04/11: Ensure the sequence tests target the implied scheme, ELPA Syncer, 2025/01/12
- [elpa] externals/denote 6e9473cd4f 09/11: Merge branch 'alphanumeric-sequence-extension',
ELPA Syncer <=
- [elpa] externals/denote 43bd30e6eb 01/11: Make the groundwork for alphanumeric (Luhmann) sequences in denote-sequence.el, ELPA Syncer, 2025/01/12
- [elpa] externals/denote a717f46eee 08/11: Make denote-sequence-convert also convert complete sequences, ELPA Syncer, 2025/01/12
- [elpa] externals/denote 4e62c3f2b6 11/11: Rewrite and expand the manual about denote-sequence.el, ELPA Syncer, 2025/01/12