From 0c148eb777e05650c6e951170f365c767594c149 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 3 Dec 2017 11:43:16 -0800 Subject: [PATCH] Move functions around for better logical grouping --- lisp/use-package/use-package.el | 1139 +++++++++++++++---------------- 1 file changed, 546 insertions(+), 593 deletions(-) diff --git a/lisp/use-package/use-package.el b/lisp/use-package/use-package.el index fe307235c99..16a1df6dc3f 100644 --- a/lisp/use-package/use-package.el +++ b/lisp/use-package/use-package.el @@ -42,67 +42,105 @@ (require 'bind-key) (require 'bytecomp) (require 'cl-lib) + (eval-when-compile (require 'cl) - (require 'regexp-opt)) - -(declare-function package-installed-p "package") -(declare-function package-read-all-archive-contents "package" ()) + (require 'regexp-opt) -(defconst use-package-version "2.4" - "This version of use-package.") + (declare-function package-installed-p "package") + (declare-function package-read-all-archive-contents "package" ())) (defgroup use-package nil "A use-package declaration for simplifying your `.emacs'." :group 'startup) +(defconst use-package-version "2.4" + "This version of use-package.") + (defcustom use-package-verbose nil "Whether to report about loading and configuration details. If you customize this, then you should require the `use-package' feature in files that use `use-package', even if these files only -contain compiled expansions of the macros. If you don't do so, +contain compiled expansions of the macros. If you don't do so, then the expanded macros do their job silently." - :type '(choice (const :tag "Quiet" nil) (const :tag "Verbose" t) + :type '(choice (const :tag "Quiet" nil) + (const :tag "Verbose" t) (const :tag "Debug" debug)) :group 'use-package) (defcustom use-package-check-before-init nil "If non-nil, check that package exists before executing its `:init' block. -The check is performed by looking for the module using `locate-library'." +This check is performed by calling `locate-library'." :type 'boolean :group 'use-package) (defcustom use-package-always-defer nil - "If non-nil, assume `:defer t` unless `:demand t` is given." + "If non-nil, assume `:defer t' unless `:demand' is used. +See also `use-package-defaults', which uses this value." :type 'boolean :group 'use-package) (defcustom use-package-always-demand nil - "If non-nil, assume `:demand t` unless `:defer t` is given." + "If non-nil, assume `:demand t' unless `:defer' is used. +See also `use-package-defaults', which uses this value." :type 'boolean :group 'use-package) (defcustom use-package-always-ensure nil - "Treat every package as though it had specified `:ensure SEXP`." + "Treat every package as though it had specified using `:ensure SEXP'. +See also `use-package-defaults', which uses this value." :type 'sexp :group 'use-package) (defcustom use-package-always-pin nil - "Treat every package as though it had specified `:pin SYM`." + "Treat every package as though it had specified using `:pin SYM'. +See also `use-package-defaults', which uses this value." :type 'symbol :group 'use-package) +(defcustom use-package-defaults + '(;; this '(t) has special meaning; see `use-package-handler/:config' + (:config '(t) t) + (:init nil t) + (:defer use-package-always-defer + (lambda (args) + (and use-package-always-defer + (not (plist-member args :defer)) + (not (plist-member args :demand))))) + (:demand use-package-always-demand + (lambda (args) + (and use-package-always-demand + (not (plist-member args :defer)) + (not (plist-member args :demand))))) + (:ensure use-package-always-ensure + (lambda (args) + (and use-package-always-ensure + (not (plist-member args :load-path))))) + (:pin use-package-always-pin use-package-always-pin)) + "Alist of default values for `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 +is a form that can be evaluated to determine whether or not to +assign a default value; if it evaluates to nil, then the default +value is not assigned even if the keyword is not present in the +`use-package' form. This third element may also be a function, in +which case it receives the list of keywords (in normalized form), +and should return nil or t according to whether defaulting should +be attempted." + :type `(repeat + (list (choice :tag "Keyword" + ,@(mapcar #'(lambda (k) (list 'const k)) + use-package-keywords)) + (choice :tag "Default value" sexp) + (choice :tag "Enable if non-nil" sexp function))) + :group 'use-package) + (defcustom use-package-minimum-reported-time 0.1 "Minimal load time that will be reported. - -Note that `use-package-verbose' has to be set to t, for anything -to be reported at all. - -If you customize this, then you should require the `use-package' -feature in files that use `use-package', even if these files only -contain compiled expansions of the macros. If you don't do so, -then the expanded macros do their job silently." +Note that `use-package-verbose' has to be set to a non-nil value +for anything to be reported at all." :type 'number :group 'use-package) @@ -120,10 +158,15 @@ This way, you can add to these hooks before evaluation of a `use-package` declaration, and exercise some control over what happens. -Note that if either `pre-init' hooks returns a nil value, that -block's user-supplied configuration is not evaluated, so be -certain to return `t' if you only wish to add behavior to what -the user specified." +NOTE: These hooks are run even if the user does not specify an +`:init' or `:config' block, and they will happen at the regular +time when initialization and configuration would have been +performed. + +NOTE: If the `pre-init' hook return a nil value, that block's +user-supplied configuration is not evaluated, so be certain to +return `t' if you only wish to add behavior to what the user +had specified." :type 'boolean :group 'use-package) @@ -160,30 +203,64 @@ the user specified." :config :diminish :delight) - "Establish which keywords are valid, and the order they are processed in. - -Note that `:disabled' is special, in that it causes nothing at all to happen, -even if the rest of the use-package declaration is incorrect." + "The set of valid keywords, in the order they are processed in. +The order of this list is *very important*, so it is only +advisable to insert new keywords, never to delete or reorder +them. Further, attention should be paid to the NEWS.md if the +default order ever changes, as they may have subtle effects on +the semantics of use-package declarations and may necessitate +changing where you had inserted a new keyword earlier. + +Note that `:disabled' is special in this list, as it causes +nothing at all to happen, even if the rest of the use-package +declaration is incorrect." :type '(repeat symbol) :group 'use-package) (defcustom use-package-expand-minimally nil "If non-nil, make the expanded code as minimal as possible. This disables: + - Printing to the *Messages* buffer of slowly-evaluating forms - - Capture of load errors (normally redisplayed as warnings) + - Capturing of load errors (normally redisplayed as warnings) - Conditional loading of packages (load failures become errors) -The only advantage is that, if you know your configuration works, -then your byte-compiled init file is as minimal as possible." + +The main advantage to this variable is that, if you know your +configuration works, it will make the byte-compiled file as +minimal as possible. It can also help with reading macro-expanded +definitions, to understand the main intent of what's happening." :type 'boolean :group 'use-package) +(defcustom use-package-form-regexp-eval + `(concat ,(eval-when-compile + (concat "^\\s-*(" + (regexp-opt '("use-package" "require") t) + "\\s-+\\(")) + (or (bound-and-true-p lisp-mode-symbol-regexp) + "\\(?:\\sw\\|\\s_\\|\\\\.\\)+") "\\)") + "Sexp providing regexp for finding use-package forms in user files. +This is used by `use-package-jump-to-package-form' and +`use-package-enable-imenu-support'." + :type 'sexp + :group 'use-package) + (defcustom use-package-enable-imenu-support nil "If non-nil, adjust `lisp-imenu-generic-expression' to include support for finding `use-package' and `require' forms. Must be set before loading use-package." :type 'boolean + :set + #'(lambda (sym value) + (if value + (eval-after-load 'lisp-mode + `(add-to-list 'lisp-imenu-generic-expression + (list "Packages" ,use-package-form-regexp-eval 2))) + (eval-after-load 'lisp-mode + `(setq lisp-imenu-generic-expression + (remove (list "Packages" ,use-package-form-regexp-eval 2) + lisp-imenu-generic-expression))))) :group 'use-package) (defcustom use-package-ensure-function 'use-package-ensure-elpa @@ -204,125 +281,71 @@ The default value uses package.el to install the package." (function :tag "Custom")) :group 'use-package) -(defcustom use-package-defaults - '((:config '(t) t) ; this '(t) has special meaning; see - ; the handler for :config - (:init nil t) - (:defer use-package-always-defer - (lambda (args) - (and use-package-always-defer - (not (plist-member args :defer)) - (not (plist-member args :demand))))) - (:demand use-package-always-demand - (lambda (args) - (and use-package-always-demand - (not (plist-member args :defer)) - (not (plist-member args :demand))))) - (:ensure use-package-always-ensure - (lambda (args) - (and use-package-always-ensure - (not (plist-member args :load-path))))) - (:pin use-package-always-pin use-package-always-pin)) - "Alist of default values for `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 -is a form that can be evaluated to determine whether or not to -assign a default value; if it evaluates to nil, then the default -value is not assigned even if the keyword is not present in the -`use-package' form. This third element may also be a function, in -which case it receives the list of keywords (in normalized form), -and should return nil or t according to whether defaulting should -be attempted." - :type `(repeat - (list (choice :tag "Keyword" - ,@(mapcar #'(lambda (k) (list 'const k)) - use-package-keywords)) - (choice :tag "Default value" sexp) - (choice :tag "Enable if non-nil" sexp function))) - :group 'use-package) - -;;; jww (2017-12-02): This should be in the :set for the option. -(when use-package-enable-imenu-support - (eval-after-load 'lisp-mode - `(let ((sym-regexp (or (bound-and-true-p lisp-mode-symbol-regexp) - "\\(?:\\sw\\|\\s_\\|\\\\.\\)+"))) - (add-to-list - 'lisp-imenu-generic-expression - (list "Packages" - (concat "^\\s-*(" - ,(eval-when-compile - (regexp-opt '("use-package" "require") t)) - "\\s-+\\(" sym-regexp "\\)") - 2))))) - -(defvar use-package-form-regexp "^\\s-*(\\s-*use-package\\s-+\\_<%s\\_>" - "Regexp used in `use-package-jump-to-package-form' to find use -package forms in user files.") - -(defun use-package-find-require (package) - "Find file that required PACKAGE by searching -`load-history'. Returns an absolute file path or nil if none is -found." - (catch 'suspect - (dolist (filespec load-history) - (dolist (entry (cdr filespec)) - (when (equal entry (cons 'require package)) - (throw 'suspect (car filespec))))))) +(defconst use-package-font-lock-keywords + '(("(\\(use-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?" + (1 font-lock-keyword-face) + (2 font-lock-constant-face nil t)))) -(defun use-package-jump-to-package-form (package) - "Attempt to find and jump to the `use-package' form that loaded -PACKAGE. This will only find the form if that form actually -required PACKAGE. If PACKAGE was previously required then this -function will jump to the file that originally required PACKAGE -instead." - (interactive (list (completing-read "Package: " features))) - (let* ((package (if (stringp package) (intern package) package)) - (requiring-file (use-package-find-require package)) - file location) - (if (null requiring-file) - (user-error "Can't find file that requires this feature.") - (setq file (if (string= (file-name-extension requiring-file) "elc") - (concat (file-name-sans-extension requiring-file) ".el") - requiring-file)) - (when (file-exists-p file) - (find-file-other-window file) - (save-excursion - (goto-char (point-min)) - (setq location - (re-search-forward - (format use-package-form-regexp package) nil t))) - (if (null location) - (message "No use-package form found.") - (goto-char location) - (beginning-of-line)))))) +(font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Utility functions ;; -(defun use-package-as-symbol (string-or-symbol) +(defsubst use-package-error (msg) + "Report MSG as an error, so the user knows it came from this package." + (error "use-package: %s" msg)) + +(defsubst use-package-concat (&rest elems) + "Delete all empty lists from ELEMS (nil or (list nil)), and append them." + (apply #'append (delete nil (delete (list nil) elems)))) + +(defsubst use-package-non-nil-symbolp (sym) + (and sym (symbolp sym))) + +(defsubst use-package-as-symbol (string-or-symbol) "If STRING-OR-SYMBOL is already a symbol, return it. Otherwise convert it to a symbol and return that." (if (symbolp string-or-symbol) string-or-symbol (intern string-or-symbol))) -(defun use-package-as-string (string-or-symbol) +(defsubst use-package-as-string (string-or-symbol) "If STRING-OR-SYMBOL is already a string, return it. Otherwise convert it to a string and return that." (if (stringp string-or-symbol) string-or-symbol (symbol-name string-or-symbol))) +(defsubst use-package-regex-p (re) + "Return t if RE is some regexp-like thing." + (or (and (listp re) (eq (car re) 'rx)) + (stringp re))) + +(defun use-package-normalize-regex (re) + "Given some regexp-like thing, resolve it down to a regular expression." + (cond + ((and (listp re) (eq (car re) 'rx)) (eval re)) + ((stringp re) re) + (t (error "Not recognized as regular expression: %s" re)))) + +(defsubst use-package-is-pair (x car-pred cdr-pred) + "Return non-nil if X is a cons satisfying the given predicates. +CAR-PRED and CDR-PRED are applied to X's `car' and `cdr', +respectively." + (and (consp x) + (funcall car-pred (car x)) + (funcall cdr-pred (cdr x)))) + (defun use-package-as-mode (string-or-symbol) "If STRING-OR-SYMBOL ends in `-mode' (or its name does), return it as a symbol. Otherwise, return it as a symbol with `-mode' appended." (let ((string (use-package-as-string string-or-symbol))) - (intern (if (string-match "-mode\\'" string) string + (intern (if (string-match "-mode\\'" string) + string (concat string "-mode"))))) -(defun use-package-load-name (name &optional noerror) +(defsubst use-package-load-name (name &optional noerror) "Return a form which will load or require NAME depending on whether it's a string or symbol." (if (stringp name) @@ -372,31 +395,18 @@ ARGS is a list of forms, so `((foo))' if only `foo' is being called." (if no-require body (use-package-with-elapsed-timer - (format "Loading package %s" name) - `((if (not ,(use-package-load-name name t)) - (ignore - (display-warning 'use-package - (format "Cannot load %s" ',name) - :error)) - ,@body)))))) - -(defsubst use-package-error (msg) - "Report MSG as an error, so the user knows it came from this package." - (error "use-package: %s" msg)) - -(defsubst use-package-plist-maybe-put (plist property value) - "Add a VALUE for PROPERTY to PLIST, if it does not already exist." - (if (plist-member plist property) - plist - (plist-put plist property value))) - -(defsubst use-package-plist-cons (plist property value) - "Cons VALUE onto the head of the list at PROPERTY in PLIST." - (plist-put plist property (cons value (plist-get plist property)))) + (format "Loading package %s" name) + `((if (not ,(use-package-load-name name t)) + (ignore + (display-warning 'use-package + (format "Cannot load %s" ',name) + :error)) + ,@body)))))) -(defsubst use-package-plist-append (plist property value) - "Append VALUE onto the front of the list at PROPERTY in PLIST." - (plist-put plist property (append value (plist-get plist property)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Property lists +;; (defun use-package-plist-delete (plist property) "Delete PROPERTY from PLIST. @@ -420,6 +430,20 @@ This is in contrast to merely setting it to 0." plist (cddr plist)))) p)) +(defsubst use-package-plist-maybe-put (plist property value) + "Add a VALUE for PROPERTY to PLIST, if it does not already exist." + (if (plist-member plist property) + plist + (plist-put plist property value))) + +(defsubst use-package-plist-cons (plist property value) + "Cons VALUE onto the head of the list at PROPERTY in PLIST." + (plist-put plist property (cons value (plist-get plist property)))) + +(defsubst use-package-plist-append (plist property value) + "Append VALUE onto the front of the list at PROPERTY in PLIST." + (plist-put plist property (append value (plist-get plist property)))) + (defun use-package-split-list (pred xs) (let ((ys (list nil)) (zs (list nil)) flip) (dolist (x xs) @@ -432,6 +456,11 @@ This is in contrast to merely setting it to 0." (nconc ys (list x))))) (cons (cdr ys) (cdr zs)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Keywords +;; + (defun use-package-keyword-index (keyword) (loop named outer with index = 0 @@ -455,25 +484,6 @@ This is in contrast to merely setting it to 0." (setq result (cons (car x) (cons (cdr x) result)))) result))) -(defsubst use-package-concat (&rest elems) - "Delete all empty lists from ELEMS (nil or (list nil)), and append them." - (apply #'append (delete nil (delete (list nil) elems)))) - -(defsubst use-package-non-nil-symbolp (sym) - (and sym (symbolp sym))) - -(defconst use-package-font-lock-keywords - '(("(\\(use-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?" - (1 font-lock-keyword-face) - (2 font-lock-constant-face nil t)))) - -(font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Keyword processing -;; - (defun use-package-unalias-keywords (name args) (setq args (cl-nsubstitute :if :when args)) (let (temp) @@ -554,28 +564,6 @@ This is in contrast to merely setting it to 0." ;; Sort the list of keywords based on the order of `use-package-keywords'. (use-package-sort-keywords args))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Normalization functions -;; - -(defsubst use-package-regex-p (re) - "Return t if RE is some regexp-like thing." - (or (and (listp re) - (eq (car re) 'rx)) - (stringp re))) - -(defun use-package-normalize-regex (re) - "Given some regexp-like thing, resolve it down to a regular expression." - (cond - ((and (listp re) - (eq (car re) 'rx)) - (eval re)) - ((stringp re) - re) - (t - (error "Not recognized as regular expression: %s" re)))) - (defun use-package-normalize-plist (name input &optional plist merge-function) "Given a pseudo-plist, normalize it to a regular plist. The normalized key/value pairs from input are added to PLIST, @@ -638,17 +626,7 @@ next value for the STATE." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;;; :disabled -;; - -(defalias 'use-package-normalize/:disabled 'ignore) - -(defun use-package-handler/:disabled (name keyword arg rest state) - (use-package-process-keywords name rest state)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :pin +;;; Arguments ;; (defun use-package-only-one (label args f) @@ -664,15 +642,280 @@ next value for the STATE." (put 'use-package-only-one 'lisp-indent-function 'defun) -(defun use-package-normalize/:pin (name keyword args) - (use-package-only-one (symbol-name keyword) args - #'(lambda (label arg) - (cond - ((stringp arg) arg) - ((use-package-non-nil-symbolp arg) (symbol-name arg)) - (t - (use-package-error - ":pin wants an archive name (a string)")))))) +(defun use-package-as-one (label args f &optional allow-empty) + "Call F on the first element of ARGS if it has one element, or all of ARGS. +If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." + (declare (indent 1)) + (if (if args + (and (listp args) (listp (cdr args))) + allow-empty) + (if (= (length args) 1) + (funcall f label (car args)) + (funcall f label args)) + (use-package-error + (concat label " wants a non-empty list")))) + +(put 'use-package-as-one 'lisp-indent-function 'defun) + +(defun use-package-memoize (f arg) + "Ensure the macro-expansion of F applied to ARG evaluates ARG +no more than once." + (let ((loaded (gensym "use-package--loaded")) + (result (gensym "use-package--result")) + (next (gensym "use-package--next"))) + `((lexical-let (,loaded ,result) + (lexical-let ((,next (lambda () + (if ,loaded + ,result + (setq ,loaded t) + (setq ,result ,arg))))) + ,(funcall f ``(funcall ,,next))))))) + +(defsubst use-package-normalize-value (label arg) + "Normalize a value." + (cond ((null arg) nil) + ((eq t arg) t) + ((use-package-non-nil-symbolp arg) + `(symbol-value ',arg)) + ((functionp arg) + `(funcall #',arg)) + (t arg))) + +(defun use-package-normalize-symbols (label arg &optional recursed) + "Normalize a list of symbols." + (cond + ((use-package-non-nil-symbolp arg) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (car (use-package-normalize-symbols label x t))) arg)) + (t + (use-package-error + (concat label " wants a symbol, or list of symbols"))))) + +(defun use-package-normalize-symlist (name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-symbols)) + +(defun use-package-normalize-recursive-symbols (label arg) + "Normalize a list of symbols." + (cond + ((use-package-non-nil-symbolp arg) + arg) + ((and (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (use-package-normalize-recursive-symbols label x)) + arg)) + (t + (use-package-error + (concat label " wants a symbol, or nested list of symbols"))))) + +(defun use-package-normalize-recursive-symlist (name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-recursive-symbols)) + +(defun use-package-normalize-paths (label arg &optional recursed) + "Normalize a list of filesystem paths." + (cond + ((and arg (or (use-package-non-nil-symbolp arg) (functionp arg))) + (let ((value (use-package-normalize-value label arg))) + (use-package-normalize-paths label (eval value)))) + ((stringp arg) + (let ((path (if (file-name-absolute-p arg) + arg + (expand-file-name arg user-emacs-directory)))) + (list path))) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) + (car (use-package-normalize-paths label x t))) arg)) + (t + (use-package-error + (concat label " wants a directory path, or list of paths"))))) + +(defun use-package-normalize-predicate (name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + #'use-package-normalize-value))) + +(defun use-package-normalize-form (label args) + "Given a list of forms, return it wrapped in `progn'." + (unless (listp (car args)) + (use-package-error (concat label " wants a sexp or list of sexps"))) + (mapcar #'(lambda (form) + (if (and (consp form) + (eq (car form) 'use-package)) + (macroexpand form) + form)) args)) + +(defun use-package-normalize-forms (name keyword args) + (use-package-normalize-form (symbol-name keyword) args)) + +(defun use-package-normalize-pairs + (key-pred val-pred name label arg &optional recursed) + "Normalize a list of pairs. +KEY-PRED and VAL-PRED are predicates recognizing valid keys and +values, respectively. +If RECURSED is non-nil, recurse into sublists." + (cond + ((funcall key-pred arg) + (list (cons arg (use-package-as-symbol name)))) + ((use-package-is-pair arg key-pred val-pred) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (let (last-item) + (mapcar + #'(lambda (x) + (prog1 + (let ((ret (use-package-normalize-pairs + key-pred val-pred name label x t))) + ;; Currently, the handling of keyword arguments by + ;; `use-package' and `bind-key' is non-uniform and + ;; undocumented. As a result, `use-package-normalize-pairs' + ;; (as it is currently implemented) does not correctly handle + ;; the keyword-argument syntax of `bind-keys'. A permanent + ;; solution to this problem will require a careful + ;; consideration of the desired keyword-argument interface + ;; for `use-package' and `bind-key'. However, in the + ;; meantime, we have a quick patch to fix a serious bug in + ;; the handling of keyword arguments. Namely, the code below + ;; would normally unwrap lists that were passed as keyword + ;; arguments (for example, the `:filter' argument in `:bind') + ;; without the (not (keywordp last-item)) clause. See #447 + ;; for further discussion. + (if (and (listp ret) + (not (keywordp last-item))) + (car ret) + ret)) + (setq last-item x))) arg))) + (t arg))) + +(defun use-package-recognize-function (v &optional binding additional-pred) + "A predicate that recognizes functional constructions: + nil + sym + 'sym + (quote sym) + #'sym + (function sym) + (lambda () ...) + '(lambda () ...) + (quote (lambda () ...)) + #'(lambda () ...) + (function (lambda () ...))" + (pcase v + ((and x (guard (if binding + (symbolp x) + (use-package-non-nil-symbolp x)))) t) + (`(,(or `quote `function) + ,(pred use-package-non-nil-symbolp)) t) + ((and x (guard (if binding (commandp x) (functionp x)))) t) + (_ (and additional-pred + (funcall additional-pred v))))) + +(defun use-package-normalize-function (v) + "Reduce functional constructions to one of two normal forms: + sym + #'(lambda () ...)" + (pcase v + ((pred symbolp) v) + (`(,(or `quote `function) + ,(and sym (pred symbolp))) sym) + (`(lambda . ,_) v) + (`(quote ,(and lam `(lambda . ,_))) lam) + (`(function ,(and lam `(lambda . ,_))) lam) + (_ 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))))) + +(defun use-package-normalize-binder (name keyword args) + (use-package-as-one (symbol-name keyword) args + #'(lambda (label arg) + (unless (consp arg) + (use-package-error + (concat label " a ( . )" + " or list of these"))) + (use-package-normalize-pairs + #'(lambda (k) + (pcase k + ((pred stringp) t) + ((pred vectorp) t))) + #'(lambda (v) (use-package-recognize-function v t #'stringp)) + name label arg)))) + +;;;###autoload +(defun use-package-autoload-keymap (keymap-symbol package override) + "Loads PACKAGE and then binds the key sequence used to invoke +this function to KEYMAP-SYMBOL. It then simulates pressing the +same key sequence a again, so that the next key pressed is routed +to the newly loaded keymap. + +This function supports use-package's :bind-keymap keyword. It +works by binding the given key sequence to an invocation of this +function for a particular keymap. The keymap is expected to be +defined by the package. In this way, loading the package is +deferred until the prefix key sequence is pressed." + (if (not (require package nil t)) + (use-package-error (format "Cannot load package.el: %s" package)) + (if (and (boundp keymap-symbol) + (keymapp (symbol-value keymap-symbol))) + (let* ((kv (this-command-keys-vector)) + (key (key-description kv)) + (keymap (symbol-value keymap-symbol))) + (if override + (bind-key* key keymap) + (bind-key key keymap)) + (setq unread-command-events + (listify-key-sequence kv))) + (use-package-error + (format "use-package: package.el %s failed to define keymap %s" + package keymap-symbol))))) + +(defun use-package-normalize-mode (name keyword args) + "Normalize arguments for keywords which add regexp/mode pairs to an alist." + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-pairs + #'use-package-regex-p + #'use-package-recognize-function + name))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Handlers +;; + +;;;; :disabled + +(defalias 'use-package-normalize/:disabled 'ignore) + +(defun use-package-handler/:disabled (name keyword arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :pin + +(defun use-package-normalize/:pin (name keyword args) + (use-package-only-one (symbol-name keyword) args + #'(lambda (label arg) + (cond + ((stringp arg) arg) + ((use-package-non-nil-symbolp arg) (symbol-name arg)) + (t + (use-package-error + ":pin wants an archive name (a string)")))))) (eval-when-compile (defvar package-pinned-packages) @@ -716,11 +959,10 @@ manually updated package." (push pin-form body)) ; or else wait until runtime. body)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :ensure -;; +;;;; :ensure + (defvar package-archive-contents) + (defun use-package-normalize/:ensure (name keyword args) (if (null args) t @@ -773,89 +1015,29 @@ manually updated package." body)) body)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :if, :when and :unless -;; - -(defsubst use-package-normalize-value (label arg) - "Normalize a value." - (cond ((null arg) nil) - ((eq t arg) t) - ((use-package-non-nil-symbolp arg) - `(symbol-value ',arg)) - ((functionp arg) - `(funcall #',arg)) - (t arg))) +;;;; :if, :when and :unless (defun use-package-normalize-test (name keyword args) (use-package-only-one (symbol-name keyword) args #'use-package-normalize-value)) (defalias 'use-package-normalize/:if 'use-package-normalize-test) -(defalias 'use-package-normalize/:when 'use-package-normalize-test) -(defalias 'use-package-normalize/:unless 'use-package-normalize-test) (defun use-package-handler/:if (name keyword pred rest state) (let ((body (use-package-process-keywords name rest state))) `((when ,pred ,@body)))) +(defalias 'use-package-normalize/:when 'use-package-normalize-test) + (defalias 'use-package-handler/:when 'use-package-handler/:if) +(defalias 'use-package-normalize/:unless 'use-package-normalize-test) + (defun use-package-handler/:unless (name keyword pred rest state) (let ((body (use-package-process-keywords name rest state))) `((unless ,pred ,@body)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :requires -;; - -(defun use-package-as-one (label args f &optional allow-empty) - "Call F on the first element of ARGS if it has one element, or all of ARGS. -If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." - (declare (indent 1)) - (if (if args - (and (listp args) (listp (cdr args))) - allow-empty) - (if (= (length args) 1) - (funcall f label (car args)) - (funcall f label args)) - (use-package-error - (concat label " wants a non-empty list")))) - -(put 'use-package-as-one 'lisp-indent-function 'defun) - -(defun use-package-normalize-symbols (label arg &optional recursed) - "Normalize a list of symbols." - (cond - ((use-package-non-nil-symbolp arg) - (list arg)) - ((and (not recursed) (listp arg) (listp (cdr arg))) - (mapcar #'(lambda (x) (car (use-package-normalize-symbols label x t))) arg)) - (t - (use-package-error - (concat label " wants a symbol, or list of symbols"))))) - -(defun use-package-normalize-symlist (name keyword args) - (use-package-as-one (symbol-name keyword) args - #'use-package-normalize-symbols)) - -(defun use-package-normalize-recursive-symbols (label arg) - "Normalize a list of symbols." - (cond - ((use-package-non-nil-symbolp arg) - arg) - ((and (listp arg) (listp (cdr arg))) - (mapcar #'(lambda (x) (use-package-normalize-recursive-symbols label x)) - arg)) - (t - (use-package-error - (concat label " wants a symbol, or nested list of symbols"))))) - -(defun use-package-normalize-recursive-symlist (name keyword args) - (use-package-as-one (symbol-name keyword) args - #'use-package-normalize-recursive-symbols)) +;;;; :requires (defalias 'use-package-normalize/:requires 'use-package-normalize-symlist) @@ -868,28 +1050,7 @@ If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." `(featurep ',(car requires))) ,@body))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :load-path -;; - -(defun use-package-normalize-paths (label arg &optional recursed) - "Normalize a list of filesystem paths." - (cond - ((and arg (or (use-package-non-nil-symbolp arg) (functionp arg))) - (let ((value (use-package-normalize-value label arg))) - (use-package-normalize-paths label (eval value)))) - ((stringp arg) - (let ((path (if (file-name-absolute-p arg) - arg - (expand-file-name arg user-emacs-directory)))) - (list path))) - ((and (not recursed) (listp arg) (listp (cdr arg))) - (mapcar #'(lambda (x) - (car (use-package-normalize-paths label x t))) arg)) - (t - (use-package-error - (concat label " wants a directory path, or list of paths"))))) +;;;; :load-path (defun use-package-normalize/:load-path (name keyword args) (use-package-as-one (symbol-name keyword) args @@ -902,39 +1063,14 @@ If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." `(eval-and-compile (add-to-list 'load-path ,path))) arg) body))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :no-require -;; - -(defun use-package-normalize-predicate (name keyword args) - (if (null args) - t - (use-package-only-one (symbol-name keyword) args - #'use-package-normalize-value))) +;;;; :no-require (defalias 'use-package-normalize/:no-require 'use-package-normalize-predicate) (defun use-package-handler/:no-require (name keyword arg rest state) (use-package-process-keywords name rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :preface -;; - -(defun use-package-normalize-form (label args) - "Given a list of forms, return it wrapped in `progn'." - (unless (listp (car args)) - (use-package-error (concat label " wants a sexp or list of sexps"))) - (mapcar #'(lambda (form) - (if (and (consp form) - (eq (car form) 'use-package)) - (macroexpand form) - form)) args)) - -(defun use-package-normalize-forms (name keyword args) - (use-package-normalize-form (symbol-name keyword) args)) +;;;; :preface (defalias 'use-package-normalize/:preface 'use-package-normalize-forms) @@ -945,146 +1081,21 @@ If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." `((eval-and-compile ,@arg))) body))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :defines -;; - -(defalias 'use-package-normalize/:defines 'use-package-normalize-symlist) - -(defun use-package-handler/:defines (name keyword arg rest state) - (use-package-process-keywords name rest state)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :functions -;; - -(defalias 'use-package-normalize/:functions 'use-package-normalize-symlist) - -(defun use-package-handler/:functions (name keyword arg rest state) - (use-package-process-keywords name rest state)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :bind, :bind* -;; +;;;; :defines -(defsubst use-package-is-pair (x car-pred cdr-pred) - "Return non-nil if X is a cons satisfying the given predicates. -CAR-PRED and CDR-PRED are applied to X's `car' and `cdr', -respectively." - (and (consp x) - (funcall car-pred (car x)) - (funcall cdr-pred (cdr x)))) +(defalias 'use-package-normalize/:defines 'use-package-normalize-symlist) -(defun use-package-normalize-pairs - (key-pred val-pred name label arg &optional recursed) - "Normalize a list of pairs. -KEY-PRED and VAL-PRED are predicates recognizing valid keys and -values, respectively. -If RECURSED is non-nil, recurse into sublists." - (cond - ((funcall key-pred arg) - (list (cons arg (use-package-as-symbol name)))) - ((use-package-is-pair arg key-pred val-pred) - (list arg)) - ((and (not recursed) (listp arg) (listp (cdr arg))) - (let (last-item) - (mapcar - #'(lambda (x) - (prog1 - (let ((ret (use-package-normalize-pairs - key-pred val-pred name label x t))) - ;; Currently, the handling of keyword arguments by - ;; `use-package' and `bind-key' is non-uniform and - ;; undocumented. As a result, `use-package-normalize-pairs' - ;; (as it is currently implemented) does not correctly handle - ;; the keyword-argument syntax of `bind-keys'. A permanent - ;; solution to this problem will require a careful - ;; consideration of the desired keyword-argument interface - ;; for `use-package' and `bind-key'. However, in the - ;; meantime, we have a quick patch to fix a serious bug in - ;; the handling of keyword arguments. Namely, the code below - ;; would normally unwrap lists that were passed as keyword - ;; arguments (for example, the `:filter' argument in `:bind') - ;; without the (not (keywordp last-item)) clause. See #447 - ;; for further discussion. - (if (and (listp ret) - (not (keywordp last-item))) - (car ret) - ret)) - (setq last-item x))) arg))) - (t arg))) +(defun use-package-handler/:defines (name keyword arg rest state) + (use-package-process-keywords name rest state)) -(defun use-package-recognize-function (v &optional binding additional-pred) - "A predicate that recognizes functional constructions: - nil - sym - 'sym - (quote sym) - #'sym - (function sym) - (lambda () ...) - '(lambda () ...) - (quote (lambda () ...)) - #'(lambda () ...) - (function (lambda () ...))" - (pcase v - ((and x (guard (if binding - (symbolp x) - (use-package-non-nil-symbolp x)))) t) - (`(,(or `quote `function) - ,(pred use-package-non-nil-symbolp)) t) - ((and x (guard (if binding (commandp x) (functionp x)))) t) - (_ (and additional-pred - (funcall additional-pred v))))) +;;;; :functions -(defun use-package-normalize-function (v) - "Reduce functional constructions to one of two normal forms: - sym - #'(lambda () ...)" - (pcase v - ((pred symbolp) v) - (`(,(or `quote `function) - ,(and sym (pred symbolp))) sym) - (`(lambda . ,_) v) - (`(quote ,(and lam `(lambda . ,_))) lam) - (`(function ,(and lam `(lambda . ,_))) lam) - (_ v))) +(defalias 'use-package-normalize/:functions 'use-package-normalize-symlist) -(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))))) +(defun use-package-handler/:functions (name keyword arg rest state) + (use-package-process-keywords name rest state)) -(defun use-package-normalize-binder (name keyword args) - (use-package-as-one (symbol-name keyword) args - #'(lambda (label arg) - (unless (consp arg) - (use-package-error - (concat label " a ( . )" - " or list of these"))) - (use-package-normalize-pairs - #'(lambda (k) - (pcase k - ((pred stringp) t) - ((pred vectorp) t))) - #'(lambda (v) (use-package-recognize-function v t #'stringp)) - name label arg)))) +;;;; :bind, :bind* (defalias 'use-package-normalize/:bind 'use-package-normalize-binder) (defalias 'use-package-normalize/:bind* 'use-package-normalize-binder) @@ -1105,42 +1116,11 @@ representing symbols (that may need to be autloaded)." (defun use-package-handler/:bind* (name keyword arg rest state) (use-package-handler/:bind name keyword arg rest state 'bind-keys*)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :bind-keymap, :bind-keymap* -;; +;;;; :bind-keymap, :bind-keymap* (defalias 'use-package-normalize/:bind-keymap 'use-package-normalize-binder) (defalias 'use-package-normalize/:bind-keymap* 'use-package-normalize-binder) -;;;###autoload -(defun use-package-autoload-keymap (keymap-symbol package override) - "Loads PACKAGE and then binds the key sequence used to invoke -this function to KEYMAP-SYMBOL. It then simulates pressing the -same key sequence a again, so that the next key pressed is routed -to the newly loaded keymap. - -This function supports use-package's :bind-keymap keyword. It -works by binding the given key sequence to an invocation of this -function for a particular keymap. The keymap is expected to be -defined by the package. In this way, loading the package is -deferred until the prefix key sequence is pressed." - (if (not (require package nil t)) - (use-package-error (format "Cannot load package.el: %s" package)) - (if (and (boundp keymap-symbol) - (keymapp (symbol-value keymap-symbol))) - (let* ((kv (this-command-keys-vector)) - (key (key-description kv)) - (keymap (symbol-value keymap-symbol))) - (if override - (bind-key* key keymap) - (bind-key key keymap)) - (setq unread-command-events - (listify-key-sequence kv))) - (use-package-error - (format "use-package: package.el %s failed to define keymap %s" - package keymap-symbol))))) - (defun use-package-handler/:bind-keymap (name keyword arg rest state &optional override) (let ((form @@ -1162,18 +1142,7 @@ deferred until the prefix key sequence is pressed." (defun use-package-handler/:bind-keymap* (name keyword arg rest state) (use-package-handler/:bind-keymap name keyword arg rest state t)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :interpreter -;; - -(defun use-package-normalize-mode (name keyword args) - "Normalize arguments for keywords which add regexp/mode pairs to an alist." - (use-package-as-one (symbol-name keyword) args - (apply-partially #'use-package-normalize-pairs - #'use-package-regex-p - #'use-package-recognize-function - name))) +;;;; :interpreter (defun use-package-handle-mode (name alist args rest state) "Handle keywords which add regexp/mode pairs to an alist." @@ -1197,40 +1166,28 @@ deferred until the prefix key sequence is pressed." (defun use-package-handler/:interpreter (name keyword arg rest state) (use-package-handle-mode name 'interpreter-mode-alist arg rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :mode -;; +;;;; :mode (defalias 'use-package-normalize/:mode 'use-package-normalize-mode) (defun use-package-handler/:mode (name keyword arg rest state) (use-package-handle-mode name 'auto-mode-alist arg rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :magic -;; +;;;; :magic (defalias 'use-package-normalize/:magic 'use-package-normalize-mode) (defun use-package-handler/:magic (name keyword arg rest state) (use-package-handle-mode name 'magic-mode-alist arg rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :magic-fallback -;; +;;;; :magic-fallback (defalias 'use-package-normalize/:magic-fallback 'use-package-normalize-mode) (defun use-package-handler/:magic-fallback (name keyword arg rest state) (use-package-handle-mode name 'magic-fallback-mode-alist arg rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :hook -;; +;;;; :hook (defun use-package-normalize/:hook (name keyword args) (use-package-as-one (symbol-name keyword) args @@ -1273,10 +1230,7 @@ deferred until the prefix key sequence is pressed." (use-package-plist-append rest :commands commands)) state)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :commands -;; +;;;; :commands (defalias 'use-package-normalize/:commands 'use-package-normalize-symlist) @@ -1298,10 +1252,7 @@ deferred until the prefix key sequence is pressed." (delete-dups arg)))) (use-package-process-keywords name rest state))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :defer -;; +;;;; :defer (defalias 'use-package-normalize/:defer 'use-package-normalize-predicate) @@ -1318,10 +1269,7 @@ deferred until the prefix key sequence is pressed." (list (use-package-require-after-load name (macroexp-progn body))))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :after -;; +;;;; :after (defun use-package-normalize/:after (name keyword args) (setq args (use-package-normalize-recursive-symlist name keyword args)) @@ -1364,20 +1312,6 @@ no keyword implies `:all'." (`(,feat . ,rest) (use-package-require-after-load (cons :all (cons feat rest)) body)))) -(defun use-package-memoize (f arg) - "Ensure the macro-expansion of F applied to ARG evaluates ARG -no more than once." - (let ((loaded (gensym "use-package--loaded")) - (result (gensym "use-package--result")) - (next (gensym "use-package--next"))) - `((lexical-let (,loaded ,result) - (lexical-let ((,next (lambda () - (if ,loaded - ,result - (setq ,loaded t) - (setq ,result ,arg))))) - ,(funcall f ``(funcall ,,next))))))) - (defun use-package-handler/:after (name keyword arg rest state) (let ((body (use-package-process-keywords name rest state)) (uses (use-package-after-count-uses arg))) @@ -1390,20 +1324,14 @@ no more than once." (apply-partially #'use-package-require-after-load arg) (macroexp-progn body)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :demand -;; +;;;; :demand (defalias 'use-package-normalize/:demand 'use-package-normalize-predicate) (defun use-package-handler/:demand (name keyword arg rest state) (use-package-process-keywords name rest state)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :custom -;; +;;;; :custom (defun use-package-normalize/:custom (name keyword args) "Normalize use-package custom keyword." @@ -1420,24 +1348,24 @@ no more than once." (defun use-package-handler/:custom (name keyword args rest state) "Generate use-package custom keyword code." (use-package-concat - (mapcar #'(lambda (def) - (let ((variable (nth 0 def)) - (value (nth 1 def)) - (comment (nth 2 def))) - (unless (and comment (stringp comment)) - (setq comment (format "Customized with use-package %s" name))) - `(customize-set-variable (quote ,variable) ,value ,comment))) - args) + (mapcar + #'(lambda (def) + (let ((variable (nth 0 def)) + (value (nth 1 def)) + (comment (nth 2 def))) + (unless (and comment (stringp comment)) + (setq comment (format "Customized with use-package %s" name))) + `(customize-set-variable (quote ,variable) ,value ,comment))) + args) (use-package-process-keywords name rest state))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :custom-face -;; +;;;; :custom-face (defun use-package-normalize/:custom-face (name-symbol keyword arg) "Normalize use-package custom-face keyword." - (let ((error-msg (format "%s wants a ( ) or list of these" name-symbol))) + (let ((error-msg + (format "%s wants a ( ) or list of these" + name-symbol))) (unless (listp arg) (use-package-error error-msg)) (dolist (def arg arg) @@ -1456,16 +1384,12 @@ no more than once." (mapcar #'(lambda (def) `(custom-set-faces (quote ,def))) args) (use-package-process-keywords name rest state))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :init -;; +;;;; :init (defalias 'use-package-normalize/:init 'use-package-normalize-forms) (defun use-package-handler/:init (name keyword arg rest state) (use-package-concat - ;; The user's initializations (let ((init-body (use-package-hook-injector (use-package-as-string name) :init arg))) @@ -1475,10 +1399,7 @@ no more than once." init-body)) (use-package-process-keywords name rest state))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :load -;; +;;;; :load (defun use-package-normalize/:load (name keyword args) (setq args (use-package-normalize-recursive-symlist name keyword args)) @@ -1492,10 +1413,7 @@ no more than once." (setq body (use-package-require pkg nil body))) body)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :config -;; +;;;; :config (defalias 'use-package-normalize/:config 'use-package-normalize-forms) @@ -1513,10 +1431,7 @@ no more than once." body (list t)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :diminish -;; +;;;; :diminish (defun use-package-normalize-diminish (name label arg &optional recursed) "Normalize the arguments to diminish down to a list of one of two forms: @@ -1554,10 +1469,7 @@ no more than once." arg) body))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; :delight -;; +;;;; :delight (defun use-package-normalize-delight (name args) "Normalize ARGS for a single call to `delight'." @@ -1712,6 +1624,47 @@ this file. Usage: (put 'use-package 'lisp-indent-function 'defun) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Jump to declaration +;; + +(defun use-package-find-require (package) + "Find file that required PACKAGE by searching `load-history'. +Returns an absolute file path or nil if none is found." + (catch 'suspect + (dolist (filespec load-history) + (dolist (entry (cdr filespec)) + (when (equal entry (cons 'require package)) + (throw 'suspect (car filespec))))))) + +(defun use-package-jump-to-package-form (package) + "Attempt to find and jump to the `use-package' form that loaded +PACKAGE. This will only find the form if that form actually +required PACKAGE. If PACKAGE was previously required then this +function will jump to the file that originally required PACKAGE +instead." + (interactive (list (completing-read "Package: " features))) + (let* ((package (if (stringp package) (intern package) package)) + (requiring-file (use-package-find-require package)) + file location) + (if (null requiring-file) + (user-error "Can't find file requiring file; may have been autoloaded") + (setq file (if (string= (file-name-extension requiring-file) "elc") + (concat (file-name-sans-extension requiring-file) ".el") + requiring-file)) + (when (file-exists-p file) + (find-file-other-window file) + (save-excursion + (goto-char (point-min)) + (setq location + (re-search-forward + (format (eval use-package-form-regexp-eval) package) nil t))) + (if (null location) + (message "No use-package form found.") + (goto-char location) + (beginning-of-line)))))) + (provide 'use-package) ;; Local Variables: -- 2.39.2