emacs-elpa-diffs
[Top][All Lists]
Advanced

[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
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]