Subject: Re: bug#79106: [PATCH] Add new optional keywords to use-package

From: Elijah Gabe Pérez <[email-masked]>
Date: Tue, 29 Jul 2025 00:33:36 -0600

Philip Kaludercic <[email-masked]> writes:

> Elijah Gabe Pérez <[email-masked]> writes:
>
> I guess it would be good to discuss the content of the patch now that we
> are considering to merge it.

Sure.

>> Tags: patch
>>
>> As discussed in
>> https://lists.gnu.org/archive/html/emacs-devel/2025-07/msg00354.html
>>
>> This patch add new optional keywords to use-package, most of them are
>> inspired by suggestions from use-package bug-tracker on github
>>
>> This includes following keywords:
>>
>> * :advice Add or remove function advices.
>
> I did not understand how this uses :advice to create something
> use-package specific that has an advantage over `define-advice'.

This acts more to `advice-add` than `define-advice`.  It is intended to
add advices from some packages, such as consult, which has the
`consult-register-window` function, that is intended to override
`register-preview`.

This is also inspired by leaf.el which have `:advice` and `:advice-remove`.

>> * :defvar-keymap Define a new keymap or override an existent one.
>
> Why is this something that a user would be interested in when
> configuring a package?

:defvar-keymap is meant for people that wants to use their own
keybindings in a mode (such as cua-mode) instead mode keybindings.

In my case it's useful when i want to use a few keybindings from a mode,
not all.

>> * :doc Document the use-package declaration instead using comments.
>>
>> * :tag Like :doc, but for categorizing.
>>   (This and :doc does nothing)
>
> Can you explain what these tags are good for?

At this moment, both :tag (and :doc) are useless, the only use I can see
for it would be as an identifier for when someone wants to export their
configuration as a web page.  I decided include them because I've seen
some configurations that use them (either from leaf.el or a personal
extension).

Perhaps in the future they can be modified to give a better use, but at
this moment I can't think of any.

>> * :hook+ Like default :hook, but allows to set the hook depth and set
>>   multiple functions in the hook (or for each hook).
>>
>>   Although I tried to make it compatible with :hook, there may be some
>>   things I missed, so I decided to put it separately instead of merging
>>   it with :hook.  In addition, its syntax is more complex compared to
>>   :hook.
>
> I can imagine that the name is confusing to beginners, and also question
> if it might be cleaner to just advise users to use `add-hook' if they
> want to do something specific and more technical.

Yeah, but :hook+ already covers most of the basics for add-hook.

It can put the same depth for all the hooks in a region:

  (:depth -10
    (prog-mode . whitespace-mode) ; <- -10
    (xml-mode . electric-pair-mode)) ; <- -10

>> * :setopt Like :custom, but can also bind plain variables.
>>   This uses the `setopt` function for bind the variables.
>
> Would it make sense to re-use :custom but modify the behaviour with a
> user-option?

I didn't quite understand this, did you mean to modify :custom for
handle this instead? In that case, it would better to ask to John.

> Wrt. the patch, I would still merge the files into one, instead of
> having each keyword in a separate file.

This is inconvenient, since if someone wants to use only one of these
keywords, they would have to load the entire file.

Keeping them separate prevents this and is also easier to maintain and
move them to ELPA (if it is decided in the future).

Also I'm sending a fixed patch, I'm not sure if the additional keywords
should be documented in the manual.

>From 0f66925a9028c74402ec1797925f8d6b3053552d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= <[email-masked]>
Date: Sat, 26 Jul 2025 22:43:38 -0600
Subject: [PATCH] Add new optional keywords to use-package

