jwiegley / use-package

A use-package declaration for simplifying your .emacs
https://jwiegley.github.io/use-package
GNU General Public License v3.0
4.38k stars 260 forks source link

custom use-package keyword only initializing last form #1025

Closed shadowrylander closed 1 year ago

shadowrylander commented 1 year ago

My package deino is a fork of hydra, which allows me to create temporary keymaps; here it is being used along with my package prime:

With following macro:

(defmacro prime* (parent first-call key func &optional name* &rest args)
    (let* ((ds (deino--create-dataset
                (if (stringp name*) name* (if (symbolp func) (symbol-name func) nil))
                key
                parent
                func
                #'prime--construct-name))

            (next-key (s-join " " (cdr (d--g ds :keys))))
            (next-deino-body (if (d--g ds :two-key) func (meq/inconcat (d--g ds :next-name) "/body")))
            (next-deino-settings (when (d--g ds :two-key) args)))
        (when first-call (eval `(defdeino+ primus nil (,(d--g ds :carkeys)
                                                        ,(d--g ds :current-body)
                                                        ,(d--g ds :current-name)))))
        (unless (d--g ds :one-key)
            (eval `(prime* ,(d--g ds :current-parent) nil ,next-key ,func ,name* ,@next-deino-settings))
            `(,(meq/inconcat "defdeino" (if (d--g ds :current-body-plus) "+" ""))
                ,(intern (d--g ds :current-name))
                ,@(if (d--g ds :current-body-plus)
                    (list nil)
                    `((:color blue) nil
                        ("`" nil "cancel")
                        ("DEL" ,(if first-call #'primus/body (meq/inconcat (prime--construct-name parent) "/body")) "back")))
                (,(d--g ds :spare-keys) ,next-deino-body ,(d--g ds :next-name) ,@next-deino-settings)))))

And the following use-package setup, where the normalize bit is also used for the :config, :init, and :preface keywords as well:

  ;;;###autoload
  (defalias 'use-package-normalize/:prime 'use-package-normalize-forms)

  ;;;###autoload
  (defun use-package-handler/:prime (name keyword args rest state)
    "Generate prime with NAME for `:prime' KEYWORD.
  ARGS, REST, and STATE are prepared by `use-package-normalize/:prime'."
    (use-package-concat
    (mapcar #'(lambda (def) `(prime ,@def)) args)
    (use-package-process-keywords name rest state)))

  (add-to-list 'use-package-keywords :prime t)

Using the keyword :prime only initializes the last form, as used in this declaration:

(use-package uru :demand t :prime ("u u" uru "uru") ("u m" minoru "minoru"))

Only minoru appears as a key, not uru. All the following methods of initialization work for both:

(mapcar #'(lambda (args) (eval `(prime ,@args))) '(("u u" uru "uru") ("u m" minoru "minoru")))

;; OR

(prime "u u" uru "uru")
(prime "u m" minoru "minoru")

;; OR

(use-package uru :demand t
    :config (mapcar #'(lambda (args) (eval `(prime ,@args)))
                '(("u u" uru "uru") ("u m" minoru "minoru"))))

Interestingly, using :config with just the individual prime calls doesn't work either:

(use-package uru
    :demand t
    :config (prime "u u" uru "uru")
        (prime "u m" minoru "minoru"))
shadowrylander commented 1 year ago

Turns out I hadn't defined the base deino / hydra; I resolved this by changing the first-call conditional to include the declaration if the deino / hydra wasn't already defined, so that further invocations of the prime* function with the same inital key don't clobber it:

(when first-call
    (unless (d--g ds :current-body-plus)
        (eval `(defdeino ,(intern (d--g ds :current-name)) (:color blue) nil ("`" nil "cancel"))))
    (eval `(defdeino+
            ,(intern (meta--construct-name name))
            nil
            (,(d--g ds :carkeys)
                ,(d--g ds :current-body)
                ,(symbol-name (d--g ds :current-body))))))