branch: externals/denote
commit 5fc634ebc43b29932ea784f25aaa98a654e7013a
Author: Protesilaos Stavrou <[email protected]>
Commit: Protesilaos Stavrou <[email protected]>
REMOVE denote-sequence.el as it will be its own package
This is part of the wider reorganisation of the project, as discussed
in issue 543: <https://github.com/protesilaos/denote/issues/543>.
---
README.org | 224 +----------
denote-sequence.el | 1009 --------------------------------------------------
tests/denote-test.el | 209 -----------
3 files changed, 13 insertions(+), 1429 deletions(-)
diff --git a/README.org b/README.org
index 1c18ed32e6..6f4bc2a75a 100644
--- a/README.org
+++ b/README.org
@@ -4034,18 +4034,13 @@ you get all the parameters included:
[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-This section is about the optional extension =denote-sequence.el=,
-which is part of the ~denote~ package. Its commands are autoloaded, so
-they are avaialble in the =M-x= interface at the outset and will load
-their file when they are invoked. Though users can explicitly load
-the file on demand with:
-
-#+begin_src emacs-lisp
-(require 'denote-sequence)
-#+end_src
+This section is about the external package ~denote-sequence~ (by
+Protesilaos). The original idea was to include the code as part of the
+~denote~ package, but we decided to keep each optional extension as a
+separate package.
Denote defines an optional file name component called the =SIGNATURE=
-([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). This is
a free form field that users can
+([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). This is
a free-form field that users can
fill in with whatever text they want, such as to have a video split up
into =part1= and =part2=, or to set some kind of priority like =a= and
=b=, or even to have a special tag that stands out from the rest of
@@ -4056,209 +4051,16 @@ hierarchical relationship between notes, such that the
thoughts they
expound on form sequences. For example, an article about the Labrador
Retriever dog breed is a continuation of a thought process that
extends something about dog breeds in general which, in turn, is a
-topic that belongs to the wider theme of dogs. A sequence, then, is a
-representation of such relationships. A note with a =SIGNATURE= of
-~1=1~ (the ~=~ is the field separator of signatures, per the Denote
-file-naming scheme) is thus the first child of note =1= and the
-sibling of note ~1=2~. In this regard, something unrelated to dogs
-will be its own parent, such as =2=, and so on.
-
-All the relevant functions we provide take care to automatically use
-the right number for a given sequence
([[#h:6293ec17-05ef-4e41-9ae3-25df2ad86303][Create parent, child, or sibling
sequence notes]]).
-If, for example, we create a new child of parent ~1=1~, we make sure
-that it is the largest number among any existing children, so if
-~1=1=1~ already exists we use ~1=1=2~, and the like.
-
-The =denote-sequence.el= optional extension is not necessary for such
-a workflow. Users can always define whatever =SIGNATURE= they want
-manually. The purpose of this extension is to streamline this work.
-
-** Select a sequencing scheme for ~denote-sequence-scheme~
-:PROPERTIES:
-:CUSTOM_ID: h:8c682f08-f162-4ddd-be03-805e87737d55
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-#+vindex: denote-sequence-scheme
-The user option ~denote-sequence-scheme~ allows users to select either
-the =numeric= scheme, which is like ~1=1=2~ or the =alphanumeric=
-scheme, which is =1a2= for the same sequence
([[#h:373710df-a62e-4400-961c-87fac019b0a0][Convert from one sequencing scheme
to another]]):
-
-- Numeric sequencing scheme :: A numeric sequence consists only of
- numbers. The level of depth is derived from the number of fields in
- the sequence, separated by the equals sign. Thus, the sequence
- ~1=1=2~ consists of three levels of depth. For deeper sequences, the
- numeric scheme will get longer, which some users may consider
- unwieldy. The upside, however, is that is easier to reason about
- larger numbers, such as ~1=100=2=50~.
-
-- Alphanumeric sequencing scheme :: An alphanumeric sequence combines
- numbers and letters. The level of depth is undestand by the
- alteration from numbers to letters and vice versa. As such, the
- sequence =1a2= has three levels of depth. This scheme is more
- compact, which users may like but can be harder to reason about
- large numbers, such as =1zzzv2zx= corresponding to the numeric
- ~1=100=2=50~ (this is because the number 26 is z, 27 is za, 52 is
- zz, and so on). In practice, large numbers may not be a problem,
- though this is something to keep in mind.
-
-*** Convert from one sequencing scheme to another
-:PROPERTIES:
-:CUSTOM_ID: h:373710df-a62e-4400-961c-87fac019b0a0
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-The decision on the desired ~denote-sequence-scheme~ wil affect new
-notes long-term ([[#h:8c682f08-f162-4ddd-be03-805e87737d55][Select a
sequencing scheme for ~denote-sequence-scheme~]]).
-It thus is important to think through your needs and proceed accordingly.
-
-#+findex: denote-sequence-convert
-Still, one cannot be sure which scheme they prefer until they
-experiment with it. It then is inconvenient to manually revert to the
-alternative scheme. To this end, we provide the command
-~denote-sequence-convert~. It convers one or more files from their
-current scheme to its counterpart.
-
-When called from inside a Denote file, it converts that file. When
-called from a Dired buffer, it operates on the marked files. If no
-files are marked, it works with the Dired file at point.
-
-Note that ~denote-sequence-convert~ DOES NOT REPARENT OR ANYHOW CHECK
-THE RESULTING SEQUENCES FOR DUPLICATES
([[#h:98eb8ee5-93c4-49ba-9092-65a3b61c69c6][Re-parent a file to extend a given
sequence]]).
-
-** Create parent, child, or sibling sequence notes
-:PROPERTIES:
-:CUSTOM_ID: h:6293ec17-05ef-4e41-9ae3-25df2ad86303
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-[ In the interest of simplicity, here we provide examples using the
- =numeric= value of ~denote-sequence-scheme~, though the =alphanumeric=
- will work as well ([[#h:8c682f08-f162-4ddd-be03-805e87737d55][Select a
sequencing scheme for ~denote-sequence-scheme~]]). ]
-
-A new sequence note can be of the type =parent=, =child=, and
-=sibling=. For the convenience of the user, we provide commands to
-create such "sequence notes", link only between them (as opposed to
-a link to any other file with the Denote file-naming scheme
([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]])), and
-re-parent them on demand.
-
-Concretely, we provide the following commands:
+topic that belongs to the wider theme of dogs.
-#+findex: denote-sequence
-- ~denote-sequence~ :: The most general way to create a new sequence
- note. It prompts for a type of sequence among =parent=, =child=, and
- =sibling= and the rest of the work accordingly. If the new sequence
- is not a parent, it thus prompts for an existing file to extend
- from. The rest of the interaction is that of all the usual Denote
- commands, such as to prompt for a title and keywords
([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]).
-
-#+findex: denote-sequence-new-parent
-- ~denote-sequence-new-parent~ :: This is a convenience wrapper of
- ~denote-sequence~ which directly creates a parent sequence.
-
-#+findex: denote-sequence-new-child
-- ~denote-sequence-new-child~ :: This is a convenience wrapper of
- ~denote-sequence~ which directly creates a child of an existing
- sequence, prompting for it using minibuffer completion.
-
-#+findex: denote-sequence-new-child-of-current
-- ~denote-sequence-new-child-of-current~ :: This will create a new
- child of the current file's sequence. If the current file does not
- have such a sequence, then the command behaves the same as the
- aforementioned ~denote-sequence-new-child~.
-
-#+findex: denote-sequence-new-sibling
-- ~denote-sequence-new-sibling~ :: This is a convenience wrapper of
- ~denote-sequence~ which directly creates a sibling of an existing
- sequence, prompting for it using minibuffer completion.
-
-#+findex: denote-sequence-new-sibling-of-current
-- ~denote-sequence-new-sibling-of-current~ :: This will create a new
- sibling of the current file's sequence. If the current file does not
- have such a sequence, then the command behaves the same as the
- aforementioned ~denote-sequence-new-sibling~.
-
-** Find a relative of the current sequence
-:PROPERTIES:
-:CUSTOM_ID: h:8ec774f4-4154-4599-b8cf-fa767e18d22d
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-#+findex: denote-sequence-find
-While reading a file with a sequence, you may want to find what its
-relatives are about. To this end, the command ~denote-sequence-find~
-prompts for a type among =parent=, =sibling=, =child=, and then asks
-to select a file among those matching the given type. It then visits
-the file.
-
-#+findex: denote-sequence-find-dired
-Instead of selecting a single file, the command ~denote-sequence-find-dired~
-puts all the matching files in a bespoke Dired buffer
([[#h:7811300e-5758-4b86-89bf-1d904bf1598a][Show all or some sequences in a
Dired buffer]]).
-
-** Link only to sequences
-:PROPERTIES:
-:CUSTOM_ID: h:e83902ff-4594-494b-88cd-722f6b4fb0b7
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-#+findex: denote-sequence-link
-The command ~denote-sequence-link~ is a variant of the standard
-~denote-link~ command which limits the list of files only to those
-which contain a sequence ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking
notes]]). Consider it a convenience to
-link to sequence notes more quickly. It is by no means necessary
-though, as the regular linking commands will work as expected with any
-Denote file, including those which contain a sequence as their file
-name =SIGNATURE= ([[#h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f][Write sequence
notes or "folgezettel"]]).
-
-** Re-parent a file to extend a given sequence
-:PROPERTIES:
-:CUSTOM_ID: h:98eb8ee5-93c4-49ba-9092-65a3b61c69c6
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
-
-#+findex: denote-sequence-reparent
-The command ~denote-sequence-reparent~ can be used from inside a file
-or for the file-at-point in Dired to make that file a child of a given
-sequence. It does so by prompting for the target file using minibuffer
-completion. Files available at this prompt are only those which
-contain a sequence as their file name =SIGNATURE=
([[#h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f][Write sequence notes or
"folgezettel"]]).
-
-** Show all or some sequences in a Dired buffer
-:PROPERTIES:
-:CUSTOM_ID: h:7811300e-5758-4b86-89bf-1d904bf1598a
-:END:
-
-[ The =denote-sequence.el= is part of {{{development-version}}}. ]
+The ~denote-sequence~ package has a manual that explains these
+concepts and relevant commands in further detail:
-[ In the interest of simplicity, here we provide examples using the
- =numeric= value of ~denote-sequence-scheme~, though the =alphanumeric=
- will work as well ([[#h:8c682f08-f162-4ddd-be03-805e87737d55][Select a
sequencing scheme for ~denote-sequence-scheme~]]). ]
-
-#+findex: denote-sequence-dired
-The command ~denote-sequence-dired~ produces a bespoke and fully
-fledged Dired buffers that contains all the sequences in their order
-(as opposed to a regular Dired which sorts files using the =ls=
-flags).
-
-With an optional =C-u= prefix argument, this command prompts for a
-prefix to only show sequences that include it (e.g. only show notes
-with ~1=1~, like ~1=1=1~ and ~1=1=2~ but not ~1=2~).
-
-With an optional double prefix argument of =C-u C-u=, this command
-will prompt for the prefix as well as the level of depth to limit the
-results to. Here "depth" means how deep to go in a sequence where, for
-example, ~1=1=2~ is three levels of depth. It is possible to use an
-empty string at the prefix prompt to not limit the results to any
-prefix.
-
-A more specialised alternative for only relatives of a given sequence
-is also available ([[#h:8ec774f4-4154-4599-b8cf-fa767e18d22d][Find a relative
of the current sequence]]).
++ Package name (GNU ELPA): ~denote-sequence~
++ Official manual: <https://protesilaos.com/emacs/denote-sequence>
++ Git repository: <https://github.com/protesilaos/denote-sequence>
++ Backronym: Denote... Sequences Efficiently Queue Unsorted Entries
+ Notwithstanding Curation Efforts.
* Sort files by component
:PROPERTIES:
diff --git a/denote-sequence.el b/denote-sequence.el
deleted file mode 100644
index d448545972..0000000000
--- a/denote-sequence.el
+++ /dev/null
@@ -1,1009 +0,0 @@
-;;; denote-sequence.el --- Sequence notes extension for Denote -*-
lexical-binding: t -*-
-
-;; Copyright (C) 2024-2025 Free Software Foundation, Inc.
-
-;; Author: Protesilaos Stavrou <[email protected]>
-;; Maintainer: Protesilaos Stavrou <[email protected]>
-;; URL: https://github.com/protesilaos/denote
-
-;; This file is NOT part of GNU Emacs.
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Sequence notes extension for Denote. It uses the SIGNATURE file
-;; name component of Denote to establish a hierarchy between notes.
-;; As such, with the default numeric `denote-sequence-scheme', note
-;; 1=1=2 is the second child of the first child of note 1. While with
-;; the alphanumeric scheme, note 1a2 is the equivalent. The rest of
-;; the Denote file naming scheme continues to apply as described in
-;; the manual, as do all the other features of Denote.
-;;
-;; A new sequence note can be of the type `parent', `child', and
-;; `sibling'. For the convenience of the user, we provide commands to
-;; create such "sequence notes", link only between them (as opposed to
-;; a link to any other file with the Denote file-naminng scheme), and
-;; re-parent them on demand, as well as display them in a Dired buffer
-;; in accordance with their inherent order.
-;;
-;; All the relevant functions we provide take care to automatically
-;; use the right number for a given sequence, per the user option
-;; `denote-sequence-scheme'. If, for example, we create a new child
-;; for parent 1=1, we make sure that it is the new largest number
-;; among any existing children, so if 1=1=1 already exists we use
-;; 1=1=2, and so on.
-;;
-;; This optional extension is not necessary for such a workflow.
-;; Users can always define whatever SIGNATURE they want manually. The
-;; purpose of this extension is to streamline that work.
-
-;;; Code:
-
-;; 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.
-(require 'denote)
-
-(defgroup denote-sequence ()
- "Sequence notes extension for Denote."
- :group 'denote
- :link '(info-link "(denote) top")
- :link '(info-link "(denote) Sequence notes")
- :link '(url-link :tag "homepage" "https://protesilaos.com/emacs/denote"))
-
-(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-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 "[[:alpha:]]" 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-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 "[[:alpha:]]+" 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 those
-mentioned in `denote-sequence-scheme'.
-
-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 the sequence if Denote signature of FILE is a sequence.
-A sequence is string that conforms with `denote-sequence-p'."
- (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 those mentioned in `denote-sequence-scheme'.
-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-make-conversion (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 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'."
- (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-files (&optional files)
- "Return list of files or buffers in the variable `denote-directory'.
-With optional FILES only consider those, otherwise use
`denote-directory-files'."
- (delete-dups (append (denote--buffer-file-names) (or files
(denote-directory-files)))))
-
-(defun denote-sequence-get-all-files (&optional files)
- "Return all files in variable `denote-directory' with a sequence.
-A sequence is a Denote signature that conforms with `denote-sequence-p'.
-
-With optional FILES consider only those, otherwise use the return value
-of `denote-directory-files'."
- (seq-filter #'denote-sequence-file-p (denote-sequence--get-files files)))
-
-(defun denote-sequence--sequence-prefix-p (prefix sequence)
- "Return non-nil if SEQUENCE has prefix sequence PREFIX.
-
-SEQUENCE is a Denote signatures that conforms with `denote-sequence-p'.
-PREFIX is a list of strings containing the components of the prefix
-sequence, as is returned by `denote-sequence-split'.
-
-If PREFIX is nil, return non-nil as if the SEQUENCE has PREFIX."
- (let ((value (denote-sequence-split sequence))
- (depth (length prefix))
- (matched 0))
- (while (and value
- (< matched depth)
- (string-equal (pop value) (nth matched prefix)))
- (setq matched (1+ matched)))
- (= matched depth)))
-
-(defun denote-sequence-get-all-files-with-prefix (sequence &optional files)
- "Return all files in variable `denote-directory' with prefix SEQUENCE.
-A sequence is a Denote signature that conforms with `denote-sequence-p'.
-
-With optional FILES, operate on them, else use the return value of
-`denote-directory-files'."
- (when-let* (((not (string-empty-p sequence)))
- (prefix (denote-sequence-split sequence)))
- (seq-filter
- (lambda (file)
- (when-let* ((file-sequence (denote-sequence-file-p file)))
- (denote-sequence--sequence-prefix-p prefix file-sequence)))
- (denote-sequence-get-all-files files))))
-
-(defun denote-sequence-get-all-files-with-max-depth (depth &optional files)
- "Return all files with sequence depth up to DEPTH (inclusive).
-With optional FILES, operate on them, else use the return value of
-`denote-sequence-get-all-files'."
- (delq nil
- (mapcar
- (lambda (file)
- (when-let* ((sequence (denote-retrieve-filename-signature file))
- (components (denote-sequence-split sequence))
- ((>= depth (length components))))
- file))
- (denote-sequence-get-all-files files))))
-
-(defun denote-sequence-get-all-sequences (&optional files)
- "Return all sequences in `denote-directory-files'.
-With optional FILES return all sequences among them instead.
-
-A sequence is a Denote signature that conforms with `denote-sequence-p'."
- (delq nil (mapcar #'denote-sequence-file-p (denote-sequence-get-all-files
files))))
-
-(defun denote-sequence-get-all-sequences-with-prefix (sequence &optional
sequences)
- "Get all sequences which extend SEQUENCE.
-With optional SEQUENCES operate on those, else use the return value of
-`denote-sequence-get-all-sequences'.
-
-A sequence is a Denote signature that conforms with `denote-sequence-p'."
- (when-let* (((not (string-empty-p sequence)))
- (prefix (denote-sequence-split sequence)))
- (seq-filter
- (lambda (string)
- (denote-sequence--sequence-prefix-p prefix string))
- (or sequences (denote-sequence-get-all-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'."
- (let* ((strings (or sequences (denote-sequence-get-all-sequences)))
- (lists-all (mapcar #'denote-sequence-split strings))
- (lists (seq-filter (lambda (element) (>= depth (length element)))
lists-all)))
- (delete-dups
- (mapcar
- (lambda (strings)
- (denote-sequence-join (seq-take strings depth)
(denote-sequence--scheme-of-strings strings)))
- lists))))
-
-(defun denote-sequence--pad (sequence type)
- "Create a new SEQUENCE with padded spaces for TYPE.
-TYPE is a symbol among `denote-sequence-types'. The special TYPE `all'
-means to pad the full length of the sequence."
- (let* ((sequence-separator-p (denote-sequence--children-implied-p sequence))
- (split (denote-sequence-split sequence))
- (s (cond
- ((eq type 'all) split)
- (sequence-separator-p
- (pcase type
- ('parent (car split))
- ('sibling split)
- ('child (car (nreverse split)))
- (_ (error "The type `%s' is not among `denote-sequence-types'"
type))))
- (t sequence))))
- (if (listp s)
- (combine-and-quote-strings
- (mapcar
- (lambda (part)
- (string-pad part 5 32 :pad-from-start))
- s)
- "=")
- (string-pad s 32 32 :pad-from-start))))
-
-(defun denote-sequence--get-largest-by-order (sequences type)
- "Sort SEQUENCES of TYPE to get largest in order, using
`denote-sequence--pad'."
- (car
- (reverse
- (sort sequences
- (lambda (s1 s2)
- (string<
- (denote-sequence--pad s1 type)
- (denote-sequence--pad s2 type)))))))
-
-(defun denote-sequence--string-length-sans-delimiter (string)
- "Return length of STRING without the equals sign."
- (if (eq denote-sequence-scheme 'numeric)
- (length (replace-regexp-in-string "=" "" string))
- (length string)))
-
-(defun denote-sequence--get-largest-by-length (sequences)
- "Compare length of SEQUENCES to determine the largest among them.
-If there are more than one sequences of equal length, return them."
- (let* ((seqs-with-length (mapcar (lambda (sequence)
- (cons
(denote-sequence--string-length-sans-delimiter sequence) sequence))
- sequences))
- (longest (apply #'max (mapcar #'car seqs-with-length)))
- (largest-sequence (delq nil
- (mapcar (lambda (element)
- (unless (< (car element) longest)
- (cdr element)))
- seqs-with-length))))
- (if (= (length largest-sequence) 1)
- (car largest-sequence)
- largest-sequence)))
-
-(defun denote-sequence--get-largest (sequences type)
- "Return largest sequence in SEQUENCES given TYPE.
-TYPE is a symbol among `denote-sequence-types'."
- (if (eq type 'child)
- (let ((largest (denote-sequence--get-largest-by-length sequences)))
- (if (listp largest)
- (denote-sequence--get-largest-by-order largest type)
- largest))
- (denote-sequence--get-largest-by-order sequences type)))
-
-(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--alphanumeric-partial-p (substring
sequence -1))
- "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
-return value of `denote-sequence-get-all-sequences'."
- (if-let* ((all (or sequences (denote-sequence-get-all-sequences))))
- (let* ((largest (denote-sequence--get-largest all 'parent))
- (first-component (car (denote-sequence-split largest)))
- (current-number (string-to-number first-component)))
- (number-to-string (+ current-number 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))
- (start-child (denote-sequence--get-start sequence
:prepend-delimiter)))
- (if (= (length all-unfiltered) 1)
- (format "%s%s" (car all-unfiltered) start-child)
- (if-let* ((all-schemeless (cond
-
((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."
- (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.
-Optional SEQUENCES has the same meaning as that specified in the
-function `denote-sequence-get-all-sequences-with-prefix'."
- (let* ((children-p (denote-sequence--children-implied-p sequence)))
- (if-let* ((depth (denote-sequence-depth sequence))
- (all-unfiltered (if children-p
-
(denote-sequence-get-all-sequences-with-prefix
- (denote-sequence--get-prefix-for-siblings
sequence)
- sequences)
- (denote-sequence-get-all-sequences)))
- (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
- (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' using scheme `%s'"
sequence denote-sequence-scheme))))
-
-(defun denote-sequence-get-new (type &optional sequence sequences)
- "Return a sequence given TYPE among `denote-sequence-types'.
-If TYPE is either `child' or `sibling', then optional SEQUENCE must be
-non-nil and conform with `denote-sequence-p'.
-
-With optional SEQUENCES consider only those, otherwise operate on the
-return value of `denote-sequence-get-all-sequences'."
- (pcase type
- ('parent (denote-sequence--get-new-parent sequences))
- ('child (denote-sequence--get-new-child sequence sequences))
- ('sibling (denote-sequence--get-new-sibling sequence sequences))
- (_ (error "The type `%s' is not among `denote-sequence-types'" type))))
-
-(defun denote-sequence-get-relative (sequence type &optional files)
- "Get files of TYPE given the SEQUENCE.
-With optional FILES consider only those, otherwise operate on all files
-returned by `denote-sequence-get-all-files'."
- (let* ((depth (denote-sequence-depth sequence))
- (scheme (cdr (denote-sequence-and-scheme-p sequence)))
- (components (denote-sequence-split sequence))
- (filter-common (lambda (comparison prefix)
- (seq-filter
- (lambda (file)
- (funcall comparison (denote-sequence-depth
(denote-retrieve-filename-signature file)) depth))
- (denote-sequence-get-all-files-with-prefix prefix
files)))))
- (pcase type
- ('all-parents (let ((parents nil)
- (butlast (butlast components)))
- (while (>= (length butlast) 1)
- (when-let* ((prefix (denote-sequence-join butlast
scheme))
- (parent (seq-find
- (lambda (file)
- (string=
(denote-retrieve-filename-signature file) prefix))
- (denote-sequence-get-all-files
files))))
- (push parent parents)
- (setq butlast (butlast butlast))))
- parents))
- ('parent (let ((butlast (denote-sequence-join (butlast components)
scheme)))
- (seq-find
- (lambda (file)
- (string= (denote-retrieve-filename-signature file)
butlast))
- (denote-sequence-get-all-files files))))
- ('siblings (funcall filter-common '= (denote-sequence-join (butlast
components) scheme)))
- ('all-children (funcall filter-common '> sequence))
- ('children (seq-filter
- (lambda (file)
- (= (denote-sequence-depth (denote-sequence-file-p file))
(+ depth 1)))
- (funcall filter-common '> sequence)))
- (_ (error "The type `%s' is not among the allowed types" type)))))
-
-(defvar denote-sequence-type-history nil
- "Minibuffer history of `denote-sequence-type-prompt'.")
-
-(defun denote-sequence-annotate-types (type)
- "Annotate completion candidate of TYPE for `denote-sequence-type-prompt'."
- (when-let* ((text (pcase type
- ("parent" "Parent sequence")
- ("sibling" "Sibling of another sequence")
- ("child" "Child of another sequence"))))
- (format "%s-- %s"
- (propertize " " 'display '(space :align-to 10))
- (propertize text 'face 'completions-annotations))))
-
-(defun denote-sequence-type-prompt (&optional prompt-text types annotation-fn)
- "Prompt for sequence type among `denote-sequence-types'.
-Return selected type as a symbol.
-
-With optional PROMPT-TEXT use it instead of the generic prompt.
-
-With optional TYPES use those instead of the `denote-sequence-types'.
-
-With optional ANNOTATION-FN use it to annotate the completion candidates
-instead of the default `denote-sequence-annotate-types'."
- (let ((default (car denote-sequence-type-history))
- (completion-extra-properties
- (list :annotation-function (or annotation-fn
#'denote-sequence-annotate-types))))
- (intern
- (completing-read
- (format-prompt (or prompt-text "Select sequence type") default)
- (denote--completion-table 'denote-sequence-type (or types
denote-sequence-types))
- nil :require-match nil
- 'denote-sequence-type-history default))))
-
-(defvar denote-sequence-file-history nil
- "Minibuffer history for `denote-sequence-file-prompt'.")
-
-(defun denote-sequence-file-prompt (&optional prompt-text files-with-sequences)
- "Prompt for file with sequence in variable `denote-directory'.
-A sequence is a Denote signature that conforms with `denote-sequence-p'.
-
-With optional PROMPT-TEXT use it instead of a generic prompt.
-
-With optional FILES-WITH-SEQUENCES as a list of strings, use them as
-completion candidates. Else use `denote-sequence-get-all-files'."
- (if-let* ((relative-files (mapcar
#'denote-get-file-name-relative-to-denote-directory
- (or files-with-sequences
(denote-sequence-get-all-files))))
- (prompt (format-prompt (or prompt-text "Select FILE with
sequence") nil))
- (input (completing-read
- prompt
- (denote--completion-table 'file relative-files)
- nil :require-match
- nil 'denote-sequence-file-history)))
- (concat (denote-directory) input)
- (error "There are no sequence notes in the `denote-directory'")))
-
-;;;###autoload
-(defun denote-sequence (type &optional file-with-sequence)
- "Create a new sequence note of TYPE among `denote-sequence-types'.
-If TYPE is either `child' or `sibling', then it is an extension of
-FILE-WITH-SEQUENCE.
-
-When called interactively, prompt for TYPE and, when necessary, for
-FILE-WITH-SEQUENCE whose sequence will be used to derive a new sequence.
-Files available at the minibuffer prompt are those returned by
-`denote-sequence-get-all-files'."
- (interactive
- (let ((selected-type (denote-sequence-type-prompt)))
- (list
- selected-type
- (when (memq selected-type (delq 'parent denote-sequence-types))
- (denote-sequence-file-prompt (format "Make a new %s of SEQUENCE"
selected-type))))))
- (let* ((sequence (when file-with-sequence
(denote-retrieve-filename-signature file-with-sequence)))
- (new-sequence (denote-sequence-get-new type sequence))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-;;;###autoload
-(defun denote-sequence-new-parent ()
- "Like `denote-sequence' to directly create new parent."
- (interactive)
- (let* ((new-sequence (denote-sequence-get-new 'parent))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-;;;###autoload
-(defun denote-sequence-new-sibling (sequence)
- "Like `denote-sequence' to directly create new sibling of SEQUENCE.
-When called interactively, SEQUENCE is a file among files in the variable
-`denote-directory' that have a sequence (per `denote-sequence-file-p').
-
-When called from Lisp, SEQUENCE is a string that conforms with
-`denote-sequence-p'."
- (interactive
- (list
- (denote-retrieve-filename-signature
- (denote-sequence-file-prompt "Make a new sibling of SEQUENCE"))))
- (let* ((new-sequence (denote-sequence-get-new 'sibling sequence))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-(defun denote-sequence--get-file-in-dired-or-prompt (prompt-text)
- "Get the file at point in Dired, the current one, or prompt with
PROMPT-TEXT."
- (cond
- ((when-let* (((derived-mode-p 'dired-mode))
- (file-at-point (dired-get-filename nil t)))
- (denote-sequence-file-p file-at-point)))
- ((and buffer-file-name (denote-sequence-file-p buffer-file-name)))
- (t
- (denote-retrieve-filename-signature (denote-sequence-file-prompt
prompt-text)))))
-
-;;;###autoload
-(defun denote-sequence-new-sibling-of-current (sequence)
- "Create a new sibling sequence of the current file with SEQUENCE.
-If the current file does not have a sequence, then behave exactly like
-`denote-sequence-new-sibling'."
- (interactive (list (denote-sequence--get-file-in-dired-or-prompt "Make a new
sibling of SEQUENCE")))
- (let* ((new-sequence (denote-sequence-get-new 'sibling sequence))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-;;;###autoload
-(defun denote-sequence-new-child (sequence)
- "Like `denote-sequence' to directly create new child of SEQUENCE.
-When called interactively, SEQUENCE is a file among files in the variable
-`denote-directory' that have a sequence (per `denote-sequence-file-p').
-
-When called from Lisp, SEQUENCE is a string that conforms with
-`denote-sequence-p'."
- (interactive
- (list
- (denote-retrieve-filename-signature
- (denote-sequence-file-prompt "Make a new child of SEQUENCE"))))
- (let* ((new-sequence (denote-sequence-get-new 'child sequence))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-;;;###autoload
-(defun denote-sequence-new-child-of-current (sequence)
- "Create a new child sequence of the current file with SEQUENCE.
-If the current file does not have a sequence, then behave exactly like
-`denote-sequence-new-child'."
- (interactive (list (denote-sequence--get-file-in-dired-or-prompt "Make a new
child of SEQUENCE")))
- (let* ((new-sequence (denote-sequence-get-new 'child sequence))
- (denote-use-signature new-sequence))
- (call-interactively 'denote)))
-
-(defvar denote-sequence-relative-types
- '(all-parents parent siblings children all-children)
- "Types of sequence relatives.")
-
-(defun denote-sequence-annotate-relative-types (type)
- "Annotate completion candidate of TYPE for `denote-sequence-type-prompt'."
- (when-let* ((text (pcase type
- ("all-parents" "All parent sequences")
- ("parent" "Immediate parent")
- ("siblings" "All siblings")
- ("all-children" "All children")
- ("children" "Immediate children"))))
- (format "%s-- %s"
- (propertize " " 'display '(space :align-to 15))
- (propertize text 'face 'completions-annotations))))
-
-;;;###autoload
-(defun denote-sequence-find (type)
- "Find all relatives of the given TYPE using the current file's sequence.
-Prompt for TYPE among `denote-sequence-relative-types' and then prompt
-for a file among the matching files."
- (interactive
- (list
- (denote-sequence-type-prompt "Find relatives of TYPE"
- '(all-parents parent siblings children
all-children)
- #'denote-sequence-annotate-relative-types)))
- (if-let* ((sequence (denote-sequence-file-p buffer-file-name)))
- (if-let* ((matches (denote-sequence-get-relative sequence type))
- (relatives (delete buffer-file-name matches)))
- (find-file (if (stringp relatives)
- relatives
- (denote-sequence-file-prompt "Select a relative"
relatives)))
- (user-error "The sequence `%s' has no relatives of type `%s'" sequence
type))
- (user-error "The current file has no sequence")))
-
-;;;###autoload
-(defun denote-sequence-link (file &optional id-only)
- "Link to FILE with sequence.
-This is like the `denote-link' command but only accepts to link to a
-file that conforms with `denote-sequence-file-p'. When called
-interactively, only relevant files are shown for minibuffer completion
-from the variable `denote-directory'.
-
-Optional ID-ONLY has the same meaning as the `denote-link' command."
- (interactive (list (denote-sequence-file-prompt "Link to file with
sequence")))
- (unless (denote-sequence-file-p file)
- (error "Can only link to file with a sequence; else use `denote-link' and
related"))
- (let* ((type (denote-filetype-heuristics buffer-file-name))
- (description (denote-get-link-description file)))
- (denote-link file type description id-only)))
-
-(defun denote-sequence-sort-files (files-with-sequence)
- "Sort FILES-WITH-SEQUENCE according to their sequence."
- (sort
- files-with-sequence
- (lambda (file-with-sequence-1 file-with-sequence-2)
- (let ((s1 (denote-retrieve-filename-signature file-with-sequence-1))
- (s2 (denote-retrieve-filename-signature file-with-sequence-2)))
- (string<
- (denote-sequence--pad s1 'all)
- (denote-sequence--pad s2 'all))))))
-
-(defvar denote-sequence-history nil
- "Minibuffer history of `denote-sequence-prompt'.")
-
-(defun denote-sequence-prompt (&optional prompt-text sequences)
- "Prompt for a sequence.
-With optional PROMPT-TEXT use it instead of a generic prompt.
-
-With optional SEQUENCES as a list of strings, use them as completion
-candidates. Else use the return value of `denote-sequence-get-all-sequences'.
-A sequence is a string conforming with `denote-sequence-p'. Any other string
-is ignored."
- (completing-read
- (format-prompt (or prompt-text "Select an existing sequence (empty for
all)") nil)
- (or sequences (denote-sequence-get-all-sequences))
- #'denote-sequence-p :require-match nil 'denote-sequence-history))
-
-(defun denote-sequence-depth-prompt (&optional prompt-text)
- "Prompt for the depth of a sequence.
-With optional PROMPT-TEXT use it instead of the generic one."
- (read-number
- (or prompt-text
- (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.
-Use optional PREFIX and DEPTH to format the string accordingly."
- (let ((time (format-time-string "%F %T")))
- (cond
- ((and prefix depth)
- (format-message "*Denote sequences of prefix `%s' and depth `%s', %s*"
prefix depth time))
- ((and prefix (not (string-empty-p prefix)))
- (format-message "*Denote sequences of prefix `%s', %s*" prefix time))
- (t
- (format "*Denote sequences, %s*" time)))))
-
-;;;###autoload
-(defun denote-sequence-dired (&optional prefix depth)
- "Produce a Dired listing of all sequence notes.
-Sort sequences from smallest to largest.
-
-With optional PREFIX string, show only files whose sequence matches it.
-
-With optional DEPTH as a number, limit the list to files whose sequence
-is that many levels deep. For example, 1=1=2 is three levels deep.
-
-For a more specialised case, see `denote-sequence-find-relatives-dired'."
- (interactive
- (let ((arg (prefix-numeric-value current-prefix-arg)))
- (cond
- ((= arg 16)
- (list
- (denote-sequence-prompt "Limit to files that extend SEQUENCE (empty
for all)")
- (denote-sequence-depth-prompt)))
- ((= arg 4)
- (list
- (denote-sequence-prompt "Limit to files that extend SEQUENCE (empty
for all)")))
- (t
- nil))))
- (if-let* ((default-directory (denote-directory))
- (all (if prefix
- (denote-sequence-get-all-files-with-prefix prefix)
- (denote-sequence-get-all-files)))
- (files-with-depth (if depth
-
(denote-sequence-get-all-files-with-max-depth depth all)
- all))
- (files-sorted (denote-sequence-sort-files files-with-depth))
- (buffer-name (denote-sequence--get-dired-buffer-name prefix
depth)))
- (let ((dired-buffer (dired (cons buffer-name (mapcar
#'file-relative-name files-sorted)))))
- (with-current-buffer dired-buffer
- (setq-local revert-buffer-function
- (lambda (&rest _)
- ;; FIXME 2025-01-04: Killing the buffer has
- ;; the unintended side effect of affecting the
- ;; window configuration when we call
- ;; `denote-update-dired-buffers'.
- (kill-buffer dired-buffer)
- (denote-sequence-dired prefix depth)))))
- (user-error "No Denote sequences matching those terms")))
-
-;;;###autoload
-(defun denote-sequence-find-dired (type)
- "Like `denote-sequence-find' for TYPE but put the matching files in Dired.
-Also see `denote-sequence-dired'."
- (interactive
- (list (denote-sequence-type-prompt "Find relatives of TYPE"
- '(all-parents
- parent
- siblings
- all-children
- children))))
- (if-let* ((sequence (denote-sequence-file-p buffer-file-name)))
- (if-let* ((default-directory (denote-directory))
- (relatives (delete buffer-file-name
- (ensure-list
- (denote-sequence-get-relative sequence
type))))
- (files-sorted (denote-sequence-sort-files relatives)))
- (dired (cons (format-message "*`%s' type relatives of `%s'" type
sequence)
- (mapcar #'file-relative-name files-sorted)))
- (user-error "The sequence `%s' has no relatives of type `%s'" sequence
type))
- (user-error "The current file has no sequence")))
-
-;; TODO 2025-01-14: We need to have an operation that reparents
-;; recursively. This can be done inside of the `denote-sequence-reparent',
-;; where if it finds that the current file has children, it prompts
-;; for a confirmation and then continues to reparent all of them.
-;;;###autoload
-(defun denote-sequence-reparent (current-file file-with-sequence)
- "Re-parent the CURRENT-FILE to be a child of FILE-WITH-SEQUENCE.
-If CURRENT-FILE has a sequence (the Denote file name signature), change
-it. Else create a new one.
-
-When called interactively, CURRENT-FILE is either the current file, or a
-special Org buffer (like those of `org-capture'), or the file at point in
-Dired.
-
-When called interactively, prompt for FILE-WITH-SEQUENCE showing only
-the files in the variable `denote-directory' which have a sequence. If
-no such files exist, throw an error.
-
-When called from Lisp, CURRENT-FILE is a string pointing to a file.
-
-When called from Lisp, FILE-WITH-SEQUENCE is either a file with a
-sequence (per `denote-sequence-file-p') or the sequence string as
-such (per `denote-sequence-p'). In both cases, what matters is to know
-the target sequence."
- (interactive
- (list
- (if (denote--file-type-org-extra-p)
- denote-last-path-after-rename
- (denote--rename-dired-file-or-current-file-or-prompt))
- (denote-sequence-file-prompt
- (format "Reparent `%s' to be a child of"
- (propertize
- (denote--rename-dired-file-or-current-file-or-prompt)
- 'face 'denote-faces-prompt-current-name)))))
- (let* ((target-sequence (or (denote-sequence-file-p file-with-sequence)
- (denote-sequence-p file-with-sequence)
- (user-error "No sequence of `denote-sequence-p'
found in `%s'" file-with-sequence)))
- (new-sequence (denote-sequence--get-new-child target-sequence)))
- (denote-rename-file current-file 'keep-current 'keep-current new-sequence
'keep-current)))
-
-;;;###autoload
-(defun denote-sequence-convert (files)
- "Convert the sequence scheme of FILES to match `denote-sequence-scheme'.
-When called from inside a Denote file, FILES is just the current file.
-When called from a Dired buffer, FILES are the marked files. If no
-files are marked, then the one at point is considered.
-
-Do not make any changes if the file among the FILES has no sequence or
-if it already matches the value of `denote-sequence-scheme'. A file has
-a sequence when it conforms with `denote-sequence-file-p'.
-
-This command is for users who once used a `denote-sequence-scheme' and
-have since decided to switch to another. IT DOES NOT REPARENT OR ANYHOW
-CHECK THE RESULTING SEQUENCES FOR DUPLICATES."
- (interactive
- (list
- (if (derived-mode-p 'dired-mode)
- (dired-get-marked-files)
- buffer-file-name))
- dired-mode)
- (unless (listp files)
- (setq files (list files)))
- (dolist (file files)
- (when-let* ((old-sequence (denote-sequence-file-p file))
- (new-sequence (denote-sequence-make-conversion old-sequence
:is-complete-sequence)))
- (denote-rename-file file 'keep-current 'keep-current new-sequence
'keep-current)))
- (denote-update-dired-buffers))
-
-(provide 'denote-sequence)
-;;; denote-sequence.el ends here
diff --git a/tests/denote-test.el b/tests/denote-test.el
index 209f906c7e..a6aded1723 100644
--- a/tests/denote-test.el
+++ b/tests/denote-test.el
@@ -588,215 +588,6 @@ does not involve the time zone."
(let ((denote-journal-extras-title-format
'day-date-month-year-24h))
(denote-journal-extras-daily--title-format))))))
-;;;; Tests for denote-sequence.el
-
-(require 'denote-sequence)
-
-(ert-deftest dt-denote-sequence--get-new-exhaustive ()
- "Test if we get the correct parent, child, sibling, or relatives of a
sequence.
-Use the function `denote-sequence-get-new' for child and sibling with
-the numeric and alphanumeric `denote-sequence-scheme', as well as the
-function `denote-sequence-get-relative'."
- (let* ((denote-sequence-scheme 'numeric)
- (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)))
- '("20241230T075023==1--test__testing.txt"
- "20241230T075023==1=1--test__testing.txt"
- "20241230T075023==1=1=1--test__testing.txt"
- "20241230T075023==1=1=2--test__testing.txt"
- "20241230T075023==1=2--test__testing.txt"
- "20241230T075023==1=2=1--test__testing.txt"
- "20241230T075023==1=2=1=1--test__testing.txt"
- "20241230T075023==2--test__testing.txt"
- "20241230T075023==10--test__testing.txt"
- "20241230T075023==10=1--test__testing.txt"
- "20241230T075023==10=1=1--test__testing.txt"
- "20241230T075023==10=2--test__testing.txt"
- "20241230T075023==10=10--test__testing.txt"
- "20241230T075023==10=10=1--test__testing.txt")))
- (sequences (denote-sequence-get-all-sequences files)))
- (should (string= (denote-sequence-get-new 'parent) "11"))
-
- (should (and (string= (denote-sequence-get-new 'child "1" sequences) "1=3")
- (string= (denote-sequence-get-new 'child "1=1" sequences)
"1=1=3")
- (string= (denote-sequence-get-new 'child "1=1=2" sequences)
"1=1=2=1")
- (string= (denote-sequence-get-new 'child "1=2" sequences)
"1=2=2")
- (string= (denote-sequence-get-new 'child "1=2=1" sequences)
"1=2=1=2")
- (string= (denote-sequence-get-new 'child "2" sequences)
"2=1")))
- (should-error (denote-sequence-get-new 'child "11" sequences))
-
- (should (and (string= (denote-sequence-get-new 'sibling "1" sequences)
"11")
- (string= (denote-sequence-get-new 'sibling "1=1" sequences)
"1=3")
- (string= (denote-sequence-get-new 'sibling "1=1=1" sequences)
"1=1=3")
- (string= (denote-sequence-get-new 'sibling "1=1=2" sequences)
"1=1=3")
- (string= (denote-sequence-get-new 'sibling "1=2" sequences)
"1=3")
- (string= (denote-sequence-get-new 'sibling "1=2=1" sequences)
"1=2=2")
- (string= (denote-sequence-get-new 'sibling "2" sequences)
"11")))
- (should-error (denote-sequence-get-new 'sibling "12" sequences))
-
- (should (string= (denote-sequence-get-relative "1=2=1=1" 'parent files)
- (expand-file-name
"20241230T075023==1=2=1--test__testing.txt" denote-directory)))
- (should (string= (denote-sequence-get-relative "10=1=1" 'parent files)
- (expand-file-name
"20241230T075023==10=1--test__testing.txt" denote-directory)))
- (should (string= (denote-sequence-get-relative "10=10=1" 'parent files)
- (expand-file-name
"20241230T075023==10=10--test__testing.txt" denote-directory)))
- (should (equal (denote-sequence-get-relative "1=2=1=1" 'all-parents files)
- (list
- (expand-file-name "20241230T075023==1--test__testing.txt"
denote-directory)
- (expand-file-name
"20241230T075023==1=2--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==1=2=1--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "10=1=1" 'all-parents files)
- (list
- (expand-file-name "20241230T075023==10--test__testing.txt"
denote-directory)
- (expand-file-name
"20241230T075023==10=1--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "10=10=1" 'all-parents files)
- (list
- (expand-file-name "20241230T075023==10--test__testing.txt"
denote-directory)
- (expand-file-name
"20241230T075023==10=10--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1=1" 'siblings files)
- (list
- (expand-file-name
"20241230T075023==1=1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==1=2--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "10=1" 'siblings files)
- (list
- (expand-file-name
"20241230T075023==10=1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10=2--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10=10--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1" 'children files)
- (list
- (expand-file-name
"20241230T075023==1=1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==1=2--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "10" 'children files)
- (list
- (expand-file-name
"20241230T075023==10=1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10=2--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10=10--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1=1" 'all-children files)
- (list
- (expand-file-name
"20241230T075023==1=1=1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==1=1=2--test__testing.txt" denote-directory)))))
-
- (let* ((denote-sequence-scheme 'alphanumeric)
- (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)))
- '("20241230T075023==1--test__testing.txt"
- "20241230T075023==1a--test__testing.txt"
- "20241230T075023==1a1--test__testing.txt"
- "20241230T075023==1a2--test__testing.txt"
- "20241230T075023==1b--test__testing.txt"
- "20241230T075023==1b1--test__testing.txt"
- "20241230T075023==1b1a--test__testing.txt"
- "20241230T075023==2--test__testing.txt"
- "20241230T075023==10--test__testing.txt"
- "20241230T075023==10a--test__testing.txt"
- "20241230T075023==10b--test__testing.txt")))
- (sequences (denote-sequence-get-all-sequences files)))
- (should (string= (denote-sequence-get-new 'parent) "11"))
-
- (should (and (string= (denote-sequence-get-new 'child "1" sequences) "1c")
- (string= (denote-sequence-get-new 'child "1a" sequences)
"1a3")
- (string= (denote-sequence-get-new 'child "1a2" sequences)
"1a2a")
- (string= (denote-sequence-get-new 'child "1b" sequences)
"1b2")
- (string= (denote-sequence-get-new 'child "1b1" sequences)
"1b1b")
- (string= (denote-sequence-get-new 'child "2" sequences)
"2a")))
- (should-error (denote-sequence-get-new 'child "11" sequences))
-
- (should (and (string= (denote-sequence-get-new 'sibling "1" sequences)
"11")
- (string= (denote-sequence-get-new 'sibling "1a" sequences)
"1c")
- (string= (denote-sequence-get-new 'sibling "1a1" sequences)
"1a3")
- (string= (denote-sequence-get-new 'sibling "1a2" sequences)
"1a3")
- (string= (denote-sequence-get-new 'sibling "1b" sequences)
"1c")
- (string= (denote-sequence-get-new 'sibling "1b1" sequences)
"1b2")
- (string= (denote-sequence-get-new 'sibling "2" sequences)
"11")))
- (should-error (denote-sequence-get-new 'sibling "12" sequences))
-
- (should (string= (denote-sequence-get-relative "1b1a" 'parent files)
- (expand-file-name
"20241230T075023==1b1--test__testing.txt" denote-directory)))
- (should (string= (denote-sequence-get-relative "10a" 'parent files)
- (expand-file-name
"20241230T075023==10--test__testing.txt" denote-directory)))
- (should (equal (denote-sequence-get-relative "1b1a" 'all-parents files)
- (list
- (expand-file-name "20241230T075023==1--test__testing.txt"
denote-directory)
- (expand-file-name "20241230T075023==1b--test__testing.txt"
denote-directory)
- (expand-file-name
"20241230T075023==1b1--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1a" 'siblings files)
- (list
- (expand-file-name "20241230T075023==1a--test__testing.txt"
denote-directory)
- (expand-file-name "20241230T075023==1b--test__testing.txt"
denote-directory))))
- (should (equal (denote-sequence-get-relative "10a" 'siblings files)
- (list
- (expand-file-name
"20241230T075023==10a--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10b--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1" 'children files)
- (list
- (expand-file-name "20241230T075023==1a--test__testing.txt"
denote-directory)
- (expand-file-name "20241230T075023==1b--test__testing.txt"
denote-directory))))
- (should (equal (denote-sequence-get-relative "10" 'children files)
- (list
- (expand-file-name
"20241230T075023==10a--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==10b--test__testing.txt" denote-directory))))
- (should (equal (denote-sequence-get-relative "1a" 'all-children files)
- (list
- (expand-file-name
"20241230T075023==1a1--test__testing.txt" denote-directory)
- (expand-file-name
"20241230T075023==1a2--test__testing.txt" denote-directory))))))
-
-(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-make-conversion ()
- "Test that `denote-sequence-make-conversion' converts from alpha to numeric
and vice versa."
- (should (and (string= (denote-sequence-make-conversion "3") "c")
- (string= (denote-sequence-make-conversion "18") "r")
- (string= (denote-sequence-make-conversion "26") "z")
- (string= (denote-sequence-make-conversion "27") "za")
- (string= (denote-sequence-make-conversion "130") "zzzzz")
- (string= (denote-sequence-make-conversion "131") "zzzzza")
- (string= (denote-sequence-make-conversion "c") "3")
- (string= (denote-sequence-make-conversion "r") "18")
- (string= (denote-sequence-make-conversion "z") "26")
- (string= (denote-sequence-make-conversion "za") "27")
- (string= (denote-sequence-make-conversion "zzzzz") "130")
- (string= (denote-sequence-make-conversion "zzzzza") "131")))
- (should (and (string= (denote-sequence-make-conversion "1=1=2"
:string-is-sequence) "1a2")
- (string= (denote-sequence-make-conversion "1a2"
:string-is-sequence) "1=1=2")
- (string= (denote-sequence-make-conversion "1=27=2=55"
:string-is-sequence) "1za2zzc")
- (string= (denote-sequence-make-conversion "1za2zzc"
:string-is-sequence) "1=27=2=55")
- (string= (denote-sequence-make-conversion "1=1=2=2=4=1"
:string-is-sequence) "1a2b4a")
- (string= (denote-sequence-make-conversion "1a2b4a"
:string-is-sequence) "1=1=2=2=4=1")))
- (should-error (denote-sequence-make-conversion "111=a" :string-is-sequence))
- (should-error (denote-sequence-make-conversion "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