Subject: [PATCH] Add new optional keywords to use-package

From: Elijah Gabe Pérez <[email-masked]>
Date: Sun, 27 Jul 2025 14:18:57 -0600

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.

* :custom-face* Like :custom-face but override the face specs.
  This closes bug#77928 dilemma.

* :defvar-keymap Define a new keymap or override an existent one.

* :doc Document the use-package declaration instead using comments.

* :tag Like :doc, but for categorizing.
  (This and :doc does nothing)

* :emacs<, :emacs<=, :emacs=, :emacs>, :emacs>=, shorthand for
  `:if (version...)`

* :hook-suffix Changes `use-package-hook-name-suffix' value only in the
  current use-package declaration.

* :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.

* :local-set Bind variables locally when entering to a mode or to a hook(s).
  An alternative to  `:hook (hook . (lambda () (setq-local ...)))`

* :setopt Like :custom, but can also bind plain variables.
  This uses the `setopt` function for bind the variables.

* :which-key-replacement A simple way to set which-key replacements.

>From 356eef67922b2a4339ccde858e0bc1ede9947f54 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#)

* 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.
---
 lisp/use-package/use-package-core.el          |  16 ++
 .../use-package-x/use-package-x-advice.el     |  78 +++++++
 .../use-package-x-custom-face*.el             |  56 +++++
 .../use-package-x-defvar-keymap.el            |  77 +++++++
 .../use-package-x/use-package-x-docs.el       |  66 ++++++
 .../use-package-x-emacs-versions.el           | 100 +++++++++
 .../use-package-x/use-package-x-hook+.el      | 211 ++++++++++++++++++
 .../use-package-x/use-package-x-local-set.el  |  91 ++++++++
 .../use-package-x/use-package-x-setopt.el     |  72 ++++++
 .../use-package-x-which-key-replacement.el    | 126 +++++++++++
 10 files changed, 893 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/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el
index 65b9485b4e3..b0e2d699507 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
@@ -236,6 +247,11 @@ use-package-defaults
 
 (defcustom use-package-merge-key-alist
   '((:if    . (lambda (new old) `(and ,new ,old)))
+    (:emacs< . (lambda (new old) `(and ,new ,old)))
+    (:emacs<= . (lambda (new old) `(and ,new ,old)))
+    (:emacs= . (lambda (new old) `(and ,new ,old)))
+    (:emacs> . (lambda (new old) `(and ,new ,old)))
+    (:emacs>= . (lambda (new old) `(and ,new ,old)))
     (:after . (lambda (new old) `(:all ,new ,old)))
     (:defer . (lambda (new old) old))
     (:bind  . (lambda (new old) (append new (list :break) old))))
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..46605934f65
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-advice.el
@@ -0,0 +1,78 @@
+;;; use-package-x-advice.el --- :advice keyword 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 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; 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..ec43abe508c
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-custom-face*.el
@@ -0,0 +1,56 @@
+;;; 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; 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..30a5a0979c0
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-defvar-keymap.el
@@ -0,0 +1,77 @@
+;;; 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
+;;          ... you can define multiple in the same list
+;;          :map another-mode-map
+;;          "C-c foo" #'bar
+;;          "C-c foo2" #'bar2)
+;;
+;; To use it load this library in your init file:
+;;
+;;   (require 'use-package-x-defvar-keymap)
+
+;;; Code:
+
+;;; Requires
+(require 'cl-lib)
+(require 'use-package)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:defvar-keymap (_name keyword args)
+  (use-package-as-one (symbol-name keyword) args
+    (lambda (label arg)
+      (unless (eq (car arg) :map)
+        (use-package-error
+         (concat label
+                 " value must be (:map <keymap> <string> #'<function> ...)"
+                 " ensure `:map <keymap>' is in the list.")))
+      ;; For some reason this is returned as
+      ;; (nil (...))
+      ;; so, only return the list we want.
+      (cdr (use-package-split-list-at-keys :map arg)))))
+
+;;;###autoload
+(defun use-package-handler/:defvar-keymap (name _keyword args rest state)
+  (use-package-concat
+   (mapcar
+    (lambda (elt)
+      `(defvar-keymap ,(car elt)
+         ,@(cdr 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..9ff30c79b1e
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-docs.el
@@ -0,0 +1,66 @@
+;;; use-package-x-docs.el --- :doc :tags 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 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; 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..53b138d9c49
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-emacs-versions.el
@@ -0,0 +1,100 @@
+;;; 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; 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..398bfb016bd
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-hook+.el
@@ -0,0 +1,211 @@
+;;; use-package-x-hook+.el --- :hook+ 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 keywords for
+;; `use-package':
+;;
+;; * :hook-suffix
+;;   Change `use-package-hook-name-suffix' value to only the current
+;;   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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; 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..69f6dbd75a9
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-local-set.el
@@ -0,0 +1,91 @@
+;;; 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 'cl-lib)
+(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-create-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-create-hook x (cddr elt)))
+               hook)
+            (list (use-package-x-create-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-create-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..1dac29034eb
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-setopt.el
@@ -0,0 +1,72 @@
+;;; 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:setopt (_name keyword args)
+  "Normalize :setopt keyword, ensure the values in ARGS are valid."
+  (mapcar
+   (lambda (elt)
+     (use-package-as-one (symbol-name keyword) (list elt)
+       (lambda (label arg)
+         (unless (and (consp arg) (use-package-non-nil-symbolp (car arg)))
+           (use-package-error
+            (concat label
+                    " must be a (<symbol> [<optional value>])"
+                    " or a list of these")))
+         arg)))
+   args))
+
+;;;###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..04ec914a515
--- /dev/null
+++ b/lisp/use-package/use-package-x/use-package-x-which-key-replacement.el
@@ -0,0 +1,126 @@
+;;; use-package-x-which-key-replacement.el --- :which-key-replacement keyword 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 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 'cl-lib)
+(require 'use-package)
+
+
+
+;;; Functions
+
+;;;###autoload
+(defun use-package-normalize/:which-key-replacement (_name keyword args)
+  (let ((arg args)
+        args*)
+    (while arg
+      (let ((x (car arg)))
+        (cond
+         ;; CONS-CELL
+         ((and (consp x)
+               (stringp (car x)))
+          (setq args* (nconc args* (list x)))
+          (setq arg (cdr arg)))
+         ;; KEYWORDS:
+         ;;   :map KEYMAP
+         ;;   :mode MAJOR-MODE
+         ((and (memq (car x) '(:keymap :mode))
+               (symbolp (cadr x)))
+          (setq args* (nconc args* `(,x)))
+          (setq arg (cdr arg)))
+         (t
+          ;; Error!
+          (use-package-error
+           (concat (symbol-name keyword)
+                   " values must be a (<string> . <string>)"
+                   " or (:keymap <symbol> (<string> <string>"
+                   " <a `which-key-add-keymap-based-replacements' valid replacement>) ...)"
+                   " or (:mode <symbol> (<string> . <string>))"))))))
+    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.