This includes new optional keywords to use-packages, most of
them are intended to be used for a more advanced customization,
thus they are not loaded.  (bug#)

* etc/NEWS: Announce changes.
* lisp/use-package/use-package-core.el: Add new keywords to
keywords list.
* lisp/use-package/use-package-x/use-package-x-advice.el:
* lisp/use-package/use-package-x/use-package-x-custom-face*.el:
* lisp/use-package/use-package-x/use-package-x-defvar-keymap.el:
* lisp/use-package/use-package-x/use-package-x-docs.el:
* lisp/use-package/use-package-x/use-package-x-emacs-versions.el:
* lisp/use-package/use-package-x/use-package-x-hook+.el:
* lisp/use-package/use-package-x/use-package-x-local-set.el:
* lisp/use-package/use-package-x/use-package-x-setopt.el:
* lisp/use-package/use-package-x/use-package-x-which-key-replacement.el:
New files.
---
 etc/NEWS                                      |  46 ++++
 lisp/use-package/use-package-core.el          |  11 +
 .../use-package-x/use-package-x-advice.el     |  77 +++++++
 .../use-package-x-custom-face*.el             |  55 +++++
 .../use-package-x-defvar-keymap.el            |  71 ++++++
 .../use-package-x/use-package-x-docs.el       |  65 ++++++
 .../use-package-x-emacs-versions.el           |  99 +++++++++
 .../use-package-x/use-package-x-hook+.el      | 210 ++++++++++++++++++
 .../use-package-x/use-package-x-local-set.el  |  90 ++++++++
 .../use-package-x/use-package-x-setopt.el     |  70 ++++++
 .../use-package-x-which-key-replacement.el    | 111 +++++++++
 11 files changed, 905 insertions(+)
 create mode 100644 lisp/use-package/use-package-x/use-package-x-advice.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-custom-face*.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-defvar-keymap.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-docs.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-emacs-versions.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-hook+.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-local-set.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-setopt.el
 create mode 100644 lisp/use-package/use-package-x/use-package-x-which-key-replacement.el

diff --git a/etc/NEWS b/etc/NEWS
index a81d1105443..345e2116d93 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2419,6 +2419,52 @@ A major mode based on the tree-sitter library for editing "go.work"
 files.  If tree-sitter is properly set-up by the user, it can be
 enabled for files named "go.work".
 
+** New packages for additional keywords for 'use-package'
+The following includes additional and optional keywords for
+'use-package':
+
+*** New package use-package-x-advice
+This package includes the ':advice' keyword, which allows to add or
+remove function advices.
+
+*** New package use-package-x-custom-face*
+This package includes the ':custom-face*' keyword, which acts like
+':custom-face', but overrides the face specs.
+
+*** New package use-package-x-defvar-keymap
+This package includes the ':defvar-keymap' keyword, which allows to
+define a new keymap or override an existent one.
+
+*** New package use-package-x-docs
+This package includes the ':doc' and ':tag' keywords, which allows to
+document or tag the use-package declaration instead of using comments.
+
+*** New package use-package-x-emacs-versions
+This package includes the ':emacs<', ':emacs<=', ':emacs=', ':emacs>'
+and ':emacs>=' keywords, which initialize the use-package declaration
+only for an Emacs version or versions.
+
+*** New package use-package-x-hook+
+This package includes the following keywords:
+
+':hook-suffix', that allows to change the 'use-package-hook-name-suffix'
+value to only the use-package declaration.
+
+':hook+', which acts like ':hook' but also supports hooks depths and set
+multiple functions.
+
+*** New package use-package-x-local-set
+This package includes the ':local-set' keyword, that allows to bind
+variables locally to a hook.
+
+*** New package use-package-x-setopt
+This package includes the ':setopt' keyword, which acts like ':custom'
+but can also bind plain variables.
+
+*** New package use-package-x-which-key-replacement
+This package includes the ':which-key-replacement' keyword, which allows
+to define and replace key descriptions.
+
 
 * Incompatible Lisp Changes in Emacs 31.1
 
diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el
index 65b9485b4e3..e38280d670c 100644
--- a/lisp/use-package/use-package-core.el
+++ b/lisp/use-package/use-package-core.el
@@ -77,22 +77,33 @@ use-package-keywords
     :defines
     :functions
     :preface
+    :hook-suffix
     :if :when :unless
+    :emacs< :emacs<= :emacs=
+    :emacs> :emacs>=
     :vc
     :no-require
     :catch
     :after
+    :setopt
+    :local-set
     :custom
     :custom-face
+    :custom-face*
     :bind
     :bind*
     :bind-keymap
     :bind-keymap*
+    :defvar-keymap
+    :which-key-replacement
+    :advice
     :interpreter
     :mode
     :magic
     :magic-fallback
     :hook
+    :hook+
+    :doc :tag
     ;; Any other keyword that also declares commands to be autoloaded (such as
     ;; :bind) must appear before this keyword.
     :commands
diff --git a/lisp/use-package/use-package-x/use-package-x-advice.el b/lisp/use-package/use-package-x/use-package-x-advice.el
new file mode 100644
index 00000000000..b9839953950
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-advice.el
@@ -0,0 +1,77 @@
+;;; use-package-x-advice.el --- :advice keyword definition -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+;;
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :advice
+;;   Add or remove function advices.
+;;   This is a shorthand for ":init (advice-[add|remove] ...)"
+;;
+;;    :advice
+;;    (:add my-function <ADVICE-ADD-HOW> other-function)
+;;    (:remove other-function my-function)
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-advice)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:advice (_name keyword args)
+  (mapcar
+   (lambda (elt)
+     (use-package-as-one (symbol-name keyword) (list elt)
+       (lambda (label arg)
+         (unless (and (consp arg) (memq (car arg) '(:add :remove)))
+           (use-package-error
+            (concat label
+                    " must be a (:add <symbol> <advice-how-keywords> <symbol>)"
+                    " or (:remove <symbol> <symbol>)"
+                    " or list of these" label)))
+         arg)))
+   args))
+
+;;;###autoload
+(defun use-package-handler/:advice (name _keyword args rest state)
+  (use-package-concat
+   (mapcar
+    (lambda (elt)
+      (if (eq (car elt) :add)
+          (let ((fn (nth 1 elt))
+                (how (nth 2 elt))
+                (fn2 (nth 3 elt)))
+            `(advice-add (function ,fn) ,how (function ,fn2)))
+        `(advice-remove (function ,(nth 1 elt)) (function ,(nth 2 elt)))))
+    args)
+   (use-package-process-keywords name rest state)))
+
+
+(provide 'use-package-x-advice)
+;;; use-package-x-advice.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-custom-face*.el b/lisp/use-package/use-package-x/use-package-x-custom-face*.el
new file mode 100644
index 00000000000..7d482ba3737
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-custom-face*.el
@@ -0,0 +1,55 @@
+;;; use-package-x-custom-face*.el --- :custom-face* keyword definition  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :custom-face*
+;;   Like :custom-face but override the face specs.
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-custom-face*)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+;;;###autoload
+(defalias 'use-package-normalize/:custom-face* 'use-package-normalize/:custom-face)
+
+;;;###autoload
+(defun use-package-handler/:custom-face* (name _keyword args rest state)
+  (use-package-concat
+   (mapcar #'(lambda (def)
+               `(progn
+                  (apply #'face-spec-set (backquote ,def))
+                  (put ',(car def) 'face-modified t)))
+           args)
+   (use-package-process-keywords name rest state)))
+
+
+(provide 'use-package-x-custom-face*)
+;;; use-package-x-custom-face*.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-defvar-keymap.el b/lisp/use-package/use-package-x/use-package-x-defvar-keymap.el
new file mode 100644
index 00000000000..4253f5a02aa
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-defvar-keymap.el
@@ -0,0 +1,71 @@
+;;; use-package-x-defvar-keymap.el --- :defvar-keymap keyword definition  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :defvar-keymap
+;;    Define a new keymap or override an existent one.
+;;    The form is similar to `defvar-keymap' arguments, which see.
+;;
+;;    :defvar-keymap
+;;    (:map my-mode-map
+;;          "C-x foo" #'bar
+;;          "C-x foo2" #'bar2)
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-defvar-keymap)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:defvar-keymap (_name keyword args)
+  (use-package-as-one (symbol-name keyword) args
+    (lambda (label arg)
+      (unless (or (eq (car arg) :map) (listp arg))
+        (use-package-error
+         (concat label
+                 " value must be"
+                 " (:map <keymap> <string> #'<function> ...)")))
+      (if (eq (car arg) :map)
+          (list arg)
+        arg))))
+
+;;;###autoload
+(defun use-package-handler/:defvar-keymap (name _keyword args rest state)
+  (use-package-concat
+   (mapcar
+    (lambda (elt)
+      `(defvar-keymap ,(cadr elt)
+         ,@(cddr elt)))
+    args)
+   (use-package-process-keywords name rest state)))
+
+(provide 'use-package-x-defvar-keymap)
+;;; use-package-x-defvar-keymap.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-docs.el b/lisp/use-package/use-package-x/use-package-x-docs.el
new file mode 100644
index 00000000000..2c586ffd340
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-docs.el
@@ -0,0 +1,65 @@
+;;; use-package-x-docs.el --- :doc :tag keywords definitions  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+;; This file provides the following extra keywords for
+;; `use-package':
+;;
+;; * :doc
+;;   Document your use-package declaration instead of using comments.
+;;
+;;     :doc <anything>...
+;;
+;; * :tag
+;;
+;;   Categorize your use-package declaration, this doesn't have any
+;;   effect.
+;;
+;;     :tag <anything>...
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-docs)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+(defun use-package-x-handle-always (name _keyword _args rest state)
+  "Ignore ARGS, do nothing, and process the next `use-package' keyword."
+  (use-package-concat (use-package-process-keywords name rest state)))
+
+;;; :doc
+;;;###autoload
+(defalias 'use-package-normalize/:doc 'always)
+;;;###autoload
+(defalias 'use-package-handler/:doc 'use-package-x-handle-always)
+
+;;; :tag
+;;;###autoload
+(defalias 'use-package-normalize/:tag 'always)
+;;;###autoload
+(defalias 'use-package-handler/:tag 'use-package-x-handle-always)
+
+(provide 'use-package-x-docs)
+;;; use-package-x-docs.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-emacs-versions.el b/lisp/use-package/use-package-x/use-package-x-emacs-versions.el
new file mode 100644
index 00000000000..08d4185569a
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-emacs-versions.el
@@ -0,0 +1,99 @@
+;;; use-package-x-emacs-versions.el --- :emacs< :emacs<= :emacs= :emacs> :emacs>= definitions  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keywords for
+;; `use-package':
+;;
+;; * :emacs< <number-or-string>
+;;   Shorthand for :if (version< emacs-version <version>)
+;;
+;; * :emacs<= <number-or-string>
+;;   Shorthand for :if (version<= emacs-version <version>)
+;;
+;; * :emacs= <number-or-string>
+;;   Shorthand for :if (version= emacs-version <version>)
+;;
+;; * :emacs> <number-or-string>
+;;   Shorthand for :if (version< <version> emacs-version)
+;;
+;; * :emacs>= <number-or-string>
+;;   Shorthand for :if (version<= <version> emacs-version)
+;;
+;; To use them load this library in your init file:
+;;
+;;   (require 'use-package-x-emacs-versions)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+(defun use-package-x-normalize-version (_name keyword args)
+  "Normalize version in ARGS.
+If version is a number, convert it to a string."
+  (use-package-only-one (symbol-name keyword) args
+    (lambda (_label version)
+      (let ((version (if (numberp version) (number-to-string version) version)))
+        (pcase keyword
+          (:emacs<  `(version< emacs-version ,version))
+          (:emacs<= `(version<= emacs-version ,version))
+          (:emacs=  `(version= emacs-version ,version))
+          (:emacs>  `(version< ,version emacs-version))
+          (:emacs>= `(version<= ,version emacs-version)))))))
+
+;;; :emacs<
+
+;;;###autoload
+(defalias 'use-package-normalize/:emacs< 'use-package-x-normalize-version)
+;;;###autoload
+(defalias 'use-package-handler/:emacs< 'use-package-handler/:if)
+
+;;; :emacs<=
+;;;###autoload
+(defalias 'use-package-normalize/:emacs<= 'use-package-x-normalize-version)
+;;;###autoload
+(defalias 'use-package-handler/:emacs<= 'use-package-handler/:if)
+
+;;; :emacs=
+;;;###autoload
+(defalias 'use-package-normalize/:emacs= 'use-package-x-normalize-version)
+;;;###autoload
+(defalias 'use-package-handler/:emacs= 'use-package-handler/:if)
+
+;;; :emacs>
+
+;;;###autoload
+(defalias 'use-package-normalize/:emacs> 'use-package-x-normalize-version)
+;;;###autoload
+(defalias 'use-package-handler/:emacs> 'use-package-handler/:if)
+
+;;; :emacs>=
+
+;;;###autoload
+(defalias 'use-package-normalize/:emacs>= 'use-package-x-normalize-version)
+;;;###autoload
+(defalias 'use-package-handler/:emacs>= 'use-package-handler/:if)
+
+(provide 'use-package-x-emacs-versions)
+;;; use-package-x-emacs-versions.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-hook+.el b/lisp/use-package/use-package-x/use-package-x-hook+.el
new file mode 100644
index 00000000000..e9e32230881
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-hook+.el
@@ -0,0 +1,210 @@
+;;; use-package-x-hook+.el --- :hook+ :hook-suffix keywords definitions  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keywords for
+;; `use-package':
+;;
+;; * :hook-suffix
+;;   Change `use-package-hook-name-suffix' value to only the use-package
+;;   declaration.
+;;
+;; * :hook+
+;;   An enchanted :hook which supports hooks depths and
+;;   set multiple functions.
+;;
+;;   The hook depth is provided using the sub-keyword :depth
+;;   e.g.
+;;     :hook+
+;;     (:depth 10
+;;       (major-mode . my-func))
+;;
+;;   To set multiple functions (including lambdas) to the
+;;   hook or list of hooks you can use the :multi keyword:
+;;
+;;   :hook (my-hook-or-list-of-hooks
+;;           . (:multi fn1 fn2 (lambda () ..) ...))
+;;
+;;   This also supports the :hook valid forms:
+;;
+;;     :hook+
+;;     (:depth 10
+;;       (major-mode . my-func-or-lambda)
+;;       (mode mode2 mode3)
+;;       single-mode)
+;;     ((hook1 hook2) . (:multi fn1 fn2 fn3))
+;;     ...and also use it as a normal :hook...
+;;     (major-mode . my-func-or-lambda)
+;;     (mode mode2 mode3)
+;;     single-mode
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-hook+)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+
+;;; :hook-suffix
+;;;###autoload
+(defun use-package-normalize/:hook-suffix (_name keyword args)
+  "Normalize :hook-prefix, ensure it's a string or nil."
+  (use-package-only-one (symbol-name keyword) args
+    (lambda (label arg)
+      (unless (or (stringp arg) (null arg))
+        (use-package-error
+         (concat label " must be nil or a string")))
+      arg)))
+
+;;;###autoload
+(defun use-package-handler/:hook-suffix (name _keyword arg rest state)
+  "Normalize :hook-prefix.
+Set `use-package-hook-name-suffix' to ARG only in the current
+`use-package' declaration."
+  (dlet ((use-package-hook-name-suffix arg))
+    (use-package-process-keywords name rest state)))
+
+
+
+;;; :hook+
+(defun use-package-x--normalize-pairs (list label name)
+  "Normalize all the pairs in the LIST."
+  (if (ignore-errors (eq (cadar list) :multi))
+      ;; FIXME: There is not a better way to include this into the
+      ;; loop (below), so just return the LIST without normalizing.
+      list
+    (use-package-normalize-pairs
+     (lambda (k)
+       (or (use-package-non-nil-symbolp k)
+           (and k (let ((every t))
+                    (while (and every k)
+                      (if (and (consp k)
+                               (use-package-non-nil-symbolp (car k)))
+                          (setq k (cdr k))
+                        (setq every nil)))
+                    every))))
+     #'use-package-recognize-function
+     (if (string-suffix-p "-mode" (symbol-name name))
+         name
+       (intern (concat (symbol-name name) "-mode")))
+     label list)))
+
+;;;###autoload
+(defun use-package-normalize/:hook+ (name keyword args)
+  "Normalize :hook+ keyword.
+Return the proper list or ARGS for `use-package-autoloads/:hook+'
+and `use-package-handler/:hook+'."
+  (use-package-as-one (symbol-name keyword) args
+    (lambda (label arg)
+      (unless (or (use-package-non-nil-symbolp arg) (consp arg))
+        (use-package-error
+         (concat label
+                 " must be"
+                 " a <symbol> or a list or these"
+                 " or (<symbol or list of symbols> . <function>)"
+                 " or (<symbol or list of symbols> . (:multi <functions> ...))"
+                 " or (:depth <depth number> <any of previous forms>...)")))
+      ;; Check if :depth is defined and return (<n-depth>
+      ;; <the-hook-form>) for the handler function, otherwise just
+      ;; return the normal list
+      (mapcan
+       (lambda (elt)
+         (if (eq (car-safe elt) :depth)
+             (mapcar (lambda (pairs)
+                       (cons (nth 1 elt) (list pairs)))
+                     (use-package-x--normalize-pairs (cddr elt) label name))
+           (use-package-x--normalize-pairs (list elt) label name)))
+       args))))
+
+(defun use-package-x--normalize-commands (list)
+  "Like `use-package-normalize-commands' but for supporting the :depth keyword."
+  (mapcar (lambda (x)
+            (cond ((numberp (car x))
+                   (cons (car x) (use-package-normalize-commands (cdr x))))
+                  ((consp x)
+                   (cons (car x) (use-package-normalize-function (cdr x))))
+                  (t x)))
+          list))
+
+(defun use-package-autoloads/:hook+ (_name _keyword args)
+  "Like `use-package-autoloads-mode' but supports the :depth and :multi keywords."
+  (setq args
+        (mapcar
+         (lambda (list)
+           (cond
+            ;; return the list without the depth number
+            ((integerp (car list)) (cadr list))
+            (t list)))
+         args))
+
+  (cl-loop for x in args
+           for multi = (ignore-errors (eq (cadr x) :multi))
+           when (and (consp x) (or (use-package-non-nil-symbolp (cdr x)) multi))
+           if multi append
+           (cl-loop for cm in (cddr x)
+                    if (use-package-non-nil-symbolp cm)
+                    collect (cons cm 'command))
+           else collect (cons (cdr x) 'command)))
+
+(defun use-package-x--create-hook (sym fun depth)
+  "Return the proper `add-hook' for mode SYM with FUN and DEPTH (if there is)."
+  (let ((symname (symbol-name sym)))
+    (if (and (boundp sym)
+             (not (string-suffix-p "-mode" symname)))
+        `(add-hook (quote ,sym) (function ,fun) ,depth)
+      `(add-hook
+        (quote ,(intern
+                 (concat symname use-package-hook-name-suffix)))
+        (function ,fun)
+        ,depth))))
+
+;;;###autoload
+(defun use-package-handler/:hook+ (name _keyword args rest state)
+  "Hadle :hook+ keyword.
+Add the proper `add-hook' to use-package expanded form,
+compared to normal :hook, this handle hook depths and multiple
+functions."
+  (use-package-concat
+   (use-package-process-keywords name rest state)
+   (cl-mapcan
+    (lambda (def)
+      (let* ((car (car def))
+             (depth (if (integerp car) car))
+             (syms (if depth (caadr def) car))
+             (fun (if depth (cdadr def) (cdr def)))
+             (multi-p (and (listp fun) (eq (car fun) :multi))))
+        (when fun
+          (cl-loop
+           for mode in (use-package-hook-handler-normalize-mode-symbols syms)
+           if multi-p append ; For `:multi'
+           (cl-loop for fn in (cdr fun) collect
+                    (use-package-x--create-hook mode fn depth))
+           else
+           collect (use-package-x--create-hook mode fun depth)))))
+    (use-package-x--normalize-commands args))))
+
+(provide 'use-package-x-hook+)
+;;; use-package-x-hook+.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-local-set.el b/lisp/use-package/use-package-x/use-package-x-local-set.el
new file mode 100644
index 00000000000..0074ecd3718
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-local-set.el
@@ -0,0 +1,90 @@
+;;; use-package-x-local-set.el --- :local-set keyword definition  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :local-set
+;;
+;;   Set variables locally when entering to a mode.
+;;
+;;     :local-set
+;;     (<variable> <value> ...) <- Automatically set to package mode hook
+;;     --or--
+;;     (:hook <hook-or-list-of-hooks> <- Set it only to hook(s),
+;;       <variable> <value>              compared to :hook and :hook+
+;;       ...)                            you need to add the full hook name
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-local-set)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package)
+
+
+
+;;;###autoload
+(defun use-package-normalize/:local-set (_name keyword args)
+  "Normalize :local-set keyword, ensure ARGS are valid."
+  (use-package-as-one (symbol-name keyword) args
+    (lambda (label arg)
+      (let ((arg-car (car arg)))
+        (unless (or (eq arg-car :hook) (consp arg-car) (use-package-non-nil-symbolp arg-car))
+          (use-package-error
+           (concat label
+                   " can be (<symbol> <value>...)"
+                   " or (:hook <symbol-hook> <symbol> <value> ...)")))
+        args))))
+
+(defun use-package-x--local-hook (hook values)
+  `(add-hook (quote ,hook)
+             (lambda (&rest _)
+               (setq-local ,@values))))
+
+;;;###autoload
+(defun use-package-handler/:local-set (name _keyword args rest state)
+  (use-package-concat
+   (mapcan
+    (lambda (elt)
+      (if (eq (car elt) :hook)
+          ;; :hook
+          (if-let* ((hook (nth 1 elt))
+                    ((listp hook)))
+              (mapcar
+               (lambda (x) (use-package-x--local-hook x (cddr elt)))
+               hook)
+            (list (use-package-x--local-hook hook (cddr elt))))
+        ;; plain variables
+        (let* ((sym-name (symbol-name name))
+               (hook (intern (concat
+                              sym-name
+                              (if (string-suffix-p "-mode" sym-name)
+                                  "-hook"
+                                "-mode-hook")))))
+          (list (use-package-x--local-hook hook elt)))))
+    args)
+   (use-package-process-keywords name rest state)))
+
+(provide 'use-package-x-local-set)
+;;; use-package-x-local-set.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-setopt.el b/lisp/use-package/use-package-x/use-package-x-setopt.el
new file mode 100644
index 00000000000..df63448b4f8
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-setopt.el
@@ -0,0 +1,70 @@
+;;; use-package-x-setopt.el --- :setopt keyword definition  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :setopt
+;;   Similar to :custom, but can also bind plain variables.
+;;   This uses the `setopt' function, for bind the variables.
+;;
+;;     :setopt (<variable> [value]) ...
+;;
+;;   VALUE is optional, if omitted, VARIABLE will be set to nil.
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-setopt)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:setopt (_name keyword args)
+  "Normalize :setopt keyword, ensure the values in ARGS are valid."
+  (use-package-as-one (symbol-name keyword) args
+    (lambda (label arg)
+      (unless (listp arg)
+        (use-package-error
+         (concat label
+                 " must be a (<symbol> [value])"
+                 " or a list of these")))
+      (if (use-package-non-nil-symbolp (car arg))
+          (list arg)
+        arg))))
+
+;;;###autoload
+(defun use-package-handler/:setopt (name _keyword args rest state)
+  (use-package-concat
+   `((setopt
+      ,@(mapcan
+         (lambda (list) (list (car list) (nth 1 list)))
+         args)))
+   (use-package-process-keywords name rest state)))
+
+(provide 'use-package-x-setopt)
+;;; use-package-x-setopt.el ends here
diff --git a/lisp/use-package/use-package-x/use-package-x-which-key-replacement.el b/lisp/use-package/use-package-x/use-package-x-which-key-replacement.el
new file mode 100644
index 00000000000..186cf20bc7b
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-which-key-replacement.el
@@ -0,0 +1,111 @@
+;;; use-package-x-which-key-replacement.el --- :which-key-replacement keyword definition  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Elias G. Perez <[email-masked]>
+
+;; 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:
+
+;; This file provides the following extra keyword for
+;; `use-package':
+;;
+;; * :which-key-replacement
+;;   A simple way to set your which-key replacement keybindings.
+;;
+;;   The form can be a any of these options:
+;;
+;;   A cons-cell which will be passed as arguments for
+;;   which-key-add-key-based-replacements
+;;
+;;     :which-key-replacement
+;;     ("C-c d" . "foo")
+;;     ("C-x 8" . '("unicode" . "Unicode keys"))
+;;     ...
+;;
+;;   A list which specifies a keymap, passed as arguments for
+;;   which-key-add-keymap-based-replacements
+;;
+;;     :which-key-replacement
+;;     (:keymap global-map
+;;              ("C-x a" "pretty name" command-name)
+;;              -- To add a prefix command, enclose it into a list--
+;;              ("C-c a" "prefix" (prefix-map))
+;;              ...)
+;;
+;;   Or a list which specifies a major mode, passed as arguments for
+;;   which-key-add-major-mode-key-based-replacements
+;;
+;;     :which-key-replacement
+;;     (:mode major-mode
+;;            ("C-c f" . "foo")
+;;            ("C-c e" . '("prefix" . "Pretty name"))
+;;            ...)
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-which-key-replacement)
+
+;;; Code:
+
+;;; Requires
+(require 'use-package-core)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:which-key-replacement (_name keyword args)
+  "Normalize :which-key-replacement keyword."
+  (dolist (arg args)
+    (let ((x (car-safe arg)))
+      (unless (or x
+                  (and (consp arg) (stringp x))
+                  (and (memq x '(:keymap :mode)) (symbolp (cadr arg))))
+        (use-package-error
+         (concat (symbol-name keyword)
+                 " values must be (<string> . <string-or-list>)"
+                 " or (:keymap <symbol> (<string> <string> <symbol-or-list>) ...)"
+                 " or (:mode <symbol> (<string> . <string-or-list>))")))))
+  args)
+
+;;;###autoload
+(defun use-package-handler/:which-key-replacement (name _keyword args rest state)
+  (use-package-concat
+   (use-package-process-keywords name rest state)
+   (mapcar
+    (lambda (elt)
+      (let ((car (car elt)))
+        (cond ((stringp car)
+               `(which-key-add-key-based-replacements ,car ,(cdr elt)))
+              ((eq :keymap car)
+               `(which-key-add-keymap-based-replacements ,(nth 1 elt)
+                  ,@(cl-loop for (key string command) in (cddr elt)
+                             append `(,key
+                                      (quote
+                                       ,(cons string
+                                              ;; For keymaps or functions
+                                              (if (consp command)
+                                                  (symbol-value (car command))
+                                                command)))))))
+              ((eq :mode car)
+               `(which-key-add-major-mode-key-based-replacements ,(nth 1 elt)
+                  ,@(cl-loop for (key . replacement) in (cddr elt)
+                             append `(,key ,replacement)))))))
+    args)))
+
+(provide 'use-package-x-which-key-replacement)
+;;; use-package-x-which-key-replacement.el ends here
-- 
2.50.1

-- 
- E.G via Gnus and Org.