parameter, but are now done as an extension to `rest`. Please see
`use-package-handler/:bind` for a canonical example.
-- For extension authors, if you add a keyword to `use-package-keywords` whose
- presence should indicate deferred loading, please also add it to
- `use-package-deferring-keywords`.
-
### Other changes
- Upgrade license to GPL 3.
it is loaded, `helm-descbinds` itself is not loaded until the user presses
`C-h b`.
+- For extension authors, if you add a keyword to `use-package-keywords` whose
+ presence should indicate deferred loading, please also add it to
+ `use-package-deferring-keywords`. Note that this is a bit of a sledgehammer,
+ in that the mere presence of these keywords implies deferred loading. For a
+ more subtle approach, see the new `use-package-autoloads/<KEYWORD>` support
+ mentioned in the next bullet.
+
+- For extension authors, if you wish deferred loading to possibly occur,
+ create functions named `use-package-autoloads/<KEYWORD>` for each keyword
+ that you define, returning an alist of the form `(SYMBOL . TYPE)` of symbols
+ to be autoloaded. `SYMBOL` should be an interactive function, and `TYPE` the
+ smybol `command`, but this functionality may be extended in future. These
+ autoloads are established if deferred loading is to happen.
+
+- If you specify a lambda form rather than a function symbol in any of the
+ constructs that *might* introduce autoloads: `:bind`, `:bind*`,
+ `:interpreter`, `:mode`, `:magic`, `:magic-fallback`, and `:hook`: then
+ deferred loading will no longer be implied, since there's nothing to
+ associate an autoload with that could later load the module. In these cases,
+ it will be as if you'd specified `:demand t`, in order to ensure the lambda
+ form is able to execute in the context of the loaded package.
+
- For extension authors, there is a new customization variable
`use-package-merge-key-alist` that specifies how values passed to multiple
occurences of the same key should be merged into a single value, during
:group 'use-package)
(defcustom use-package-deferring-keywords
- '(:bind
- :bind*
- :bind-keymap
+ '(:bind-keymap
:bind-keymap*
- :interpreter
- :mode
- :magic
- :magic-fallback
- :hook
:commands)
- "Unless `:demand' is used, keywords in this list imply deferred loading."
+ "Unless `:demand' is used, keywords in this list imply deferred loading.
+The reason keywords like `:hook' are not in this list is that
+they only imply deferred loading if they reference actual
+function symbols that can be autoloaded from the module; whereas
+the default keywords provided here always defer loading unless
+otherwise requested."
:type '(repeat symbol)
:group 'use-package)
(and use-package-always-demand
(not (plist-member args :defer))
(not (plist-member args :demand))))))
- "Alist of default values for `use-package' keywords.
+ "Default values for specified `use-package' keywords.
Each entry in the alist is a list of three elements. The first
element is the `use-package' keyword and the second is a form
that can be evaluated to get the default value. The third element
(xs (use-package-split-list #'keywordp (cdr input)))
(args (car xs))
(tail (cdr xs))
- (normalizer (intern (concat "use-package-normalize/"
- (symbol-name keyword))))
+ (normalizer
+ (intern-soft (concat "use-package-normalize/"
+ (symbol-name keyword))))
(arg (and (functionp normalizer)
(funcall normalizer name keyword args))))
(if (memq keyword use-package-keywords)
(setq args (use-package-plist-maybe-put
args (nth 0 spec) (eval (nth 1 spec))))))
+ ;; Determine any autoloads implied by the keywords used.
+ (let ((iargs args)
+ commands)
+ (while iargs
+ (when (keywordp (car iargs))
+ (let ((autoloads
+ (intern-soft (concat "use-package-autoloads/"
+ (symbol-name (car iargs))))))
+ (when (functionp autoloads)
+ (setq commands
+ ;; jww (2017-12-07): Right now we just ignored the type of
+ ;; the autoload being requested, and assume they are all
+ ;; `command'.
+ (append (mapcar
+ #'car
+ (funcall autoloads name-symbol (car iargs)
+ (cadr iargs)))
+ commands)))))
+ (setq iargs (cddr iargs)))
+ (when commands
+ (setq args
+ ;; Like `use-package-plist-append', but removing duplicates.
+ (plist-put args :commands
+ (delete-dups
+ (append commands (plist-get args :commands)))))))
+
;; If byte-compiling, pre-load the package so all its symbols are in
;; scope. This is done by prepending statements to the :preface.
(when (bound-and-true-p byte-compile-current-file)
use-package-deferring-keywords)))
(setq args (append args '(:defer t))))
+ ;; The :load keyword overrides :no-require
(when (and (plist-member args :load)
(plist-member args :no-require))
(setq args (use-package-plist-delete args :no-require)))
+ ;; If at this point no :load, :defer or :no-require has been seen, then
+ ;; :load the package itself.
(when (and (not (plist-member args :load))
(not (plist-member args :defer))
(not (plist-member args :no-require)))
(t v)))
(defun use-package-normalize-commands (args)
- "Map over ARGS of the form ((_ . F) ...).
-Normalizing functional F's and returning a list of F's
-representing symbols (that may need to be autloaded)."
- (let ((nargs (mapcar
- #'(lambda (x)
- (if (consp x)
- (cons (car x)
- (use-package-normalize-function (cdr x)))
- x)) args)))
- (cons nargs
- (delete
- nil (mapcar
- #'(lambda (x)
- (and (consp x)
- (use-package-non-nil-symbolp (cdr x))
- (cdr x))) nargs)))))
+ "Map over ARGS of the form ((_ . F) ...), normalizing functional F's."
+ (mapcar #'(lambda (x)
+ (if (consp x)
+ (cons (car x) (use-package-normalize-function (cdr x)))
+ x))
+ args))
(defun use-package-normalize-mode (name keyword args)
"Normalize arguments for keywords which add regexp/mode pairs to an alist."
#'use-package-recognize-function
name)))
+(defun use-package-autoloads-mode (name keyword args)
+ (mapcar
+ #'(lambda (x) (cons (cdr x) 'command))
+ (cl-remove-if-not #'(lambda (x)
+ (and (consp x)
+ (use-package-non-nil-symbolp (cdr x))))
+ args)))
+
+(defun use-package-handle-mode (name alist args rest state)
+ "Handle keywords which add regexp/mode pairs to an alist."
+ (use-package-concat
+ (use-package-process-keywords name rest state)
+ `((ignore
+ ,@(mapcar
+ #'(lambda (thing)
+ `(add-to-list
+ ',alist
+ ',(cons (use-package-normalize-regex (car thing))
+ (cdr thing))))
+ (use-package-normalize-commands args))))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Statistics
;;;; :interpreter
-(defun use-package-handle-mode (name alist args rest state)
- "Handle keywords which add regexp/mode pairs to an alist."
- (let* ((result (use-package-normalize-commands args))
- (nargs (car result))
- (commands (cdr result)))
- (use-package-concat
- (use-package-process-keywords name
- (use-package-sort-keywords
- (use-package-plist-append rest :commands commands))
- state)
- `((ignore
- ,@(mapcar
- #'(lambda (thing)
- `(add-to-list
- ',alist
- ',(cons (use-package-normalize-regex (car thing))
- (cdr thing))))
- nargs))))))
-
(defalias 'use-package-normalize/:interpreter 'use-package-normalize-mode)
+(defalias 'use-package-autoloads/:interpreter 'use-package-autoloads-mode)
(defun use-package-handler/:interpreter (name keyword arg rest state)
(use-package-handle-mode name 'interpreter-mode-alist arg rest state))
;;;; :mode
(defalias 'use-package-normalize/:mode 'use-package-normalize-mode)
+(defalias 'use-package-autoloads/:mode 'use-package-autoloads-mode)
(defun use-package-handler/:mode (name keyword arg rest state)
(use-package-handle-mode name 'auto-mode-alist arg rest state))
;;;; :magic
(defalias 'use-package-normalize/:magic 'use-package-normalize-mode)
+(defalias 'use-package-autoloads/:magic 'use-package-autoloads-mode)
(defun use-package-handler/:magic (name keyword arg rest state)
(use-package-handle-mode name 'magic-mode-alist arg rest state))
;;;; :magic-fallback
(defalias 'use-package-normalize/:magic-fallback 'use-package-normalize-mode)
+(defalias 'use-package-autoloads/:magic-fallback 'use-package-autoloads-mode)
(defun use-package-handler/:magic-fallback (name keyword arg rest state)
(use-package-handle-mode name 'magic-fallback-mode-alist arg rest state))
#'use-package-recognize-function
name label arg))))
+(defalias 'use-package-autoloads/:hook 'use-package-autoloads-mode)
+
(defun use-package-handler/:hook (name keyword args rest state)
"Generate use-package custom keyword code."
- (let* ((result (use-package-normalize-commands args))
- (nargs (car result))
- (commands (cdr result)))
- (use-package-concat
- (use-package-process-keywords name
- (use-package-sort-keywords
- (use-package-plist-append rest :commands commands))
- state)
- `((ignore
- ,@(cl-mapcan
- #'(lambda (def)
- (let ((syms (car def))
- (fun (cdr def)))
- (when fun
- (mapcar
- #'(lambda (sym)
- `(add-hook
- (quote ,(intern
- (concat (symbol-name sym)
- use-package-hook-name-suffix)))
- (function ,fun)))
- (if (use-package-non-nil-symbolp syms) (list syms) syms)))))
- nargs))))))
+ (use-package-concat
+ (use-package-process-keywords name rest state)
+ `((ignore
+ ,@(cl-mapcan
+ #'(lambda (def)
+ (let ((syms (car def))
+ (fun (cdr def)))
+ (when fun
+ (mapcar
+ #'(lambda (sym)
+ `(add-hook
+ (quote ,(intern
+ (concat (symbol-name sym)
+ use-package-hook-name-suffix)))
+ (function ,fun)))
+ (if (use-package-non-nil-symbolp syms) (list syms) syms)))))
+ (use-package-normalize-commands args))))))
;;;; :commands
(ert-deftest use-package-test/:hook-1 ()
(let ((byte-compile-current-file t))
- (should
- (equal
- (expand-minimally
- (use-package foo
- :bind (("C-a" . key))
- :hook (hook . fun)))
- '(progn
- (eval-and-compile
- (eval-when-compile
- (with-demoted-errors
- "Cannot load foo: %S" nil
- (load "foo" nil t))))
- (unless (fboundp 'fun)
- (autoload #'fun "foo" nil t))
- (eval-when-compile
- (declare-function fun "foo"))
- (unless (fboundp 'key)
- (autoload #'key "foo" nil t))
- (eval-when-compile
- (declare-function key "foo"))
- (ignore
- (add-hook 'hook-hook #'fun))
- (ignore
- (bind-keys :package foo ("C-a" . key))))))))
+ (match-expansion
+ (use-package foo
+ :bind (("C-a" . key))
+ :hook (hook . fun))
+ `(progn
+ (eval-and-compile
+ (eval-when-compile
+ (with-demoted-errors
+ "Cannot load foo: %S" nil
+ (load "foo" nil t))))
+ (unless
+ (fboundp 'key)
+ (autoload #'key "foo" nil t))
+ (eval-when-compile
+ (declare-function key "foo"))
+ (unless
+ (fboundp 'fun)
+ (autoload #'fun "foo" nil t))
+ (eval-when-compile
+ (declare-function fun "foo"))
+ (ignore
+ (add-hook 'hook-hook #'fun))
+ (ignore
+ (bind-keys :package foo ("C-a" . key)))))))
(ert-deftest use-package-test/:hook-2 ()
(match-expansion
#'(lambda nil
(bind-key "\ 3\16" erefactor-map emacs-lisp-mode-map)))))))))
+(ert-deftest use-package-test/:hook-6 ()
+ (match-expansion
+ (use-package erefactor
+ :load-path "foo"
+ :after elisp-mode
+ :hook (emacs-lisp-mode . function))
+ `(progn
+ (eval-and-compile
+ (add-to-list 'load-path "/Users/johnw/.emacs.d/foo"))
+ (eval-after-load 'elisp-mode
+ '(progn
+ (unless (fboundp 'function)
+ (autoload #'function "erefactor" nil t))
+ (ignore
+ (add-hook 'emacs-lisp-mode-hook #'function)))))))
+
+(ert-deftest use-package-test/:hook-7 ()
+ (match-expansion
+ (use-package erefactor
+ :load-path "foo"
+ :after elisp-mode
+ :hook (emacs-lisp-mode . (lambda () (function))))
+ `(progn
+ (eval-and-compile
+ (add-to-list 'load-path "/Users/johnw/.emacs.d/foo"))
+ (eval-after-load 'elisp-mode
+ '(progn
+ (require 'erefactor nil nil)
+ (ignore
+ (add-hook 'emacs-lisp-mode-hook #'(lambda nil (function)))))))))
+
(ert-deftest use-package-test-normalize/:custom ()
(flet ((norm (&rest args)
(apply #'use-package-normalize/:custom