diff options
32 files changed, 7240 insertions, 84 deletions
diff --git a/ChangeLog.3 b/ChangeLog.3 index 136a276b632..edb738b56c9 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -113661,7 +113661,7 @@ 2019-09-04 Lars Ingebrigtsen <larsi@gnus.org> - Make the NSM not pop up an X dialogue on non-mouse actions + Make the NSM not pop up an X dialog on non-mouse actions * lisp/emacs-lisp/rmc.el (read-multiple-choice): Don't pop up X dialogs on (url-retrieve "https://expired.badssl.com/" #'ignore) @@ -124088,7 +124088,7 @@ This is fine when the GPG key is cached and each entry can be decrypted without user interaction. However, for security some people have their GPG on a hardware token like a Yubikey setup so that they - have to touch a sensor on the toke for every cryptographic operation, + have to touch a sensor on the token for every cryptographic operation, in which case it becomes inconvenient as each attempt to find an entry requires a variable number of touches of the hardware token. @@ -202762,7 +202762,7 @@ (w32_frame_parm_handlers): Add entries for new frame parameters. * src/w32font.c (Fx_select_font): Handle `z-group-above' - frames during font selection dialogue. + frames during font selection dialog. * src/w32term.c (construct_mouse_wheel): Construct mouse wheel event from F's w32 window. (w32_mouse_position): Handle child frames. diff --git a/admin/git-bisect-start b/admin/git-bisect-start index cf0c8cde410..7a715b4261d 100755 --- a/admin/git-bisect-start +++ b/admin/git-bisect-start @@ -35,6 +35,10 @@ git bisect start -# Prune commits 1e5b753bf4..806734c1b1 introduced by 0186faf2a1 (Eglot -# merge on Oct 20 2022) +# Prune commits 1e5b753bf4..806734c1b1 introduced by 0186faf2a1 +# (Eglot merge on Oct 20 2022) git bisect good 806734c1b1f433de43d59d9a5e3a1e89d64315f6 + +# Prune commits 31ea42e15e..a6cbfdd8f1 introduced by 4a1e9d61b5 +# (use-package merge on Nov 30 2022) +git bisect good 4a1e9d61b57c36255752437a2668e037e79fe870 diff --git a/admin/notes/tree-sitter/build-module/build.sh b/admin/notes/tree-sitter/build-module/build.sh index a71ccaa4edb..d562f1a7846 100755 --- a/admin/notes/tree-sitter/build-module/build.sh +++ b/admin/notes/tree-sitter/build-module/build.sh @@ -34,7 +34,7 @@ git clone "https://github.com/tree-sitter/${repo}.git" \ --depth 1 --quiet cp "${grammardir}"/grammar.js "${sourcedir}" # We have to go into the source directory to compile, because some -# C files referes to files like "../../common/scanner.h". +# C files refer to files like "../../common/scanner.h". cd "${sourcedir}" ### Build diff --git a/configure.ac b/configure.ac index 2770694b9c5..6e37d56d316 100644 --- a/configure.ac +++ b/configure.ac @@ -5989,7 +5989,7 @@ case $opsys in gnu-linux | gnu-kfreebsd ) AC_DEFINE([USG], []) AC_DEFINE([GNU_LINUX], [], - [Define if ths system is compatible with GNU/Linux.]) + [Define if this system is compatible with GNU/Linux.]) ;; hpux*) diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi index 1457a8bc3a3..96f881ca124 100644 --- a/doc/emacs/macos.texi +++ b/doc/emacs/macos.texi @@ -154,7 +154,7 @@ titlebar. @vindex ns-confirm-quit @item ns-confirm-quit This variable specifies whether to display a graphical confirmation -dialogue on quitting. +dialog on quitting. @vindex ns-auto-hide-menu-bar @item ns-auto-hide-menu-bar diff --git a/doc/misc/Makefile.in b/doc/misc/Makefile.in index a7dbbbb48fe..49cd8e13b03 100644 --- a/doc/misc/Makefile.in +++ b/doc/misc/Makefile.in @@ -67,14 +67,14 @@ MAKEINFO_OPTS = --force -I$(emacsdir) DOCMISC_W32 = @DOCMISC_W32@ ## Info files to build and install on all platforms. -INFO_COMMON = auth autotype bovine calc ccmode cl \ - dbus dired-x ebrowse ede ediff edt efaq eglot eieio \ - emacs-gnutls emacs-mime epa erc ert eshell eudc eww \ - flymake forms gnus htmlfontify idlwave ido info.info \ - mairix-el message mh-e modus-themes newsticker nxml-mode \ - octave-mode org pcl-cvs pgg rcirc reftex remember sasl \ - sc semantic ses sieve smtpmail speedbar srecode todo-mode \ - tramp transient url vhdl-mode vip viper vtable widget wisent woman +INFO_COMMON = auth autotype bovine calc ccmode cl dbus dired-x \ + ebrowse ede ediff edt efaq eglot eieio emacs-gnutls \ + emacs-mime epa erc ert eshell eudc eww flymake forms gnus \ + htmlfontify idlwave ido info.info mairix-el message mh-e \ + modus-themes newsticker nxml-mode octave-mode org pcl-cvs pgg \ + rcirc reftex remember sasl sc semantic ses sieve smtpmail \ + speedbar srecode todo-mode tramp transient url use-package \ + vhdl-mode vip viper vtable widget wisent woman ## Info files to install on current platform. INFO_INSTALL = $(INFO_COMMON) $(DOCMISC_W32) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index e6ddcf11dfa..8aa160d84eb 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -256,12 +256,40 @@ as an argument will ``spread'' the elements into multiple arguments: @end example @subsection Quoting and escaping -As with other shells, you can escape special characters and spaces -with by prefixing the character with a backslash (@code{\}), or by -surrounding the string with apostrophes (@code{''}) or double quotes -(@code{""}). This is needed especially for file names with special -characters like pipe (@code{|}), which could be part of remote file -names. +As with other shells, you can escape special characters and spaces by +prefixing the character with a backslash (@samp{\}), or by surrounding +the string with apostrophes (@samp{''}) or double quotes (@samp{""}). +This is needed especially for file names with special characters like +pipe (@samp{|}), which could be part of remote file names. + +When you escape a character with @samp{\} outside of any quotes, the +result is the literal character immediately following it. For +example, @code{\$10} means the literal string @code{$10}. + +Inside of double quotes, most characters have no special meaning. +However, @samp{\}, @samp{"}, and @samp{$} are still special; to escape +them, use backslash as above. Thus, if the value of the variable +@var{answer} is @code{42}, then @code{"The answer is: \"$answer\""} +returns the string @code{The answer is: "42"}. However, when escaping +characters with no special meaning, the result is the full +@code{\@var{c}} sequence. For example, @code{"foo\bar"} means the +literal string @code{foo\bar}. + +Additionally, when escaping a newline, the whole escape sequence is +removed by the parser. This lets you continue commands across +multiple lines: + +@example +~ $ echo "foo\ +bar" +foobar +@end example + +Inside apostrophes, escaping works differently. All characters +between the apostrophes have their literal meaning except @samp{'}, +which ends the quoted string. To insert a literal apostrophe, you can +use @samp{''}, so @code{'It''s me'} means the literal string +@code{It's me}. When using expansions (@pxref{Expansion}) in an Eshell command, the result may potentially be of any data type. To ensure that the result diff --git a/doc/misc/use-package.texi b/doc/misc/use-package.texi new file mode 100644 index 00000000000..e7bddd628c9 --- /dev/null +++ b/doc/misc/use-package.texi @@ -0,0 +1,1821 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename ../../use-package.info +@settitle use-package User Manual +@include docstyle.texi +@syncodeindex vr cp +@syncodeindex fn cp +@c %**end of header + +@copying +This manual is for use-package, a configuration macro for simplifying +your init file. + +Copyright @copyright{} 2022 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with the Front-Cover Texts being ``A GNU Manual'', +and with the Back-Cover Texts as in (a) below. A copy of the license +is included in the section entitled ``GNU Free Documentation License''. + +(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and +modify this GNU manual.'' +@end quotation +@end copying + +@dircategory Emacs misc features +@direntry +* use-package: (use-package). Declarative package configuration for Emacs. +@end direntry + +@finalout +@titlepage +@title use-package User Manual +@subtitle for version 2.4.5 +@author John Wiegley & Stefan Kangas +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents + +@ifnottex +@node Top +@top use-package User Manual + +The @code{use-package} macro allows you to isolate package +customization in your init file in a declarative way. It takes care +of a lot of things for you that would otherwise require a lot of +repetitive boilerplate code. It can help with common customization, +such as binding keys, setting up hooks, customizing user options and +faces, autoloading, and more. It also helps you keep Emacs startup +fast, even when you use many (even hundreds) of packages. + +Note that use-package is not a package manager. Although use-package +does have the useful capability to interface with the Emacs package +manager, its primary purpose is for the configuration and loading of +packages. + +@insertcopying + +@menu +* Basic Concepts:: Basic concepts of use-package. +* Getting Started:: A gentle introduction to use-package. +* Loading Packages:: How and when packages are loaded. +* Configuring Packages:: Package configuration keywords. +* Installing packages:: Ensuring packages are available. +* Byte-compiling:: Byte-compiling your init file. +* Troubleshooting:: What to do when there's trouble. + +Appendices +* Keyword extensions:: Adding new use-package keywords. +* History:: History and acknowledgments. +* GNU Free Documentation License:: The license for this manual. +* Index:: +@end menu +@end ifnottex + +@c ---------------------------------------------------------------------------- +@node Basic Concepts +@chapter Basic Concepts + +use-package provides the @code{use-package} macro, that simplifies the +customization and use of packages in Emacs. It was created for a few +basic reasons, each of which drove the design. Understanding these +reasons may help make some of those decisions clearer: + +@enumerate +@item +To gather all configuration details of a package into one place, +making it easier to copy, disable, or move it elsewhere in the init +file. + +@item +To reduce duplication and boilerplate, capturing several common +practices as mere keywords both easy and intuitive to use. + +@item +To make startup time of Emacs as quick as possible, without +sacrificing the quantity of add-on packages used. + +@item +To make it so errors encountered during startup disable only the +package raising the error, and as little else as possible, leaving as +close to a functional Emacs as possible. + +@item +To allow byte-compilation of one's init file so that any warnings or +errors seen are meaningful. In this way, even if byte-compilation is +not used for speed (reason 3), it can still be used as a sanity check. +@end enumerate + +It is worth noting that use-package is not intended to replace the +standard @w{@code{M-x customize}}. On the contrary, it is designed to +work together with it, for things that customize cannot do. + +@c ---------------------------------------------------------------------------- +@node Getting Started +@chapter Getting Started + +This chapter provides instructions and examples for quickly getting +started with use-package. The first thing you need to do is make sure +that @samp{use-package} itself is loaded. To do that, put this at the +top of your init file: + +@lisp +(require 'use-package) +(require 'bind-key) ; if you use any :bind variant +@end lisp + +The above makes the @code{use-macro} for in the rest of your init +file. In this manual, we call each call to @code{use-macro} a +@dfn{declaration}, to highlight the declarative nature of its +semantic. + +To unconditionally load a package named @samp{foo}, add the following +declaration to your init file: + +@lisp +(use-package foo) +@end lisp + +@noindent +This declaration is equivalent to using @code{require}, with some +use-package specific error handling added in. Just like require, it +needs the package @samp{foo} to be installed and available in your +@code{load-path} (@pxref{Installing packages}). + +To evaluate Lisp code @emph{before} the @samp{foo} package is loaded, +use the @code{:init} keyword: + +@lisp +(use-package foo + :init + (setq foo-variable t)) +@end lisp + +Similarly, @code{:config} can be used to execute code @emph{after} a +package is loaded. In cases where loading is done lazily +(@pxref{Loading Packages}), this execution is deferred until after the +autoload occurs. As you might expect, you can use @code{:init} and +@code{:config} together: + +@lisp +(use-package foo + :init + (setq foo-variable t) + :config + (foo-mode 1)) +@end lisp + +The above declarations will all load the @samp{foo} package +immediately. In most cases, this is not necessary or desirable, as +that will slow down Emacs startup. Instead, you should try to set +things up so that packages are only loaded when they are actually +needed (autoloading). If you have installed a package from +@acronym{GNU ELPA} that provides it's own autoloads, it is often +enough to say: + +@lisp +(use-package foo + :defer t) +@end lisp + +@noindent +This will avoid loading the package. Now, when you run any autoloaded +command, the package @samp{foo} is loaded automatically. Package +authors will make their own decisions about which commands are marked +to autoload by default. + +In some cases, you might need or want to provide your own autoloads. +The below more complex example autoloads the commands +@code{isearch-moccur} and @code{isearch-all} from +@file{color-moccur.el}, and binds keys both globally and in +@code{isearch-mode-map}. When one of these commands are used, the +package is loaded. At that point, @code{moccur-edit} is also loaded, +to allow editing of the @code{moccur} buffer. + +@lisp +(use-package color-moccur + :commands (isearch-moccur isearch-all) + :bind (("M-s O" . moccur) + :map isearch-mode-map + ("M-o" . isearch-moccur) + ("M-O" . isearch-moccur-all)) + :init + (setq isearch-lazy-highlight t) + :config + (use-package moccur-edit)) +@end lisp + +Some packages will suggest ready-made @code{use-package} declarations +that you can use. Where possible, it is a good idea to copy them, and +use that as a starting point. + +That should be enough to get you started! + +@c ---------------------------------------------------------------------------- +@node Loading Packages +@chapter Loading Packages + +@cindex loading packages +Before use-package can load an Emacs Lisp package, it must be +available in a directory on your @code{load-path}. When you install +packages using the built-in @code{install-package} command, it will do +this automatically for you. Packages shipped with Emacs (built-in +packages) are always available. + +If you install packages manually, you must make sure they are +available on your @code{load-path}. @xref{Lisp Libraries,,, emacs, +GNU Emacs Manual} for details. + +Some packages have more than one library. In those cases, you might +need more than one @code{use-package} declaration to make sure it is +properly loaded. For complex configurations, you might also need more +than one declaration for a package with the same name. + +use-package can interface with @samp{package.el} to install packages +on Emacs start. @xref{Installing packages} for details. + +@menu +* Loading basics:: How and when packages are loaded. +* Deferring loading:: Loading packages later. +* Forcing loading:: Loading packages immediately. +* Conditional loading:: Loading packages conditionally. +* Loading sequentially:: Loading packages in sequence. +* Load dependencies:: Don't load without dependencies. +* Load path:: Using a custom @code{load-path}. +* Manual autoloads:: Setting up autoloads manually. +@end menu + +@node Loading basics +@section How and when use-package loads packages + +The @code{use-package} macro either will either load a package +immediately, or when they are first used (autoloading). In the +simplest case, a @code{use-package} declaration loads a package when +it is evaluated.@footnote{This happens both at run-time and at +compile-time. @xref{Byte-compiling}.} If the declaration is in your +init file, this happens automatically each time Emacs is started. + +For example, the below declaration immediately loads the library +@code{foo}, just like @code{require} would. If the library @samp{foo} +is not available in your @code{load-path}, it logs a warning to the +@samp{*Messages*} buffer: + +@lisp +(use-package foo) +@end lisp + +Note that a ``package'' is different from an Emacs Lisp ``library''. +The above declaration tells use-package to load the @emph{library} +@file{foo.el}, which the overwhelming majority of cases also resides +in a @emph{package} named @code{foo}. But the @code{foo} package +might also contain a library named @file{foo-extra.el}. If that +library is not loaded automatically, you will need a separate +@code{use-package} declaration to make sure that it is. This manual +will often use these terms interchangeably, as this distinction does +not usually matter, but you should keep it in mind for the cases when +it does. + +The details of how and when you should load a package might differ +from one package to another. When in doubt, refer to the package +documentation for details. + +@node Deferring loading +@section Deferring package loading + +@cindex autoloading packages +@cindex loading lazily +In the examples we have seen so far, use-package loads packages every +time you start Emacs, even if that package is never used. That will +make starting Emacs slower. use-package therefore tries to set things +up in such a way that it only loads packages when a command is first +used (either with @kbd{M-x} or some key binding). This is based on +autoloading, a full description of which is outside the scope of this +manual. @xref{Autoload,,, elisp, GNU Emacs Lisp Reference Manual} for +the full story. + +@cindex triggers, for loading packages +Some @code{use-package} keywords provide autoload @dfn{triggers} that +cause a package to be loaded when certain events occur. For example, +the @code{:hook} keyword sets up a trigger that fires when the +specified hook is run, and then loads the package automatically. The +other trigger keywords, all of which are described later in this +manual, are @code{:commands}, @code{:bind}, @code{:bind*}, +@code{:bind-keymap}, @code{:bind-keymap*}, @code{:mode}, and +@code{:interpreter}. + +@subheading The @code{:defer} keyword + +@findex :defer +If you did not specify any autoloading keyword, use-package will fall +back to loading the package immediately (typically when Emacs is +starting up). This can be overridden using the @code{:defer} keyword. +It takes one boolean argument: a non-@code{nil} value means to stop +this package from being immediately loaded. Here is an example of +using @code{:defer} to postpone loading the package @samp{foo}: + +@lisp +(use-package foo + :defer t) +@end lisp + +Using @code{:defer t} by itself like this is rarely useful. +Typically, you would only use it together with a keyword like +@code{:config} (@pxref{Lisp Configuration}), or @code{:ensure} +(@pxref{Installing packages}). + +@subheading Defer loading until idle for N seconds + +You can also give a numeric argument @var{N} to @w{@code{:defer}} to +specify that a package should be loaded (if it hasn't already) after +Emacs has been idle for @var{N} seconds. For example, use this to +make use-package load @samp{foo} after 30 seconds of idle time: + +@lisp +(use-package foo + :defer 30) +@end lisp + +@subheading When to use @code{:defer} + +When using autoloading keywords, there is no need to also use +@code{:defer}. It doesn't hurt anything to add it in this case, +perhaps for extra clarity, but it is redundant. + +You should use @code{:defer} to force deferred loading, in cases when +use-package isn't creating any autoloads for you. For example, you +might know that some other package will already do something to cause +your package to load at the appropriate time. This is usually the +case when you install a package using @code{package-install}, as +packages installed in this way normally always have their own +autoloads already set up. + +@subheading Making @w{@code{:defer t}} the default + +@vindex use-package-always-defer +If you customize the user option @code{use-package-always-defer} to +non-@code{nil}, the @code{use-package} macro will behave as if +@w{@code{:defer t}} is always specified. This can be overridden for +individual declarations using either @w{@code{:defer nil}} or +@w{@code{:demand t}} (@pxref{Forcing loading}). + +@node Forcing loading +@section Forcing package to load immediately + +@findex :demand +The presence of autoloading trigger keywords can be overridden using +@code{:demand t}, which forces the package to load immediately. Thus, +even if you use an autoloading keyword such as @code{:bind} +(@pxref{Key bindings}), adding @code{:demand} will force loading to +occur immediately. It will also avoid creating an autoload for the +bound key, as it would be redundant. + +If you specify both @w{@code{:demand t}} and @w{@code{:defer t}}, the +@code{:defer} keyword will take precedence. + +@node Conditional loading +@section Loading packages conditionally + +@findex :if +@findex :when +@findex :unless +The @code{:if}, @code{:when}, and @code{:unless} keywords predicates +the loading and initialization of packages. They all accept one +argument, an Emacs Lisp form that is evaluated at run-time. + +If the argument of the @code{:if} keyword evaluates to non-@code{nil}, +the package will be loaded and initialized. The @code{:when} keyword +is provided as an alias for @code{:if}. Finally, the @code{:unless} +keyword is the inverse of @code{:if}, such that @w{@code{:unless foo}} +means the same thing as @w{@code{:if (not foo)}}. + +For example, if you only want @samp{foo} in graphical Emacs sessions, +you could use the following: + +@lisp +(use-package foo + :if (display-graphic-p)) +@end lisp + +Another common use case is to make it conditional on the operating +system: + +@lisp +(use-package foo + :if (memq window-system '(mac ns))) +@end lisp + +@cindex conditional loading before @code{:preface} or @code{:ensure} +If you need to conditionalize a use-package form so that the condition +occurs before even @code{:ensure} or @code{:preface}, use @code{when} +around the use-package form itself. For example: + +@lisp +(when (memq window-system '(mac ns)) + (use-package foo + :ensure t)) +@end lisp + +@node Loading sequentially +@section Loading packages in sequence + +@findex :after +Sometimes it only makes sense to configure a package after another one +has been loaded, because certain variables or functions are not in +scope until that time. This can achieved with the @code{:after} +keyword, which allows a fairly rich description of the exact +conditions when loading should occur. It takes either a symbol +indicating the package name, a list of such symbols, or a list of +selectors (see below). + +Here is an example of using the @acronym{GNU ELPA} packages hydra, +ivy, and ivy-hydra. Note that ivy-hydra will always be loaded last: + +@lisp +(use-package hydra) + +(use-package ivy) + +(use-package ivy-hydra + :after (ivy hydra)) +@end lisp + +In this case, because the declarations are evaluated in the order they +occur, the use of @code{:after} is not strictly necessary. However, +if @samp{hydra} and @samp{ivy} were to be autoloaded, using +@code{:after} guarantees that @samp{ivy-hydra} is not loaded until it +is actually needed. By using @code{:after}, the above code will also +work even if the order of the declaration changes. This means that +moving things around in your init file is less likely to break things. + +@subheading Using @code{:after} selectors + +@findex :all (with :after) +@findex :any (with :after) +The @code{:after} keyword also accepts a list of selectors. By +default, @code{:after (foo bar)} is the same as @w{@code{:after (:all +foo bar)}}, meaning that loading of the given package will not happen +until both @code{foo} and @code{bar} have been loaded. Here are some +of the other possibilities: + +@verbatim +:after (foo bar) +:after (:all foo bar) +:after (:any foo bar) +:after (:all (:any foo bar) (:any baz quux)) +:after (:any (:all foo bar) (:all baz quux)) +@end verbatim + +When you nest selectors, such as @code{(:any (:all foo bar) (:all baz +quux))}, it means that the package will be loaded when either both +@code{foo} and @code{bar} have been loaded, or when both @code{baz} +and @code{quux} have been loaded. + +Pay attention when setting @code{use-package-always-defer} to a +non-@code{nil} value, and also using the @code{:after} keyword. In +this case, you will need to specify how the declared package is to be +loaded: for example, by some @code{:bind}. If you are not using one +of the keywords that registers autoloads, such as @code{:bind} or +@code{:hook}, and your package manager does not provide autoloads, it +is possible that your package will never be loaded if you do not add +@code{:demand t} to those declarations. + +@node Load dependencies +@section Prevent loading if dependencies are missing + +@findex :requires +While the @code{:after} keyword delays loading until the dependencies +are loaded, the somewhat simpler @code{:requires} keyword @emph{never} +loads the package if the dependencies are not available when the +@code{use-package} declaration is evaluated. In this context, +``available'' means that @code{foo} is available if @w{@code{(featurep +'foo)}} evaluates to a non-@code{nil} value. For example: + +@lisp +(use-package abbrev + :requires foo) +@end lisp + +This is the same as: + +@lisp +(use-package abbrev + :if (featurep 'foo)) +@end lisp + +As a convenience, a list of such packages may be specified: + +@lisp +(use-package abbrev + :requires (foo bar baz)) +@end lisp + +For more complex logic, such as that supported by @code{:after}, +simply use @code{:if} and the appropriate Lisp expression. + +@node Load path +@section Setting a custom @code{load-path} + +@findex :load-path +If a package resides in some directory that is not in your +@code{load-path}, use the @code{:load-path} keyword to add it. It +takes a symbol, a function, a string or a list of strings. If the +path is relative, it is expanded within @code{user-emacs-directory}. + +For example: + +@lisp +(use-package ess-site + :load-path "site-lisp/ess/lisp/" + :commands R) +@end lisp + +Note that when using a symbol or a function to provide a dynamically +generated list of paths, you must inform the byte-compiler of this +definition so that the value is available at byte-compilation time. +This is done by using the special form @code{eval-and-compile} (as +opposed to @code{eval-when-compile}). Further, this value is fixed at +whatever was determined during compilation, to avoid looking up the +same information again on each startup. For example: + +@lisp +(eval-and-compile + (defun ess-site-load-path () + (shell-command "find ~ -path ess/lisp"))) + +(use-package ess-site + :load-path (lambda () (list (ess-site-load-path))) + :commands R) +@end lisp + +@node Manual autoloads +@section Setting up autoloads manually + +@findex :commands +@findex :autoload +To autoload an interactive command, use the @code{:commands} keyword. +When you use the @code{:commands} keyword, it creates autoloads for +those commands (which defers loading of the module until they are +used). The @code{:commands} keyword takes either a symbol or a list +of symbols. + +The @code{:autoload} keyword works like @code{:commands}, but is used +to autoload non-interactive functions. Here is an example: + +@lisp +(use-package org-crypt + :autoload org-crypt-use-before-save-magic) +@end lisp + +@c ---------------------------------------------------------------------------- +@node Configuring Packages +@chapter Configuring Packages + +This chapter describes the various keywords provided by +@code{use-package} that helps you configure packages. + +@menu +* Lisp Configuration:: Using Lisp to configure packages. +* Key bindings:: Making your own keybindings. +* Hooks:: Adding functions to hooks. +* Modes and interpreters:: Enabling modes automatically. +* Magic handlers:: Using regexps to enable modes. +* User options:: Setting user options. +* Faces:: Customizing faces. +* Hiding minor modes:: Tidying up the mode line. +@end menu + +@node Lisp Configuration +@section Using Lisp code for configuring packages + +The most general way to add customizations are the @code{:preface}, +@code{:init}, and @code{:config} keywords. They all accept one or +more Emacs Lisp forms, up to the next keyword, that are evaluated in +order. This lets you add arbitrary Lisp code to your +@code{use-package} declarations. + +The only difference between these keywords is when they are evaluated. + +@menu +* Preface keyword:: Evaluate code before anything else. +* Init keyword:: Evaluate code before loading package. +* Config keyword:: Evaluate code after loading package. +* Best practices:: When to use @code{:config}, @code{:init}, and @code{:preface}. +@end menu + +@node Preface keyword +@subsection @code{:preface} is evaluated first + +@findex :preface +The @code{:preface} section is evaluated before anything else, except +@code{:disabled} and @code{:ensure}. It can be used to establish +function and variable definitions that will: + +@enumerate +@item +Make the byte-compiler happy. It will not complain about functions +whose definitions are unknown because you have them within a guard +block. + +@item +Define code that can be used in an @code{:if} test. +@end enumerate + +Note that whatever is specified within @code{:preface} is evaluated +both at load time and at byte-compilation time, in order to ensure +that definitions are seen by both the Lisp evaluator and the +byte-compiler. Therefore, you should avoid having any side-effects in +your preface, and restrict it to symbol declarations and definitions. + +@node Init keyword +@subsection @code{:init} is evaluated before loading package + +@findex :init +The @code{:init} section is evaluated just before the package is +loaded. Note that the @code{:init} form is run unconditionally -- +even if the @code{foo} package happens to not exist on your system. +You must therefore remember to restrict @code{:init} code to only what +would succeed either way. @code{:init} also always happens before +package load, whether @code{:config} has been deferred or not. + +@node Config keyword +@subsection @code{:config} is evaluated after loading package + +@findex :config +The @code{:config} section is evaluated after the package has been +loaded. If the package is loaded immediately, this happens +immediately after that, but if loading is done lazily (@pxref{Loading +Packages}), this is deferred until after the package has been loaded. + +In general, you should keep @code{:init} forms as simple and quick as +possible, and put as much as you can get away with into the +@code{:config} section. That way, deferred loading can help your +Emacs start as quickly as possible. + +@node Best practices +@subheading When to use @code{:preface}, @code{:config} and @code{:init}? + +Where possible, it is better to avoid @code{:preface}, @code{:config} +and @code{:init}. Instead, prefer autoloading keywords such as +@code{:bind}, @code{:hook}, and @code{:mode}, as they will take care +of setting up autoloads for you without any need for boilerplate code. +For example, consider the following declaration: + +@lisp +(use-package foo + :init + (add-hook 'some-hook 'foo-mode)) +@end lisp + +This has two problems. First, it will unconditionally load the +package @samp{foo} on startup, which will make things slower. You can +fix this by adding @code{:defer t}: + +@lisp +(use-package foo + :defer t + :init + (add-hook 'some-hook 'foo-mode)) +@end lisp + +This is better, as @samp{foo} is now only loaded when it is actually +needed (that is, when the hook @samp{some-hook} is run). + +The second problem is that there is a lot of boilerplate that you have +to write. In this case, it might not be so bad, but avoiding that was +what use-package was made to avoid. The better option in this case is +therefore to use @code{:hook} (@xref{Hooks}), which also implies +@w{@code{:defer t}}. The above is thereby reduced down to: + +@lisp +(use-package foo + :hook some-hook) +@end lisp + +use-package will set up autoloading for you, and your Emacs startup +time will not suffer one bit. + +@node Key bindings +@section Key bindings + +@cindex :bind +@cindex binding keys +@cindex key bindings +One common thing to do when loading a package is to bind a key to +commands within that module. Without use-package, this would be done +using a combination of @code{keymap-local-set}, +@code{keymap-global-set} and various autoloads. With use-package, you +can simplify this using the @code{:bind} keyword. + +@menu +* Global keybindings:: Bindings you can use anywhere. +* Binding in keymaps:: Bindings for particular modes. +* Binding to a keymap:: Binding a key to a keymap. +* Binding to repeat-maps:: Binding repeating keys. +* Displaying keybindings:: Displaying personal key bindings. +@end menu + +@node Global keybindings +@subsection Global keybindings + +@findex :bind +To bind keys globally, the @code{:bind} keyword takes either a single +cons or a list of conses. Every cons has the form @code{(@var{key} +. @var{command}}, where @var{key} is a string indicating the key to +bind, and @var{command} is the name of a command (a symbol). The +syntax for the keys is similar to the syntax used by the @code{kbd} +function (@pxref{Init Rebinding,,, emacs, GNU Emacs Manual} for more +information). + +@subheading Using @code{:bind} with a single cons + +Here is an example of using a single cons: + +@lisp +(use-package ace-jump-mode + :bind ("C-." . ace-jump-mode)) +@end lisp + +This does two things: first, it creates an autoload for the +@code{ace-jump-mode} command and defers loading of the +@code{ace-jump-mode} package until you actually use it. Second, it +binds the key @code{C-.} to that command globally. + +@subheading Using @code{:bind} with a list of conses + +Here is an example of using @code{:bind} with a list of conses: + +@lisp +(use-package hi-lock + :bind (("M-o l" . highlight-lines-matching-regexp) + ("M-o r" . highlight-regexp) + ("M-o w" . highlight-phrase))) +@end lisp + +@subheading Using special keys + +Inside key strings, special keys like @kbd{TAB} or @kbd{F1}--@kbd{F12} +have to be written inside angle brackets, e.g. @code{"C-<up>"}. +Standalone special keys (and some combinations) can be written in +square brackets, e.g.@ @code{[tab]} instead of @code{"<tab>"}. + +Examples: + +@lisp +(use-package helm + :bind (("M-x" . helm-M-x) + ("M-<f5>" . helm-find-files) + ([f10] . helm-buffers-list) + ([S-f10] . helm-recentf))) +@end lisp + +@subheading Remapping commands + +Remapping commands with @code{:bind} and @code{bind-key} works as +expected, because when the binding is a vector, it is passed straight +to @code{define-key}. @xref{Remapping Commands,,, elisp, GNU Emacs +Lisp Reference Manual}) for more information about command remapping. +For example, the following declaration will rebind +@code{fill-paragraph} (bound to @kbd{M-q} by default) to +@code{unfill-toggle}: + +@lisp +(use-package unfill + :bind ([remap fill-paragraph] . unfill-toggle)) +@end lisp + +@subheading What @code{:bind} does behind the scenes + +To understand what @code{:bind} does behind the scenes, it might be +useful to consider an example: + +@lisp +(use-package ace-jump-mode + :bind ("C-." . ace-jump-mode)) +@end lisp + +This could be expressed in a much more verbose way with the +@code{:commands} and @code{:init} keywords. + +@lisp +(use-package ace-jump-mode + :commands ace-jump-mode + :init + (bind-key "C-." 'ace-jump-mode)) +@end lisp + +Without using even the @code{:commands} keyword, we could also write +the above like so: + +@lisp +(use-package ace-jump-mode + :defer t + :init + (autoload 'ace-jump-mode "ace-jump-mode" nil t) + (bind-key "C-." 'ace-jump-mode)) +@end lisp + +Although these three forms are all equivalent, the first form is +usually the best, as it will save some typing. + +@node Binding in keymaps +@subsection Key bindings in local keymaps + +@findex :map, inside :bind +Slightly different from binding a key to a keymap, is binding a key +@emph{within} a local keymap that only exists after the package is +loaded. @code{use-package} supports this with a @code{:map} modifier, +taking the local keymap to bind to: + +@lisp +(use-package helm + :bind (:map helm-command-map + ("C-c h" . helm-execute-persistent-action))) +@end lisp + +The effect of this statement is to wait until @code{helm} has loaded, +and then to bind the key @code{C-c h} to +@code{helm-execute-persistent-action} within Helm's local keymap, +@code{helm-command-map}. + +Multiple uses of @code{:map} may be specified. Any binding occurring +before the first use of @code{:map} are applied to the global keymap: + +@lisp +(use-package term + :bind (("C-c t" . term) + :map term-mode-map + ("M-p" . term-send-up) + ("M-n" . term-send-down) + :map term-raw-map + ("M-o" . other-window) + ("M-p" . term-send-up) + ("M-n" . term-send-down))) +@end lisp + +@node Binding to a keymap +@subsection Binding to keymaps + +@findex :bind-keymap, inside :bind +Normally @code{:bind} expects that commands are functions that will be +autoloaded from the given package. However, this does not work if one of +those commands is actually a keymap, since keymaps are not functions, +and cannot be autoloaded using the built-in @code{autoload} function. + +To handle this case, @code{use-package} offers a special, limited +variant of @code{:bind} called @code{:bind-keymap}. The only difference +is that the ``commands'' bound to by @code{:bind-keymap} must be keymaps +defined in the package, rather than command functions. This is handled +behind the scenes by generating custom code that loads the package +containing the keymap, and then re-executes your keypress after the +first load, to reinterpret that keypress as a prefix key. + +For example: + +@lisp +(use-package foo + :bind-keymap ("C-c p" . foo-command-map)) +@end lisp + +@node Binding to repeat-maps +@subsection Binding to repeat-maps + +@findex :repeat-map, inside :bind +@cindex repeat-mode and use-package, using +A special case of binding within a local keymap is when that keymap is +used by @code{repeat-mode} @pxref{Repeating,,, emacs, GNU Emacs +Manual}. These keymaps are usually defined specifically for +this. Using the @code{:repeat-map} keyword, and passing it a name for +the map it defines, will bind all following keys inside that map, and +(by default) set the @code{repeat-map} property of each bound command +to that map. + +The following example creates a keymap called +@code{git-gutter+-repeat-map}, makes four bindings in it as above, +then sets the @code{repeat-map} property of each bound command +(@code{git-gutter+-next-hunk} @code{git-gutter+-previous-hunk}, +@code{git-gutter+-stage-hunks} and @code{git-gutter+-revert-hunk}) to +that keymap. + +@lisp +(use-package git-gutter+ + :bind + (:repeat-map git-gutter+-repeat-map + ("n" . git-gutter+-next-hunk) + ("p" . git-gutter+-previous-hunk) + ("s" . git-gutter+-stage-hunks) + ("r" . git-gutter+-revert-hunk))) +@end lisp + +@findex :exit, inside :repeat-map and :bind +Specifying @code{:exit} inside the scope of @code{:repeat-map} will +prevent the @code{repeat-map} property being set, so that the command +can be used from within the repeat map, but after it using it the repeat +map will no longer be available. This is useful for commands often used +at the end of a series of repeated commands: + +@lisp +(use-package git-gutter+ + :bind + (:repeat-map my/git-gutter+-repeat-map + ("n" . git-gutter+-next-hunk) + ("p" . git-gutter+-previous-hunk) + ("s" . git-gutter+-stage-hunks) + ("r" . git-gutter+-revert-hunk) + :exit + ("c" . magit-commit-create) + ("C" . magit-commit) + ("b" . magit-blame))) +@end lisp + +@findex :continue, inside :repeat-map and :bind +Specifying @code{:continue} @emph{forces} setting the +@code{repeat-map} property (just like @emph{not} specifying +@code{:exit}), so the above snippet is equivalent to: + +@lisp +(use-package git-gutter+ + :bind + (:repeat-map my/git-gutter+-repeat-map + :exit + ("c" . magit-commit-create) + ("C" . magit-commit) + ("b" . magit-blame) + :continue + ("n" . git-gutter+-next-hunk) + ("p" . git-gutter+-previous-hunk) + ("s" . git-gutter+-stage-hunks) + ("r" . git-gutter+-revert-hunk))) +@end lisp + +@node Displaying keybindings +@subsection Displaying personal keybinding + +@findex describe-personal-keybindings +The @code{:bind} keyword uses the @code{bind-keys} macro from the +@samp{bind-key.el} library to set up keybindings. It keeps track of +all keybindings you make, so that you can display them separately from +the default keybindings. + +Use @w{@code{M-x describe-personal-keybindings}} to see all +keybindings you've set using either the @code{:bind} keyword or the +@code{bind-keys} macro. + +@node Hooks +@section Hooks + +@cindex hooks +@findex :hook +The @code{:hook} keyword allows adding functions onto hooks. It takes +one argument of the form @var{hooks}, specifying one or more functions +to add to one or more hooks. For the purposes of @code{:hook}, the +name of hook variables should always exclude the @samp{-hook} suffix. +It is appended automatically for you, to save some typing. + +For example, consider the following @code{use-package} declaration +that sets up autoloads for @code{company-mode} from the @samp{company} +package, and adds @samp{company-mode} to @code{prog-mode-hook}: + +@lisp +(use-package company + :commands company-mode + :init + (add-hook 'prog-mode-hook #'company-mode)) +@end lisp + +Using @code{:hook}, this can be simplified to: + +@lisp +(use-package company + :hook (prog-mode . company-mode)) +@end lisp + +Here, @code{:hook} will automatically set up autoloads for the +@code{company-mode} command, so there is no need to use +@code{:commands}. + +The @code{:hook} keyword will also assume that the name of the +function you want to add is the same as the package name with +@samp{-mode} appended to it. Taking this into account, you can +simplify the above to the equivalent: + +@lisp +(use-package company + :hook prog-mode) +@end lisp + +@cindex multiple hooks +You can also provide a list of hooks. When multiple hooks should be +applied, the following examples are all equivalent: + +@lisp +(use-package company + :hook (prog-mode text-mode)) + +(use-package company + :hook ((prog-mode text-mode) . company-mode)) + +(use-package company + :hook ((prog-mode . company-mode) + (text-mode . company-mode))) + +(use-package company + :commands company-mode + :init + (add-hook 'prog-mode-hook #'company-mode) + (add-hook 'text-mode-hook #'company-mode)) +@end lisp + +One common mistake when using @code{:hook} is to forget to omit the +@samp{-hook} suffix, which, as already explained, is appended +automatically. Therefore, the following will not work, as it attempts +to add a function to non-existent @code{prog-mode-hook-hook}: + +@lisp +;; DOES NOT WORK +(use-package ace-jump-mode + :hook (prog-mode-hook . ace-jump-mode)) +@end lisp + +@vindex use-package-hook-name-suffix +If you do not like this behavior, you can customize the user option +@code{use-package-hook-name-suffix} to @code{nil}. The value of this +variable is @samp{"-hook"} by default. + +The use of @code{:hook}, as with @code{:bind}, @code{:mode}, +@code{:interpreter}, etc., causes the functions being hooked to +implicitly be read as @code{:commands}. This means that they will +establish interactive @code{autoload} definitions for that module, if +not already defined as functions), and so @code{:defer t} is also +implied by @code{:hook}. + +@node Modes and interpreters +@section Modes and interpreters + +@findex :mode +@findex :interpreter +Similar to @code{:bind}, you can use @code{:mode} and +@code{:interpreter} to establish a deferred binding within the +@code{auto-mode-alist} and @code{interpreter-mode-alist} variables. +The specifier to either keyword can be a cons cell, a list of cons +cells, or a string or regexp. + +The following example reproduces the default @code{ruby-mode} +configuration, exactly as it is in Emacs out-of-the-box. That mode is +enabled automatically when a file whose name matches the regexp +@code{"\\.rb\\'"} (a file with the @samp{.rb} extension), or when the +first line of the file (known as the ``shebang'') matches the string +@code{"ruby"}: + +@lisp +(use-package ruby-mode + :mode "\\.rb\\'" + :interpreter "ruby") +@end lisp + +The default @code{python-mode} configuration can be reproduced using +the below declaration. Note that the package that should be loaded +differs from the mode name in this case, so we must use a cons: + +@lisp +;; The package is "python" but the mode is "python-mode": +(use-package python + :mode ("\\.py\\'" . python-mode) + :interpreter ("python" . python-mode)) +@end lisp + +Both the @code{:mode} and @code{:interpreter} keywords also accept a +list of regexps: + +@lisp +(use-package foo + ;; Equivalent to "\\(ba[rz]\\)\\'": + :mode ("\\.bar\\'" "\\.baz\\'") + ;; Equivalent to "\\(foo[ab]\\)": + :interpreter ("fooa" "foob")) +@end lisp + +@node Magic handlers +@section Magic handlers + +@findex :magic +@findex :magic-fallback +Similar to @code{:mode} and @code{:interpreter}, you can also use +@code{:magic} and @code{:magic-fallback} to cause certain function to +be run if the beginning of a file matches a given regular expression. +The difference between @code{:magic} and @code{:magic-fallback}, is +that the latter has a lower priority than @code{:mode}. + +Here is an example: + +@lisp +(use-package pdf-tools + :magic ("%PDF" . pdf-view-mode) + :config + (pdf-tools-install :no-query)) +@end lisp + +This registers an autoloaded command for @code{pdf-view-mode}, defers +loading of @code{pdf-tools}, and runs @code{pdf-view-mode} if the +beginning of a buffer matches the string @code{"%PDF"}. + +@node User options +@section User options + +@findex :custom +In Emacs, you normally set customizable variables (user options) using +the @code{M-x customize} interface (@pxref{Easy Customization,,, +emacs, GNU Emacs Manual}). We recommended this method for most users. +However, it is also possible to set them in your @code{use-package} +declarations by using the @code{:custom} keyword. + +@lisp +(use-package comint + :defer t + :custom + (comint-buffer-maximum-size 20000 "Increase comint buffer size.") + (comint-prompt-read-only t "Make the prompt read only.")) +@end lisp + +This is better than using @code{setq} in a @code{:config} block, as +customizable variables might have some code associated with it that +Emacs will execute when you assign values to them. In Emacs 29, there +is also the new @code{setopt} macro that does this for you. + +Note that the values customized using this keyword are @emph{not} +saved in the standard Emacs @code{custom-file}. You should therefore +set each user option using either the @code{:custom} keyword @emph{or} +@w{@code{M-x customize-option}}, which will save customized values in +the Emacs @code{custom-file}. Do not use both for the same variable, +as this risk having conflicting values in your use-package declaration +and your @code{custom-file}. This can lead to problems that are both +tricky and tedious to debug. + +@node Faces +@section Faces + +@findex :custom-face +The @code{:custom-face} keyword allows customization of package custom +faces. + +@lisp +(use-package eruby-mode + :custom-face + (eruby-standard-face ((t (:slant italic))))) + +(use-package example + :custom-face + (example-1-face ((t (:foreground "LightPink")))) + (example-2-face ((t (:foreground "LightGreen"))) face-defspec-spec)) + +(use-package zenburn-theme + :preface + (setq my/zenburn-colors-alist + '((fg . "#DCDCCC") (bg . "#1C1C1C") (cyan . "#93E0E3"))) + :custom-face + (region ((t (:background ,(alist-get my/zenburn-colors-alist 'cyan))))) + :config + (load-theme 'zenburn t)) +@end lisp + +@node Hiding minor modes +@section Hiding minor modes with diminish and delight + +@code{use-package} supports the diminish and delight packages, both of +which make it possible remove or change minor mode strings in your +mode-line. Which one to use is up to you, but you should normally +only use one or the other -- never both.@footnote{When in doubt, you +might as well use diminish.} To use either of them, you must first +install the corresponding package from @acronym{GNU ELPA}. + +@menu +* Diminish:: Hiding minor modes with Diminish. +* Delight:: Hiding minor modes with Delight. +@end menu + +@node Diminish +@subsection Diminish + +@findex :diminish +When diminish@footnote{The diminish package is installable from +@acronym{GNU ELPA}.} is installed, you can use the @code{:diminish} +keyword. First, add the following declaration to the beginning of +your init file. The optional @w{@code{:ensure t}} makes sure the +package is installed if it isn't already (@pxref{Installing +packages}). + +@lisp +(use-package diminish :ensure t) +@end lisp + +The @code{:diminish} keyword takes either a minor mode symbol, a cons +of the symbol and its replacement string, or just a replacement +string, in which case the minor mode symbol is guessed to be the +package name with @samp{-mode} appended at the end: + +@lisp +(use-package abbrev + :diminish abbrev-mode + :config + (if (file-exists-p abbrev-file-name) + (quietly-read-abbrev-file))) +@end lisp + +@node Delight +@subsection Delight + +@findex :delight +When delight@footnote{The @samp{delight} package is installable from +GNU ELPA.} is installed, you can use the @code{:delight} keyword. +First, add the following declaration to the beginning of your init +file. The optional @w{@code{:ensure t}} makes sure the package is +installed if it isn't already (@pxref{Installing packages}). + +@lisp +(use-package delight :ensure t) +@end lisp + +The @code{:delight} keyword takes a minor mode symbol, a replacement +string, or quoted mode line data (in which case the minor mode symbol +is guessed to be the package name with @samp{-mode} appended at the +end), both of these, or several lists of both. @xref{Mode Line +Data,,, elisp, GNU Emacs Lisp Reference Manual}. If no arguments are +provided, the default mode name is hidden completely. + +@lisp +;; Don't show anything for rainbow-mode. +(use-package rainbow-mode + :delight) + +;; Don't show anything for auto-revert-mode, which doesn't match +;; its package name. +(use-package autorevert + :delight auto-revert-mode) + +;; Remove the mode name for projectile-mode, but show the project name. +(use-package projectile + :delight '(:eval (concat " " (projectile-project-name)))) + +;; Completely hide visual-line-mode and change auto-fill-mode to " AF". +(use-package emacs + :delight + (auto-fill-function " AF") + (visual-line-mode)) +@end lisp + +@c ---------------------------------------------------------------------------- +@node Installing packages +@chapter Installing packages automatically + +The standard Emacs package manager is documented in the Emacs manual +(@pxref{Package Installation,,, emacs, GNU Emacs Manual}). The +@code{use-package} macro provides the @code{:ensure} and @code{:pin} +keywords, that interface with that package manager to automatically +install packages. This is particularly useful if you use your init +file on more than one system. + +@menu +* Install package:: +* Pinning packages:: +* Other package managers:: +@end menu + +@node Install package +@section Installing package + +@findex :ensure +The @code{:ensure} keyword makes use-package ask the Emacs package +manager to install a package if it is not already present on your +system. + +For example: + +@lisp +(use-package magit + :ensure t) +@end lisp + +If you need to install a different package from the one named by +@code{use-package}, you can use a symbol: + +@lisp +(use-package tex + :ensure auctex) +@end lisp + +You can customize the user option @code{use-package-always-ensure} to +non-@code{nil} if you want this behavior to be global for all +packages. + +@lisp +(require 'use-package-ensure) +(setq use-package-always-ensure t) +@end lisp + +@noindent +You can override the above setting for a single package by adding +@w{@code{:ensure nil}} to its declaration. + +@node Pinning packages +@section Pinning packages using @code{:pin} + +@findex :pin +use-package can pin a package to a specific archive using the +@code{:pin} keyword.@footnote{The @code{:pin} keyword has no effect on +Emacs versions older than 24.4.} This allows you to mix and match +packages from different archives. The primary use-case for this is +preferring to install packages from @acronym{GNU ELPA} or +@acronym{NonGNU ELPA} (indicated by @code{gnu} and @code{nongnu}, +respectively), while installing specific packages from third-party +archives. + +For example: + +@lisp +(use-package company + :ensure t + :pin gnu) ; GNU ELPA +@end lisp + +@vindex use-package-always-pin +Unfortunately, the third-party archive @acronym{MELPA} uses a +versioning scheme based on dates, which means that packages from that +archive are always preferred. If you are using that archive, we +strongly encourage you to customize @code{use-package-always-pin} to +@code{nongnu}. This guarantees that you are using a version of that +package that has been specifically marked for release by its +developer, and not a development snapshot. + +@c FIXME: This needs clarifying. AFAIK, :ensure does not update packages. +If you want to manually keep a package updated and ignore upstream +updates, you can pin it to @samp{manual}. This will work as long as +you have not customized a repository to use that name in the +@code{package-archives} variable. + +Example: + +@lisp +(use-package org + :ensure t + ;; ignore org-mode from upstream and use a manually installed version + :pin manual) +@end lisp + +@code{use-package} signals an error if you try to pin a package to an +archive that is not configured using @code{package-archives} (except +from the special @samp{manual} archive). + +@node Other package managers +@section Non-standard package managers + +By default, use-package assumes that you are using the built-in +@code{package.el} package manager. We expect that most users will +find that it is more than capable enough, even for advanced use cases. + +@vindex use-package-ensure-function +However, some users might prefer to use a third-party package manager +for a specific circumstance or use case. By setting the user option +@code{use-package-ensure-function} to the name of a function, you can +direct @code{:ensure} to use a different package manager for +installing packages. + +For more details, please see the documentation of the package manager +you are using. If you run into any bugs, it is often best to report +them directly to the developers of that package manager. + +@c ---------------------------------------------------------------------------- +@node Byte-compiling +@chapter Byte-compiling your init file + +Some users might want to byte-compile their init file to make Emacs +startup even faster. This is not recommended in most cases, as the +speed-up is often too small to be worth it, and can lead to confusion +if the byte-compiled files are out-of-date. If you still want to do +it, read on. + +@code{use-package} always loads every library that it can while a file +is being byte-compiled. This helps silence spurious warnings about +unknown variables and functions. + +@findex :defines +@findex :functions +However, there are times when this is just not enough. For those +times, use the @code{:defines} and @code{:functions} keywords to +introduce dummy variable and function declarations solely for the sake +of silencing byte-compiler warnings. For example: + +@lisp +(use-package texinfo + :defines texinfo-section-list + :commands texinfo-mode + :init + (add-to-list 'auto-mode-alist '("\\.texi$" . texinfo-mode))) +@end lisp + +If you need to silence a missing function warning, you can use +@code{:functions}: + +@lisp +(use-package ruby-mode + :mode "\\.rb\\'" + :interpreter "ruby" + :functions inf-ruby-keys + :config + (defun my-ruby-mode-hook () + (require 'inf-ruby) + (inf-ruby-keys)) + + (add-hook 'ruby-mode-hook 'my-ruby-mode-hook)) +@end lisp + +@findex :no-require +@cindex prevent a package from loading at compile-time +Normally, @code{use-package} will load each package at compile time +before compiling the configuration, to ensure that any necessary +symbols are in scope to satisfy the byte-compiler. At times this can +cause problems, since a package may have special loading requirements, +and all that you want to use @code{use-package} for is to add a +configuration to the @code{eval-after-load} hook. In such cases, use +the @code{:no-require} keyword: + +@lisp +(use-package foo + :no-require t + :config + (message "Evaluate this immediately after loading `foo'")) +@end lisp + +@c ---------------------------------------------------------------------------- +@node Troubleshooting +@chapter Troubleshooting + +@cindex troubleshooting +@cindex debugging +If an error occurs while initializing or configuring a package, this +will not stop your Emacs from loading. Instead, @code{use-package} +captures the error and reports it in a special @code{*Warnings*} popup +buffer, so that you can debug the situation in an otherwise functional +Emacs. + +If you are having trouble when starting Emacs, you can pass Emacs the +@samp{--debug-init} command line flag. @xref{Initial Options,,, +emacs, GNU Emacs Manual}. To get even more information when using +that flag, add the following to your init file (these options are +documented below): + +@lisp +(when init-file-debug + (setq use-package-verbose t + use-package-expand-minimally nil + use-package-compute-statistics t + debug-on-error t)) +@end lisp + +@cindex reporting bugs +@cindex expanding macro, for troubleshooting +Since @code{use-package} is a macro, the first step when you need to +dig deeper is usually to see what Emacs Lisp code your declaration +expands to. You can either use the command @w{@kbd{M-x +pp-macroexpand-last-sexp}}, or wrap the use-package declaration in +@code{macroexpand} and evaluate it. It is a good idea to include +their output in any bugs you file for use-package. + +@menu +* Troubleshooting Options:: +* Gathering Statistics:: +* Disabling a package:: +@end menu + +@node Troubleshooting Options +@section Options that help when troubleshooting + +@vindex use-package-expand-minimally +By default, use-package will attempts to catch and report errors that +occur during expansion of use-package declarations in your init file. +Customize the user option @code{use-package-expand-minimally} to a +non-@code{nil} value to disable this checking. + +@findex :catch +This behavior may be overridden locally using the @code{:catch} +keyword. If @code{t} or @code{nil}, it enables or disables catching +errors at load time. It can also be a function taking two arguments: +the keyword being processed at the time the error was encountered, and +the error object (as generated by @code{condition-case}). For +example: + +@lisp +(use-package example + ;; Note that errors are never trapped in the preface, since doing so would + ;; hide definitions from the byte-compiler. + :preface (message "I'm here at byte-compile and load time") + :init (message "I'm always here at startup") + :config + (message "I'm always here after the package is loaded") + (error "oops") + ;; Don't try to (require 'example), this is just an example! + :no-require t + :catch (lambda (keyword err) + (message (error-message-string err)))) +@end lisp + +Evaluating the above form will print these messages: + +@verbatim +I’m here at byte-compile and load time +I’m always here at startup +Configuring package example... +I’m always here after the package is loaded +oops +@end verbatim + +@node Gathering Statistics +@section Gathering Statistics + +@vindex use-package-verbose +When a package is loaded, and if you have @code{use-package-verbose} +set to @code{t}, or if the package takes longer than 0.1 seconds to +load, you will see a message to indicate this loading activity in the +@code{*Messages*} buffer. The same will happen for configuration, or +@code{:config} blocks, that take longer than 0.1 seconds to execute. + +@vindex use-package-compute-statistics +If you'd like to see a summary how many packages you've loaded, what +stage of initialization they've reached, and how much aggregate time +they've spent (roughly), you can customize the user option +@code{use-package-compute-statistics} to a non-@code{nil} value. Then +reload your packages, normally by restarting Emacs, to make sure that +use-package can gather statistics for all your packages. + +@cindex use-package-report +Run the command @kbd{M-x use-package-report} to see the results. The +buffer displayed is a tabulated list. To sort rows based on a +particular column, move point to it and type @kbd{S}, or click the +column name at the top of the buffer on graphical displays. + +@cindex use-package-reset-statistics +To reset all statistics that use-package has gathered for the current +Emacs invocation, run the command @kbd{M-x use-package-reset-statistics}. + +Note that, if you are setting @code{use-package-compute-statistics} +directly in your init file, and not with @code{customize}, you must do +this after loading @code{use-package}, but before any +@code{use-package} forms. + +@node Disabling a package +@section Disabling a package + +@cindex disable package +@findex :disabled +The @code{:disabled} keyword inhibits loading a package, and all it's +customizations. It is equivalent to commenting out or deleting the +definition. + +You could use this, for example, to temporarily disable a package that +you're having difficulties with, or to avoid loading a package that +you're not currently using. + +This example disables the @samp{foo} package: + +@lisp +(use-package foo + :disabled) +@end lisp + +When byte-compiling your init file, use-package omits disabled +declarations from the output entirely, in order to make Emacs startup +faster. + +@c ---------------------------------------------------------------------------- +@node Keyword extensions +@appendix Keyword extensions + +use-package is based on an extensible framework that makes it easy for +package authors to add new keywords, or modify the behavior of +existing keywords. + +Some keyword extensions are included with @code{use-package}, and can +be optionally enabled. + +@menu +* use-package-ensure-system-package:: +* Creating an extension:: +@end menu + +@node use-package-ensure-system-package +@section :use-package-ensure-system-package + +@findex :ensure-system-package +The @code{:ensure-system-package} keyword allows you to ensure certain +executables are available on your system alongside your package +declarations.@footnote{On macOS, you will want to make sure +@code{exec-path} is cognisant of all binary package names that you +would like to ensure are installed. The +@uref{https://github.com/purcell/exec-path-from-shell,@samp{exec-path-from-shell}} +package is often a good way to do this.} + +To use this extension, add this immediately after loading +@code{use-package}: + +@lisp +(use-package use-package-ensure-system-package) +@end lisp + +Now you can use the @code{:ensure-system-package} keyword. +Here's an example usage: + +@lisp +(use-package foo + :ensure-system-package foo) +@end lisp + +This will expect a global binary package to exist called @code{foo}. +If it does not, it will use your system package manager to attempt an +install of a binary by the same name asynchronously. This requires +the GNU ELPA package +@uref{https://gitlab.com/jabranham/system-packages,@samp{system-packages}}, +so for this to work you must install that first. + +One way of making sure it is installed is with @code{use-package} +together with @code{:ensure}. + +@lisp +(use-package system-packages + :ensure t) +@end lisp + +For example, on a @code{Debian GNU/Linux} system, this would call +@samp{apt-get install foo}. + +If the package is named differently than the binary, you can use a +cons in the form of @code{(binary . package-name)}. For example: + +@lisp +(use-package foo + :ensure-system-package + (foocmd . foo)) +@end lisp + +On a @code{Debian GNU/Linux} system, this would call @code{apt install +foo} if Emacs could not locate the executable +@code{foocmd}.@footnote{For manual testing, you could use the +@code{executable-find} function, which is what @samp{system-packages} +uses internally.} + +@code{:ensure-system-package} can also take a cons where its +@code{cdr} is a string that will get called by +@code{(async-shell-command)} to install if it isn't found. This does +not depend upon any external package. + +@lisp +(use-package tern + :ensure-system-package (tern . "npm i -g tern")) +@end lisp + +To install several packages, you can pass in a list of conses: + +@lisp +(use-package ruby-mode + :ensure-system-package + ((rubocop . "gem install rubocop") + (ruby-lint . "gem install ruby-lint") + (ripper-tags . "gem install ripper-tags") + (pry . "gem install pry"))) +@end lisp + +Finally, in case the package dependency does not provide a global +executable, you can ensure packages exist by checking the presence of a +file path by providing a string like so: + +@lisp +(use-package dash-at-point + :if (eq system-type 'darwin) + :ensure-system-package + ("/Applications/Dash.app" . "brew cask install dash")) +@end lisp + +@code{:ensure-system-package} will use @code{system-packages-install} +to install system packages, except where a custom command has been +specified, in which case it will be executed verbatim by +@code{async-shell-command}. + +The user options @code{system-packages-package-manager} and +@code{system-packages-use-sudo} are honored, but not for custom +commands. Custom commands should include the call to sudo in the +command if needed. + +@node Creating an extension +@section How to create an extension keyword + +This section describes how to create a new keyword. + +@enumerate +@item +Add the keyword. + +The first step is to add your keyword at the right place in +@code{use-package-keywords}. This list determines the order in which +things will happen in the expanded code. You should never change this +order, but it gives you a framework within which to decide when your +keyword should fire. + +@item +Create a normalizer. + +The job of the normalizer is take a list of arguments (possibly +@code{nil}), and turn it into the single argument (which could still +be a list) that should appear in the final property list used by +@code{use-package}. + +Define a normalizer for your keyword by defining a function named +after the keyword, for example: + +@lisp +(defun use-package-normalize/:pin (name-symbol keyword args) + (use-package-only-one (symbol-name keyword) args + (lambda (label arg) + (cond + ((stringp arg) arg) + ((symbolp arg) (symbol-name arg)) + (t + (use-package-error + ":pin wants an archive name (a string)")))))) +@end lisp + +@item +Create a handler. + +Once you have a normalizer, you must create a handler for the keyword. + +Handlers can affect the handling of keywords in two ways. First, it +can modify the @code{state} plist before recursively processing the +remaining keywords, to influence keywords that pay attention to the +state (one example is the state keyword @code{:deferred}, not to be +confused with the @code{use-package} keyword @code{:defer}). Then, +once the remaining keywords have been handled and their resulting +forms returned, the handler may manipulate, extend, or just ignore +those forms. + +The task of each handler is to return a @emph{list of forms} +representing code to be inserted. It does not need to be a +@code{progn} list, as this is handled automatically in other places. +Thus it is common to see the idiom of using @code{use-package-concat} +to add new functionality before or after a code body, so that only the +minimum code necessary is emitted as the result of a +@code{use-package} expansion. + +This is an example handler: + +@lisp +(defun use-package-handler/:pin (name-symbol keyword archive-name rest state) + (let ((body (use-package-process-keywords name-symbol rest state))) + ;; This happens at macro expansion time, not when the expanded code is + ;; compiled or evaluated. + (if (null archive-name) + body + (use-package-pin-package name-symbol archive-name) + (use-package-concat + body + `((push '(,name-symbol . ,archive-name) + package-pinned-packages)))))) +@end lisp + +@item +Test it. + +After the keyword has been inserted into @code{use-package-keywords}, +and a normalizer and a handler defined, you can now test it by seeing +how usages of the keyword will expand. For this, use @code{M-x +pp-macroexpand-last-sexp} with the cursor set immediately after the +@code{(use-package ...)} expression. +@end enumerate + +@c ---------------------------------------------------------------------------- +@node History +@appendix History and acknowledgments + +use-package was written by John Wiegley. Its development started in +2012, and it got merged into Emacs in 2022, in preparation of the +release of Emacs 29.1. + +Dozens of people have contributed to use-package over the years with +bug reports, documentation and code. They are too many to list here, +but we thank them all for their contributions. + +This Texinfo manual was written by Stefan Kangas, as a significant +rewrite of the old use-package manual and @file{README}. + +@node GNU Free Documentation License +@appendix GNU Free Documentation License +@include doclicense.texi + +@node Index +@unnumbered Index +@printindex cp + +@bye diff --git a/etc/NEWS.29 b/etc/NEWS.29 index dc758f0bc83..99c71f243a5 100644 --- a/etc/NEWS.29 +++ b/etc/NEWS.29 @@ -2963,6 +2963,13 @@ based on data provided by language servers using the Language Server Protocol (LSP). +++ +*** use-package: Declarative package configuration. +use-package is shipped with Emacs. It provides the 'use-package' +macro, which allows you to isolate package configuration in your init +file in a way that is declarative, tidy, and performance-oriented. +See the new Info manual 'use-package' for more. + ++++ ** New commands 'image-crop' and 'image-cut'. These commands allow interactively cropping/cutting the image at point. The commands are bound to keys 'i c' and 'i x' (respectively) diff --git a/lisp/comint.el b/lisp/comint.el index 6742360d16b..7ba423e65de 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -384,7 +384,7 @@ This variable is buffer-local." "\\(?:\\(?:, try\\)? *again\\| (empty for no passphrase)\\| (again)\\)?" ;; "[[:alpha:]]" used to be "for", which fails to match non-English. "\\(?: [[:alpha:]]+ .+\\)?[[:blank:]]*[::៖][[:space:]]*\\'" - ;; The ccrypt encryption dialogue doesn't end with a colon, so + ;; The ccrypt encryption dialog doesn't end with a colon, so ;; treat it specially. "\\|^Enter encryption key: (repeat) *\\'" ;; openssh-8.6p1 format: "(user@host) Password:". diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el index f87cc2f20aa..48ac3e2bd4d 100644 --- a/lisp/eshell/esh-arg.el +++ b/lisp/eshell/esh-arg.el @@ -146,9 +146,10 @@ If POS is nil, the location of point is checked." When each function on this hook is called, point will be at the current position within the argument list. The function should either return nil, meaning that it did no argument parsing, or it should -return the result of the parse as a sexp. It is also responsible for -moving the point forward to reflect the amount of input text that was -parsed. +return the result of the parse as a sexp. If the function did do +argument parsing, but the result was nothing at all, it should return +`eshell-empty-token'. The function is also responsible for moving the +point forward to reflect the amount of input text that was parsed. If the hook determines that it has reached the end of an argument, it should call `eshell-finish-arg' to complete processing of the current @@ -325,13 +326,14 @@ Point is left at the end of the arguments." (prog1 (char-to-string (char-after)) (forward-char))))) - (if (not eshell-current-argument) - (setq eshell-current-argument result) - (unless eshell-arg-listified - (setq eshell-current-argument - (list eshell-current-argument) - eshell-arg-listified t)) - (nconc eshell-current-argument (list result)))))) + (unless (eq result 'eshell-empty-token) + (if (not eshell-current-argument) + (setq eshell-current-argument result) + (unless eshell-arg-listified + (setq eshell-current-argument + (list eshell-current-argument) + eshell-arg-listified t)) + (nconc eshell-current-argument (list result))))))) (when (and outer eshell-current-argument) (add-text-properties arg-begin (1+ arg-begin) '(arg-begin t rear-nonsticky @@ -375,15 +377,20 @@ after are both returned." (when (eshell-looking-at-backslash-return (point)) (throw 'eshell-incomplete ?\\)) (forward-char 2) ; Move one char past the backslash. - ;; If the char is in a quote, backslash only has special meaning - ;; if it is escaping a special char. - (if eshell-current-quoted - (if (memq (char-before) eshell-special-chars-inside-quoting) + (if (eq (char-before) ?\n) + ;; Escaped newlines are extra-special: they expand to an empty + ;; token to allow for continuing Eshell commands across + ;; multiple lines. + 'eshell-empty-token + ;; If the char is in a quote, backslash only has special meaning + ;; if it is escaping a special char. + (if eshell-current-quoted + (if (memq (char-before) eshell-special-chars-inside-quoting) + (list 'eshell-escape-arg (char-to-string (char-before))) + (concat "\\" (char-to-string (char-before)))) + (if (memq (char-before) eshell-special-chars-outside-quoting) (list 'eshell-escape-arg (char-to-string (char-before))) - (concat "\\" (char-to-string (char-before)))) - (if (memq (char-before) eshell-special-chars-outside-quoting) - (list 'eshell-escape-arg (char-to-string (char-before))) - (char-to-string (char-before)))))) + (char-to-string (char-before))))))) (defun eshell-parse-literal-quote () "Parse a literally quoted string. Nothing has special meaning!" diff --git a/lisp/external-completion.el b/lisp/external-completion.el new file mode 100644 index 00000000000..a661f031c77 --- /dev/null +++ b/lisp/external-completion.el @@ -0,0 +1,174 @@ +;;; external-completion.el --- Let external tools control completion style -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2022 Free Software Foundation, Inc. + +;; Version: 0.1 +;; Author: Stefan Monnier <monnier@iro.umontreal.ca> +;; Maintainer: João Távora <joaotavora@gmail.com> +;; Keywords: + +;; 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: + +;; Written by Stefan Monnier circa 2016. Variants of this code have +;; been working stably in SLY and other packages for a long time. + +;; The `external' completion style is used with a "programmable +;; completion" table that gathers completions from an external tool +;; such as a shell utility, an inferior process, an http server. + +;; The table and external tool are fully in control of the matching of +;; the pattern string to the potential candidates of completion. When +;; `external' is in use, the usual styles configured by the user or +;; other in `completion-styles' are ignored. +;; +;; This compromise is for speed: all other styles need the full data +;; set to be available in Emacs' addressing space, which is often slow +;; if not completely unfeasible. +;; +;; To make use of the `external' style the function +;; `external-completion-table' should be used. See its docstring. + +;;; Code: +(require 'cl-lib) + +(add-to-list 'completion-styles-alist + '(external + external-completion--try-completion + external-completion--all-completions + "Ad-hoc completion style provided by the completion table.")) + +(defun external-completion-table (category lookup + &optional metadata + try-completion-function) + "Make completion table using the `external' completion style. + +The `external' style is particularly useful when the caller +interfaces with an external tool that provides completions. This +may be a shell utility, an inferior process, an http server, etc. +Given a pattern string, the external tool matches it to an +arbitrarily large set of candidates. Since the full set doesn't +need to be transferred to Emacs's address space, this often +results in much faster overall experience, at the expense of the +convenience of offered by other completion styles. + +CATEGORY is a symbol uniquely naming the external tool. This +function links CATEGORY to the style `external', by modifying +`completion-category-defaults', overriding any styles normally +set in `completion-styles'. + +LOOKUP is a function taking a string PATTERN and a number +POINT. The function should contact the tool and return a list of +strings representing the completions for PATTERN given that POINT +is the location of point within it. LOOKUP decides if PATTERN is +interpreted as a substring, a regular expression, or any other +type of matching method. The strings returned may be propertized +with `completions-common-part' to illustrate the specific method +used. LOOKUP may ignore POINT if it doesn't meaningfully alter +the results. + +LOOKUP is a synchronous blocking function. Since it contacts an +external tool, it's possible that it takes significant time to +return results. To maintain Emacs's responsiveness, LOOKUP +should detect pending user input using `while-no-input' or +`sit-for' (which see). In those cases, LOOKUP should attempt to +cancel the request (if possible) and immediately return any +non-list. + +METADATA is an alist of additional properties such as +`cycle-sort-function' to associate with CATEGORY. This means +that the caller may still retain control the sorting of the +candidates while the tool controls the matching. + +Optional TRY-COMPLETION-FUNCTION helps some frontends partially +or fully expand PATTERN before finishing the completion +operation. If supplied, it is a function taking a (PATTERN POINT +ALL-COMPLETIONS), where PATTERN and POINT are as described above +and ALL-COMPLETIONS are gathered by LOOKUP for these +arguments (this function ensures LOOKUP isn't called more than +needed). If you know the matching method that the external tool +using, TRY-COMPLETION-FUNCTION may return a cons +cell (EXPANDED-PATTERN . NEW-POINT). For example, if the tool is +completing by prefix, one could call `try-completion' to find the +largest common prefix in ALL-COMPLETIONS and then return that as +EXPANDED-PATTERN." + (let ((probe (alist-get category completion-category-defaults))) + (if probe + (cl-assert (equal '(external) (alist-get 'styles probe)) + nil "Category `%s' must only use `external' style" category) + (push `(,category (styles external)) + completion-category-defaults))) + (let ((cache (make-hash-table :test #'equal))) + (cl-flet ((lookup-internal (string point) + (let* ((key (cons string point)) + (probe (gethash key cache 'external--notfound))) + (if (eq probe 'external--notfound) + (puthash key (funcall lookup string point) cache) + probe)))) + (lambda (string pred action) + (pcase action + (`metadata + `(metadata (category . ,category) . ,metadata)) + ;; Note: the `--tryc' `--allc' suffixes are made akward on + ;; purpose, so it's easy to pick them apart from the jungle + ;; of combinations of "try" and "all" and "completion" that + ;; inhabit Emacs's completion logic. + (`(external-completion--tryc . ,point) + ;; FIXME: Obey `pred'? Pass it to `try-completion-function'? + `(external-completion--tryc + . ,(if try-completion-function + (funcall try-completion-function + string + point + (lookup-internal string point)) + (cons string point)))) + (`(external-completion--allc . ,point) + (let ((all (lookup-internal string point))) + `(external-completion--allc + . ,(if pred (cl-remove-if-not pred all) all)))) + (`(boundaries . ,_) nil) + (_method + (let ((all (lookup-internal string (length string)))) + ;; This branch might be taken: + ;; + ;; * when users work around + ;; `completion-category-defaults' (via + ;; `completion-category-overrides') and access this + ;; table with another completion style. We assume + ;; these users know what they are doing, but it might + ;; not work very well, as this whatever is in `all' + ;; very often doesn't equate the full set of candidates + ;; (many tools cap to sth like 100-1000 results). + ;; + ;; * when `_method' is `nil' or `lambda' which some + ;; frontends will invoke. Here, `all' should be + ;; sufficient information for `complete-with-action' to + ;; do the job correctly. + (complete-with-action action all string pred)))))))) + +(defun external-completion--call (op string table pred point) + (when (functionp table) + (let ((res (funcall table string pred (cons op point)))) + (when (eq op (car-safe res)) + (cdr res))))) + +(defun external-completion--try-completion (string table pred point) + (external-completion--call 'external-completion--tryc string table pred point)) + +(defun external-completion--all-completions (string table pred point) + (external-completion--call 'external-completion--allc string table pred point)) + +(provide 'external-completion) +;;; external-completion.el ends here diff --git a/lisp/international/ogonek.el b/lisp/international/ogonek.el index ac9a213ec75..216d2fd0f20 100644 --- a/lisp/international/ogonek.el +++ b/lisp/international/ogonek.el @@ -214,7 +214,7 @@ The functions come in the following groups. is stored in the constant `ogonek-name-encoding-alist'. The `ogonek' functions refer to five variables in which the suggested - answers to dialogue questions are stored. The variables and their + answers to dialog questions are stored. The variables and their default values are: ogonek-from-encoding iso8859-2 @@ -266,7 +266,7 @@ The functions come in the following groups. (switch-to-buffer " *ogonek-how*") (goto-char (point-min))) -;; ---- Variables keeping the suggested answers to dialogue questions ----- +;; ---- Variables keeping the suggested answers to dialog questions ----- (defvar ogonek-encoding-choices (cons 'choice (mapcar (lambda (x) (list 'const (car x))) diff --git a/lisp/org/ox.el b/lisp/org/ox.el index 5c0a8f2424b..f6629cde4dc 100644 --- a/lisp/org/ox.el +++ b/lisp/org/ox.el @@ -2559,7 +2559,7 @@ variable and do not do anything that might alter it (like calling a major mode) to prevent data corruption. Also, do note that Emacs may jump into the created buffer if the original file buffer is closed and then re-opened. Making edits in the buffer copy may also trigger -Emacs save dialogue. Prefer using `org-export-with-buffer-copy' macro +Emacs save dialog. Prefer using `org-export-with-buffer-copy' macro when possible. When optional key `:to-buffer' is non-nil, copy into BUFFER. diff --git a/lisp/progmodes/bat-mode.el b/lisp/progmodes/bat-mode.el index 6bac297a298..5e833047af1 100644 --- a/lisp/progmodes/bat-mode.el +++ b/lisp/progmodes/bat-mode.el @@ -44,7 +44,7 @@ ;; Separate package `dos-indent' (Matthew Fidler) provides rudimentary ;; indentation, see https://www.emacswiki.org/emacs/dos-indent.el. ;; -;; Acknowledgements: +;; Acknowledgments: ;; ;; Inspired by `batch-mode' (Agnar Renolen) and `cmd-mode' (Tadamegu Furukawa). diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 2c42505ac94..63008bca4da 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -22,7 +22,6 @@ ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <http://www.gnu.org/licenses/>. - ;;; Commentary: ;; @@ -114,14 +113,14 @@ "static" "strictfp" "switch" "synchronized" "throw" "throws" "to" "transient" "transitive" "try" "uses" "volatile" "while" "with" "record") - "C keywords for tree-sitter font-locking.") + "Java keywords for tree-sitter font-locking.") (defvar java-ts-mode--operators '("+" ":" "++" "-" "--" "&" "&&" "|" "||" "=" "!=" "==" "*" "/" "%" "<" "<=" ">" ">=" "-=" "+=" "*=" "/=" "%=" "->" "^" "^=" "|=" "~" ">>" ">>>" "<<" "::" "?" "&=") - "C operators for tree-sitter font-locking.") + "Java operators for tree-sitter font-locking.") (defvar java-ts-mode--font-lock-settings (treesit-font-lock-rules @@ -238,7 +237,7 @@ :language 'java :feature 'delimiter '((["," ":" ";"]) @font-lock-delimiter-face)) - "Tree-sitter font-lock settings.") + "Tree-sitter font-lock settings for `java-ts-mode'.") (defun java-ts-mode--imenu-1 (node) "Helper for `java-ts-mode--imenu'. diff --git a/lisp/progmodes/which-func.el b/lisp/progmodes/which-func.el index 14b749296cb..07050fb3d6b 100644 --- a/lisp/progmodes/which-func.el +++ b/lisp/progmodes/which-func.el @@ -51,9 +51,6 @@ ;;; Code: -;; So that we can use the edebug spec in `lisp-current-defun-name'. -(require 'edebug) - ;; Variables for customization ;; --------------------------- ;; diff --git a/lisp/use-package/bind-key.el b/lisp/use-package/bind-key.el new file mode 100644 index 00000000000..bbe3319d9bb --- /dev/null +++ b/lisp/use-package/bind-key.el @@ -0,0 +1,562 @@ +;;; bind-key.el --- A simple way to manage personal keybindings -*- lexical-binding: t; -*- + +;; Copyright (c) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> +;; Created: 16 Jun 2012 +;; Version: 2.4.1 +;; Package-Requires: ((emacs "24.3")) +;; Keywords: keys keybinding config dotemacs extensions +;; URL: https://github.com/jwiegley/use-package + +;; 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: + +;; If you have lots of keybindings set in your .emacs file, it can be hard to +;; know which ones you haven't set yet, and which may now be overriding some +;; new default in a new Emacs version. This module aims to solve that +;; problem. +;; +;; Bind keys as follows in your .emacs: +;; +;; (require 'bind-key) +;; +;; (bind-key "C-c x" 'my-ctrl-c-x-command) +;; +;; If the keybinding argument is a vector, it is passed straight to +;; `define-key', so remapping a key with `[remap COMMAND]' works as +;; expected: +;; +;; (bind-key [remap original-ctrl-c-x-command] 'my-ctrl-c-x-command) +;; +;; If you want the keybinding to override all minor modes that may also bind +;; the same key, use the `bind-key*' form: +;; +;; (bind-key* "<C-return>" 'other-window) +;; +;; If you want to rebind a key only in a particular keymap, use: +;; +;; (bind-key "C-c x" 'my-ctrl-c-x-command some-other-mode-map) +;; +;; To unbind a key within a keymap (for example, to stop your favorite major +;; mode from changing a binding that you don't want to override everywhere), +;; use `unbind-key': +;; +;; (unbind-key "C-c x" some-other-mode-map) +;; +;; To bind multiple keys at once, or set up a prefix map, a `bind-keys' macro +;; is provided. It accepts keyword arguments, please see its documentation +;; for a detailed description. +;; +;; To add keys into a specific map, use :map argument +;; +;; (bind-keys :map dired-mode-map +;; ("o" . dired-omit-mode) +;; ("a" . some-custom-dired-function)) +;; +;; To set up a prefix map, use `:prefix-map' and `:prefix' arguments (both are +;; required) +;; +;; (bind-keys :prefix-map my-customize-prefix-map +;; :prefix "C-c c" +;; ("f" . customize-face) +;; ("v" . customize-variable)) +;; +;; You can combine all the keywords together. Additionally, +;; `:prefix-docstring' can be specified to set documentation of created +;; `:prefix-map' variable. +;; +;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings +;; will not be overridden by other modes), you may use `bind-keys*' macro: +;; +;; (bind-keys* +;; ("C-o" . other-window) +;; ("C-M-n" . forward-page) +;; ("C-M-p" . backward-page)) +;; +;; After Emacs loads, you can see a summary of all your personal keybindings +;; currently in effect with this command: +;; +;; M-x describe-personal-keybindings +;; +;; This display will tell you if you've overridden a default keybinding, and +;; what the default was. Also, it will tell you if the key was rebound after +;; your binding it with `bind-key', and what it was rebound it to. + +;;; Code: + +(require 'cl-lib) +(require 'easy-mmode) + +(defgroup bind-key nil + "A simple way to manage personal keybindings." + :group 'keyboard + :group 'convenience + :link '(emacs-commentary-link :tag "Commentary" "bind-key.el") + :version "29.1") + +(defcustom bind-key-column-widths '(18 . 40) + "Width of columns in `describe-personal-keybindings'." + :type '(cons integer integer) + :group 'bind-key) + +(defcustom bind-key-segregation-regexp + "\\`\\(\\(C-[chx] \\|M-[gso] \\)\\([CM]-\\)?\\|.+-\\)" + "Regexp used by \\[describe-personal-keybindings] to divide key sets." + :type 'regexp + :group 'bind-key) + +(defcustom bind-key-describe-special-forms nil + "If non-nil, extract docstrings from lambdas, closures and keymaps if possible." + :type 'boolean + :group 'bind-key) + +;; Create override-global-mode to force key remappings + +(defvar override-global-map (make-keymap) + "Keymap for `override-global-mode'.") + +(define-minor-mode override-global-mode + "A minor mode for allowing keybindings to override other modes. +The main purpose of this mode is to simplify bindings keys in +such a way that they take precedence over other modes. + +To achieve this, the keymap `override-global-map' is added to +`emulation-mode-map-alists', which makes it take precedence over +keymaps in `minor-mode-map-alist'. Thereby, key bindings get an +even higher precedence than global key bindings defined with +`keymap-global-set' (or, in Emacs 28 or older, `global-set-key'). + +The macro `bind-key*' (which see) provides a convenient way to +add keys to that keymap." + :init-value t + :lighter "") + +;; the keymaps in `emulation-mode-map-alists' take precedence over +;; `minor-mode-map-alist' +(add-to-list 'emulation-mode-map-alists + `((override-global-mode . ,override-global-map))) + +(defvar personal-keybindings nil + "List of bindings performed by `bind-key'. + +Elements have the form ((KEY . [MAP]) CMD ORIGINAL-CMD)") + +;;;###autoload +(defmacro bind-key (key-name command &optional keymap predicate) + "Bind KEY-NAME to COMMAND in KEYMAP (`global-map' if not passed). + +KEY-NAME may be a vector, in which case it is passed straight to +`define-key'. Or it may be a string to be interpreted as +spelled-out keystrokes, e.g., \"C-c C-z\". See the documentation +of `edmacro-mode' for details. + +COMMAND must be an interactive function or lambda form. + +KEYMAP, if present, should be a keymap variable or symbol. +For example: + + (bind-key \"M-h\" #\\='some-interactive-function my-mode-map) + + (bind-key \"M-h\" #\\='some-interactive-function \\='my-mode-map) + +If PREDICATE is non-nil, it is a form evaluated to determine when +a key should be bound. It must return non-nil in such cases. +Emacs can evaluate this form at any time that it does redisplay +or operates on menu data structures, so you should write it so it +can safely be called at any time." + (let ((namevar (make-symbol "name")) + (keyvar (make-symbol "key")) + (kmapvar (make-symbol "kmap")) + (kdescvar (make-symbol "kdesc")) + (bindingvar (make-symbol "binding"))) + `(let* ((,namevar ,key-name) + (,keyvar ,(if (stringp key-name) (read-kbd-macro key-name) + `(if (vectorp ,namevar) ,namevar + (read-kbd-macro ,namevar)))) + (,kmapvar (or (if (and ,keymap (symbolp ,keymap)) + (symbol-value ,keymap) ,keymap) + global-map)) + (,kdescvar (cons (if (stringp ,namevar) ,namevar + (key-description ,namevar)) + (if (symbolp ,keymap) ,keymap (quote ,keymap)))) + (,bindingvar (lookup-key ,kmapvar ,keyvar))) + (let ((entry (assoc ,kdescvar personal-keybindings)) + (details (list ,command + (unless (numberp ,bindingvar) + ,bindingvar)))) + (if entry + (setcdr entry details) + (add-to-list 'personal-keybindings (cons ,kdescvar details)))) + ,(if predicate + `(define-key ,kmapvar ,keyvar + '(menu-item "" nil :filter (lambda (&optional _) + (when ,predicate + ,command)))) + `(define-key ,kmapvar ,keyvar ,command))))) + +;;;###autoload +(defmacro unbind-key (key-name &optional keymap) + "Unbind the given KEY-NAME, within the KEYMAP (if specified). +See `bind-key' for more details." + (let ((namevar (make-symbol "name")) + (kdescvar (make-symbol "kdesc"))) + `(let* ((,namevar ,key-name) + (,kdescvar (cons (if (stringp ,namevar) ,namevar + (key-description ,namevar)) + (if (symbolp ,keymap) ,keymap (quote ,keymap))))) + (bind-key--remove (if (vectorp ,namevar) ,namevar + (read-kbd-macro ,namevar)) + (or (if (and ,keymap (symbolp ,keymap)) + (symbol-value ,keymap) ,keymap) + global-map)) + (setq personal-keybindings + (cl-delete-if (lambda (k) (equal (car k) ,kdescvar)) + personal-keybindings)) + nil))) + +(defun bind-key--remove (key keymap) + "Remove KEY from KEYMAP. + +In contrast to `define-key', this function removes the binding from the keymap." + (define-key keymap key nil) + ;; Split M-key in ESC key + (setq key (cl-mapcan (lambda (k) + (if (and (integerp k) (/= (logand k ?\M-\0) 0)) + (list ?\e (logxor k ?\M-\0)) + (list k))) + key)) + ;; Delete single keys directly + (if (= (length key) 1) + (delete key keymap) + ;; Lookup submap and delete key from there + (let* ((prefix (vconcat (butlast key))) + (submap (lookup-key keymap prefix))) + (unless (keymapp submap) + (error "Not a keymap for %s" key)) + (when (symbolp submap) + (setq submap (symbol-function submap))) + (delete (last key) submap) + ;; Delete submap if it is empty + (when (= 1 (length submap)) + (bind-key--remove prefix keymap))))) + +;;;###autoload +(defmacro bind-key* (key-name command &optional predicate) + "Similar to `bind-key', but overrides any mode-specific bindings." + `(bind-key ,key-name ,command override-global-map ,predicate)) + +(defun bind-keys-form (args keymap) + "Bind multiple keys at once. + +Accepts keyword arguments: +:map MAP - a keymap into which the keybindings should be + added +:prefix KEY - prefix key for these bindings +:prefix-map MAP - name of the prefix map that should be created + for these bindings +:prefix-docstring STR - docstring for the prefix-map variable +:menu-name NAME - optional menu string for prefix map +:repeat-docstring STR - docstring for the repeat-map variable +:repeat-map MAP - name of the repeat map that should be created + for these bindings. If specified, the + `repeat-map' property of each command bound + (within the scope of the `:repeat-map' keyword) + is set to this map. +:exit BINDINGS - Within the scope of `:repeat-map' will bind the + key in the repeat map, but will not set the + `repeat-map' property of the bound command. +:continue BINDINGS - Within the scope of `:repeat-map' forces the + same behaviour as if no special keyword had + been used (that is, the command is bound, and + it's `repeat-map' property set) +:filter FORM - optional form to determine when bindings apply + +The rest of the arguments are conses of keybinding string and a +function symbol (unquoted)." + (let (map + prefix-doc + prefix-map + prefix + repeat-map + repeat-doc + repeat-type ;; Only used internally + filter + menu-name + pkg) + + ;; Process any initial keyword arguments + (let ((cont t) + (arg-change-func 'cddr)) + (while (and cont args) + (if (cond ((and (eq :map (car args)) + (not prefix-map)) + (setq map (cadr args))) + ((eq :prefix-docstring (car args)) + (setq prefix-doc (cadr args))) + ((and (eq :prefix-map (car args)) + (not (memq map '(global-map + override-global-map)))) + (setq prefix-map (cadr args))) + ((eq :repeat-docstring (car args)) + (setq repeat-doc (cadr args))) + ((and (eq :repeat-map (car args)) + (not (memq map '(global-map + override-global-map)))) + (setq repeat-map (cadr args)) + (setq map repeat-map)) + ((eq :continue (car args)) + (setq repeat-type :continue + arg-change-func 'cdr)) + ((eq :exit (car args)) + (setq repeat-type :exit + arg-change-func 'cdr)) + ((eq :prefix (car args)) + (setq prefix (cadr args))) + ((eq :filter (car args)) + (setq filter (cadr args)) t) + ((eq :menu-name (car args)) + (setq menu-name (cadr args))) + ((eq :package (car args)) + (setq pkg (cadr args)))) + (setq args (funcall arg-change-func args)) + (setq cont nil)))) + + (when (or (and prefix-map (not prefix)) + (and prefix (not prefix-map))) + (error "Both :prefix-map and :prefix must be supplied")) + + (when repeat-type + (unless repeat-map + (error ":continue and :exit require specifying :repeat-map"))) + + (when (and menu-name (not prefix)) + (error "If :menu-name is supplied, :prefix must be too")) + + (unless map (setq map keymap)) + + ;; Process key binding arguments + (let (first next) + (while args + (if (keywordp (car args)) + (progn + (setq next args) + (setq args nil)) + (if first + (nconc first (list (car args))) + (setq first (list (car args)))) + (setq args (cdr args)))) + + (cl-flet + ((wrap (map bindings) + (if (and map pkg (not (memq map '(global-map + override-global-map)))) + `((if (boundp ',map) + ,(macroexp-progn bindings) + (eval-after-load + ,(if (symbolp pkg) `',pkg pkg) + ',(macroexp-progn bindings)))) + bindings))) + + (append + (when prefix-map + `((defvar ,prefix-map) + ,@(when prefix-doc `((put ',prefix-map 'variable-documentation ,prefix-doc))) + ,@(if menu-name + `((define-prefix-command ',prefix-map nil ,menu-name)) + `((define-prefix-command ',prefix-map))) + ,@(if (and map (not (eq map 'global-map))) + (wrap map `((bind-key ,prefix ',prefix-map ,map ,filter))) + `((bind-key ,prefix ',prefix-map nil ,filter))))) + (when repeat-map + `((defvar ,repeat-map (make-sparse-keymap) + ,@(when repeat-doc `(,repeat-doc))))) + (wrap map + (cl-mapcan + (lambda (form) + (let ((fun (and (cdr form) (list 'function (cdr form))))) + (if prefix-map + `((bind-key ,(car form) ,fun ,prefix-map ,filter)) + (if (and map (not (eq map 'global-map))) + ;; Only needed in this branch, since when + ;; repeat-map is non-nil, map is always + ;; non-nil + `(,@(when (and repeat-map (not (eq repeat-type :exit))) + `((put ,fun 'repeat-map ',repeat-map))) + (bind-key ,(car form) ,fun ,map ,filter)) + `((bind-key ,(car form) ,fun nil ,filter)))))) + first)) + (when next + (bind-keys-form `(,@(when repeat-map `(:repeat-map ,repeat-map)) + ,@(if pkg + (cons :package (cons pkg next)) + next)) map))))))) + +;;;###autoload +(defmacro bind-keys (&rest args) + "Bind multiple keys at once. + +Accepts keyword arguments: +:map MAP - a keymap into which the keybindings should be + added +:prefix KEY - prefix key for these bindings +:prefix-map MAP - name of the prefix map that should be created + for these bindings +:prefix-docstring STR - docstring for the prefix-map variable +:menu-name NAME - optional menu string for prefix map +:repeat-docstring STR - docstring for the repeat-map variable +:repeat-map MAP - name of the repeat map that should be created + for these bindings. If specified, the + `repeat-map' property of each command bound + (within the scope of the `:repeat-map' keyword) + is set to this map. +:exit BINDINGS - Within the scope of `:repeat-map' will bind the + key in the repeat map, but will not set the + `repeat-map' property of the bound command. +:continue BINDINGS - Within the scope of `:repeat-map' forces the + same behaviour as if no special keyword had + been used (that is, the command is bound, and + it's `repeat-map' property set) +:filter FORM - optional form to determine when bindings apply + +The rest of the arguments are conses of keybinding string and a +function symbol (unquoted)." + (macroexp-progn (bind-keys-form args nil))) + +;;;###autoload +(defmacro bind-keys* (&rest args) + "Bind multiple keys at once, in `override-global-map'. +Accepts the same keyword arguments as `bind-keys' (which see). + +This binds keys in such a way that bindings are not overridden by +other modes. See `override-global-mode'." + (macroexp-progn (bind-keys-form args 'override-global-map))) + +(defun get-binding-description (elem) + (cond + ((listp elem) + (cond + ((memq (car elem) '(lambda function)) + (if (and bind-key-describe-special-forms + (stringp (nth 2 elem))) + (nth 2 elem) + "#<lambda>")) + ((eq 'closure (car elem)) + (if (and bind-key-describe-special-forms + (stringp (nth 3 elem))) + (nth 3 elem) + "#<closure>")) + ((eq 'keymap (car elem)) + "#<keymap>") + (t + elem))) + ;; must be a symbol, non-symbol keymap case covered above + ((and bind-key-describe-special-forms (keymapp elem)) + (let ((doc (get elem 'variable-documentation))) + (if (stringp doc) doc elem))) + ((symbolp elem) + elem) + (t + "#<byte-compiled lambda>"))) + +(defun compare-keybindings (l r) + (let* ((regex bind-key-segregation-regexp) + (lgroup (and (string-match regex (caar l)) + (match-string 0 (caar l)))) + (rgroup (and (string-match regex (caar r)) + (match-string 0 (caar r)))) + (lkeymap (cdar l)) + (rkeymap (cdar r))) + (cond + ((and (null lkeymap) rkeymap) + (cons t t)) + ((and lkeymap (null rkeymap)) + (cons nil t)) + ((and lkeymap rkeymap + (not (string= (symbol-name lkeymap) (symbol-name rkeymap)))) + (cons (string< (symbol-name lkeymap) (symbol-name rkeymap)) t)) + ((and (null lgroup) rgroup) + (cons t t)) + ((and lgroup (null rgroup)) + (cons nil t)) + ((and lgroup rgroup) + (if (string= lgroup rgroup) + (cons (string< (caar l) (caar r)) nil) + (cons (string< lgroup rgroup) t))) + (t + (cons (string< (caar l) (caar r)) nil))))) + +;;;###autoload +(defun describe-personal-keybindings () + "Display all the personal keybindings defined by `bind-key'." + (interactive) + (with-output-to-temp-buffer "*Personal Keybindings*" + (princ (format (concat "Key name%s Command%s Comments\n%s %s " + "---------------------\n") + (make-string (- (car bind-key-column-widths) 9) ? ) + (make-string (- (cdr bind-key-column-widths) 8) ? ) + (make-string (1- (car bind-key-column-widths)) ?-) + (make-string (1- (cdr bind-key-column-widths)) ?-))) + (let (last-binding) + (dolist (binding + (setq personal-keybindings + (sort personal-keybindings + (lambda (l r) + (car (compare-keybindings l r)))))) + + (if (not (eq (cdar last-binding) (cdar binding))) + (princ (format "\n\n%s: %s\n%s\n\n" + (cdar binding) (caar binding) + (make-string (+ 21 (car bind-key-column-widths) + (cdr bind-key-column-widths)) ?-))) + (if (and last-binding + (cdr (compare-keybindings last-binding binding))) + (princ "\n"))) + + (let* ((key-name (caar binding)) + (at-present (lookup-key (or (symbol-value (cdar binding)) + (current-global-map)) + (read-kbd-macro key-name))) + (command (nth 1 binding)) + (was-command (nth 2 binding)) + (command-desc (get-binding-description command)) + (was-command-desc (and was-command + (get-binding-description was-command))) + (at-present-desc (get-binding-description at-present))) + (let ((line + (format + (format "%%-%ds%%-%ds%%s\n" (car bind-key-column-widths) + (cdr bind-key-column-widths)) + key-name (format "`%s\'" command-desc) + (if (string= command-desc at-present-desc) + (if (or (null was-command) + (string= command-desc was-command-desc)) + "" + (format "was `%s\'" was-command-desc)) + (format "[now: `%s\']" at-present))))) + (princ (if (string-match "[ \t]+\n" line) + (replace-match "\n" t t line) + line)))) + + (setq last-binding binding))))) + +(provide 'bind-key) + +;; Local Variables: +;; outline-regexp: ";;;\\(;* [^\s\t\n]\\|###autoload\\)\\|(" +;; End: + +;;; bind-key.el ends here diff --git a/lisp/use-package/use-package-bind-key.el b/lisp/use-package/use-package-bind-key.el new file mode 100644 index 00000000000..75def7febdf --- /dev/null +++ b/lisp/use-package/use-package-bind-key.el @@ -0,0 +1,171 @@ +;;; use-package-bind-key.el --- Support for the :bind/:bind-keymap keywords -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides support for the :bind, :bind*, :bind-keymap and :bind-keymap* +;; keywords. Note that these are currently still baked into +;; `use-package-keywords' and `use-package-deferring-keywords', although this +;; is harmless if they are never used. + +;;; Code: + +(require 'use-package-core) +(require 'bind-key) + +;;;###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 + (mapcar (lambda (ev) (cons t ev)) + (listify-key-sequence kv)))) + (use-package-error + (format "package.el %s failed to define keymap %s" + package keymap-symbol))))) + +;;;###autoload +(defun use-package-normalize-binder (name keyword args) + (let ((arg args) + args*) + (while arg + (let ((x (car arg))) + (cond + ;; (KEY . COMMAND) + ((and (consp x) + (or (stringp (car x)) + (vectorp (car x))) + (or (use-package-recognize-function (cdr x) t #'stringp))) + (setq args* (nconc args* (list x))) + (setq arg (cdr arg))) + ;; KEYWORD + ;; :map KEYMAP + ;; :prefix-docstring STRING + ;; :prefix-map SYMBOL + ;; :prefix STRING + ;; :repeat-docstring STRING + ;; :repeat-map SYMBOL + ;; :filter SEXP + ;; :menu-name STRING + ;; :package SYMBOL + ;; :continue and :exit are used within :repeat-map + ((or (and (eq x :map) (symbolp (cadr arg))) + (and (eq x :prefix) (stringp (cadr arg))) + (and (eq x :prefix-map) (symbolp (cadr arg))) + (and (eq x :prefix-docstring) (stringp (cadr arg))) + (and (eq x :repeat-map) (symbolp (cadr arg))) + (eq x :continue) + (eq x :exit) + (and (eq x :repeat-docstring) (stringp (cadr arg))) + (eq x :filter) + (and (eq x :menu-name) (stringp (cadr arg))) + (and (eq x :package) (symbolp (cadr arg)))) + (setq args* (nconc args* (list x (cadr arg)))) + (setq arg (cddr arg))) + ((listp x) + (setq args* + (nconc args* (use-package-normalize-binder name keyword x))) + (setq arg (cdr arg))) + (t + ;; Error! + (use-package-error + (concat (symbol-name name) + " wants arguments acceptable to the `bind-keys' macro," + " or a list of such values")))))) + args*)) + +;;;; :bind, :bind* + +;;;###autoload +(defalias 'use-package-normalize/:bind 'use-package-normalize-binder) +;;;###autoload +(defalias 'use-package-normalize/:bind* 'use-package-normalize-binder) + +;; jww (2017-12-07): This is too simplistic. It will fail to determine +;; autoloads in this situation: +;; (use-package foo +;; :bind (:map foo-map (("C-a" . func)))) +;;;###autoload +(defalias 'use-package-autoloads/:bind 'use-package-autoloads-mode) +;;;###autoload +(defalias 'use-package-autoloads/:bind* 'use-package-autoloads-mode) + +;;;###autoload +(defun use-package-handler/:bind + (name _keyword args rest state &optional bind-macro) + (use-package-concat + (use-package-process-keywords name rest state) + `(,@(mapcar + #'(lambda (xs) + `(,(if bind-macro bind-macro 'bind-keys) + :package ,name ,@(use-package-normalize-commands xs))) + (use-package-split-list-at-keys :break args))))) + +(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* + +;;;###autoload +(defalias 'use-package-normalize/:bind-keymap 'use-package-normalize-binder) +;;;###autoload +(defalias 'use-package-normalize/:bind-keymap* 'use-package-normalize-binder) + +;;;###autoload +(defun use-package-handler/:bind-keymap + (name _keyword args rest state &optional override) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (binding) + `(,(if override 'bind-key* 'bind-key) + ,(car binding) + #'(lambda () + (interactive) + (use-package-autoload-keymap + ',(cdr binding) ',(use-package-as-symbol name) + ,override)))) + args))) + +;;;###autoload +(defun use-package-handler/:bind-keymap* (name keyword arg rest state) + (use-package-handler/:bind-keymap name keyword arg rest state t)) + +(provide 'use-package-bind-key) + +;;; use-package-bind-key.el ends here diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el new file mode 100644 index 00000000000..5ebe6576190 --- /dev/null +++ b/lisp/use-package/use-package-core.el @@ -0,0 +1,1692 @@ +;;; use-package-core.el --- A configuration macro for simplifying your .emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; The `use-package' declaration macro allows you to isolate package +;; configuration in your ".emacs" in a way that is performance-oriented and, +;; well, just tidy. I created it because I have over 80 packages that I use +;; in Emacs, and things were getting difficult to manage. Yet with this +;; utility my total load time is just under 1 second, with no loss of +;; functionality! +;; +;; Please see README.md from the same repository for documentation. + +;;; Code: + +(require 'bytecomp) +(require 'cl-lib) +(require 'tabulated-list) + +(eval-and-compile + ;; Declare a synthetic theme for :custom variables. + ;; Necessary in order to avoid having those variables saved by custom.el. + (deftheme use-package)) + +(enable-theme 'use-package) +;; Remove the synthetic use-package theme from the enabled themes, so +;; iterating over them to "disable all themes" won't disable it. +(setq custom-enabled-themes (remq 'use-package custom-enabled-themes)) + +(eval-when-compile + (if (and (eq emacs-major-version 24) (eq emacs-minor-version 3)) + (progn + (defsubst hash-table-keys (hash-table) + "Return a list of keys in HASH-TABLE." + (cl-loop for k being the hash-keys of hash-table collect k)) + (defsubst string-suffix-p (suffix string &optional ignore-case) + (let ((start-pos (- (length string) (length suffix)))) + (and (>= start-pos 0) + (eq t (compare-strings suffix nil nil + string start-pos nil ignore-case)))))) + (require 'subr-x))) + +(eval-when-compile + (require 'regexp-opt)) + +(defgroup use-package nil + "A `use-package' declaration for simplifying your `.emacs'." + :group 'initialization + :link '(custom-manual "(use-package) Top") + :version "29.1") + +(defconst use-package-version "2.4.4" + "This version of `use-package'.") + +(defcustom use-package-keywords + '(:disabled + :load-path + :requires + :defines + :functions + :preface + :if :when :unless + :no-require + :catch + :after + :custom + :custom-face + :bind + :bind* + :bind-keymap + :bind-keymap* + :interpreter + :mode + :magic + :magic-fallback + :hook + ;; Any other keyword that also declares commands to be autoloaded (such as + ;; :bind) must appear before this keyword. + :commands + :autoload + :init + :defer + :demand + :load + ;; This must occur almost last; the only forms which should appear after + ;; are those that must happen directly after the config forms. + :config) + "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-deferring-keywords + '(:bind-keymap + :bind-keymap* + :commands + :autoload) + "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) + +(defcustom use-package-ignore-unknown-keywords nil + "If non-nil, warn instead of signaling error for unknown keywords. +The unknown keyword and its associated arguments will be ignored +in the `use-package' expansion." + :type 'boolean + :group 'use-package) + +(defcustom use-package-use-theme t + "If non-nil, use a custom theme to avoid saving :custom +variables twice (once in the Custom file, once in the use-package +call)." + :type 'boolean + :group '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, +then the expanded macros do their job silently." + :type '(choice (const :tag "Quiet, without catching errors" errors) + (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. +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' 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' is used. +See also `use-package-defaults', which uses this value." + :type 'boolean + :group 'use-package) + +(defcustom use-package-defaults + '(;; this '(t) has special meaning; see `use-package-handler/:config' + (:config '(t) t) + (:init nil t) + (:catch t (lambda (name args) + (not use-package-expand-minimally))) + (:defer use-package-always-defer + (lambda (name args) + (and use-package-always-defer + (not (plist-member args :defer)) + (not (plist-member args :demand))))) + (:demand use-package-always-demand + (lambda (name args) + (and use-package-always-demand + (not (plist-member args :defer)) + (not (plist-member args :demand)))))) + "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. + +The second is a form that can be evaluated to get the default +value. It can also be a function that will receive the name of +the `use-package' declaration and the keyword plist given to +`use-package', in normalized form. The value it returns should +also be in normalized form (which is sometimes *not* what one +would normally write in a `use-package' declaration, so use +caution). + +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 name of the package (as +a symbol) and a list of keywords (in normalized form). It should +return nil or non-nil depending on 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 function) + (choice :tag "Enable if non-nil" sexp function))) + :group 'use-package) + +(defcustom use-package-merge-key-alist + '((:if . (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)))) + "Alist of keys and the functions used to merge multiple values. +For example, if the following form is provided: + + (use-package foo :if pred1 :if pred2) + +Then based on the above defaults, the merged result will be: + + (use-package foo :if (and pred1 pred2)) + +This is done so that, at the stage of invoking handlers, each +handler is called only once." + :type `(repeat + (cons (choice :tag "Keyword" + ,@(mapcar #'(lambda (k) (list 'const k)) + use-package-keywords) + (const :tag "Any" t)) + function)) + :group 'use-package) + +(defcustom use-package-hook-name-suffix "-hook" + "Text append to the name of hooks mentioned by :hook. +Set to nil if you don't want this to happen; it's only a +convenience." + :type '(choice string (const :tag "No suffix" nil)) + :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 a non-nil value +for anything to be reported at all." + :type 'number + :group 'use-package) + +(defcustom use-package-inject-hooks nil + "If non-nil, add hooks to the `:init' and `:config' sections. +In particular, for a given package `foo', the following hooks +become available: + + `use-package--foo--pre-init-hook' + `use-package--foo--post-init-hook' + `use-package--foo--pre-config-hook' + `use-package--foo--post-config-hook' + +This way, you can add to these hooks before evaluation of a +`use-package` declaration, and exercise some control over what +happens. + +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) + +(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 + - Capturing of load errors (normally redisplayed as warnings) + - Conditional loading of packages (load failures become errors) + +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, cause imenu to see `use-package' declarations. +This is done by adjusting `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) + (eval-after-load 'lisp-mode + (if value + `(add-to-list 'lisp-imenu-generic-expression + (list "Packages" ,use-package-form-regexp-eval 2)) + `(setq lisp-imenu-generic-expression + (remove (list "Packages" ,use-package-form-regexp-eval 2) + lisp-imenu-generic-expression)))) + (set-default sym value)) + :group 'use-package) + +(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) + +(defcustom use-package-compute-statistics nil + "If non-nil, compute statistics concerned `use-package' declarations. +View the statistical report using `use-package-report'. Note that +if this option is enabled, you must require `use-package' in your +user init file at loadup time, or you will see errors concerning +undefined variables." + :type 'boolean + :group 'use-package) + +(defvar use-package-statistics (make-hash-table)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Utility functions +;; + +(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))) + +(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 in RE, resolve 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 + (concat string "-mode"))))) + +(defsubst use-package-load-name (name &optional noerror) + "Return a form which will load or require NAME. +It does the right thing no matter if NAME is a string or symbol. +Argument NOERROR means to indicate load failures as a warning." + (if (stringp name) + `(load ,name ,noerror) + `(require ',name nil ,noerror))) + +(defun use-package-hook-injector (name-string keyword body) + "Wrap pre/post hook injections around the given BODY for KEYWORD. +The BODY is a list of forms, so `((foo))' if only `foo' is being called." + (if (not use-package-inject-hooks) + body + (let ((keyword-name (substring (format "%s" keyword) 1))) + `((when (run-hook-with-args-until-failure + ',(intern (concat "use-package--" name-string + "--pre-" keyword-name "-hook"))) + ,@body + (run-hooks + ',(intern (concat "use-package--" name-string + "--post-" keyword-name "-hook")))))))) + +(defun use-package-with-elapsed-timer (text body) + "BODY is a list of forms, so `((foo))' if only `foo' is being called." + (declare (indent 1)) + (if use-package-expand-minimally + body + (let ((nowvar (make-symbol "now"))) + (if (bound-and-true-p use-package-verbose) + `((let ((,nowvar (current-time))) + (message "%s..." ,text) + (prog1 + ,(macroexp-progn body) + (let ((elapsed + (float-time (time-subtract (current-time) ,nowvar)))) + (if (> elapsed ,use-package-minimum-reported-time) + (message "%s...done (%.3fs)" ,text elapsed) + (message "%s...done" ,text)))))) + body)))) + +(put 'use-package-with-elapsed-timer 'lisp-indent-function 1) + +(defun use-package-require (name &optional no-require body) + (if use-package-expand-minimally + (use-package-concat + (unless no-require + (list (use-package-load-name name))) + body) + (if no-require + body + (use-package-with-elapsed-timer + (format "Loading package %s" name) + `((if (not ,(use-package-load-name name t)) + (display-warning 'use-package + (format "Cannot load %s" ',name) + :error) + ,@body)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Property lists +;; + +(defun use-package-plist-delete (plist property) + "Delete PROPERTY from PLIST. +This is in contrast to merely setting it to 0." + (let (p) + (while plist + (if (not (eq property (car plist))) + (setq p (plist-put p (car plist) (nth 1 plist)))) + (setq plist (cddr plist))) + p)) + +(defun use-package-plist-delete-first (plist property) + "Delete PROPERTY from PLIST. +This is in contrast to merely setting it to 0." + (let (p) + (while plist + (if (eq property (car plist)) + (setq p (nconc p (cddr plist)) + plist nil) + (setq p (nconc p (list (car plist) (cadr plist))) + 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) + (cl-dolist (x xs) + (if flip + (nconc zs (list x)) + (if (funcall pred x) + (progn + (setq flip t) + (nconc zs (list x))) + (nconc ys (list x))))) + (cons (cdr ys) (cdr zs)))) + +(defun use-package-split-list-at-keys (key lst) + (and lst + (let ((xs (use-package-split-list (apply-partially #'eq key) lst))) + (cons (car xs) (use-package-split-list-at-keys key (cddr xs)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Keywords +;; + +(defun use-package-keyword-index (keyword) + (cl-loop named outer + with index = 0 + for k in use-package-keywords do + (if (eq k keyword) + (cl-return-from outer index)) + (cl-incf index))) + +(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, +extending any keys already present." + (if (null input) + plist + (let* ((keyword (car input)) + (xs (use-package-split-list #'keywordp (cdr input))) + (args (car xs)) + (tail (cdr xs)) + (normalizer + (intern-soft (concat "use-package-normalize/" + (symbol-name keyword)))) + (arg (and (functionp normalizer) + (funcall normalizer name keyword args))) + (error-string (format "Unrecognized keyword: %s" keyword))) + (if (memq keyword use-package-keywords) + (progn + (setq plist (use-package-normalize-plist + name tail plist merge-function)) + (plist-put plist keyword + (if (plist-member plist keyword) + (funcall merge-function keyword arg + (plist-get plist keyword)) + arg))) + (if use-package-ignore-unknown-keywords + (progn + (display-warning 'use-package error-string) + (use-package-normalize-plist + name tail plist merge-function)) + (use-package-error error-string)))))) + +(defun use-package-unalias-keywords (_name args) + (setq args (cl-nsubstitute :if :when args)) + (let (temp) + (while (setq temp (plist-get args :unless)) + (setq args (use-package-plist-delete-first args :unless) + args (append args `(:if (not ,temp)))))) + args) + +(defun use-package-merge-keys (key new old) + (let ((merger (assq key use-package-merge-key-alist))) + (if merger + (funcall (cdr merger) new old) + (append new old)))) + +(defun use-package-sort-keywords (plist) + (let (plist-grouped) + (while plist + (push (cons (car plist) (cadr plist)) + plist-grouped) + (setq plist (cddr plist))) + (let (result) + (cl-dolist + (x + (nreverse + (sort plist-grouped + #'(lambda (l r) (< (use-package-keyword-index (car l)) + (use-package-keyword-index (car r))))))) + (setq result (cons (car x) (cons (cdr x) result)))) + result))) + +(defun use-package-normalize-keywords (name args) + (let* ((name-symbol (if (stringp name) (intern name) name)) + (name-string (symbol-name name-symbol))) + + ;; The function `elisp--local-variables' inserts this unbound variable into + ;; macro forms to determine the locally bound variables for + ;; `elisp-completion-at-point'. It ends up throwing a lot of errors since it + ;; can occupy the position of a keyword (or look like a second argument to a + ;; keyword that takes one). Deleting it when it's at the top level should be + ;; harmless since there should be no locally bound variables to discover + ;; here anyway. + (setq args (delq 'elisp--witness--lisp args)) + + ;; Reduce the set of keywords down to its most fundamental expression. + (setq args (use-package-unalias-keywords name-symbol args)) + + ;; Normalize keyword values, coalescing multiple occurrences. + (setq args (use-package-normalize-plist name-symbol args nil + #'use-package-merge-keys)) + + ;; Add default values for keywords not specified, when applicable. + (cl-dolist (spec use-package-defaults) + (when (let ((func (nth 2 spec))) + (if (and func (functionp func)) + (funcall func name args) + (eval func))) + (setq args (use-package-plist-maybe-put + args (nth 0 spec) + (let ((func (nth 1 spec))) + (if (and func (functionp func)) + (funcall func name args) + (eval func))))))) + + ;; 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) + (setq args + (use-package-plist-append + args :preface + (use-package-concat + (mapcar #'(lambda (var) `(defvar ,var)) + (plist-get args :defines)) + (mapcar #'(lambda (fn) `(declare-function ,fn ,name-string)) + (plist-get args :functions)) + `((eval-when-compile + (with-demoted-errors + ,(format "Cannot load %s: %%S" name-string) + ,(when (eq use-package-verbose 'debug) + `(message ,(format "Compiling package %s" name-string))) + ,(unless (plist-get args :no-require) + `(unless (featurep ',name-symbol) + (load ,name-string nil t)))))))))) + + ;; Certain keywords imply :defer, if :demand was not specified. + (when (and (not (plist-member args :demand)) + (not (plist-member args :defer)) + (not (or (equal '(t) (plist-get args :load)) + (equal (list (use-package-as-string name)) + (mapcar #'use-package-as-string + (plist-get args :load))))) + (cl-some #'identity + (mapcar (apply-partially #'plist-member args) + 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))) + (setq args (append args `(:load (,name))))) + + ;; Sort the list of keywords based on the order of `use-package-keywords'. + (use-package-sort-keywords args))) + +(defun use-package-process-keywords (name plist &optional state) + "Process the next keyword in the free-form property list PLIST. +The values in the PLIST have each been normalized by the function +use-package-normalize/KEYWORD (minus the colon). + +STATE is a property list that the function may modify and/or +query. This is useful if a package defines multiple keywords and +wishes them to have some kind of stateful interaction. + +Unless the KEYWORD being processed intends to ignore remaining +keywords, it must call this function recursively, passing in the +plist with its keyword and argument removed, and passing in the +next value for the STATE." + (declare (indent 1)) + (unless (null plist) + (let* ((keyword (car plist)) + (arg (cadr plist)) + (rest (cddr plist))) + (unless (keywordp keyword) + (use-package-error (format "%s is not a keyword" keyword))) + (let* ((handler (concat "use-package-handler/" (symbol-name keyword))) + (handler-sym (intern handler))) + (if (functionp handler-sym) + (funcall handler-sym name keyword arg rest state) + (use-package-error + (format "Keyword handler not defined: %s" handler))))))) + +(put 'use-package-process-keywords 'lisp-indent-function 'defun) + +(defun use-package-list-insert (elem xs &optional anchor after test) + "Insert ELEM into the list XS. +If ANCHOR is also a keyword, place the new KEYWORD before that +one. +If AFTER is non-nil, insert KEYWORD either at the end of the +keywords list, or after the ANCHOR if one has been provided. +If TEST is non-nil, it is the test used to compare ELEM to list +elements. The default is `eq'. +The modified list is returned. The original list is not modified." + (let (result) + (dolist (k xs) + (if (funcall (or test #'eq) k anchor) + (if after + (setq result (cons k result) + result (cons elem result)) + (setq result (cons elem result) + result (cons k result))) + (setq result (cons k result)))) + (if anchor + (nreverse result) + (if after + (nreverse (cons elem result)) + (cons elem (nreverse result)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Argument Processing +;; + +(defun use-package-only-one (label args f) + "Call F on the first member of ARGS if it has exactly one element." + (declare (indent 1)) + (cond + ((and (listp args) (listp (cdr args)) + (= (length args) 1)) + (funcall f label (car args))) + (t + (use-package-error + (concat label " wants exactly one argument"))))) + +(put 'use-package-only-one 'lisp-indent-function 'defun) + +(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 (cl-gentemp "use-package--loaded")) + (result (cl-gentemp "use-package--result")) + (next (cl-gentemp "use-package--next"))) + `((defvar ,loaded nil) + (defvar ,result nil) + (defvar ,next #'(lambda () (if ,loaded ,result + (setq ,loaded t ,result ,arg)))) + ,@(funcall f `((funcall ,next)))))) + +(defsubst use-package-normalize-value (_label arg) + "Normalize the Lisp value given by ARG. +The argument LABEL is ignored." + (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) + (memq (car form) + '(use-package bind-key bind-key* + unbind-key bind-keys bind-keys*))) + (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))) + (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 () ...))" + (or (if binding + (symbolp v) + (use-package-non-nil-symbolp v)) + (and (listp v) + (memq (car v) '(quote function)) + (use-package-non-nil-symbolp (cadr v))) + (if binding (commandp v) (functionp v)) + (and additional-pred + (funcall additional-pred v)))) + +(defun use-package-normalize-function (v) + "Reduce functional constructions to one of two normal forms: + sym + #\\='(lambda () ...)" + (cond ((symbolp v) v) + ((and (listp v) + (memq (car v) '(quote function)) + (use-package-non-nil-symbolp (cadr v))) + (cadr v)) + ((and (consp v) + (eq 'lambda (car v))) + v) + ((and (listp v) + (memq (car v) '(quote function)) + (eq 'lambda (car (cadr v)))) + (cadr v)) + (t v))) + +(defun use-package-normalize-commands (args) + "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-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-pairs + #'use-package-regex-p + #'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) + (mapcar + #'(lambda (thing) + `(add-to-list + ',alist + ',(cons (use-package-normalize-regex (car thing)) + (cdr thing)))) + (use-package-normalize-commands args)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Statistics +;; + +(defun use-package-reset-statistics () + "Reset statistics for `use-package'. +See also `use-package-statistics'." + (interactive) + (setq use-package-statistics (make-hash-table))) + +(defun use-package-statistics-status (package) + "Return loading configuration status of PACKAGE statistics." + (cond ((gethash :config package) "Configured") + ((gethash :init package) "Initialized") + ((gethash :preface package) "Prefaced") + ((gethash :use-package package) "Declared"))) + +(defun use-package-statistics-last-event (package) + "Return the date when PACKAGE's status last changed. +The date is returned as a string." + (or (gethash :config package) + (gethash :init package) + (gethash :preface package) + (gethash :use-package package))) + +(defun use-package-statistics-time (package) + "Return the time is took for PACKAGE to load." + (+ (float-time (gethash :config-secs package '(0 0 0 0))) + (float-time (gethash :init-secs package '(0 0 0 0))) + (float-time (gethash :preface-secs package '(0 0 0 0))) + (float-time (gethash :use-package-secs package '(0 0 0 0))))) + +(defun use-package-statistics-convert (package) + "Return information about PACKAGE. + +The information is formatted in a way suitable for +`use-package-statistics-mode'." + (let ((statistics (gethash package use-package-statistics))) + (list + package + (vector + (symbol-name package) + (use-package-statistics-status statistics) + (format-time-string + "%H:%M:%S.%6N" + (use-package-statistics-last-event statistics)) + (format "%.2f" (use-package-statistics-time statistics)))))) + +(defun use-package-report () + "Show current statistics gathered about `use-package' declarations. +In the table that's generated, the status field has the following +meaning: + Configured :config has been processed (the package is loaded!) + Initialized :init has been processed (load status unknown) + Prefaced :preface has been processed + Declared the use-package declaration was seen" + (interactive) + (with-current-buffer (get-buffer-create "*use-package statistics*") + (setq tabulated-list-entries + (mapcar #'use-package-statistics-convert + (hash-table-keys use-package-statistics))) + (use-package-statistics-mode) + (tabulated-list-print) + (display-buffer (current-buffer)))) + +(defvar use-package-statistics-status-order + '(("Declared" . 0) + ("Prefaced" . 1) + ("Initialized" . 2) + ("Configured" . 3))) + +(define-derived-mode use-package-statistics-mode tabulated-list-mode + "use-package statistics" + "Show current statistics gathered about `use-package' declarations." + (setq tabulated-list-format + ;; The sum of column width is 80 characters: + [("Package" 25 t) + ("Status" 13 + (lambda (a b) + (< (assoc-default + (use-package-statistics-status + (gethash (car a) use-package-statistics)) + use-package-statistics-status-order) + (assoc-default + (use-package-statistics-status + (gethash (car b) use-package-statistics)) + use-package-statistics-status-order)))) + ("Last Event" 23 + (lambda (a b) + (< (float-time + (use-package-statistics-last-event + (gethash (car a) use-package-statistics))) + (float-time + (use-package-statistics-last-event + (gethash (car b) use-package-statistics)))))) + ("Time" 10 + (lambda (a b) + (< (use-package-statistics-time + (gethash (car a) use-package-statistics)) + (use-package-statistics-time + (gethash (car b) use-package-statistics)))))]) + (setq tabulated-list-sort-key '("Time" . t)) + (tabulated-list-init-header)) + +(defun use-package-statistics-gather (keyword name after) + (let* ((hash (gethash name use-package-statistics + (make-hash-table))) + (before (and after (gethash keyword hash (current-time))))) + (puthash keyword (current-time) hash) + (when after + (puthash (intern (concat (symbol-name keyword) "-secs")) + (time-subtract (current-time) before) hash)) + (puthash name hash use-package-statistics))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Handlers +;; + +;;;; :disabled + +;; Don't alias this to `ignore', as that will cause the resulting +;; function to be interactive. +(defun use-package-normalize/:disabled (_name _keyword _arg) + "Do nothing, return nil.") + +(defun use-package-handler/:disabled (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :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) + +(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 + +(defalias 'use-package-normalize/:requires 'use-package-normalize-symlist) + +(defun use-package-handler/:requires (name _keyword requires rest state) + (let ((body (use-package-process-keywords name rest state))) + (if (null requires) + body + `((when ,(if (> (length requires) 1) + `(not (member nil (mapcar #'featurep ',requires))) + `(featurep ',(car requires))) + ,@body))))) + +;;;; :load-path + +(defun use-package-normalize/:load-path (_name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-paths)) + +(defun use-package-handler/:load-path (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (mapcar #'(lambda (path) + `(eval-and-compile (add-to-list 'load-path ,path))) + arg) + body))) + +;;;; :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)) + +;;;; :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)) + +;;;; :preface + +(defalias 'use-package-normalize/:preface 'use-package-normalize-forms) + +(defun use-package-handler/:preface (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :preface ',name nil))) + (when arg + `((eval-and-compile ,@arg))) + body + (when use-package-compute-statistics + `((use-package-statistics-gather :preface ',name t)))))) + +;;;; :catch + +(defvar use-package--form) +(defvar use-package--hush-function #'(lambda (_keyword body) body)) + +(defsubst use-package-hush (context keyword body) + `((condition-case-unless-debug err + ,(macroexp-progn body) + (error (funcall ,context ,keyword err))))) + +(defun use-package-normalize/:catch (_name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + use-package--hush-function))) + +(defun use-package-handler/:catch (name keyword arg rest state) + (let* ((context (cl-gentemp "use-package--warning"))) + (cond + ((not arg) + (use-package-process-keywords name rest state)) + ((eq arg t) + `((defvar ,context + #'(lambda (keyword err) + (let ((msg (format "%s/%s: %s" ',name keyword + (error-message-string err)))) + ,@(when (eq use-package-verbose 'debug) + `((with-current-buffer + (get-buffer-create "*use-package*") + (goto-char (point-max)) + (insert "-----\n" msg ,use-package--form) + (emacs-lisp-mode)) + (setq msg + (concat msg + " (see the *use-package* buffer)")))) + (display-warning 'use-package msg :error)))) + ,@(let ((use-package--hush-function + (apply-partially #'use-package-hush context))) + (funcall use-package--hush-function keyword + (use-package-process-keywords name rest state))))) + ((functionp arg) + `((defvar ,context ,arg) + ,@(let ((use-package--hush-function + (apply-partially #'use-package-hush context))) + (funcall use-package--hush-function keyword + (use-package-process-keywords name rest state))))) + (t + (use-package-error "The :catch keyword expects 't' or a function"))))) + +;;;; :interpreter + +(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)) + +;;;; :hook + +(defun use-package-normalize/:hook (name keyword args) + (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 " a <symbol> or (<symbol or list of symbols> . <symbol or function>)" + " or list of these"))) + (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 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." + (use-package-concat + (use-package-process-keywords name rest state) + (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))) + (use-package-hook-handler-normalize-mode-symbols syms))))) + (use-package-normalize-commands args)))) + +(defun use-package-hook-handler-normalize-mode-symbols (syms) + "Ensure that `SYMS' turns into a list of modes." + (if (use-package-non-nil-symbolp syms) (list syms) syms)) + +;;;; :commands + +(defalias 'use-package-normalize/:commands 'use-package-normalize-symlist) + +(defun use-package-handler/:commands (name _keyword arg rest state) + (use-package-concat + ;; Since we deferring load, establish any necessary autoloads, and also + ;; keep the byte-compiler happy. + (let ((name-string (use-package-as-string name))) + (cl-mapcan + #'(lambda (command) + (when (symbolp command) + (append + (unless (plist-get state :demand) + `((unless (fboundp ',command) + (autoload #',command ,name-string nil t)))) + (when (bound-and-true-p byte-compile-current-file) + `((eval-when-compile + (declare-function ,command ,name-string))))))) + (delete-dups arg))) + (use-package-process-keywords name rest state))) + +;;;; :autoload + +(defalias 'use-package-normalize/:autoload 'use-package-normalize/:commands) + +(defun use-package-handler/:autoload (name _keyword arg rest state) + (use-package-concat + ;; Since we deferring load, establish any necessary autoloads, and also + ;; keep the byte-compiler happy. + (let ((name-string (use-package-as-string name))) + (cl-mapcan + #'(lambda (command) + (when (symbolp command) + (append + (unless (plist-get state :demand) + `((unless (fboundp ',command) + (autoload #',command ,name-string)))) + (when (bound-and-true-p byte-compile-current-file) + `((eval-when-compile + (declare-function ,command ,name-string))))))) + (delete-dups arg))) + (use-package-process-keywords name rest state))) + +;;;; :defer + +(defalias 'use-package-normalize/:defer 'use-package-normalize-predicate) + +(defun use-package-handler/:defer (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + ;; Load the package after a set amount of idle time, if the argument to + ;; `:defer' was a number. + (when (numberp arg) + `((run-with-idle-timer ,arg nil #'require + ',(use-package-as-symbol name) nil t))) + (if (or (not arg) (null body)) + body + `((eval-after-load ',name ',(macroexp-progn body))))))) + +;;;; :after + +(defun use-package-normalize/:after (name keyword args) + (setq args (use-package-normalize-recursive-symlist name keyword args)) + (if (consp args) + args + (list args))) + +(defun use-package-after-count-uses (features*) + "Count the number of time the body would appear in the result." + (cond ((use-package-non-nil-symbolp features*) + 1) + ((and (consp features*) + (memq (car features*) '(:or :any))) + (let ((num 0)) + (cl-dolist (next (cdr features*)) + (setq num (+ num (use-package-after-count-uses next)))) + num)) + ((and (consp features*) + (memq (car features*) '(:and :all))) + (apply #'max (mapcar #'use-package-after-count-uses + (cdr features*)))) + ((listp features*) + (use-package-after-count-uses (cons :all features*))))) + +(defun use-package-require-after-load (features* body) + "Generate `eval-after-load' statements to represents FEATURES*. +FEATURES* is a list containing keywords `:and' and `:all', where +no keyword implies `:all'." + (cond + ((use-package-non-nil-symbolp features*) + `((eval-after-load ',features* ',(macroexp-progn body)))) + ((and (consp features*) + (memq (car features*) '(:or :any))) + (cl-mapcan #'(lambda (x) (use-package-require-after-load x body)) + (cdr features*))) + ((and (consp features*) + (memq (car features*) '(:and :all))) + (cl-dolist (next (cdr features*)) + (setq body (use-package-require-after-load next body))) + body) + ((listp features*) + (use-package-require-after-load (cons :all features*) body)))) + +(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))) + (if (or (null uses) (null body)) + body + (if (<= uses 1) + (use-package-require-after-load arg body) + (use-package-memoize + (apply-partially #'use-package-require-after-load arg) + (macroexp-progn body)))))) + +;;;; :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 + +(defun use-package-normalize/:custom (_name keyword args) + "Normalize use-package custom keyword." + (use-package-as-one (symbol-name keyword) args + #'(lambda (label arg) + (unless (listp arg) + (use-package-error + (concat label " a (<symbol> <value> [comment])" + " or list of these"))) + (if (use-package-non-nil-symbolp (car arg)) + (list arg) + arg)))) + +(defun use-package-handler/:custom (name _keyword args rest state) + "Generate use-package custom keyword code." + (use-package-concat + (if (bound-and-true-p use-package-use-theme) + `((let ((custom--inhibit-theme-enable nil)) + ;; Declare the theme here so use-package can be required inside + ;; eval-and-compile without warnings about unknown theme. + (unless (memq 'use-package custom-known-themes) + (deftheme use-package) + (enable-theme 'use-package) + (setq custom-enabled-themes (remq 'use-package custom-enabled-themes))) + (custom-theme-set-variables + 'use-package + ,@(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))) + `'(,variable ,value nil () ,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 + +(defun use-package-normalize/:custom-face (name-symbol _keyword arg) + "Normalize use-package custom-face keyword." + (let ((error-msg + (format "%s wants a (<symbol> <face-spec> [spec-type]) or list of these" + name-symbol))) + (unless (listp arg) + (use-package-error error-msg)) + (cl-dolist (def arg arg) + (unless (listp def) + (use-package-error error-msg)) + (let ((face (nth 0 def)) + (spec (nth 1 def))) + (when (or (not face) + (not spec) + (> (length def) 3)) + (use-package-error error-msg)))))) + +(defun use-package-handler/:custom-face (name _keyword args rest state) + "Generate use-package custom-face keyword code." + (use-package-concat + (mapcar #'(lambda (def) `(apply #'face-spec-set (backquote ,def))) args) + (use-package-process-keywords name rest state))) + +;;;; :init + +(defalias 'use-package-normalize/:init 'use-package-normalize-forms) + +(defun use-package-handler/:init (name _keyword arg rest state) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :init ',name nil))) + (let ((init-body + (use-package-hook-injector (use-package-as-string name) + :init arg))) + (when init-body + (funcall use-package--hush-function :init + (if use-package-check-before-init + `((when (locate-library ,(use-package-as-string name)) + ,@init-body)) + init-body)))) + (use-package-process-keywords name rest state) + (when use-package-compute-statistics + `((use-package-statistics-gather :init ',name t))))) + +;;;; :load + +(defun use-package-normalize/:load (name keyword args) + (setq args (use-package-normalize-recursive-symlist name keyword args)) + (if (consp args) + args + (list args))) + +(defun use-package-handler/:load (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (cl-dolist (pkg arg) + (setq body (use-package-require (if (eq t pkg) name pkg) nil body))) + body)) + +;;;; :config + +(defalias 'use-package-normalize/:config 'use-package-normalize-forms) + +(defun use-package-handler/:config (name _keyword arg rest state) + (let* ((body (use-package-process-keywords name rest state)) + (name-symbol (use-package-as-symbol name))) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :config ',name nil))) + (if (and (or (null arg) (equal arg '(t))) (not use-package-inject-hooks)) + body + (use-package-with-elapsed-timer + (format "Configuring package %s" name-symbol) + (funcall use-package--hush-function :config + (use-package-concat + (use-package-hook-injector + (symbol-name name-symbol) :config arg) + body + (list t))))) + (when use-package-compute-statistics + `((use-package-statistics-gather :config ',name t)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; The main macro +;; + +(defmacro use-package-core (name args) + `(let* ((args* (use-package-normalize-keywords ,name ,args)) + (use-package--form + (if (eq use-package-verbose 'debug) + (concat "\n\n" + (pp-to-string `(use-package ,name ,@,args)) + "\n -->\n\n" + (pp-to-string `(use-package ,name ,@args*)) + "\n ==>\n\n" + (pp-to-string + (macroexp-progn + (let ((use-package-verbose 'errors) + (use-package-expand-minimally t)) + (use-package-process-keywords name args* + (and (plist-get args* :demand) + (list :demand t))))))) + ""))) + (use-package-process-keywords name args* + (and (plist-get args* :demand) + (list :demand t))))) + +;;;###autoload +(defmacro use-package (name &rest args) + "Declare an Emacs package by specifying a group of configuration options. + +For full documentation, please see the README file that came with +this file. Usage: + + (use-package package-name + [:keyword [option]]...) + +:init Code to run before PACKAGE-NAME has been loaded. +:config Code to run after PACKAGE-NAME has been loaded. Note that + if loading is deferred for any reason, this code does not + execute until the lazy load has occurred. +:preface Code to be run before everything except `:disabled'; this + can be used to define functions for use in `:if', or that + should be seen by the byte-compiler. + +:mode Form to be added to `auto-mode-alist'. +:magic Form to be added to `magic-mode-alist'. +:magic-fallback Form to be added to `magic-fallback-mode-alist'. +:interpreter Form to be added to `interpreter-mode-alist'. + +:commands Define autoloads for commands that will be defined by the + package. This is useful if the package is being lazily + loaded, and you wish to conditionally call functions in your + `:init' block that are defined in the package. +:autoload Similar to :commands, but it for no-interactive one. +:hook Specify hook(s) to attach this package to. + +:bind Bind keys, and define autoloads for the bound commands. +:bind* Bind keys, and define autoloads for the bound commands, + *overriding all minor mode bindings*. +:bind-keymap Bind a key prefix to an auto-loaded keymap defined in the + package. This is like `:bind', but for keymaps. +:bind-keymap* Like `:bind-keymap', but overrides all minor mode bindings + +:defer Defer loading of a package -- this is implied when using + `:commands', `:bind', `:bind*', `:mode', `:magic', `:hook', + `:magic-fallback', or `:interpreter'. This can be an integer, + to force loading after N seconds of idle time, if the package + has not already been loaded. +:after Delay the use-package declaration until after the named modules + have loaded. Once load, it will be as though the use-package + declaration (without `:after') had been seen at that moment. +:demand Prevent the automatic deferred loading introduced by constructs + such as `:bind' (see `:defer' for the complete list). + +:if EXPR Initialize and load only if EXPR evaluates to a non-nil value. +:disabled The package is ignored completely if this keyword is present. +:defines Declare certain variables to silence the byte-compiler. +:functions Declare certain functions to silence the byte-compiler. +:load-path Add to the `load-path' before attempting to load the package. +:diminish Support for diminish.el (if installed). +:delight Support for delight.el (if installed). +:custom Call `Custom-set' or `set-default' with each variable + definition without modifying the Emacs `custom-file'. + (compare with `custom-set-variables'). +:custom-face Call `custom-set-faces' with each face definition. +:ensure Loads the package using package.el if necessary. +:pin Pin the package to an archive." + (declare (indent defun)) + (unless (memq :disabled args) + (macroexp-progn + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :use-package ',name nil))) + (if (eq use-package-verbose 'errors) + (use-package-core name args) + (condition-case-unless-debug err + (use-package-core name args) + (error + (ignore + (display-warning + 'use-package + (format "Failed to parse package %s: %s" + name (error-message-string err)) :error))))) + (when use-package-compute-statistics + `((use-package-statistics-gather :use-package ',name t))))))) + +(provide 'use-package-core) + +;;; use-package-core.el ends here diff --git a/lisp/use-package/use-package-delight.el b/lisp/use-package/use-package-delight.el new file mode 100644 index 00000000000..c6abac9a643 --- /dev/null +++ b/lisp/use-package/use-package-delight.el @@ -0,0 +1,83 @@ +;;; use-package-delight.el --- Support for the :delight keyword -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides support for the :delight keyword, which is made available by +;; default by requiring `use-package'. + +;;; Code: + +(require 'use-package-core) + +(defun use-package-normalize-delight (name args) + "Normalize ARGS for a single call to `delight'." + (when (eq :eval (car args)) + ;; Handle likely common mistake. + (use-package-error ":delight mode line constructs must be quoted")) + (cond ((and (= (length args) 1) + (use-package-non-nil-symbolp (car args))) + `(,(nth 0 args) nil ,name)) + ((= (length args) 2) + `(,(nth 0 args) ,(nth 1 args) ,name)) + ((= (length args) 3) + args) + (t + (use-package-error + ":delight expects `delight' arguments or a list of them")))) + +;;;###autoload +(defun use-package-normalize/:delight (name _keyword args) + "Normalize arguments to delight." + (cond ((null args) + `((,(use-package-as-mode name) nil ,name))) + ((and (= (length args) 1) + (use-package-non-nil-symbolp (car args))) + `((,(car args) nil ,name))) + ((and (= (length args) 1) + (stringp (car args))) + `((,(use-package-as-mode name) ,(car args) ,name))) + ((and (= (length args) 1) + (listp (car args)) + (eq 'quote (caar args))) + `((,(use-package-as-mode name) ,@(cdar args) ,name))) + ((and (= (length args) 2) + (listp (nth 1 args)) + (eq 'quote (car (nth 1 args)))) + `((,(car args) ,@(cdr (nth 1 args)) ,name))) + (t (mapcar + (apply-partially #'use-package-normalize-delight name) + (if (use-package-non-nil-symbolp (car args)) + (list args) + args))))) + +;;;###autoload +(defun use-package-handler/:delight (name _keyword args rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + body + `((if (fboundp 'delight) + (delight '(,@args))))))) + +(add-to-list 'use-package-keywords :delight t) + +(provide 'use-package-delight) + +;;; use-package-delight.el ends here diff --git a/lisp/use-package/use-package-diminish.el b/lisp/use-package/use-package-diminish.el new file mode 100644 index 00000000000..9b8a09a2973 --- /dev/null +++ b/lisp/use-package/use-package-diminish.el @@ -0,0 +1,72 @@ +;;; use-package-diminish.el --- Support for the :diminish keyword -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides support for the :diminish keyword, which is made available by +;; default by requiring `use-package'. + +;;; Code: + +(require 'use-package-core) + +(defun use-package-normalize-diminish (name label arg &optional recursed) + "Normalize the arguments to diminish down to a list of one of two forms: + SYMBOL + (SYMBOL . STRING)" + (cond + ((not arg) + (list (use-package-as-mode name))) + ((use-package-non-nil-symbolp arg) + (list arg)) + ((stringp arg) + (list (cons (use-package-as-mode name) arg))) + ((and (consp arg) (stringp (cdr arg))) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (car (use-package-normalize-diminish + name label x t))) arg)) + (t + (use-package-error + (concat label " wants a string, symbol, " + "(symbol . string) or list of these"))))) + +;;;###autoload +(defun use-package-normalize/:diminish (name keyword args) + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-diminish name) t)) + +;;;###autoload +(defun use-package-handler/:diminish (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (mapcar #'(lambda (var) + `(if (fboundp 'diminish) + ,(if (consp var) + `(diminish ',(car var) ,(cdr var)) + `(diminish ',var)))) + arg) + body))) + +(add-to-list 'use-package-keywords :diminish t) + +(provide 'use-package-diminish) + +;;; use-package-diminish.el ends here diff --git a/lisp/use-package/use-package-ensure-system-package.el b/lisp/use-package/use-package-ensure-system-package.el new file mode 100644 index 00000000000..c55bacaf817 --- /dev/null +++ b/lisp/use-package/use-package-ensure-system-package.el @@ -0,0 +1,101 @@ +;;; use-package-ensure-system-package.el --- auto install system packages -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Author: Justin Talbott <justin@waymondo.com> +;; Keywords: convenience, tools, extensions +;; URL: https://github.com/waymondo/use-package-ensure-system-package +;; Version: 0.2 +;; Package-Requires: ((use-package "2.1") (system-packages "1.0.4")) +;; Filename: use-package-ensure-system-package.el + +;; 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: +;; +;; The `:ensure-system-package` keyword allows you to ensure system +;; binaries exist alongside your `use-package` declarations. +;; + +;;; Code: + +(require 'use-package) +(require 'system-packages nil t) + +(eval-when-compile + (declare-function system-packages-get-command "system-packages")) + +(defvar use-package-ensure-system-package--custom-packages '() + "List of commands used to install custom packages.") + +(defun use-package-ensure-system-package-consify (arg) + "Turn ARG into a cons of the form (PACKAGE-NAME . INSTALL-COMMAND')." + (cond + ((stringp arg) + (cons arg `(system-packages-install ,arg))) + ((symbolp arg) + (cons arg `(system-packages-install ,(symbol-name arg)))) + ((consp arg) + (cond + ((not (cdr arg)) + (use-package-ensure-system-package-consify (car arg))) + ((stringp (cdr arg)) + (progn + (push (cdr arg) use-package-ensure-system-package--custom-packages) + (cons (car arg) `(async-shell-command ,(cdr arg))))) + (t + (cons (car arg) + `(system-packages-install ,(symbol-name (cdr arg))))))))) + +(defun use-package-ensure-system-package-update-custom-packages () + "Update custom packages (not installed by system package manager). +Run the same commands used for installing them." + (interactive) + (dolist (cmd use-package-ensure-system-package--custom-packages) + (async-shell-command cmd))) + +;;;###autoload +(defun use-package-normalize/:ensure-system-package (_name-symbol keyword args) + "Turn ARGS into a list of conses of the form (PACKAGE-NAME . INSTALL-COMMAND)." + (use-package-as-one (symbol-name keyword) args + (lambda (_label arg) + (cond + ((and (listp arg) (listp (cdr arg))) + (mapcar #'use-package-ensure-system-package-consify arg)) + (t + (list (use-package-ensure-system-package-consify arg))))))) + +(defun use-package-ensure-system-package-exists? (file-or-exe) + "If FILE-OR-EXE is a string, ensure the file path exists. +If it is a symbol, ensure the binary exist." + (if (stringp file-or-exe) + (file-exists-p file-or-exe) + (executable-find (symbol-name file-or-exe)))) + + +;;;###autoload +(defun use-package-handler/:ensure-system-package (name _keyword arg rest state) + "Execute the handler for `:ensure-system-package' keyword in `use-package'." + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (mapcar #'(lambda (cons) + `(unless (use-package-ensure-system-package-exists? ',(car cons)) + ,(cdr cons))) arg) + body))) + +(add-to-list 'use-package-keywords :ensure-system-package t) + +(provide 'use-package-ensure-system-package) + +;;; use-package-ensure-system-package.el ends here diff --git a/lisp/use-package/use-package-ensure.el b/lisp/use-package/use-package-ensure.el new file mode 100644 index 00000000000..bbb8e4175b8 --- /dev/null +++ b/lisp/use-package/use-package-ensure.el @@ -0,0 +1,208 @@ +;;; use-package-ensure.el --- Support for the :ensure and :pin keywords -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides support for the :ensure and :pin keywords, which is made available +;; by default by requiring `use-package'. + +;;; Code: + +(require 'cl-lib) +(require 'use-package-core) + +(defgroup use-package-ensure nil + "Support for :ensure and :pin keywords in `use-package' declarations." + :group 'use-package + :link '(custom-manual "(use-package) Installing packages") + :version "29.1") + +(eval-when-compile + (declare-function package-installed-p "package") + (declare-function package-read-all-archive-contents "package" ())) + +(defcustom use-package-always-ensure nil + "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-ensure) + +(defcustom use-package-always-pin nil + "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-ensure) + +(defcustom use-package-ensure-function 'use-package-ensure-elpa + "Function that ensures a package is installed. +This function is called with three arguments: the name of the +package declared in the `use-package' form; the arguments passed +to all `:ensure' keywords (always a list, even if only one); and +the current `state' plist created by previous handlers. + +Note that this function is called whenever `:ensure' is provided, +even if it is nil. It is up to the function to decide on the +semantics of the various values for `:ensure'. + +This function should return non-nil if the package is installed. + +The default value uses package.el to install the package." + :type '(choice (const :tag "package.el" use-package-ensure-elpa) + (function :tag "Custom")) + :group 'use-package-ensure) + +;;;; :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) + (defvar package-archives)) + +(defun use-package-archive-exists-p (archive) + "Check if a given ARCHIVE is enabled. + +ARCHIVE can be a string or a symbol or `manual' to indicate a +manually updated package." + (if (member archive '(manual "manual")) + 't + (let ((valid nil)) + (dolist (pa package-archives) + (when (member archive (list (car pa) (intern (car pa)))) + (setq valid 't))) + valid))) + +(defun use-package-pin-package (package archive) + "Pin PACKAGE to ARCHIVE." + (unless (boundp 'package-pinned-packages) + (setq package-pinned-packages ())) + (let ((archive-symbol (if (symbolp archive) archive (intern archive))) + (archive-name (if (stringp archive) archive (symbol-name archive)))) + (if (use-package-archive-exists-p archive-symbol) + (add-to-list 'package-pinned-packages (cons package archive-name)) + (error "Archive '%s' requested for package '%s' is not available" + archive-name package)) + (unless (bound-and-true-p package--initialized) + (package-initialize t)))) + +(defun use-package-handler/:pin (name _keyword archive-name rest state) + (let ((body (use-package-process-keywords name rest state)) + (pin-form (if archive-name + `(use-package-pin-package ',(use-package-as-symbol name) + ,archive-name)))) + ;; Pinning should occur just before ensuring + ;; See `use-package-handler/:ensure'. + (if (bound-and-true-p byte-compile-current-file) + (eval pin-form) ; Eval when byte-compiling, + (push pin-form body)) ; or else wait until runtime. + body)) + +;;;; :ensure + +(defvar package-archive-contents) + +;;;###autoload +(defun use-package-normalize/:ensure (_name keyword args) + (if (null args) + (list t) + (use-package-only-one (symbol-name keyword) args + #'(lambda (_label arg) + (cond + ((symbolp arg) + (list arg)) + ((and (listp arg) (= 3 (length arg)) + (symbolp (nth 0 arg)) + (eq :pin (nth 1 arg)) + (or (stringp (nth 2 arg)) + (symbolp (nth 2 arg)))) + (list (cons (nth 0 arg) (nth 2 arg)))) + (t + (use-package-error + (concat ":ensure wants an optional package name " + "(an unquoted symbol name), or (<symbol> :pin <string>)")))))))) + +(defun use-package-ensure-elpa (name args _state &optional _no-refresh) + (dolist (ensure args) + (let ((package + (or (and (eq ensure t) (use-package-as-symbol name)) + ensure))) + (when package + (require 'package) + (when (consp package) + (use-package-pin-package (car package) (cdr package)) + (setq package (car package))) + (unless (package-installed-p package) + (condition-case-unless-debug err + (progn + (when (assoc package (bound-and-true-p + package-pinned-packages)) + (package-read-all-archive-contents)) + (if (assoc package package-archive-contents) + (package-install package) + (package-refresh-contents) + (when (assoc package (bound-and-true-p + package-pinned-packages)) + (package-read-all-archive-contents)) + (package-install package)) + t) + (error + (display-warning 'use-package + (format "Failed to install %s: %s" + name (error-message-string err)) + :error)))))))) + +;;;###autoload +(defun use-package-handler/:ensure (name _keyword ensure rest state) + (let* ((body (use-package-process-keywords name rest state))) + ;; We want to avoid installing packages when the `use-package' macro is + ;; being macro-expanded by elisp completion (see `lisp--local-variables'), + ;; but still install packages when byte-compiling, to avoid requiring + ;; `package' at runtime. + (if (bound-and-true-p byte-compile-current-file) + ;; Eval when byte-compiling, + (funcall use-package-ensure-function name ensure state) + ;; or else wait until runtime. + (push `(,use-package-ensure-function ',name ',ensure ',state) + body)) + body)) + +(add-to-list 'use-package-defaults + '(:ensure (list use-package-always-ensure) + (lambda (name args) + (and use-package-always-ensure + (not (plist-member args :load-path))))) t) + +(add-to-list 'use-package-defaults + '(:pin use-package-always-pin use-package-always-pin) t) + +(add-to-list 'use-package-keywords :ensure) +(add-to-list 'use-package-keywords :pin) + +(provide 'use-package-ensure) + +;;; use-package-ensure.el ends here diff --git a/lisp/use-package/use-package-jump.el b/lisp/use-package/use-package-jump.el new file mode 100644 index 00000000000..0c4cd20d052 --- /dev/null +++ b/lisp/use-package/use-package-jump.el @@ -0,0 +1,70 @@ +;;; use-package-jump.el --- Attempt to jump to a use-package declaration -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides the command `M-x use-package-jump-to-package-form', however it +;; only works if the package being jumped to was required during +;; initialization. If it was delay-loaded, it will not work. +;; Improvements are needed. + +;;; Code: + +(require 'use-package-core) + +(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))))))) + +;;;###autoload +(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-jump) + +;;; use-package-jump.el ends here diff --git a/lisp/use-package/use-package-lint.el b/lisp/use-package/use-package-lint.el new file mode 100644 index 00000000000..2092c0d269c --- /dev/null +++ b/lisp/use-package/use-package-lint.el @@ -0,0 +1,76 @@ +;;; use-package-lint.el --- Attempt to find errors in use-package declarations -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> + +;; 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: + +;; Provides the command `M-x use-package-lint'. + +;;; Code: + +(require 'cl-lib) +(require 'use-package-core) + +(defun use-package-lint-declaration (name plist) + (dolist (path (plist-get plist :load-path)) + (unless (file-exists-p path) + (display-warning + 'use-package + (format "%s :load-path does not exist: %s" + name path) :error))) + + (unless (or (plist-member plist :disabled) + (plist-get plist :no-require) + (locate-library (use-package-as-string name) nil + (plist-get plist :load-path))) + (display-warning + 'use-package + (format "%s module cannot be located" name) :error)) + + ;; (dolist (command (plist-get plist :commands)) + ;; (unless (string= (find-lisp-object-file-name command nil) + ;; (locate-library (use-package-as-string name) nil + ;; (plist-get plist :load-path))) + ;; (display-warning + ;; 'use-package + ;; (format "%s :command is from different path: %s" + ;; name (symbol-name command)) :error))) + ) + +;;;###autoload +(defun use-package-lint () + "Check for errors in `use-package' declarations. +For example, if the module's `:if' condition is met, but even +with the specified `:load-path' the module cannot be found." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((re (eval use-package-form-regexp-eval))) + (while (re-search-forward re nil t) + (goto-char (match-beginning 0)) + (let ((decl (read (current-buffer)))) + (when (eq (car decl) 'use-package) + (use-package-lint-declaration + (use-package-as-string (cadr decl)) + (use-package-normalize-keywords + (cadr decl) (cddr decl))))))))) + +(provide 'use-package-lint) + +;;; use-package-lint.el ends here diff --git a/lisp/use-package/use-package.el b/lisp/use-package/use-package.el new file mode 100644 index 00000000000..bafa0934a6d --- /dev/null +++ b/lisp/use-package/use-package.el @@ -0,0 +1,51 @@ +;;; use-package.el --- A configuration macro for simplifying your .emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2022 Free Software Foundation, Inc. + +;; Author: John Wiegley <johnw@newartisans.com> +;; Maintainer: John Wiegley <johnw@newartisans.com> +;; Created: 17 Jun 2012 +;; Version: 2.4.4 +;; Package-Requires: ((emacs "24.3") (bind-key "2.4")) +;; Keywords: dotemacs startup speed config package extensions +;; URL: https://github.com/jwiegley/use-package + +;; 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: + +;; The `use-package' declaration macro allows you to isolate package +;; configuration in your ".emacs" in a way that is performance-oriented and, +;; well, just tidy. I created it because I have over 80 packages that I use +;; in Emacs, and things were getting difficult to manage. Yet with this +;; utility my total load time is just under 1 second, with no loss of +;; functionality! +;; +;; Please see README.md from the same repository for documentation. + +;;; Code: + +(require 'use-package-core) + +(require 'use-package-bind-key) +(require 'use-package-diminish) +(require 'use-package-delight) +(require 'use-package-ensure) + +(declare-function use-package-jump-to-package-form "use-package-jump") +(autoload #'use-package-jump-to-package-form "use-package-jump" nil t) + +(provide 'use-package) + +;;; use-package.el ends here diff --git a/src/emacs.c b/src/emacs.c index d8a2863fd9c..2d924da1cdb 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1937,7 +1937,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem } init_alloc (); +#ifndef HAVE_UNEXEC init_bignum (); +#endif init_threads (); init_eval (); running_asynch_code = 0; diff --git a/test/lisp/eshell/esh-arg-tests.el b/test/lisp/eshell/esh-arg-tests.el new file mode 100644 index 00000000000..77f9404d4c7 --- /dev/null +++ b/test/lisp/eshell/esh-arg-tests.el @@ -0,0 +1,105 @@ +;;; esh-arg-tests.el --- esh-arg test suite -*- lexical-binding:t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Tests for Eshell's argument handling. + +;;; Code: + +(require 'ert) +(require 'esh-mode) +(require 'eshell) + +(require 'eshell-tests-helpers + (expand-file-name "eshell-tests-helpers" + (file-name-directory (or load-file-name + default-directory)))) + +(defvar eshell-test-value nil) + +;;; Tests: + +(ert-deftest esh-arg-test/escape/nonspecial () + "Test that \"\\c\" and \"c\" are equivalent when \"c\" is not a +special character." + (with-temp-eshell + (eshell-match-command-output "echo he\\llo" + "hello\n"))) + +(ert-deftest esh-arg-test/escape/nonspecial-unicode () + "Test that \"\\c\" and \"c\" are equivalent when \"c\" is a +unicode character (unicode characters are nonspecial by +definition)." + (with-temp-eshell + (eshell-match-command-output "echo Vid\\éos" + "Vidéos\n"))) + +(ert-deftest esh-arg-test/escape/special () + "Test that the backslash is not preserved for escaped special +chars." + (with-temp-eshell + (eshell-match-command-output "echo he\\\\llo" + ;; Backslashes are doubled for regexp. + "he\\\\llo\n"))) + +(ert-deftest esh-arg-test/escape/newline () + "Test that an escaped newline is equivalent to the empty string. +When newlines are *nonspecial*, an escaped newline should be +treated as just a newline." + (with-temp-eshell + (eshell-match-command-output "echo hi\\\nthere" + "hithere\n"))) + +(ert-deftest esh-arg-test/escape/newline-conditional () + "Test invocation of an if/else statement using line continuations." + (let ((eshell-test-value t)) + (eshell-command-result-equal + "if $eshell-test-value \\\n{echo yes} \\\n{echo no}" + "yes")) + (let ((eshell-test-value nil)) + (eshell-command-result-equal + "if $eshell-test-value \\\n{echo yes} \\\n{echo no}" + "no"))) + +(ert-deftest esh-arg-test/escape-quoted/nonspecial () + "Test that the backslash is preserved for escaped nonspecial +chars." + (with-temp-eshell + (eshell-match-command-output "echo \"h\\i\"" + ;; Backslashes are doubled for regexp. + "h\\\\i\n"))) + +(ert-deftest esh-arg-test/escape-quoted/special () + "Test that the backslash is not preserved for escaped special +chars." + (with-temp-eshell + (eshell-match-command-output "echo \"\\\"hi\\\\\"" + ;; Backslashes are doubled for regexp. + "\\\"hi\\\\\n"))) + +(ert-deftest esh-arg-test/escape-quoted/newline () + "Test that an escaped newline is equivalent to the empty string. +When newlines are *nonspecial*, an escaped newline should be +treated literally, as a backslash and a newline." + (with-temp-eshell + (eshell-match-command-output "echo \"hi\\\nthere\"" + "hithere\n"))) + +;; esh-arg-tests.el ends here diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index d5112146c2d..c67ac67fd36 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -105,37 +105,6 @@ (format template "format \"%s\" eshell-in-pipeline-p") "nil"))) -(ert-deftest eshell-test/escape-nonspecial () - "Test that \"\\c\" and \"c\" are equivalent when \"c\" is not a -special character." - (with-temp-eshell - (eshell-match-command-output "echo he\\llo" - "hello\n"))) - -(ert-deftest eshell-test/escape-nonspecial-unicode () - "Test that \"\\c\" and \"c\" are equivalent when \"c\" is a -unicode character (unicode characters are nonspecial by -definition)." - (with-temp-eshell - (eshell-match-command-output "echo Vid\\éos" - "Vidéos\n"))) - -(ert-deftest eshell-test/escape-nonspecial-quoted () - "Test that the backslash is preserved for escaped nonspecial -chars" - (with-temp-eshell - (eshell-match-command-output "echo \"h\\i\"" - ;; Backslashes are doubled for regexp. - "h\\\\i\n"))) - -(ert-deftest eshell-test/escape-special-quoted () - "Test that the backslash is not preserved for escaped special -chars" - (with-temp-eshell - (eshell-match-command-output "echo \"\\\"hi\\\\\"" - ;; Backslashes are doubled for regexp. - "\\\"hi\\\\\n"))) - (ert-deftest eshell-test/command-running-p () "Modeline should show no command running" (with-temp-eshell diff --git a/test/lisp/gnus/mml-sec-tests.el b/test/lisp/gnus/mml-sec-tests.el index e4e607b70e6..f8ebf2f3a99 100644 --- a/test/lisp/gnus/mml-sec-tests.el +++ b/test/lisp/gnus/mml-sec-tests.el @@ -789,7 +789,7 @@ With Ma Gnus v0.14 and earlier a signature would be created with a wrong key." ;; TODO Passphrase passing and caching in Emacs does not seem to work ;; with gpgsm at all. -;; Independently of caching settings, a pinentry dialogue is displayed. +;; Independently of caching settings, a pinentry dialog is displayed. ;; Thus, the following tests require the user to enter the correct gpgsm ;; passphrases at the correct points in time. (Either empty string or ;; "Passphrase".) diff --git a/test/lisp/use-package/use-package-tests.el b/test/lisp/use-package/use-package-tests.el new file mode 100644 index 00000000000..05969f5a95f --- /dev/null +++ b/test/lisp/use-package/use-package-tests.el @@ -0,0 +1,1957 @@ +;;; use-package-tests.el --- Tests for use-package.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2014-2022 Free Software Foundation, Inc. + +;; 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/>. + +;;; Code: + +(require 'cl-lib) +(require 'ert) +(require 'use-package) + +(setq use-package-always-ensure nil + use-package-verbose 'errors + use-package-expand-minimally t + ;; These are needed for certain tests below where the `pcase' match + ;; expression is large and contains holes, such as the :after tests. + max-lisp-eval-depth 8000 + max-specpdl-size 8000) + +(unless (fboundp 'macroexpand-1) + (defun macroexpand-1 (form &optional environment) + "Perform (at most) one step of macroexpansion." + (cond + ((consp form) + (let* ((head (car form)) + (env-expander (assq head environment))) + (if env-expander + (if (cdr env-expander) + (apply (cdr env-expander) (cdr form)) + form) + (if (not (and (symbolp head) (fboundp head))) + form + (let ((def (autoload-do-load (symbol-function head) head 'macro))) + (cond + ;; Follow alias, but only for macros, otherwise we may end up + ;; skipping an important compiler-macro (e.g. cl--block-wrapper). + ((and (symbolp def) (macrop def)) (cons def (cdr form))) + ((not (consp def)) form) + (t + (if (eq 'macro (car def)) + (apply (cdr def) (cdr form)) + form)))))))) + (t form)))) + +(defmacro expand-minimally (form) + `(let ((use-package-verbose 'errors) + (use-package-expand-minimally t)) + (macroexpand-1 ',form))) + +(defmacro expand-maximally (form) + `(let ((use-package-verbose 'debug) + (use-package-expand-minimally nil)) + (macroexpand-1 ',form))) + +(defmacro match-expansion (form &rest value) + `(should (pcase (expand-minimally ,form) + ,@(mapcar #'(lambda (x) (list x t)) value)))) + +(defun fix-expansion () + (interactive) + (save-excursion + (unless (looking-at "(match-expansion") + (backward-up-list)) + (when (looking-at "(match-expansion") + (re-search-forward "(\\(use-package\\|bind-key\\)") + (goto-char (match-beginning 0)) + (let ((decl (read (current-buffer)))) + (kill-sexp) + (let (vars) + (catch 'exit + (save-excursion + (while (ignore-errors (backward-up-list) t) + (when (looking-at "(let\\s-+") + (goto-char (match-end 0)) + (setq vars (read (current-buffer))) + (throw 'exit t))))) + (eval + `(let (,@ (append vars + '((use-package-verbose 'errors) + (use-package-expand-minimally t)))) + (insert ?\n ?\` (pp-to-string (macroexpand-1 decl)))))))))) + +(bind-key "C-c C-u" #'fix-expansion emacs-lisp-mode-map) + +(ert-deftest use-package-test-recognize-function () + (should (use-package-recognize-function nil t)) + (should-not (use-package-recognize-function nil)) + (should (use-package-recognize-function t)) + (should (use-package-recognize-function 'sym)) + (should (use-package-recognize-function #'sym)) + (should (use-package-recognize-function (lambda () ...))) + (should (use-package-recognize-function '(lambda () ...))) + (should (use-package-recognize-function #'(lambda () ...))) + + (should-not (use-package-recognize-function 1)) + (should-not (use-package-recognize-function "Hello")) + (should-not (use-package-recognize-function '(nil . nil)))) + +(ert-deftest use-package-test-normalize-function () + (should (equal (use-package-normalize-function nil) nil)) + (should (equal (use-package-normalize-function t) t)) + (should (equal (use-package-normalize-function 'sym) 'sym)) + (should (equal (use-package-normalize-function #'sym) 'sym)) + (should (equal (use-package-normalize-function '(lambda () ...)) '(lambda () ...))) + (should (equal (use-package-normalize-function ''(lambda () ...)) '(lambda () ...))) + (should (equal (use-package-normalize-function '#'(lambda () ...)) '(lambda () ...))) + + (should (equal (use-package-normalize-function 1) 1)) + (should (equal (use-package-normalize-function "Hello") "Hello")) + (should (equal (use-package-normalize-function '(nil . nil)) '(nil . nil)))) + +(ert-deftest use-package-test/:disabled-1 () + (match-expansion + (use-package foo :disabled t) + `())) + +(ert-deftest use-package-test/:preface-1 () + (match-expansion + (use-package foo :preface (t)) + `(progn + (eval-and-compile + (t)) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:preface-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :preface (t)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t)))) + (t)) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:preface-3 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo + :preface (preface) + :init (init) + :config (config) + :functions func + :defines def) + `(progn + (eval-and-compile + (defvar def) + (declare-function func "foo") + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t)))) + (preface)) + (init) + (require 'foo nil nil) + (config) + t)))) + +(ert-deftest use-package-test/:preface-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo + :preface (preface) + :init (init) + :config (config) + :functions func + :defines def + :defer t) + `(progn + (eval-and-compile + (defvar def) + (declare-function func "foo") + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t)))) + (preface)) + (init) + (eval-after-load 'foo + '(progn + (config) + t)))))) + +(ert-deftest use-package-test/:pin-1 () + (match-expansion + (use-package foo :pin foo) + `(progn + (use-package-pin-package 'foo "foo") + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:pin-2 () + (match-expansion + (use-package foo :pin "foo") + `(progn + (use-package-pin-package 'foo "foo") + (require 'foo nil nil)))) + +(ert-deftest use-package-test-normalize/:ensure () + (cl-flet ((norm (&rest args) + (apply #'use-package-normalize/:ensure + 'foopkg :ensure args))) + (should (equal (norm '(t)) '(t))) + (should (equal (norm '(nil)) '(nil))) + (should (equal (norm '(sym)) '(sym))) + (should-error (norm '(1))) + (should-error (norm '("Hello"))))) + +(ert-deftest use-package-test/:ensure-1 () + (let ((use-package-always-ensure nil)) + (match-expansion + (use-package foo :ensure t) + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-2 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure t) + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-3 () + (let ((use-package-always-ensure nil)) + (match-expansion + (use-package foo :ensure nil) + `(progn + (use-package-ensure-elpa 'foo '(nil) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-4 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure nil) + `(progn + (use-package-ensure-elpa 'foo '(nil) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-5 () + (let ((use-package-always-ensure nil)) + (match-expansion + (use-package foo :load-path "foo") + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-6 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :load-path "foo") + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-7 () + (let ((use-package-always-ensure nil)) + (match-expansion + (use-package foo :ensure nil :load-path "foo") + `(progn + (use-package-ensure-elpa 'foo '(nil) 'nil) + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-8 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure nil :load-path "foo") + `(progn + (use-package-ensure-elpa 'foo '(nil) 'nil) + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-9 () + (let ((use-package-always-ensure nil)) + (match-expansion + (use-package foo :ensure t :load-path "foo") + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-10 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure t :load-path "foo") + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-11 () + (let (tried-to-install) + (cl-letf (((symbol-function #'use-package-ensure-elpa) + (lambda (name ensure state &optional no-refresh) + (when ensure + (setq tried-to-install name)))) + ((symbol-function #'require) #'ignore)) + (use-package foo :ensure t) + (should (eq tried-to-install 'foo))))) + +(ert-deftest use-package-test/:ensure-12 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure bar) + `(progn + (use-package-ensure-elpa 'foo '(bar) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-13 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo :ensure bar :ensure quux) + `(progn + (use-package-ensure-elpa 'foo '(bar quux) 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:ensure-14 () + (match-expansion + (use-package ess-site + :ensure ess1 + :ensure ess2 + :ensure (ess3 :pin "melpa-unstable") + :pin melpa-stable) + `(progn + (use-package-pin-package 'ess-site "melpa-stable") + (use-package-ensure-elpa 'ess-site + '(ess1 ess2 + (ess3 . "melpa-unstable")) + 'nil) + (require 'ess-site nil nil)))) + +(ert-deftest use-package-test/:ensure-15 () + (let ((use-package-always-ensure t)) + (match-expansion + (use-package foo + :pin "elpa" + :ensure bar + :ensure (quux :pin "melpa")) + `(progn + (use-package-pin-package 'foo "elpa") + (use-package-ensure-elpa 'foo + '(bar + (quux . "melpa")) + 'nil) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:if-1 () + (match-expansion + (use-package foo :if t) + `(when t + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:if-2 () + (match-expansion + (use-package foo :if (and t t)) + `(when (and t t) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:if-3 () + (match-expansion + (use-package foo :if nil) + `(when nil + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:when-1 () + (match-expansion + (use-package foo :when t) + `(when t + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:when-2 () + (match-expansion + (use-package foo :when (and t t)) + `(when (and t t) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:when-3 () + (match-expansion + (use-package foo :when nil) + `(when nil + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:unless-1 () + (match-expansion + (use-package foo :unless t) + `(when (not t) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:unless-2 () + (match-expansion + (use-package foo :unless (and t t)) + `(when (not (and t t)) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:unless-3 () + (match-expansion + (use-package foo :unless nil) + `(unless nil + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:requires-1 () + (match-expansion + (use-package foo :requires bar) + `(when (featurep 'bar) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:requires-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :requires bar) + `(when (featurep 'bar) + (eval-and-compile + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:requires-3 () + (match-expansion + (use-package foo :requires (bar quux)) + `(when (not (member nil (mapcar #'featurep '(bar quux)))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:requires-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :requires bar) + `(when (featurep 'bar) + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:load-path-1 () + (match-expansion + (use-package foo :load-path "bar") + `(progn + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "bar" user-emacs-directory))))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:load-path-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :load-path "bar") + `(progn + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "bar" user-emacs-directory))))) + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:load-path-3 () + (match-expansion + (use-package foo :load-path ("bar" "quux")) + `(progn + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "bar" user-emacs-directory))))) + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "quux" user-emacs-directory))))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:load-path-4 () + (match-expansion + (use-package foo :load-path (lambda () (list "bar" "quux"))) + `(progn + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "bar" user-emacs-directory))))) + (eval-and-compile + (add-to-list 'load-path + ,(pred (apply-partially + #'string= + (expand-file-name + "quux" user-emacs-directory))))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:no-require-1 () + (match-expansion + (use-package foo :no-require t) + `nil)) + +(ert-deftest use-package-test/:no-require-2 () + (match-expansion + (use-package foo :no-require t :config (config)) + `(progn + (config) + t))) + +(ert-deftest use-package-test/:no-require-3 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :no-require t) + `(eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil nil)))))) + +(defun use-package-test-normalize-bind (&rest args) + (apply #'use-package-normalize-binder 'foo :bind args)) + +(ert-deftest use-package-test-normalize/:bind-1 () + (should (equal (use-package-test-normalize-bind + '(("C-a" . alpha))) + '(("C-a" . alpha))))) + +(ert-deftest use-package-test-normalize/:bind-2 () + (should (equal (use-package-test-normalize-bind + '(("C-a" . alpha) + :map foo-map + ("C-b" . beta))) + '(("C-a" . alpha) + :map foo-map + ("C-b" . beta))))) + +(ert-deftest use-package-test-normalize/:bind-3 () + (should (equal (use-package-test-normalize-bind + '(:map foo-map + ("C-a" . alpha) + ("C-b" . beta))) + '(:map foo-map + ("C-a" . alpha) + ("C-b" . beta))))) + +(ert-deftest use-package-test/:bind-1 () + (match-expansion + (use-package foo :bind ("C-k" . key1) ("C-u" . key2)) + `(progn + (unless + (fboundp 'key1) + (autoload #'key1 "foo" nil t)) + (unless + (fboundp 'key2) + (autoload #'key2 "foo" nil t)) + (bind-keys :package foo + ("C-k" . key1) + ("C-u" . key2))))) + +(ert-deftest use-package-test/:bind-2 () + (match-expansion + (use-package foo :bind (("C-k" . key1) ("C-u" . key2))) + `(progn + (unless (fboundp 'key1) + (autoload #'key1 "foo" nil t)) + (unless (fboundp 'key2) + (autoload #'key2 "foo" nil t)) + (bind-keys :package foo + ("C-k" . key1) + ("C-u" . key2))))) + +(ert-deftest use-package-test/:bind-3 () + (match-expansion + (use-package foo :bind (:map my-map ("C-k" . key1) ("C-u" . key2))) + `(progn + (unless + (fboundp 'key1) + (autoload #'key1 "foo" nil t)) + (unless + (fboundp 'key2) + (autoload #'key2 "foo" nil t)) + (bind-keys :package foo :map my-map + ("C-k" . key1) + ("C-u" . key2))))) + +(ert-deftest use-package-test/:bind-4 () + (should-error + (match-expansion + (use-package foo :bind :map my-map ("C-k" . key1) ("C-u" . key2)) + `(bind-keys :package foo)))) + +(ert-deftest use-package-test/:bind-5 () + (match-expansion + (use-package foo :bind ("C-k" . key1) (:map my-map ("C-u" . key2))) + `(progn + (unless (fboundp 'key1) + (autoload #'key1 "foo" nil t)) + (unless (fboundp 'key2) + (autoload #'key2 "foo" nil t)) + (bind-keys :package foo + ("C-k" . key1) + :map my-map + ("C-u" . key2))))) + +(ert-deftest use-package-test/:bind-6 () + (match-expansion + (use-package foo + :bind + ("C-k" . key1) + (:map my-map ("C-u" . key2)) + (:map my-map2 ("C-u" . key3))) + `(progn + (unless (fboundp 'key1) + (autoload #'key1 "foo" nil t)) + (unless (fboundp 'key2) + (autoload #'key2 "foo" nil t)) + (unless (fboundp 'key3) + (autoload #'key3 "foo" nil t)) + (bind-keys :package foo + ("C-k" . key1) + :map my-map ("C-u" . key2) + :map my-map2 ("C-u" . key3))))) + +(ert-deftest use-package-test/:bind-7 () + (match-expansion + (use-package foo + :ensure + :bind ("C-c r" . browse-at-remote)) + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (unless (fboundp 'browse-at-remote) + (autoload #'browse-at-remote "foo" nil t)) + (bind-keys :package foo ("C-c r" . browse-at-remote))))) + +(ert-deftest use-package-test/:bind-8 () + (match-expansion + (use-package foo + :ensure + :bind (:map foo-map + (("C-c r" . foo) + ("C-c r" . bar)))) + `(progn + (use-package-ensure-elpa 'foo '(t) 'nil) + (unless (fboundp 'foo) + (autoload #'foo "foo" nil t)) + (unless (fboundp 'bar) + (autoload #'bar "foo" nil t)) + (bind-keys :package foo :map foo-map + ("C-c r" . foo) + ("C-c r" . bar))))) + +(ert-deftest use-package-test/:bind*-1 () + (match-expansion + (use-package foo :bind* ("C-k" . key)) + `(progn + (unless (fboundp 'key) + (autoload #'key "foo" nil t)) + (bind-keys* :package foo ("C-k" . key))))) + +(ert-deftest use-package-test/:bind-keymap-1 () + (match-expansion + (use-package foo :bind-keymap ("C-k" . key)) + `(bind-key "C-k" + #'(lambda nil + (interactive) + (use-package-autoload-keymap 'key 'foo nil))))) + +(ert-deftest use-package-test/:bind-keymap*-1 () + (match-expansion + (use-package foo :bind-keymap* ("C-k" . key)) + `(bind-key* "C-k" + #'(lambda () + (interactive) + (use-package-autoload-keymap 'key 'foo t))))) + +(ert-deftest use-package-test/:interpreter-1 () + (match-expansion + (use-package foo :interpreter "interp") + `(progn + (unless (fboundp 'foo) + (autoload #'foo "foo" nil t)) + (add-to-list 'interpreter-mode-alist '("interp" . foo))))) + +(ert-deftest use-package-test/:interpreter-2 () + (match-expansion + (use-package foo :interpreter ("interp" . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-to-list 'interpreter-mode-alist '("interp" . fun))))) + +(ert-deftest use-package-test-normalize/:mode () + (cl-flet ((norm (&rest args) + (apply #'use-package-normalize/:mode + 'foopkg :mode args))) + (should (equal (norm '(".foo")) + '((".foo" . foopkg)))) + (should (equal (norm '(".foo" ".bar")) + '((".foo" . foopkg) (".bar" . foopkg)))) + (should (equal (norm '((".foo" ".bar"))) + '((".foo" . foopkg) (".bar" . foopkg)))) + (should (equal (norm '((".foo"))) + '((".foo" . foopkg)))) + (should (equal (norm '((".foo" . foo) (".bar" . bar))) + '((".foo" . foo) (".bar" . bar)))))) + +(ert-deftest use-package-test/:mode-1 () + (match-expansion + (use-package foo :mode "interp") + `(progn + (unless (fboundp 'foo) + (autoload #'foo "foo" nil t)) + (add-to-list 'auto-mode-alist '("interp" . foo))))) + +(ert-deftest use-package-test/:mode-2 () + (match-expansion + (use-package foo :mode ("interp" . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-to-list 'auto-mode-alist '("interp" . fun))))) + +(ert-deftest use-package-test/:magic-1 () + (match-expansion + (use-package foo :magic "interp") + `(progn + (unless (fboundp 'foo) + (autoload #'foo "foo" nil t)) + (add-to-list 'magic-mode-alist '("interp" . foo))))) + +(ert-deftest use-package-test/:magic-2 () + (match-expansion + (use-package foo :magic ("interp" . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-to-list 'magic-mode-alist '("interp" . fun))))) + +(ert-deftest use-package-test/:magic-fallback-1 () + (match-expansion + (use-package foo :magic-fallback "interp") + `(progn + (unless (fboundp 'foo) + (autoload #'foo "foo" nil t)) + (add-to-list 'magic-fallback-mode-alist '("interp" . foo))))) + +(ert-deftest use-package-test/:magic-fallback-2 () + (match-expansion + (use-package foo :magic-fallback ("interp" . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-to-list 'magic-fallback-mode-alist '("interp" . fun))))) + +(ert-deftest use-package-test/:commands-1 () + (match-expansion + (use-package foo :commands bar) + `(unless (fboundp 'bar) + (autoload #'bar "foo" nil t)))) + +(ert-deftest use-package-test/:commands-2 () + (match-expansion + (use-package foo :commands (bar quux)) + `(progn + (unless (fboundp 'bar) + (autoload #'bar "foo" nil t)) + (unless (fboundp 'quux) + (autoload #'quux "foo" nil t))))) + +(ert-deftest use-package-test/:commands-3 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :commands (bar quux)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (unless (fboundp 'bar) + (autoload #'bar "foo" nil t)) + (eval-when-compile + (declare-function bar "foo")) + (unless (fboundp 'quux) + (autoload #'quux "foo" nil t)) + (eval-when-compile + (declare-function quux "foo")))))) + +(ert-deftest use-package-test/:commands-4 () + (match-expansion + (use-package foo :commands bar :init (bar)) + `(progn + (unless + (fboundp 'bar) + (autoload #'bar "foo" nil t)) + (bar)))) + +(ert-deftest use-package-test/:commands-5 () + (match-expansion + (use-package gnus-harvest + :load-path "foo" + :commands gnus-harvest-install + :demand t + :config + (if (featurep 'message-x) + (gnus-harvest-install 'message-x) + (gnus-harvest-install))) + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (require 'gnus-harvest nil nil) + (if (featurep 'message-x) + (gnus-harvest-install 'message-x) + (gnus-harvest-install)) + t))) + +(ert-deftest use-package-test/:commands-6 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package gnus-harvest + :load-path "foo" + :commands gnus-harvest-install + :demand t + :config + (if (featurep 'message-x) + (gnus-harvest-install 'message-x) + (gnus-harvest-install))) + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load gnus-harvest: %S" nil + (unless (featurep 'gnus-harvest) + (load "gnus-harvest" nil t))))) + (eval-when-compile + (declare-function gnus-harvest-install "gnus-harvest")) + (require 'gnus-harvest nil nil) + (if + (featurep 'message-x) + (gnus-harvest-install 'message-x) + (gnus-harvest-install)) + t)))) + +(ert-deftest use-package-test/:autoload-1 () + (match-expansion + (use-package foo :autoload bar) + `(unless (fboundp 'bar) + (autoload #'bar "foo")))) + +(ert-deftest use-package-test/:defines-1 () + (match-expansion + (use-package foo :defines bar) + `(require 'foo nil nil))) + +(ert-deftest use-package-test/:defines-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :defines bar) + `(progn + (eval-and-compile + (defvar bar) + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:functions-1 () + (match-expansion + (use-package foo :functions bar) + `(require 'foo nil nil))) + +(ert-deftest use-package-test/:functions-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :functions bar) + `(progn + (eval-and-compile + (declare-function bar "foo") + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:functions-3 () + (match-expansion + (use-package foo :defer t :functions bar) + `nil)) + +(ert-deftest use-package-test/:functions-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :defer t :functions bar) + `(eval-and-compile + (declare-function bar "foo") + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t)))))))) + +(ert-deftest use-package-test/:functions-5 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :defer t :config (config) :functions bar) + `(progn + (eval-and-compile + (declare-function bar "foo") + (eval-when-compile + (with-demoted-errors + "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (eval-after-load 'foo + '(progn + (config) + t)))))) + +(ert-deftest use-package-test/:defer-1 () + (match-expansion + (use-package foo) + `(require 'foo nil nil))) + +(ert-deftest use-package-test/:defer-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:defer-3 () + (match-expansion + (use-package foo :defer t) + `nil)) + +(ert-deftest use-package-test/:defer-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :defer t) + `(eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t)))))))) + +(ert-deftest use-package-test-normalize/:hook () + (cl-flet ((norm (&rest args) + (apply #'use-package-normalize/:hook + 'foopkg :hook args))) + (should-error (norm nil)) + (should (equal (norm '(bar)) + '((bar . foopkg-mode)))) + (should (equal (norm '((bar . foopkg))) + '((bar . foopkg)))) + (should (equal (norm '((bar . baz))) + '((bar . baz)))) + (should (equal (norm '(((bar baz) . quux))) + '(((bar baz) . quux)))) + (should (equal (norm '(bar baz)) + '(((bar baz) . foopkg-mode)))) + (should (equal (norm '((bar baz) (quux bow))) + '(((bar baz) . foopkg-mode) ((quux bow) . foopkg-mode)))) + (should (equal (norm '((bar . baz) (quux . bow))) + '((bar . baz) (quux . bow)))) + (should (equal (norm '(((bar1 bar2) . baz) ((quux1 quux2) . bow))) + '(((bar1 bar2) . baz) ((quux1 quux2) . bow)))))) + +(ert-deftest use-package-test/:hook-1 () + (let ((byte-compile-current-file t)) + (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 + (unless (featurep 'foo) + (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")) + (add-hook 'hook-hook #'fun) + (bind-keys :package foo ("C-a" . key)))))) + +(ert-deftest use-package-test/:hook-2 () + (match-expansion + (use-package foo + :hook (hook . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-hook 'hook-hook #'fun)))) + +(ert-deftest use-package-test/:hook-3 () + (let ((use-package-hook-name-suffix nil)) + (match-expansion + (use-package foo + :hook (hook . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-hook 'hook #'fun))))) + +(ert-deftest use-package-test/:hook-4 () + (let ((use-package-hook-name-suffix "-special")) + (match-expansion + (use-package foo + :hook (hook . fun)) + `(progn + (unless (fboundp 'fun) + (autoload #'fun "foo" nil t)) + (add-hook 'hook-special #'fun))))) + +(ert-deftest use-package-test/:hook-5 () + (match-expansion + (use-package erefactor + :load-path "foo" + :after elisp-mode + :load t + :hook (emacs-lisp-mode + . (lambda () + (bind-key "\C-c\C-v" erefactor-map emacs-lisp-mode-map)))) + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (eval-after-load 'elisp-mode + '(progn + (require 'erefactor nil nil) + (add-hook + 'emacs-lisp-mode-hook + #'(lambda nil + (bind-key "" 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 ,(pred stringp))) + (eval-after-load 'elisp-mode + '(progn + (unless (fboundp 'function) + (autoload #'function "erefactor" nil t)) + (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 ,(pred stringp))) + (eval-after-load 'elisp-mode + '(progn + (require 'erefactor nil nil) + (add-hook 'emacs-lisp-mode-hook #'(lambda nil (function)))))))) + +(ert-deftest use-package-test-normalize/:custom () + (cl-flet ((norm (&rest args) + (apply #'use-package-normalize/:custom + 'foopkg :custom args))) + (should-error (norm nil)) + (should-error (norm '(bar))) + ;; (should-error (norm '((foo bar baz quux)))) + (should (equal (norm '(foo bar)) '((foo bar)))) + ;; (should-error (norm '(foo bar baz))) + ;; (should (equal (norm '(foo bar "baz")) + ;; '((foo bar baz)))) + )) + + +(ert-deftest use-package-test/:custom-1 () + (match-expansion + (use-package foo :custom (foo bar)) + `(progn + (let + ((custom--inhibit-theme-enable nil)) + (unless (memq 'use-package custom-known-themes) + (deftheme use-package) + (enable-theme 'use-package) + (setq custom-enabled-themes (remq 'use-package custom-enabled-themes))) + (custom-theme-set-variables 'use-package + '(foo bar nil nil "Customized with use-package foo"))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:custom-with-comment1 () + (match-expansion + (use-package foo :custom (foo bar "commented")) + `(progn + (let + ((custom--inhibit-theme-enable nil)) + (unless (memq 'use-package custom-known-themes) + (deftheme use-package) + (enable-theme 'use-package) + (setq custom-enabled-themes (remq 'use-package custom-enabled-themes))) + (custom-theme-set-variables 'use-package + '(foo bar nil nil "commented"))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:custom-face-1 () + (match-expansion + (use-package foo :custom-face (foo ((t (:background "#e4edfc"))))) + `(progn + (apply #'face-spec-set (backquote (foo ((t (:background "#e4edfc")))))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:custom-face-2 () + (match-expansion + (use-package example + :custom-face + (example-1-face ((t (:foreground "LightPink")))) + (example-2-face ((t (:foreground "LightGreen"))))) + `(progn + (apply #'face-spec-set + (backquote (example-1-face ((t (:foreground "LightPink")))))) + (apply #'face-spec-set + (backquote (example-2-face ((t (:foreground "LightGreen")))))) + (require 'example nil nil)))) + +(ert-deftest use-package-test/:custom-face-3 () + (match-expansion + (use-package foo :custom-face (foo ((t (:background "#e4edfc"))) face-defspec-spec)) + `(progn + (apply #'face-spec-set (backquote (foo ((t (:background "#e4edfc"))) face-defspec-spec))) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:init-1 () + (match-expansion + (use-package foo :init (init)) + `(progn + (init) + (require 'foo nil nil)))) + +(ert-deftest use-package-test/:init-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :init (init)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (init) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:catch-1 () + (match-expansion + (use-package foo :catch t) + `(progn + (defvar ,_ + #'(lambda (keyword err) + (let ((msg (format "%s/%s: %s" 'foo keyword + (error-message-string err)))) + (display-warning 'use-package msg :error)))) + (condition-case-unless-debug err + (require 'foo nil nil) + (error + (funcall ,_ :catch err)))))) + +(ert-deftest use-package-test/:catch-2 () + (match-expansion + (use-package foo :catch nil) + `(require 'foo nil nil))) + +(ert-deftest use-package-test/:catch-3 () + (match-expansion + (use-package foo :catch (lambda (keyword error))) + `(progn + (defvar ,_ (lambda (keyword error))) + (condition-case-unless-debug err + (require 'foo nil nil) + (error + (funcall ,_ :catch err)))))) + +(ert-deftest use-package-test/:after-1 () + (match-expansion + (use-package foo :after bar) + `(eval-after-load 'bar + '(require 'foo nil nil)))) + +(ert-deftest use-package-test/:after-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :after bar) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (eval-after-load 'bar + '(require 'foo nil nil)))))) + +(ert-deftest use-package-test/:after-3 () + (match-expansion + (use-package foo :after (bar quux)) + `(eval-after-load 'quux + '(eval-after-load 'bar + '(require 'foo nil nil))))) + +(ert-deftest use-package-test/:after-4 () + (match-expansion + (use-package foo :after (:all bar quux)) + `(eval-after-load 'quux + '(eval-after-load 'bar + '(require 'foo nil nil))))) + +(ert-deftest use-package-test/:after-5 () + (match-expansion + (use-package foo :after (:any bar quux)) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'bar + '(funcall ,_)) + (eval-after-load 'quux + '(funcall ,_))))) + +(ert-deftest use-package-test/:after-6 () + (match-expansion + (use-package foo :after (:all (:any bar quux) bow)) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'bow + '(progn + (eval-after-load 'bar + '(funcall ,_)) + (eval-after-load 'quux + '(funcall ,_))))))) + +(ert-deftest use-package-test/:after-7 () + (match-expansion + (use-package foo :after (:any (:all bar quux) bow)) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'quux + '(eval-after-load 'bar + '(funcall ,_))) + (eval-after-load 'bow + '(funcall ,_))))) + +(ert-deftest use-package-test/:after-8 () + (match-expansion + (use-package foo :after (:all (:any bar quux) (:any bow baz))) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'bow + '(progn + (eval-after-load 'bar + '(funcall ,_)) + (eval-after-load 'quux + '(funcall ,_)))) + (eval-after-load 'baz + '(progn + (eval-after-load 'bar + '(funcall ,_)) + (eval-after-load 'quux + '(funcall ,_))))))) + +(ert-deftest use-package-test/:after-9 () + (match-expansion + (use-package foo :after (:any (:all bar quux) (:all bow baz))) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'quux + '(eval-after-load 'bar + '(funcall ,_))) + (eval-after-load 'baz + '(eval-after-load 'bow + '(funcall ,_)))))) + +(ert-deftest use-package-test/:after-10 () + (match-expansion + (use-package foo :after (:any (:all bar quux) (:any bow baz))) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (require 'foo nil nil))))) + (eval-after-load 'quux + '(eval-after-load 'bar + '(funcall ,_))) + (eval-after-load 'bow + '(funcall ,_)) + (eval-after-load 'baz + '(funcall ,_))))) + +(ert-deftest use-package-test/:demand-1 () + (match-expansion + (use-package foo :demand t) + `(require 'foo nil nil))) + +(ert-deftest use-package-test/:demand-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :demand t) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil))))) + +(ert-deftest use-package-test/:demand-3 () + (match-expansion + (use-package foo :demand t :config (config)) + `(progn + (require 'foo nil nil) + (config) + t))) + +(ert-deftest use-package-test/:demand-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :demand t :config (config)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil) + (config) + t)))) + +(ert-deftest use-package-test/:demand-5 () + ;; #529 - :demand should not override an explicit use of :after + (match-expansion + (use-package foo :demand t :after bar) + `(eval-after-load 'bar + '(require 'foo nil nil)))) + +(ert-deftest use-package-test/:demand-6 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :demand t :after bar) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (eval-after-load 'bar + '(require 'foo nil nil)))))) + +(ert-deftest use-package-test/:demand-7 () + (match-expansion + (use-package counsel + :load-path "foo" + :after ivy + :demand t + :diminish + :bind (("C-*" . counsel-org-agenda-headlines) + ("M-x" . counsel-M-x)) + :commands (counsel-minibuffer-history + counsel-find-library + counsel-unicode-char) + :preface (preface-code) + :init + ;; This is actually wrong, but it's just part of the example. + (define-key minibuffer-local-map (kbd "M-r") + 'counsel-minibuffer-history)) + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (eval-and-compile + (preface-code)) + (eval-after-load 'ivy + '(progn + (define-key minibuffer-local-map (kbd "M-r") + 'counsel-minibuffer-history) + (require 'counsel nil nil) + (if (fboundp 'diminish) + (diminish 'counsel-mode)) + (bind-keys :package counsel + ("C-*" . counsel-org-agenda-headlines) + ("M-x" . counsel-M-x))))))) + +(ert-deftest use-package-test/:config-1 () + (match-expansion + (use-package foo :config (config)) + `(progn + (require 'foo nil nil) + (config) + t))) + +(ert-deftest use-package-test/:config-2 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :config (config)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (require 'foo nil nil) + (config) + t)))) + +(ert-deftest use-package-test/:config-3 () + (match-expansion + (use-package foo :defer t :config (config)) + `(eval-after-load 'foo + '(progn + (config) + t)))) + +(ert-deftest use-package-test/:config-4 () + (let ((byte-compile-current-file t)) + (match-expansion + (use-package foo :defer t :config (config)) + `(progn + (eval-and-compile + (eval-when-compile + (with-demoted-errors "Cannot load foo: %S" nil + (unless (featurep 'foo) + (load "foo" nil t))))) + (eval-after-load 'foo + '(progn + (config) + t)))))) + +(ert-deftest use-package-test-normalize/:diminish () + (should (equal (use-package-normalize-diminish 'foopkg :diminish nil) + '(foopkg-mode))) + (should (equal (use-package-normalize-diminish 'foopkg :diminish 'bar) + '(bar))) + (should (equal (use-package-normalize-diminish 'foopkg :diminish "bar") + '((foopkg-mode . "bar")))) + (should (equal (use-package-normalize-diminish 'foopkg :diminish 'foo-mode) + '(foo-mode))) + (should (equal (use-package-normalize-diminish 'foopkg :diminish '(foo . "bar")) + '((foo . "bar"))))) + +(ert-deftest use-package-test/:diminish-1 () + (match-expansion + (use-package foo :diminish nil) + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'foo-mode))))) + +(ert-deftest use-package-test/:diminish-2 () + (match-expansion + (use-package foo :diminish bar) + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'bar))))) + +(ert-deftest use-package-test/:diminish-3 () + (match-expansion + (use-package foo :diminish "bar") + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'foo-mode "bar"))))) + +(ert-deftest use-package-test/:diminish-4 () + (match-expansion + (use-package foo :diminish (foo . "bar")) + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'foo "bar"))))) + +(ert-deftest use-package-test-normalize/:delight () + (should (equal `((foo-mode nil foo)) + (use-package-normalize/:delight 'foo :delight nil))) + (should (equal `((foo-mode nil foo-mode)) + (use-package-normalize/:delight 'foo-mode :delight nil))) + (should (equal `((bar-mode nil foo)) + (use-package-normalize/:delight 'foo :delight '(bar-mode)))) + (should (equal `((bar-mode nil :major)) + (use-package-normalize/:delight 'foo :delight '((bar-mode nil :major))))) + (should (equal `((foo-mode "abc" foo)) + (use-package-normalize/:delight 'foo :delight '("abc")))) + (should (equal `((foo-mode (:eval 1) foo)) + (use-package-normalize/:delight 'foo :delight '('(:eval 1))))) + (should (equal (use-package-normalize/:delight 'foo :delight '((a-mode) (b-mode " b"))) + `((a-mode nil foo) (b-mode " b" foo)))) + (should-error (use-package-normalize/:delight 'foo :delight '((:eval 1))))) + +(ert-deftest use-package-test/:delight-1 () + (match-expansion + (use-package foo :delight) + `(progn + (require 'foo nil nil) + (if (fboundp 'delight) + (delight '((foo-mode nil foo))))))) + +(ert-deftest use-package-test/:delight-2 () + (should-error + (match-expansion + (use-package foo :delight nil) + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'foo-mode)))))) + +(ert-deftest use-package-test/:delight-3 () + (match-expansion + (use-package foo :delight bar) + `(progn + (require 'foo nil nil) + (if (fboundp 'delight) + (delight '((bar nil foo))))))) + +(ert-deftest use-package-test/:delight-4 () + (match-expansion + (use-package foo :delight "bar") + `(progn + (require 'foo nil nil) + (if (fboundp 'delight) + (delight '((foo-mode "bar" foo))))))) + +(ert-deftest use-package-test/:delight-5 () + (should-error + (match-expansion + (use-package foo :delight (foo . "bar")) + `(progn + (require 'foo nil nil) + (if (fboundp 'diminish) + (diminish 'foo "bar")))))) + +(ert-deftest use-package-test/:delight-6 () + (match-expansion + (use-package foo :delight (foo "bar")) + `(progn + (require 'foo nil nil) + (if (fboundp 'delight) + (delight '((foo "bar" foo))))))) + +(ert-deftest use-package-test/334-1 () + (let (foo1-map foo2-map + bar1-func1 + bar1-func2 + bar2-func1 + bar2-func2 + bar3-func1 + bar3-func2 + bar4-func1 + bar4-func2) + (match-expansion + (bind-keys :map foo1-map + ("Y" . foo1) + :prefix "y" + :prefix-map bar1-prefix-map + ("y" . bar1-func1) + ("f" . bar1-func2) + :prefix "y" + :prefix-map bar2-prefix-map + ("y" . bar2-func1) + ("f" . bar2-func2) + :map foo2-map + ("Y" . foo2) + :prefix "y" + :prefix-map bar3-prefix-map + ("y" . bar3-func1) + ("f" . bar3-func2) + :prefix "y" + :prefix-map bar4-prefix-map + ("y" . bar4-func1) + ("f" . bar4-func2)) + `(progn + (bind-key "Y" #'foo1 foo1-map nil) + (defvar bar1-prefix-map) + (define-prefix-command 'bar1-prefix-map) + (bind-key "y" 'bar1-prefix-map foo1-map nil) + (bind-key "y" #'bar1-func1 bar1-prefix-map nil) + (bind-key "f" #'bar1-func2 bar1-prefix-map nil) + (defvar bar2-prefix-map) + (define-prefix-command 'bar2-prefix-map) + (bind-key "y" 'bar2-prefix-map foo1-map nil) + (bind-key "y" #'bar2-func1 bar2-prefix-map nil) + (bind-key "f" #'bar2-func2 bar2-prefix-map nil) + (bind-key "Y" #'foo2 foo2-map nil) + (defvar bar3-prefix-map) + (define-prefix-command 'bar3-prefix-map) + (bind-key "y" 'bar3-prefix-map foo2-map nil) + (bind-key "y" #'bar3-func1 bar3-prefix-map nil) + (bind-key "f" #'bar3-func2 bar3-prefix-map nil) + (defvar bar4-prefix-map) + (define-prefix-command 'bar4-prefix-map) + (bind-key "y" 'bar4-prefix-map foo2-map nil) + (bind-key "y" #'bar4-func1 bar4-prefix-map nil) + (bind-key "f" #'bar4-func2 bar4-prefix-map nil))))) + +(ert-deftest use-package-test/334-2 () + (let (w3m-lnum-mode-map + w3m-print-current-url + w3m-lnum-print-this-url + w3m-print-this-url) + (match-expansion + (bind-keys :map w3m-lnum-mode-map + :prefix "y" + :prefix-map w3m-y-prefix-map + ("y" . w3m-print-current-url) + ("f" . w3m-lnum-print-this-url) + ("t" . w3m-print-this-url)) + `(progn + (defvar w3m-y-prefix-map) + (define-prefix-command 'w3m-y-prefix-map) + (bind-key "y" 'w3m-y-prefix-map w3m-lnum-mode-map nil) + (bind-key "y" #'w3m-print-current-url w3m-y-prefix-map nil) + (bind-key "f" #'w3m-lnum-print-this-url w3m-y-prefix-map nil) + (bind-key "t" #'w3m-print-this-url w3m-y-prefix-map nil))))) + +(ert-deftest use-package-test/482-1 () + (match-expansion + (use-package simple + :bind-keymap ("C-t " . my/transpose-map) + :bind (:map my/transpose-map + ("w" . transpose-words))) + `(progn + (unless (fboundp 'transpose-words) + (autoload #'transpose-words "simple" nil t)) + (bind-key "C-t " + #'(lambda nil + (interactive) + (use-package-autoload-keymap 'my/transpose-map 'simple nil))) + (bind-keys :package simple :map my/transpose-map + ("w" . transpose-words))))) + +(ert-deftest use-package-test/482-2 () + (match-expansion + (use-package simple + :bind (:prefix-map my/transpose-map + :prefix "C-t" + ("w" . transpose-words))) + `(progn + (unless (fboundp 'transpose-words) + (autoload #'transpose-words "simple" nil t)) + (bind-keys :package simple + :prefix-map my/transpose-map + :prefix "C-t" + ("w" . transpose-words))))) + +(ert-deftest use-package-test/482-3 () + (match-expansion + (bind-keys :package simple + :prefix-map my/transpose-map + :prefix "C-t" + ("w" . transpose-words)) + `(progn + (defvar my/transpose-map) + (define-prefix-command 'my/transpose-map) + (bind-key "C-t" 'my/transpose-map nil nil) + (bind-key "w" #'transpose-words my/transpose-map nil)))) + +(ert-deftest use-package-test/538 () + (match-expansion + (use-package mu4e + :commands (mu4e) + :bind (("<f9>" . mu4e)) + :init + :config + (config)) + `(progn + (unless (fboundp 'mu4e) + (autoload #'mu4e "mu4e" nil t)) + (eval-after-load 'mu4e + '(progn (config) t)) + (bind-keys :package mu4e ("<f9>" . mu4e))))) + +(ert-deftest use-package-test/543 () + (match-expansion + (use-package hydra + :ensure) + `(progn + (use-package-ensure-elpa 'hydra '(t) 'nil) + (require 'hydra nil nil)))) + +(ert-deftest use-package-test/545 () + (match-expansion + (use-package spacemacs-theme + :ensure t + :init ; or :config + (load-theme 'spacemacs-dark t) + ) + `(progn + (use-package-ensure-elpa 'spacemacs-theme '(t) 'nil) + (load-theme 'spacemacs-dark t) + (require 'spacemacs-theme nil nil)) + )) + +(ert-deftest use-package-test/550 () + (match-expansion + (use-package company-try-hard + :ensure t + :bind + ("C-c M-/" . company-try-hard) + (:map company-active-map + ("C-c M-/" . company-try-hard))) + `(progn + (use-package-ensure-elpa 'company-try-hard + '(t) + 'nil) + (unless + (fboundp 'company-try-hard) + (autoload #'company-try-hard "company-try-hard" nil t)) + (bind-keys :package company-try-hard + ("C-c M-/" . company-try-hard) + :map company-active-map + ("C-c M-/" . company-try-hard))))) + +(ert-deftest use-package-test/558 () + (match-expansion + (bind-keys* :package org-ref + ("C-c C-r" . org-ref-helm-insert-cite-link)) + `(bind-key "C-c C-r" #'org-ref-helm-insert-cite-link override-global-map nil))) + +(ert-deftest use-package-test/560 () + (cl-letf (((symbol-function #'executable-find) #'ignore)) + (let (notmuch-command) + (match-expansion + (use-package notmuch + :preface (setq-default notmuch-command (executable-find "notmuch")) + :if notmuch-command + :requires foo + :load-path "foo" + :defines var) + `(progn + (eval-and-compile + (add-to-list 'load-path ,(pred stringp))) + (when (featurep 'foo) + (eval-and-compile + (setq-default notmuch-command + (executable-find "notmuch"))) + (when (symbol-value 'notmuch-command) + (require 'notmuch nil nil)))))))) + +(ert-deftest use-package-test/572-1 () + (let ((use-package-always-defer t)) + (match-expansion + (use-package auth-password-store + :after auth-source + :init + (setq auth-sources '(password-store))) + `(eval-after-load 'auth-source + '(setq auth-sources '(password-store)))))) + +(ert-deftest use-package-test/572-2 () + (let ((use-package-always-defer t)) + (match-expansion + (use-package ivy-hydra :after ivy) + `nil))) + +(ert-deftest use-package-test/572-3 () + (let ((use-package-always-defer t) + (use-package-defaults + (let ((defaults (copy-alist use-package-defaults))) + (setcdr (assq :defer defaults) + '(use-package-always-defer + (lambda (name args) + (and use-package-always-defer + (not (plist-member args :after)) + (not (plist-member args :defer)) + (not (plist-member args :demand)))))) + defaults))) + (match-expansion + (use-package ivy-hydra :after ivy) + `(eval-after-load 'ivy + '(require 'ivy-hydra nil nil))))) + +(ert-deftest use-package-test/575-1 () + (match-expansion + (use-package helm + :defer t + :after (:any ido dired) + :config + (message "test. helm start")) + `(progn + (defvar ,_ nil) + (defvar ,_ nil) + (defvar ,_ + #'(lambda nil + (if ,_ ,_ + (setq ,_ t ,_ + (eval-after-load 'helm + '(progn + (message "test. helm start") + t)))))) + (eval-after-load 'ido + '(funcall ,_)) + (eval-after-load 'dired + '(funcall ,_))))) + +(ert-deftest use-package-test/575-2 () + (match-expansion + (use-package helm + :defer t + :bind ("C-c d" . helm-mini) + :config + (message "test. helm start")) + `(progn + (unless (fboundp 'helm-mini) + (autoload #'helm-mini "helm" nil t)) + (eval-after-load 'helm + '(progn + (message "test. helm start") + t)) + (bind-keys :package helm ("C-c d" . helm-mini))))) + +(ert-deftest use-package-test/585 () + (match-expansion + (use-package bug + :bind (:map bug-map ("C-a" . alpha)) + :bind (("C-b" . beta))) + `(progn + (unless (fboundp 'alpha) + (autoload #'alpha "bug" nil t)) + (unless (fboundp 'beta) + (autoload #'beta "bug" nil t)) + (bind-keys :package bug :map bug-map + ("C-a" . alpha)) + (bind-keys :package bug + ("C-b" . beta))))) + +(ert-deftest use-package-test/589 () + (let ((use-package-verbose t) + (use-package-expand-minimally t) + debug-on-error + warnings) + (cl-letf (((symbol-function #'display-warning) + (lambda (_ msg _) (push msg warnings)))) + (progn + (macroexpand-1 + '(use-package ediff :defer t (setq my-var t))) + (should (= (and (> (length warnings) 0) + (string-match ":defer wants exactly one argument" + (car warnings))) 44)))))) + +(ert-deftest use-package-test/591 () + (let ((use-package-defaults + (cons '(:if (lambda (name _) `(locate-library ,name)) t) + use-package-defaults))) + (match-expansion + (use-package nonexistent + :hook lisp-mode) + `(when (locate-library nonexistent) + (unless (fboundp 'nonexistent-mode) + (autoload #'nonexistent-mode "nonexistent" nil t)) + (add-hook 'lisp-mode-hook #'nonexistent-mode))))) + +(ert-deftest bind-key/:prefix-map () + (match-expansion + (bind-keys :prefix "<f1>" + :prefix-map my/map) + `(progn + (defvar my/map) + (define-prefix-command 'my/map) + (bind-key "<f1>" 'my/map nil nil)))) + + +(ert-deftest bind-key/845 () + (defvar test-map (make-keymap)) + (bind-key "<f1>" 'ignore 'test-map) + (should (eq (lookup-key test-map (kbd "<f1>")) 'ignore)) + (let ((binding (cl-find "<f1>" personal-keybindings :test 'string= :key 'caar))) + (message "test-map %s" test-map) + (message "binding %s" binding) + (should (eq (cdar binding) 'test-map)) + (should (eq (nth 1 binding) 'ignore)) + (should (eq (nth 2 binding) nil)))) + +;; Local Variables: +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: + +;;; use-package-tests.el ends here |