Search code examples
emacspackageversioningload-path

Load path ivy-* match any series number


I customize load-path for Ivy using a package suffix of -0.13.1:

(add-to-list 'load-path "~/.zeroemacs/elpa/ivy-0.13.1/")

However, when the ivy package upgraded to 0.14.1, I had to manually modify the load-path to

(add-to-list 'load-path "~/.zeroemacs/elpa/ivy-0.14.1/")

Is it possible to replace it with something like ivy-* which match any series numbers?


Solution

  • Version numbers come in many shapes and sizes. Rather than trying to deal with any and all version formats, the latest-file-version function below relies on comparing version strings accepted by the version-to-list function. The documentation for version-to-list includes this description of what it accepts:

    The version syntax is given by the following EBNF:

    VERSION ::= NUMBER ( SEPARATOR NUMBER )*.

    NUMBER ::= (0|1|2|3|4|5|6|7|8|9)+.

    SEPARATOR ::= ‘version-separator’ (which see)
    | ‘version-regexp-alist’ (which see).

    The NUMBER part is optional if SEPARATOR is a match for an element in ‘version-regexp-alist’.

    You can use the latest-file-version function in your load-path setting like this:

    (add-to-list 'load-path (latest-file-version "~/.zeroemacs/elpa" "ivy"))
    

    The first argument is the directory to check, and the second argument is the prefix of the versioned filenames or directory names to check.

    (defun latest-file-version (dir prefix)
      "Get the latest version of files in DIR starting with PREFIX.
    Only filenames in DIR with the form PREFIX-version are
    considered, where the version portion of the filename must have
    valid version syntax as specified for `version-to-list'. Raise an
    error if no filenames in DIR start with PREFIX or if no valid
    matching versioned filenames are found."
      (let* ((vsn-regex (concat "^" prefix "-\\(.+\\)$"))
             (vsn-entries
              (seq-reduce
               #'(lambda (acc s)
                   (if (string-match vsn-regex s)
                       (let* ((m (match-string 1 s))
                              (vsn (condition-case nil
                                       (version-to-list m)
                                     (error nil))))
                         (if vsn
                             (cons (cons m s) acc)
                           acc))
                     acc)) 
               (directory-files dir nil nil t) nil)))
        (if vsn-entries
            (concat (file-name-as-directory dir)
              (cdar (sort vsn-entries
                          #'(lambda (v1 v2)
                              (version<= (car v2) (car v1))))))
          (error "No valid versioned filenames found in %s with prefix \"%s-\""
                 dir prefix))))
    

    Tested with emacs 27.1.