        (t (cl-return-from straight--map-repos-interactively skipped-repos))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;; API
+;;;; User-facing functions
 
 ;;;###autoload
 (defun straight-declare-init-finished ()

another message, and needs to be redisplayed.")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Accessing and modifying data structures

(defmacro straight--with-plist (plist props &rest body)
  "Binding from PLIST the given PROPS, eval and return BODY.
PROPS is a list of symbols. Each one is converted to a keyword
and then its value is looked up in the PLIST and bound to the
symbol for the duration of BODY."
  (declare (indent 2))
  (let ((plist-sym (make-symbol "plist")))
    `(let* ((,plist-sym ,plist)
            ,@(mapcar (lambda (prop)
                        `(,prop
                          (plist-get
                           ,plist-sym
                           ,(intern (concat ":" (symbol-name prop))))))
                      props))
       ,@body)))

(defmacro straight--put (plist prop value)
  "Make copy of PLIST with key PROP mapped to VALUE, and re-set it.
PLIST must be a literal symbol naming a plist variable. PROP and
VALUE are evaluated."
  `(progn
     (setq ,plist (copy-sequence ,plist))
     (setq ,plist (plist-put ,plist ,prop ,value))))

(defmacro straight--remq (plist props)
  "Make copy of PLIST with keys PROPS removed, and re-set it.
PLIST must be a literal symbol naming a plist variable. PROPS is
evaluated and should result in a list. Key comparison is done
with `eq'."
  ;; The following subroutine is adapted from [1].
  ;;
  ;; [1]: https://lists.gnu.org/archive/html/help-gnu-emacs/2015-08/msg00019.html
  (let ((props-sym (make-symbol "props")))
    `(let ((,props-sym ,props))
       (setq ,plist
             (cl-loop for (prop val) on ,plist by #'cddr
                      unless (memq prop ,props-sym)
                      collect prop and collect val)))))

(defun straight--insert (n key value table)
  "Associate index N in KEY with VALUE in hash table TABLE.
TABLE should be a hash whose values are lists. This function will
set the Nth entry of the list mapped to KEY in TABLE to VALUE. If
the list does not have an Nth entry, it will be padded with nils
so that it does, before the setting happens. The TABLE will be
modified and returned."
  (let ((list (gethash key table)))
    (if (>= n (length list))
        (puthash key
                 (append list
                         (make-list (- n (length list)) nil)
                         (list value))
                 table)
      (setcar (nthcdr n list) value))
    table))

(defvar straight--not-present 'straight--not-present
  "Value used as a default argument to `gethash'.")

(defvar straight--not-present-paranoid 'straight--not-present-paranoid
  "Value used as a default argument to `gethash'.
Why do we need this? Because whoever wrote the Elisp hash table
API didn't actually know how to write hash table APIs.")

(defun straight--checkhash (key table &optional paranoid)
  "Return non-nil if KEY is present in hash TABLE.
If PARANOID is non-nil, ensure correctness even for hash tables
that may contain `straight--not-present' as a value."
  (and (eq (gethash key table straight--not-present) straight--not-present)
       (or paranoid
           (eq (gethash key table straight--not-present-paranoid)
               straight--not-present-paranoid))))

(defun straight--normalize-alist (alist &optional test)
  "Return copy of ALIST with duplicate keys removed.
The value for a duplicated key will be the last one in ALIST.
Duplicates are tested with TEST, which must be accepted by the
`make-hash-table' function and which defaults to `eq'. The order
of the entries that are kept will be the same as in ALIST."
  (let ((hash (make-hash-table :test (or test #'eq)))
        (new-alist ()))
    (dolist (entry (reverse alist))
      (unless (gethash (car entry) hash)
        (push entry new-alist)
        (puthash (car entry) t hash)))
    new-alist))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; String manipulation

(cl-defun straight--uniquify (prefix taken)
  "Generate a string with PREFIX that is not in list TAKEN.
This is done by trying PREFIX-1, PREFIX-2, etc. if PREFIX is
already in TAKEN."
  (if (member prefix taken)
      (let ((n 1))
        (while t
          (let ((candidate (format "%s-%d" prefix n)))
            (if (member candidate taken)
                (setq n (1+ n))
              (cl-return-from straight--uniquify candidate)))))
    prefix))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Constructing filesystem paths

(defun straight--dir (&rest segments)
  "Get a subdirectory of the straight.el directory.

                                            :repo "emacsmirror/epkgs"
                                            :nonrecursive t)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Recipe registration

(defvar straight--build-keywords
  '(:local-repo :files)
  "Keywords that affect how a file is built locally.
